cmtk-3.3.1/000077500000000000000000000000001276303427400124645ustar00rootroot00000000000000cmtk-3.3.1/CHANGELOG000066400000000000000000002534041276303427400137060ustar00rootroot00000000000000Release CMTK-3.3.1 (2016-01-23): (r5419) Fixed: bug in PolynomialXform class, which lead to out-of-range array access in the parameter vector when trying to get the linear matrix of a zero-degree transform (which doesn't exist). (r5418) Fixed: some compiler warnings revealed by SolarisStudio compiler. Release CMTK-3.3.0 (2016-01-16): (r5404) Fixed: removed 32bit (31, really) limit on number of pixels in an image, at least for most algorithms. Volume reconstruction will continue to be limited due to 32bit integers being used as vector indexes in AlgLib numerical code. (r5394) Changed: limit number of images in groupwise registration to 255. This reduces memory consumption significantly. (r5392) Fixed: compile problems with VisualStudio Express 2015. (r5391) Fixed: recent system DCMTK versions on Fedora (perhaps elsewhere too) have lost constants for ACR-NEMA legacy field tags. Re-added those for compatibility. Release CMTK-3.2.3 (2015-02-01): (r5381) Fixed: link error on Win64 due to missing implementation of a conversion method for 64bit integers. (r5380) Fixed: MXML 2.8 is broken; when this version is detected, fall back to bundled library version. (r5378) Fixed: bashisms fixed by merging downstream Debian patch; see https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=772217. Patch provided by Michael Gilbert . (r5377) Fixed: on Windows, recursive directory creation failed if the given path begin with a drive letter. Thanks to Florian Schulze, fschulze@vrvis.at, for finding and fixing the problem. (r5375) Fixed: when DICOM files have fractional rescale slope, read image as floating point, not integer. (r5375) Fixed: "dcm2image" tool did not respect command line options to ignore acquisition number tags and disable orientation checks. (r5374) Fixed: bashisms in two scripts. Release CMTK-3.2.2 (2014-08-01): (r5368) Fixed: XformList::ApplyInPlace terminated concatenation when an inverted B-spline FFD transformation was encountered. (r5365) Fixed: binary VTK files were previously written (illegally) as double-precision data. (r5353) Fixed: wrong binary paths in munger.pl script Release CMTK-3.2.1 (2014-06-19): (r5355) Added: ADNI phantom detection and quantification (exposed via the "detect_adni_phantom" tool) now computes maximum dimming, i.e., minimum intensity of a 10mm sphere relative to the 90th percentile of intensities over all such spheres. (r5353) Added: global scale factor support in "vol2csv" tool (r5347) Fixed: missing coordinate information in Siemens mosaic files must be treated as zero. Release CMTK-3.2.0 (2014-05-01): (r5344) Added: manually force CNR centroid fallback in "detect_adni_phantom" tool. (r5340) Packaging: RPM distribution now split into "core" (binaries) and "devel" (libraries and headers) packages. (r5340) Packaging: TGZ packages no longer include "CMTK-M.m.p/" release name prefix for stored files. (r5337) Added: CMTK_BINARY_DIR and CMTK_LIRBARY_DIR as used in a number of shell scripts included with CMTK can now be overridden by setting them before executing one such script. This should allow CMTK to work when it was installed/unpacked into a path other than the installation path defined during build. (r5333) Added: "epiunwarp" now has optional sixth positional parameter to export reverse deformation field to a given path. (r5327) Added: time units are now written to XML sidecar files for TR, TE, TI, and dwell time. (r5324) Added: sign of the phase encode direction is now extracted from Siemens EPI files and written to XML sidecar files. This information apparently is NOT available in GE files at all. (r5316) Changed: "registration" and "registrationx" tools no longer support the "603" special degree-of-freedom notation for no-scale affine transformations. This is now denoted, more logically, as "3303". Newly supported special notations have been added: "3003" and "3033" for affine with no rotation or scale, and affine with no rotation, respectively. (r5309) Added: symmetric affine registration now also supports in-plane transformations. (r5307) Improved: "imagemath" tool's "--average" operator no longer returns missing values for a pixel where a single one of the input images has missing (padded) data. (r5303) Added: new "geomatch" tool can compare grid dimensions, pixel sizes, and/or transformation matrices of two or more images to ensure that they match. (r5297) Added: "dof2mat" tool can now print only 3x3 top-left submatrix. (r5293) Added/Changed: "mat2dof" tool no longer has unintuitive single-character options for xform modifiers. Instead, it now supports writing to a single, simple transformation file. (r5281) Added: "sequence" tool can now optionally create histogram files in CSV format. (r5270) Changed: where appropriate, image processing operators now set the data class of their outputs as "LABEL". This includes a variety of binarization/thresholding techniques, label combination algorithms, and segmentations. (r5260) Added: distance map-based erosion of multi-label fields, both in the library and exposed via the new "--erode-distance-multilabel" operation of "convertx" (r5250) Fixed: compilation errors with Visual C++ 2013. (r5243) Added: exact isotropic resampling in UniformVolume class, exposed at command line via new "--resample-exact" operation of the "convertx" tool. (r5240) Added: interpolator classes now issue a warning message to the console when an unsuitable interpolator (i.e., cubic, spline, linear, sinc) is used on data flagged as "labels." (r5238) Changed: "reformatx" tool now propagates floating image data class (label or grey) to output, as represented by the "intent_code" header field when written in NIfTI format. (r5238) Added: new operations in "convertx" tool, "--labels" and "--grey", set and reset the "intent_code" field in NIfTI images to NIFTI_INTENT_LABEL and 0, respectively. (r5230) Added: the "xform2dfield" tool can now write deformation fields for use by FSL's "applywarp" tool. (r5230) Added: XformIO can now write deformation fields in NIfTI format, albeit very inflexibly so. (r5222) Improved: region-based DataGrid filters now use OpenMP acceleration where available (but they no longer report progress or allow user cancellation). (r5220) Added: freq_dim, phase_dim, slice_dim as well as slice times and acquisition order fields are now properly set in NIFTI headers for files created by "dcm2image," so long as these data were available in the input DICOM files. (r5214) Added: slice times (relative to first slice in each volume) are now extracted from Siemens DICOM files and written to XML sidecar files by "dcm2image" tool. Release CMTK-3.1.0 (2014-02-21): (r5203) Changed: "dcm2image" tool now returns exit code 3 if one or more image stacks could not be built due to non-uniform slice spacing that exceeds the given "--tolerance" threshold. (r5196) Added: "vol2csv" tool now accepts user-define column labels for the optional density-weighted volumes. (r5195) Added: DICOM import now supports Philips tags for diffusion information (b Value and gradient direction vectors). (r5187) Changed: "fview" GUI now expects only a transformation sequence on the command line. To override fixed and/or moving image (or to provide them, if they are not stored within the transformation), use the new optional arguments, "--fixed" and "--moving". (r5185) Added: for convenience, histogram- and mean/sdev-based intensity matching are now also available in "convertx", in addition to "imagemath". (r5183) Fixed: use of MacPorts should be optional, even if its directory tree exists. (r5183) Fixed: CUDA should be optional and build config file clearly labelled if it is used. (r5166) Added: polynomial transformation model and class to fit such transformations to landmarks. (r5157) Changed: "reformatx" no longer corrects Jacobian maps for global scale by default. This seems like a more natural way to do this. (r5154) Added/changed: "unwarp_image_phantom" tool now supports (and defaults to) polynomial transformations, rather then B-spline FFD. Release CMTK-3.0.0 (2014-01-09): (r5122) Fixed: wrong offset computation broke fitting of B-spline transformation to transformation series. (r5121) Fixed: over 200 code defects identified by static analysis (Coverity Scan) have been fixed over the past 200 or so commits. (r5110) Fixed: fitting a B-spline transformation to an XformList that contained a nonrigid transformation, specifically a numerically inverted one, did not properly exclude locations where the inversion failed or the input location was outside the B-spline grid's support region. (r5097) Added: "streamxform" tool can now apply only affine components of all provided transformations, via the "--affine-only" option (contribution from Greg Jefferis). (r5087) Changed: "dwi_find_bad_slices" now more appropriately named "dwi_mask_bad_slices". Tool can now mask detected bad slices, convert image pixel type, and write masked images. (r5012) Added: "dcm2image" tool (and cmtkIO library) now determine "dwell time" from DICOMs from both GE and Siemens scanners. This is necessary for applying fieldmap-based distortion corrections. (r5008) Added: new "resample" operator for "convertx" tool to create near-isotropic resampled images with the same FOV as the original. (r4983) Added: new "stream_pixels" tool should make it easier to stream image data through pipeline-based tools such as Camino's. (r4978) Changed: "dcm2image" tool no longer embeds study information into Nifti header; this is for subject privacy when data are going to be shared. (r4977) Improved: "detect_adni_phantom" tool has new options, "--refine-xform", "--refine-outliers", and "--exclude-outliers". Detection has also been made more robust by ordering 10mm spheres by increasing distance from phantom center. (r4892) Changed: by default, "mk_analyze_hdr" and "mk_nifti_hdr" now create a header with "Byte" as the image pixel data type, and "axial" as the orientation (for "mk_analyze_hdr" only). (r4855) Added: "detect_adni_phantom" tool (and library classes) now state in the output XML file whether certain fallback conditions were triggered. These usually hint at problems with the imaging phantom, e.g., missing spheres. (r4852) Added: new tool, "interleaved_bad_slices" identifies volumes (and slices) in a time series such as rs-fMRI where the entire volume is bad due to motion during the interleaved acquisition. (r4819) Added: "describe" tool can now describe transformation files as well as images. (r4797) Changed: "dcm2image" tool now enforces uniform volume spacing up to given tolerance level. Volumes with non-uniformity exceeding the threshold, no volume will be created, thus avoiding creation of invalid data. (r4792) Added: multi-iteration and residual-controlled fitting of B-spline FFD to phantom landmarks. (r4787) Added: new "vol2csv" tool writes regional volumes of a label image to a CSV files, optionally using a text file with label descriptions, a (Jacobian) pixel volume scale map, and one or more "density" maps, such as tissue probabilities etc. (r4774) Removed: the "contributed" N-CANDA pipeline scripts have been moved out of the CMTK code tree and into the N-CANDA Data Core repository, hosted at http://nitrc.org/svn/ncanda-datacore (r4770) Improved: speed-up of FFT-based matched filter sphere detection. Benefits primarily ADNI phantom detection (faster by up to about 25-30 percent) (r4760) Changed: "CMTK_BUILD_WRAPPER" is now turned ON by default (i.e., wrapper script "cmtk" is built, and all binaries are installed into a private directory) (r4758) Added: "dcm2image" tool has new option, "--no-progress" to disable progress reporting (for batch operation). Release CMTK-2.3.0 (2013-05-16): (r4751) Added: ADNI phantom detection tool now also functions when the 60mm SNR sphere is displaced. (r4732) Added: ADNI phantom detection tool can now robustly bootstrap gross orientation from CNR spheres if one or both of the 15mm phantom spheres are missing. (r4719) Added: dilation and erosion in anisotropic images by distance, not by pixels. (r4708) Added: ADNI phantom detection is now more robust in the presence of missing, cropped, or low-contrast marker spheres. (r4703) Added/Changed: dcm2image now only exports potentially protected, identifying meta-data (e.g., UIDs, paths, dates) into XML files if specifically instructed to do so. (r4699) Added: ADNI phantom detection tool now recognizes when marker spheres are (partially) outside image field of view. Optional "tolerant" mode can override program termination. (r4690) Added: ADNI phantom description file (XML) now contains estimated scale factors and nonlinearity. (r4684) Added: pixel search tool, "pxsearch." For now, this simply searches for the pixel with the maximum value in a local neighborhood. (r4680) Fixed: use of stat() rather than stat64() broke access to files on large file systems. (r4674) Fixed: FFTW dependency was not really options due to incorrect configuration conditions (Issue #6841). (r4670) Fixed: an error in WarpXform::ReplaceInitialAffine broke convert_warp tool, specifically the computation of a fractional transformation. New test cases have also been added to cover this. (r4662) Fixed: check whether FFTW libraries were found rather than relying on CMTK_USE_FFTW option being set. (r4654) Added: N-CANDA script for creating GRE field maps using FSL/prelude (r4652) Fixed: OpenMP problems using VisualStudio (r4648) Fixed: configuration problem with CMake 2.8.10 due to changed path to CMakeSystem.cmake file. (r4622) Added: N-CANDA structural pipeline in "contrib/ncanda/" (r4595) Added: more channels of the SRI24 atlas added to the CMTK repository. The atlas is also now installed and packaged with the toolkit. (r4586) Added: Otsu thresholding now integrated into "mrbias" tool. Release CMTK-2.2.6 (2012-10-29): (r4573) Fixed: DICOM stacker segfaulted (r4571) Updated: NrrdIO has been updated to current upstream code base from https://teem.svn.sourceforge.net/svnroot/teem/NrrdIO/trunk (r4562) Fixed: center of mass was not computed properly if Inf or NaN data were present in the image. Release CMTK-2.2.5 (2012-10-22): (r4551) Added: "dcm2image" tool can now substitute components of the input image paths into generated output images - "%0" represents the input file name (for the first image in a multi-file stack), "%1" through "%9" the hierarchical directories in order of decreasing depth, i.e., from deepest to highest. (r4529) Added: image type (magnitude, real, phase) can now be extracted from Siemens DICOM files. (r4527) Fixed: incorrect image origin was previously computed for Siemens mosaic DICOM images (Issue #6754). (r4522) Added: more DICOM information is now put into the dcm2image-generated XML files, such as MR imaging frequency; study, series, and frame-of-reference UIDs; device serial number; and intensity rescale intercept and slope values. Also gave all tags with values straight from the DICOM tags a "dicom:" XML namespace prefix. (r4513) Added: for GE DICOM images, the raw data type (magnitude, phase, real, or imaginary) is now written into the output XML files by the "dcm2image" tool. (r4510) Refactored: more logical API for symmetric matrix eigensystem computation. Symmetric matrices are now actually enforced, whereas previously it was silently assumed all matrices handed to the eigensystem functions were symmetric. (r4504) Added: "dcm2image" tool can now write single-slice images also (these were skipped previously). Use the new "--write-single-slices" command line switch to activate this behaviour. Release CMTK-2.2.4 (2012-08-24): (r4498) Fixed: some PACS-processed Siemens mosaic DICOM files have lost some DCM tags; missing information must be parsed from CSA headers instead. (r4485) Fixed: echo planar image unwarping created NaN deformations if any of the image columns was all zeroes, because no center of mass could be computed. For these cases, deformation is now forced to zero initially. Release CMTK-2.2.3 (2012-08-10): (r4475) Added: new "contrib" directory for third-party contributions; initially Greg Jefferis' "munger" Perl script. (r4470) Added: new "groupwise_reformat" script simplifies reformatting all images in a set aligned with one of the groupwise registration tools. (r4458) Fixed: older versions of FFTW than 3.3.2 now supported again. (r4456) Fixed: when reading NRRD files with missing "spaceOrigin" entry, CMTK now substitutes a zero-vector for the origin, rather than keeping NrrdIO's not-a-number values. (r4454) Fixed: the index-to-physical matrix of a UniformVolume was not properly modified when downsampling with pixel selection (rather than averaging). (r4449) Added: when installed system mxml library is found, its version is determined, and the bundled library is automatically built if the system library is outdated (pre-2.7). (r4447) Added: new tool, "dwi_find_bad_slices," to detect bad slices in sets of diffusion-weighted images. (r4447) Changed: image-to-physical matrix and slice spacing now work properly (as in, without generating Inf/NaN values) when splitting volume into single slices and re-stacking it. This has caused some baselines to change, because the volume field of view sizes have seen subtle changes as a result. Release CMTK-2.2.2 (2012-06-18): (r4438) Added: new tools "fit_spline_xform" and "fit_affine_xform" to fit a single B-spline or affine transformation, respectively, to a list of concentated, optionally inverted, transformations. (r4420) Fixed: "reorient" tool can now force reorientation when original image orientation is overridden by command line argument. (r4400) Obsoleted: the "mip" tool as well as library code exclusively used by it (e.g., support for PGM file format; volume/ray accumulators) have been removed. (r4385) Changed: the "fit_spline_xform" tool has been renamed to "fit_spline_dfield" to make room for a new tool which will fit splines to arbitrary transformation sequences. (r4369) Added: new "streamxform" tool to apply sequences of transformations to point coordinates read from standard input. The old tool, "gregxform", has been deprecated and may be removed in a future release of CMTK. (r4351) Changed: all nonrigid transformation files, "registration," are now written gzip-compressed. (Linear transformation files are generally much smaller and continue to be written as plain uncompressed files.) (r4347) Added: "dcm2image" tool has two new options, "--filter" and "--exclude", to include or exclude DICOM files selectively based on matching a provided text to values of user-specified DICOM tags. (r4326) Added: ADNI phantom detector now also computes SNR and four CNR estimates. These are all also written to the XML phantom description file. (r4317) Fixed: older versions of DCMTK do not provide lossless-JPEG header file , but it isn't needed anyway. Release CMTK-2.2.1 (2012-05-07): (r4313) Fixed: previous release accidentally installed and packaged the entire source tree, rather than just the Licenses/ directory. This has been fixed. Release CMTK-2.2.0 (2012-05-04): (r4280) Updated: bundled MiniXML (mxml) library is now version 2.7 (r4256) Changed: the "search" and "replace" parts of the CMTK_MOUNTPOINTS variable have been switched. The new order is far more intuitive. (r4240) Fixed: one of the functions computing Jacobian matrices for B-spline FFD transformations was broken, leading to slightly skewed inverse transformations in various tools. Due to direct optimization of the inversion error, the actual inversion results were still accurate within the given tolerance. (r4195) Added: tool for unwarping images using B-spline free-form deformation computed from a reference scan of the ADNI phantom. (r4180) Added: detection of the landmarks in the Magphan EMR051 structural imaging phantom (a.k.a. "ADNI Phantom"). Both as a library class and as a command line tool ("detect_adni_phantom"). (r4158) Added: Otsu single-threshold binarization in library as well as "convertx" tool. (r4131) Added: sphere detection filter based on FFT-based fast bipolar filter kernel convolution. Requires CMTK to be configured with FFTW support. (r4096) Added: new tool, "fit_affine_xform_landmarks" to fit affine transformations to sets of landmark pairs. Also added supporting classes for fitting and landmark I/O. (r4090) Added: two new tools, "fibxform" and "fib2image", for applying transformation sequences to UNC Fber Tracking outputs and for marking fibers in discrete images. (r4074) Fixed: "dcm2image" no longer accepts printf-style substitution code "%d" for image index. This was never safe, as format string is supplied by user (Issue #6203). (r4068) Added: CMTK now comes with a built-in model of the ADNI structural imaging phantom (a.k.a. Magphan EMR051). The new tool "mk_adni_phantom" can be used to create a digital image of the phantom for testing and potentially registration purposes. (r4052) Added: "epiunwarp" tool can now initialize deformation by matching centers of mass for each row. This seems to greatly improve unwarping results. (r4050) Changed: "dcm2image" now defaults to "sort-by-instance" file ordering; this seems more generally useful than sorting by file names. Also changed names of the command line options. (r4048) Fixed: system DCMTK (from MacPorts) can now be properly configured on MacOS. (r4041) Fixed: get correct image origin for Siemens mosaic DICOM files from CSA header. (r4036) Added: the "dcm2image" tool now ooptionally creates an XML sidecar file for each stacked image, which contains imaging parameters including b-values and diffusion-weighting vectors for DW-MRI. (r4024) Obsolete: removed unused 2d/2d registration classes. (r4016) Refactored: finally merged old-style SMP and non-SMP elastic voxel matching functional classes. (r4004) Fixed: full OpenMP support now available on MacOS. (r3984) Changed: "dcm2image" tool no longer accepts "--ge-extensions" command line option. Instead, all supported vendor-specific extensions are now automatically activated based on the vendor tag in the actual input files. (r3981) Fixed: numerous Visual C++ 2010 compiler warnings, including potentially serious use of "this" pointer in member initialization. (r3969) Added: cmtk::RegressionTracker class for checksum-based tracking down of regression problems. (r3964) Fixed: cmtk::ImagePairSimilarityJointHistogram constructor was using "this" pointer before properly constructing object (Visual C++ warning). Release CMTK-2.1.3 (2012-03-05): (r3953) Refactored: the Shape-Based Averaging and Interpolation code was moved from the "sbai.cxx" tool source code to a new library class, "cmtk::LabelCombinationShapeBasedAveragingInterpolation" (r3941) Added: the "sba" tool for Shape-Based Averaging (and the underlying library class) can now optionally detect and exclude local outlier pixels based on distribution of distances over all inputs at each pixel and for each label. (r3928) Refactored: the Shape-Based Averaging code was move from the "sba.cxx" tool source code to a new library class, "cmtk::LabelCombinationShapeBasedAveraging" (r3922) Obsoleted: the "average_edt" tool has been replaced by two new tools, "sba" and "sbai", for Shape-Based Averaging (and Interpolation). The "sba" tool implements the "--interpolate-image" mode of "average_edt", whereas "sbai" implements the "--interpolate-distance" mode. "Windowed" averaging of intensity images was not really useful and is no longer supported. (r3919) Changed: "average_edt" tool now represents labels as short integers, not bytes, so it can now support label values up to 65535. Release CMTK-2.1.2 (2012-02-22): (r3902) Fixed: compilation errors using latest zlib (1.2.6); also fixed configuration problem on Mac with system zlib and MacPorts zlib being incompatible. (r3880) Added: "fit_affine_xform" can fit an affine transformation to a nonrigid transformation, either a deformation field or a B-spline free-form deformation. The "fit_warp_xform" tool can use this functionality now to estimate the global affine component of a deformation before fitting a spline to the residual. (r3873) Added: "dof2mat" tool to convert affine transformation in degree-of-freedom representation to 4x4 transformation matrix. (r3847) Added: "lvote" local voting tool can now normalize local, patch-based similarity scores with global image-to-atlas correlations. (r3837) Added: "fit_spline_xform" tool to fit B-spline free-form deformation to pixel-wise deformation field . (r3821) Added: "groupwise_warp" tool can now protect regions from deformation if an "exclusion" mask is provided. (r3814) Added: label combination by locally-weighted voting and local shape-based averaging (tools "lvote" and "lsba") has been moved out of the "Unstable" library and added to the default CMTK build set. (r3805) Added: script for creating all officially distributed MacOS builds is now included as core/scripts/PackageMacOS (r3799) Fixed: prevent empty region traversal in cmtk::RegionIndexIterator (r3751) Fixed: "groupwise_affine" was not actually using template image specified on the command line. (r3730) Fixed: histogram-based intensity matching should preserve intensity range. (r3728) Added: "groupwise_init" tool can now center aligned image ensemble inside template field of view ("--center-template" command line switch). (r3722) Added: "registrationx" tool can now compute transformations restricted to in-plane components for a selected plane (xy, xz, or yz). This is useful for eddy current distortion correction in EPI unwarping, (r3719) Added: new "correct_dwi_distortion" script applies distortion correction to an entire series of diffusion-weighted images. Release CMTK-2.1.1 (2012-01-10): (r3711) Added: new "epiunwarp" tool implements an algorithm by Holland et al. (NeuroImage 2010) for unwarping echo-planar MR images (e.g., DWI) using two images acquired with reversed phase encoding direction. (r3698) Changed: by default, "dcm2image" now embeds concatenated DICOM tags StudyID and StudyDate into "description" fields of Analyze, NIFTI, and Nrrd output images. (r3628) Added: "similarity" tool now computes absolute and relative image differences; this feature is used by the applications test driver script to perform tolerant comparisons. (r3608) Removed: support for MPI distributed-memory parallelism has been retired. This was rarely used, poorly tested, and due to ever larger SMP systems no longer very critical. (r3604) Refactored: unified DICOM reader utility class. (r3591) Added: simple tissue class segmentation tool based on EM optimization of a Gaussian mixture model. Release CMTK-2.1.0 (2011-11-21): (r3570) Fixed: incorrect output image orientation when providing target image geometry for "reformatx", "volume_injection", or "volume_reconstruction" on the command line. (r3547) Added: as a configuration option ("CMTK_BUILD_WRAPPER"), a wrapper script "cmtk" can now be built for consistent access to CMTK's tools while avoiding name collisions with other tools installed on the same system. When this option is selection, all actual binaries are installed in the bin/ subdirectory of the configured *library* install directory. Only the wrapper script itself is installed in the configured binary install directory. (r3542) Added: "fview" tool can now automatically deduce fixed and moving image paths from the applied sequence of transformations, if these paths were stored within the transformation file. (r3529) Added: CMTK can now read Siemens MR DICOM images in mosaic format. (r3522) Fixed: TypedArray data arrays are now de-allocated with proper deallocator (free() or new[]()), depending on how they were allocated (Issue #6213). (r3505) Added: build configuration can now switch between building bundled DCMTK DICOM library and using installed system libraries (Issue #6182; see also Issue #6184). (r3484) Added: cmtk::CommandLine class can now output program descriptions in 'nroff' markup sutibale for creating man pages. In addition, the build system now creates these man pages, if "BUILD_MANPAGES" option is set. The man pages will also be installed and packaged. This was added in response to a request from Yaroslav Halchenko. (r3471) Added: multi-class STAPLE algorithm can now be limited to "disputed" pixels to improve results (via the "--mstaple-disputed" operation of "imagemath" tool). (r3442) Added: build configuration can now switch between building bundled MXML library and using installed system library. (r3438) Fixed: proactively fixed a problem reading and writing very large gzip-compressed files. Thanks to Yaroslav Halchenko for pointing this out. See http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=587912 (r3420) Added: when registration tools (warp, warpx, registration, registrationx) are interrupted, suffix "-partial" is added to any results files that may be written. This serves to prevent accidental use of incomplete results. (r3415) Added: the "dcm2image" tool can now embed selected meta information (such as Patient Name) as "image description" in Analyze, Nifti, and NRRD output files. This information is preserved by other CMTK tools (e.g., "convertx") and is now also displayed by the "describe" tool. (r3408) Refactored: global static cmtk::StackBacktrace object is now initialized in each "main.cxx" file, or in "cmtkSafeMain" wrapper, rather than forcing CMake to re-compile initializer code into separate .o file for every binary. (r3405) Added: computation of fixed control points is now parallel in "warp" and "warpx" tools if OpenMP support is enabled. (r3400) Fixed: the thread-related environment variables (mostly CMTK_NUM_THREADS) are now checked by tyhe tool-independent "cmtkSafeMain" wrapper, which guarantees that they will be observed by every CMTK command line tool. This should avoid over-parallelization with OpenMP. (r3385) Added: "mrbias" and "mrbias_cuda" tools now have an option for automatic foreground threshold selection based on estimation of image noise. For that purpose, the tools now also support defining a padding value for the input image (typically zero). Release CMTK-2.0.0 (2011-08-15): (r3360) Added: Hausdorff distance for binary shapes (r3344) Added: fast (linear time) region variance filter based on fast region mean filter. (r3337) Added: fast (linear-time) region mean filter (r3312) Fixed: crash in non-SMP "warp" B-spline registration. (r3305) Fixed: volume_injection tool selected improper padding value due to problem in the type traits implemented by the cmtkBase library. (r3303) Fixed: when moving image had float/double data type, the "registration" tool accidentally used zero as padding value and, as a result, ignored image background. (r3300) Fixed: broken array indexing made "mcwarp" tool crash when intensity correction was activated. Also produced incorrect results. (r3288) Fixed: external projects can now find and use CMTK properly. The "validation" tree has been moved out of "core/" and now uses CMTK's core like any other external project. (r3277) Changed/fixed: padding flag and value are now propagated to the new array when converting the scalar data type of an existing array. Release CMTK-1.7.1 (2011-07-14): (r3270) Added: new "probe" tool implements "volume probe" functionality formerly provided by "describe" tool, with several extensions such as flexible coordinate specification and interpolation modes. (r3265) Fixed: potential infinite loop in "imagemath" tool when images of different size were on the stack for a multi-image operation. (r3263) Fixed: crash in SMP nonrigid registration due to incorrect use of number of tasks submitted to thread pool. (r3256) Added: missing "histogram pruning" operator in "convertx" tool. (r3198) Added: symmetric optimization of forward and inverse affine transformation in "registrationx" tool. (r3190) Fixed: when reformatting a moving image, the reformatted image now inherits the input image's padding flag and value. This addresses an issue with reformatted floating-point images breaking Slicer and other tools do to "Inf" used for padding by default. The registration tools can now be run with the "--pad-flt" flag to set input image padding, which then propagates into the reformatted output image. (r3187) Added: new "destripe" tool removes stripe artifacts from accross-slice intensity variations. (r3176) Added: "convertx" tool now supports downsampling by pixel selection in addition to downsampling by pixel averaging. Release CMTK-1.7.0 (2011-04-18): (r3158) Fixed: allow OpenMP to be disabled by initial CMake cache file, which is necessary on MacOS to allow automated release building. (r3151) Added: two-level command line help; "--help" now prints only basic options, whereas "--help-all" prints all options including those marked as "advanced." this should make it easier for new users to get a quick overview of essential functions. (r3120) Fixed: Nrrd library was not properly splitting file paths containing Windows-style separators ('\' rather than '/'). (r3118) Improved: bundled NrrdIO library was updated to 1.11.0 from 1.9.0. (r3114) Fixed: CMTK now compiles with OpenMP support using VisualStudio compilers. All OpenMP parallel loops are now using signed, rather than unsigned, loop variables, thus complying with older (pre-3) versions of the OpenMP standard. (r3079) Fixed: reading of uncompressed BioRad (and potentially other format) files was broken (patch by Greg Jefferis). (r3062) Fixed: problem with directory creation due to difference path separator characters for Windows vs. POSIX. (r3059) Fixed: broken thread semaphores on Windows due to incorrect conditional compilation. (This bug was introduced in r1661.) (r3031) Fixed: problem with instantiation of static smart pointer "NULL" object led to crashes on MacOS-X platform. This has been fixed. (r3031) Fixed: linking errors when building shared libraries on MacOS, caused by missing library dependencies. (r3003) Improved: CMTK now employs a toolkit-wide, centralized, fine-grain framework for diagnostic outputs of user-defined detail level. Every command line tool supports the new "--verbose-level" option; output previously generated using the deprecated "--verbose" option is at level "1" and can thus be enabled by "--verbose-level 1". (r2974) Added: initial support for Grand Central Dispatch, including GCD-based backend for thread pool parallel model. (r2919) Added: regional image filters, such as mean, variance, and third moment added to "convertx" tool. (r2908) Changed: "--verbose" output now written to standard output, not standard error (Issue #5496). (r2905) Added: "reformatx" and "convertx" tools now support "unsigned int" pixel data type for output files (Issue #5497). Release CMTK-1.6.1 (2011-02-17): (r2886) Fixed: crash in "fview" fusion viewer application due to incorrect interface of class derived from QApplication. (see http://lists.trolltech.com/qt-interest/2008-01/thread00570-0.html) (r2884) Added: all CMTK command line tools now support the "--version" argument to print the CMTK version number. (r2878) Fixed: "--write-reformatted" option of "registration" tool was broken. In some cases, the wrong image was used as the fixed image. (r2871) Fixed: pipe-based decompression was broken on MacOSX (r2869) Improved: "runcheck" tool is now more sensitive to stdlibc++ version. Release CMTK-1.6.0 (2011-02-01): (r2858) Added: "mk_phantom_3d" can now read MRS voxel location and size from GE DICOM files and draw voxel in correct location, if image grid with matching physical scanner coordinates is imported. (r2845) Improved: "volume_injection" and "volume_reconstruction" tools now produce reconstructed images with physical space coordinates matching those of the first input image. (r2829) Improved: various fixes related to packaging. Now uses non-default installation prefix, renamed installation components, added missing RPM fields. (r2818) Fixed: building using MacOSX SDK 10.4u now uses gcc version 4.0, which works, unlike the default gcc-4.2 with this SDK version. (r2815) Added: CMTK build can now be configured to use bundled sqlite3 library, even when system library is available. This avoids problems with multiple different versions of the library in different SDKs on MacOS. (r2815) Updated: bundled sqlite3 library has been updated to version 3.7.4. (r2805) Added: "convertx" can now apply mapping functions to replace image values with other values or with padding data. (r2794) Improved: better default configurations for MacOS-X builds. (r2794) Fixed: packaging and installation directories. (r2772) Fixed: label combination by voting now supports up to 32768 labels (was 256). (r2768) Removed: unused and redundant "probe_xform" and "average_grey" applications. Use "gregxform" and "reformatx"/"average_images" instead. (r2765) Removed: largely unused and incomplete support for building a single "cmtk" binary wrapper for all command line tools. (r2760) Removed: now-obsolete "convert" tool has been retired. Operations still missing in "convertx" will be added as the need arises. (r2756) Added: "convertx" tool gained two more missing operations from "convert", namely replace padded pixels and pixels with Inf/NaN values. (r2752) Improved: addressing all warnings reported by "doxygen", source code documentation has been completed. (r2733) Removed: the "congeal", "groupwise_rmi", "congeal_warp", and "groupwise_rmi_warp" tools have been removed. These are replaced by the "groupwise_affine" and "groupwise_warp" tools introduced in the previous release. Release CMTK-1.5.4 (2011-01-13): (r2714) Changed: all command line tools now default to writing output in NIFTI format, not Analyze, if no output file name is given. (r2708) Added: new "groupwise_warp" tool for nonrigid groupwise registration unites the "congeal_warp" and "groupwise_rmi-warp" tools, which will be removed in the next release of CMTK. (r2705) Added: new "groupwise_affine" tool for affine groupwise registration unites the "congeal" and "groupwise_rmi" tools, which will be removed in the next release of CMTK. (r2695) Fixed: subtle and rare numerical problem leading to NAN values in image-to-physical matrix read from Nifti files with quaternion-based orientation. (r2684) Fixed: improved installation and packaging by fixing multiple problems related to CMTK use files and scripts. (r2680) Improved: better naming, grouping, and documentation of "film", "volume_injection", and "volume_reconstruction" command line options. Also named equivalent options consistently between these tools. (r2664) Fixed: restricting "warpx" tool deformation to certain coordinate directions was broken. Release CMTK-1.5.3 (2010-12-13): (r2652) Improved: CMTK can now be more flexibly configured to use bundled zlib over existing system zlib. This is partially addressing Issue #5395, with a patch provided by Kent Williams. This also fixes Issue #5382, because we no longer depend on Darwin's broken system zlib. (r2629) Fixed: LZMA decompression was broken on i686 due to bug in lzmadec_seek(). The decompression backend classes now use a common fake "seek" implementation if seek is not supported by the low-level backend or broken. (r2613) Fixed: "dbtool" was warning of unused command line parameters. (r2611) Fixed: properly exit "fview" application with an exception when one of the input images cannot be read. Release CMTK-1.5.2 (2010-12-06): (r2591) Fixed: previous release accidentally removed the ability of CMTK's image writer to recursively create non-existing directories in the output path. Release CMTK-1.5.1 (2010-12-02): (r2580) Fixed: segmentation fault when getting cropped subvolume after providing out-of-range cropping boundaries. (r2575) Fixed: all output images are now written in the same orientation and pixel array order as the respective input images. Previously, images were written in internal order, i.e., RAS or closest orientation supported by output file format. For backward compatibility, the old behaviour can be turned back on by defining the "CMTK_LEGACY_WRITE_IMAGES_RAS" environment variable. (r2563) Fixed: "dcm2image" tool choked on non-DICOM files. (r2560) Added: "dcm2image" tool has new option to ignore AcquisitionNumber tag when grouping images, which is useful for Siemens fMRI series. Tool also now warns if images are left over that cannot be assigned to any stack. (r2558) Fixed: CMake initial cache files for pre-defined configurations now set cache variables. Release CMTK-1.5.0 (2010-11-17): (r2544) Added: "similarity" in label mode now prints Jaccard index, J. (r2540) Fixed: all command line tools now use a C++-safe exit mechanism based on throwing an exit exception that is caught in a safe main() wrapper function. (r2540) Fixed: CommandLine class was leaking memory because some local classes did not have virtual destructors (Issue #5320). (r2512) Fixed: SmartPointer::DynamicCastFrom() was leaking memory due to implicit, rather than explicit, call to inherited constructor. (r2505) Added: "film" tool now has "--padding-value" option to define a padding value for the input image. Also to get this to work, UniformVolume::GetInterleavedSubvolume() now copies padding flag and value from input data to output. (r2502) Fixed: "ReformatVolume::CreateInterpolator" did not actually create a nearest neighbor interpolator when so requested. (r2500) Fixed: "imagemath" tool performed incorrect operations for the "exp" and "sqrt" do to incorrect function wrapper. (r2495) Added: "dcm2image" tool can now optionally sort output images by instance numbers of the input image files, rather than by their names. This is useful when using variable-length numbered files that must be stacked in order (e.g., for functional MRI). (r2483) Added: "fview" fusion viewer tool can now switch between no transformation (identity), affine-only and full nonrigid transformation. (r2479) Fixed: ITK transformations must be in LPS space, regardless of the coordinate spaces of the images being registered. (r2467) Fixed: make consistent use of C memory allocator to avoid conflicts between core CMTK and external libraries (e.g., NrrdIO). (r2467) Added: "warp" and "warpx" tools have new option, "--relax-to-unfold" to unfold deformation regions with negative Jacobians before continuing at the next resolution level. Hopefully, this will allow more aggressive deformations without incurring folding, thus improving registration accuracy. (r2450) Removed: support for pixelwise incompressibility weight maps, which was never really used and hasn't been exposed at the command line for a while. (r2445) Fixed: conversion of affine transformation to/from ITK format and native image space failed when fixed and moving images resided in different spaces (fixed image space was used for both of them). (r2440) Fixed: newly created transformation objects should, by default, set their coordinate space to CMTK's default space, RAS. (r2436) Added: "mk_nifti_hdr" tool to create NIFTI headers from scratch or patch existing headers. (r2434) Fixed: "CMakeFiles" directory gets in the way of in-source builds (Issue #5269). Thanks to Dominique Belhachemi for report and fix. (r2422) Fixed: "registrationx" tool ignored user specification for transform initializer and always used an identity transformation unless an explicit initial alignment was provided. (r2416) Changed: "make_initial_affine" now defaults to writing transformations in CMTK standard RAS space, rather than between native image spaces. This makes much more sense, because in all likelihood it will be other CMTK tools reading these transformations. (r2408) Fixed: average images created by the groupwise registration tools did not have proper coordinate space information. Also made the default output image type NIFTI (instead of Analyze) to produce files with fully specified coordinate systems. (r2395) Fixed/Added: triplanar viewer now displays RAS physical coordinates of current location; also fixed problem with repeated slice position change after a single mouse click. (r2390) Changed: by default, the DICOM image stacker, "dcm2image," now writes NIFTI files with attached header (.nii) rather than Analyze hdr/img pairs. This is because Analyze format does not properly represent the image location and orientation information obtained from the DICOM input images. (r2380) Removed: several hundred lines of unused, obsolete code was removed from the Numerics library. Release CMTK-1.4.3 (2010-09-28): (r2365) Fixed: "xform2dfield" and "xform2scalar" tools did not respect CMTK_NUM_THREADS variable, as they were only using OpenMP directly. (r2359) Added: by default, CMTK is now built with support for on-the-fly decompression of bzip2 and lzma-compressed files if libbzip2 and/or liblzmadec and their respective header files are installed on the build system. Also rewrote the decompression class, cmtk::CompressedStream, from scratch to be more easily extended. (r2345) Fixed: prevent "dcm2image" tool from trying to read compressed (using gzip, etc.) DICOM files, as these cannot be read by DCMTK right now. (r2337) Added: "describe" tool now prints machine-readable image-to-physical space transformation matrix. Also transposed matrix output to make it compatible with "mat2dof" tool. (r2329) Improved: poor description of command line arguments in "split" tool. (r2327) Fixed: broken Makefiles when source path contained space. (r2317) Refactored: eliminated several hundred lines of unused and obsolete code, including several classes. (r2309) Fixed: because gcc/OpenMP breaks POSIX semaphores on Cygwin, use CMTK's mutex/condition implementation instead (Issue #4779) (r2294) Improved: eliminating calls to floor() improved registration times by another ~10%. (r2287) Improved: apparently using histogram entropy is computed faster without OpenMP parallelization, reducing test times for the "warp" tool by about 30%. This should be noticable in production use also. No surprise, actually, because most entropy evaluations are done in parallel on a higher level anyway. (r2281) Fixed: "reformatx" tool did not properly initiatialize thread system, therefore running too many OpenMP threads even when CMTK_NUM_THREADS was set. Release CMTK-1.4.2 (2010-08-23): (r2268) Fixed: various compile errors when compiling Qt support classes using Sun/Oracle compiler. (r2261) Refactored: new "XformListIO" class provides a static member function that assembles lists of transformations with optional inversions from string vectors as supplied by the command line parse. This new class is used to unify three instances where this was previously done by code duplication. (r2254) Added: new, lightweight "fview" fusion viewer replaces old, broken, and incomplete "fusion" application. (r2213) Bugfix: UniformVolume::GridMatches sometimes returned "false" due to differences below the numerical relevance threshold. (r2202) Added: "--force-outside-value" command line argument now also offered and implemented for affine registration. (r2197) Improved: UniformVolumeInterpoaltor classes now convert input data to Types::DataItem and cache the converted array for faster computation. This particularly speeds up higher-order interpolation with cubic or sinc kernels. Release CMTK-1.4.1 (2010-08-09): (r2178) Improved: when run as Slicer plugins, GPU-accelerated command line tools now categorize themselves into "->GPU" submenus. (r2176) Refactored: command line arguments now passed to CommandLine::Parse, rather than constructor. (r2171) Fixed: "new" registration classes (exposed in "registrationx" and "warpx" tools) now respect reference image padding. Also fixed bug that led to loss of padding information during resampling. (r2166) Added: "registrationx" tool now supports special number of degrees of freedom, "603", which produces a rigid transformation plus shears, but without scaling (i.e., it uses 6+0+3 parameters of the affine transformation) (r2160) Added: on-the-fly median filtering for "registrationx" and "warpx" tools. (r2156) Added: root mean squares image similarity measure for "registrationx" and "warpx" tools. This is of course very similar to mean squares (MSD), but exhibits different sensitivity to outliers. (r2138) Added: GPU-accelerated levelset tool for CUDA, "levelset_cuda." (r2111) Refactored: simple two-phase levelset evolution moved from "levelset" tool into a new Segmentation/ library class, cmtk::SimpleLevelset. (r2107) Refactored: UniformVolume::Clone and CloneGrid now return smart rather than dumb pointers. (r2103) Added: GPU/cmtkCUDA.h defines macros to safely call CUDA API functions and check their error codes. (r2097) Added: a new class, cmtk::ImageSymmetryPlaneCommandLine, together with a derived class template unifies the tools for CPU-based and GPU-based symmetry plane computation. (r2094) Added: GPU-supported symmetry plane computation tool, "symplx_cuda", which is used analogously to CPU-based "symplx" tool. (r2060) Added: cmtk::ParametricPlane class can now provide the 4x4 matrix of the reflection transformation w.r.t. the plane. (r2035) Refactored: all cmtk header files are now included via paths relative to the library parent directory. (r2019) Refactored: split new symmetry plane functional into base class and metric-dependent class. Base class will be shared by GPU implementation. (r2016) Added: new "symplx" tool that will extend, and ultimately replace, the current "sympl" tool for symmetry plane computation. (r2012) Fixed: concurrent memory access problems in GPU kernel to populate histograms on device. (r2006) Improved: imagemath tool now prints a warning when more than one image is left on the operation stack. This often indicates a problem with the specified image operations. (r2003) Fixed: require compatible version of SQLite (>= 3.5.0), otherwise build bundled library. (r1999) Added: "--product" operation for imagemath tool. (r1996) Fixed: mrbias_cuda tool and curt validation tool now properly self-identify on the command line and via XML. Release CMTK-1.4.0 (2010-07-12): - Fixed: header files are now installed in library-specific subdirectories, which is consistent with the source tree. - Improved: bundled SQLite is now only built if no system-wide library is found. - Added: "imagemath" tool now supports applying single-image operations to the entire stack. - Improved: "imagemath" tool now uses deque, not stack, to implement image stack. This way we can perform all-image operations more easily. - Improved: log-Intensity histograms are now value scale invariant. - Removed: building against VTK is no longer supported. - Added: class for histogram population and entropy computation on GPU device. - Added: vtkxform tool can now read (and write) binary-coded VTK files. - Added: first utility classes (memory allocation and resource management, volume data transfer) for GPU computation using CUDA. - Testing: added test name to function mapper cmtk::TestFunctionMap to simplify writing of test drivers. - API: some Numerics functions from AlgLib were moved into ::alglib namespace to avoid clashes with functions also defined elsewhere (e.g., gamma, erf, etc). - Refactored: thread function within thread pool is now provided with thread index directly, rather having to perform a lookup (Issue #4939). - Added: cmtk::CommandLine class prints warning message if not all command line arguments are actually used. This usually points to a usage error. - Added: 'vtkxform' tool now accepts list of transformations, each of which can be inverted. - API: change interface of SplineWarpXform::getTransformedGridSequence() to isolate "number of points" parameter and make it harder to use this incorrectly. - Refactored: use more smart pointers in more places. Standardize Clone() and Create() member functions of different classes. - Improved: parallelized computation of FFD smoothness constraint. This greatly improves multicore scaling of nonrigid registration using this constraint. - Removed: "describe" tool no longer prints center of mass and first-order moments. If we ever need this, we'll make a separate tool for this. - Refactored: further consolidated Vector3D into FixedVector - Documentation: marked VTKWrapper library as deprecated. Release CMTK-1.3.1 (2010-06-07): - Added: "vtkxform" tool to apply transformation to point coordinates in VTK files (does not require VTK support to be enabled or VTK to be installed). - Fixed: tiny numerical differences between division and multiplication with the inverse caused failed range check assertion in SplineWarpXform (Issue #4894). This is now fixed by replacing the assertion with a value range truncation. It ain't pretty, but it works. - Configuration: added two CMake initial script files in config/ directory, which should support and standardize backward-compatible builds on MacOSX/Intel (Issue #4895; thanks to Greg Jefferis for suggesting these). - Added: several features previously implemented only in congeal_warp have been ported into groupwise_rmi_warp. Also added tests for the latter. - Refactored: completely reorganised the groupwise registration functional hierarchy - instead of redundant member functions in leaf classes, we are now using template specialization to inject the necessary transformation model-dependent code at the base of the hierarchy. - Fixed: previously undetected bug in AffineGroupwiseRegistrationRMI functional effectively led to inverse transformations to be computed. - API: renamed CongealingFunctionalBase to more appropriate GroupwiseRegistrationFunctionalXformTemplate and removed unused second template parameter (histogram bin type) from several classes. - Improved: CMTK can now determine whether to write compressed (gz) or uncompressed NIFTI images based on the given file name, e.g., image.nii.gz for compressed vs. image.nii for uncompressed. Note that for this to work as expected the "CMTK_WRITE_UNCOMPRESSED" environment vairbale must be set, because otherwise images will be written compressed by default. Also note that CMTK's behaviour with respect to writing compressed vs. uncompressed files has not changed for all other file formats (Issue #4852). - Fixed: when using an unknown image file format suffix, CMTK would state that it would write a RAW3D file instead, but then write nothing. This now defaults to single-file NIFTI, which is actually written (Issue #4852). - Refactored: consolidated affine initialization of groupwise registration functionals to reduce code duplication and thus avoid residual unfixed bugs. - Improved: when initializing groupwise registrations, translations are now computed with a zero sum over all images. Previously, we aligned all centers with the template FOV center, but there is no reason why this would be a good idea when using, for example, centers of mass of the input images. - Added: groupwise registration tools now abort with an error when at least one of the input images get transformed too far outside the FOV. This is detected in cmtk::CongealingFunctionalBase and defaults to requiring at least 1% of reformatted pixels to be inside the FOV of every image. At present, this is not configurable at run time. - Removed: support for writing joint histograms. - Refactored: value ranges are now handled by Types::Range class template. The specialization typedef'ed as Types::DataItemRange is now used for more intuitive handling of item ranges. - Fixed: selection of floating image interpolation method was implemented in warpx tool, but not exposed at the command line. - Fixed: auto-cropping by threshold was implemented in image pair registration preprocessor class, but not exposed through the command line interface. - Added: padding in convertx tool can now be turned on and off multiple times during the sequence of operations, including the use of different padding values at different times. - Added: connected components operator; also added this as a function to convertx tool. - Configuration/build: when system zlib is installed, we use it instead of the bundled zlib. This is necessary to avoid version issues when Qt support is activated, because Qt is linked against the system zlib. - Distribution: updated version of bundled zlib to 1.2.5, which fixes valgrind warnings among other things. In the process, fixed a configuration file bug by which the system zconf.h file, if present, was included rather than the one configured for CMTK. - Fixed: in some cases, TypedArray memory was allocated via malloc() but freed via delete[]. Now everything is consistently using cmtk::Memory (wraps new{} and delete[]) for allocation and deallocation. - Fixed: Correlation Ratio similarity measure was broken due to imcomplete copying of class fields in HistogramBase class. This was resolved "accidentally" when unifying and standardizing all copy constructors and assignment operators. - Refactored: non-standard copy member functions in various classes have been standardized as operator=() members. Assignment operators and copy constructors that performed only member-wise copies were deleted altogether. - API: renamed "InfinitePlane" class to "ParametricPlane" which is more descriptive and precise. - Added: CMake configuration of CUDA and OpenCL support for future GPU computations. Currently unused and experimental. - Refactored: UniformDistanceMap is now a factory class and no longer directly inherits from UniformVolume. - API: renamed "GetDownsampled" member in DataGrid and UniformVolume classes to "GetDownsampledAndAveraged" to clarify that this is a downsampling-and-averaging operation, not just a downsampling. - Refactored: greatly improved handling of grid index and region types. - Added: SmartConstPointer, based on concepts in Meyer's "More Effective C++" book. - Removed: unused classes from the deprecated "Pipeline" library - Documentation: describe library groups in doxygen documentation - Libraries: all reconstruction-related classes were consolidated in a new component library, "cmtkRecon" - Removed: unused cmtk::DirectionSetOptimizer class. - Removed: unused member functions of cmtk::Vector that complicate the API and stand in the way of eventually replacing this class with std::valarray. - Refactored: where appropriate, catch exceptions by constant references, rather than by value or by non-constant reference. Release CMTK-1.3.0 (2010-04-19): - Added: a new command line tool, "dbtool", to modify, query, and maintain databases of images and of transformations between them. - Fixed: consolidated redundant "--switch" and "--exchange" parameters to "warp" tool (Issue #3616). Also fixed initial transformation missing inversion bug (unreported) for inverse-consistent nonrigid registration. - Fixed: if this is supported, default to terminal width for line width in cmtk::Console::FormatText - Added: image/transformation database support for the following registration tools: "registration", "registrationx", "warp", "reformatx" - API: cmtk::MetaInformationObject is getting a more explicit and task-appropriate interface. - API: refactored multi-channel registration class hierarchy to factor common code out of template class and into new non-template base class cmtk::MultiChannelRegistrationFunctionalBase - Added: mat2dof tool can now read from file in addition to reading from std::cin via redirection. - API: introduced "cmtk::Units" namespace with classes that handle conversions between units, e.g., between degrees and radians. Also added unit-safe trigonometric functions to MathUtil class. - Refactored: numerous code improvements based on suggestions from the third edition of Meyers, S., "Effective C++" - Removed: obsolete real-valued mod operators in MathUtil namespace. - Removed: long unused support for GIPL image file format import - Fix: volume_reconstruction and volume_injection tools now convert affine ITK transformations (i.e., such read from .tfm files) from native into standard space (Issue #4761). - API: made all meta key strings C++ objects rather than cpp #defines; also removed CMTK_ prefix after moving them into cmtk namespace. - API: renamed "InformationObject" class to more descriptive "MetaInformationObject" - Fixed: various issues pointed out by "cppcheck" static code analysis. - API: moved morphological operators from cmtk::DataGrid into separate class, cmtk::DataGridMorphologicalOperators. - API: prevent copying of system classes where this would lead to problems. - Added: filter, levelset, make_initial_affine, mrbias, and reformatx tools now support database updating. - Added: database backend for storing images and transformations between their coordinate spaces. - Added: "convertx --binarize-thresh" operation - Bugfix: shared-library build configuration was broken. - Bugfix: "convertx --mask-inverse" was broken. - Added: "mk_phantom_3d" tool can now import either an image including existing pixel data or just the grid geometry. - Added: "mk_phantom_3d" tool now supports three different coordinate modes: absolute, relative, indexed. - Added: "--revert" image operation for "imagemath" tool to revert binary masks. - API: moved "DrawXXX" members from "DataGrid" class into a new, dedicated painter class, "UniformVolumePainter." - Added: image operation for medial skeleton based on analysis of the Hessian eigensystem. Note that this is currently broken and only kept for future investigation. - Added: two differential operator members for "UniformVolume" class, one for gradient and one for Hessian matrix, each implemented using central differences. - Added: symmetric eigensystem can now be sorted either by absolute or by actual eigenvalues. - Bugfix: UniformDistanceMap constructor failed to copy orientation and space-related information from input UniformVolume object - Added: Euclidean distance map computation in "convertx" tool. - Added: "warpx" and "congeal_warp" can now do repeated histogram-based intensity matching of floating to reference image, with a transfer function that changes as warping progresses and thus takes into account shifting volume proportions. - Added: "convertx" now does histogram equalization. Also added tests. - Refactored: created a new class for histogram-based intensity matching, cmtk::HistogramMatchingLookup. - Added: first library-level tests for TypedArray class. Release CMTK-1.2.2 (2010-02-25): - Added: cmtk::SQLite wrapper class for sqlite3 library, with tests. - Added: bundled still-unused sqlite3 "library" for future use in image/relationship database. - Added: "imagemath" now has "min-value" and "fill" operations. - Improved: "imagemath" can now set, unset, and change padding value repeatedly, so different images can be read with different padding values (Issue #4597) - Improved: better separation of code for DICOM vs non-DICOM configuration. - Improved: code for PGM output (8 and 16 bit) now more consistent and less complex. Also unified API for 8bit and 16bit write functions. - Added: bitwise "and" operation in "imagemath" tool. Added test for this. - Testing: added tests for cmtk::TypedArraySimilarity class. - Fixed: TypedArraySimilarity::GetPeakSignalToNoiseRatio was broken due to negated GetMeanSquaredDifference return value. - Testing: added tests for cmtk::ScalarImageSimilarity class (and, by extension, cmtk::TypedArraySimilarity as well). - Removed: obsolete "ImageIO" class was removed; the only two remaining former derived classes, cmtk::PGM and cmtk::DICOM, will now be used directly, as they only have static member functions left. - Fixed: initialize memory for Analyze header with zeroes in "mk_analyze_hdr" tool. - Testing: added tests for "mk_analyze_hdr" tool. - Testing: added tests for "mat2dof" tool. - Fixed: "mip" tool did not compute projection images with proper pixel sizes based on the input volume. - Testing: added tests for "mip" tool. - Improved: "mip" tool now sets black and white points automatically if not provided by the user. - Testing: added test for "Rohlfing" single-image intensity-consistent filtering in "filter" tool. - Removed: further removal of obsolete PGM and DICOM I/O code. Release CMTK-1.2.1 (2010-02-01): - Added: "statistics" tool now optionally outputs mask-based results for all up to a user-provided maximum label, even if not all labels in the range actually occur in the mask. - Added: "convertx" tool now does thresholding above and below threshold, to threshold and to padding. - Added: re-added missing "--fast" switch for warp and warpx tools. - Added: "imagemath" can now threshold above and below given values. - Added: "convertx" tool can now rescale image intensities to target range using "--scale-to-range" operation. - Fixed: reading empty (i.e., zero pixels) images crashed the toolkit - Fixed: "reformatx" did not respect user-defined padding value when computing Jacobian maps (Issue #4564). - Fixed: label mode of "avg_adm" tool was broken. - Fixed: "avg_adm" now gives consistent results regardless of number of CPUs. Also fixed minor bug that would have broken this altogether if there were more CPUs than pixels in an image row. - Fixed: "avg_adm" tests crashed on MPI; bug also affected non-MPI build (Issue #4571). - Fixed: "statistics" now tests whether grid of mask volume matches that of statistics volume. - Testing: run all warp and warpx tests in "fast" mode - Testing: added tests, inputs, and baselines for "avg_adm" tool. - Removed: deformation strain tensor computation was untested and unused; thus removed. - Removed: partial read support for AVW file format removed - Removed: support for legacy typedstream studies removed; Nrrd should be able to replace this if needed. - Removed: obsolete IO code for DCM tags etc. Release CMTK-1.2.0 (2010-01-06): - Added: cmtk::Console can now determine terminal line width on POSIX systems, and this is used by cmtk::CommandLine to format command line help output with the correct line breaking. - Added: new, experimental implementation of pairwise image registration, with more accurate metric computation and support for arbitrary floating image interpolation. These are accessible through the new "registrationx" command line tool. - Added: single-step mass-preserving reformatting in reformatx tool. Also added a new test to cover this. - Added: "convertx" tool, which will replace "convert" in the long term but is designed to apply all operations given on the command line in the exact order that they are specified and with arbitrary repetitions. This is in response to Issue #3784. - Improved: more sophisticated install and packaging configuration; still not fully clear how that translates into separated packages for different components. - Improved: ported everything related to Qt to use the actual Qt4 classes, so we can finally get rid of legacy Qt3 support classes. This should reduce the number of actually unused dynamic libraries, which we previously had to link against. - Improved: numerous small improvements to triplanar viewer - Improved: dcm2image tool now numbers images only if there would otherwise be filename collisions. The printf()-type numbering has been replaced with two new output path substitutions, "%n" and "%N". Both expand to the empty string when a path is already unique without numbering. Otherwise, "%n" substitutes to an increasing number, and "%N" substitutes to the same number with a leading dash, "-". So using "./%n/image%N.nii" generates a single file ".//image.nii" if that is a unique path, or a sequence of images "./1/image-1.nii", "./2/image-2.nii" for up to ten colliding paths. For larger numbers of collisions, the number of digits is automatically increased as necessary. - Improved: command line callback error handling was ugly and tedious; now using exceptions to achieve the same effect. - Changed: in API, all volume "origin" fields were renamed to "offset" to avoid confusion with "origin" of coordinate spaces. - Fixed: bundled DICOM library finally works on all tested platforms and all tested compilers. - Fixed: compile error on latest Mac/Intel gcc due to undefined "ushort" type. - Fixed: triplanar can now be called again with no parameters to open empty viewer UI. - Fixed: set coordinate space meta information for user-defined target grid in "reformatx" tool. - Fixed: using Sun's compiler, there was a problem with the timely initialization of SmartPointer<...>::Null when such an object was used in the global scope. This led to crashing volume_injection and volume_reconstruction tools. - Removed: support for AMD Core Math Library (ACML), which was rarely used and complicated code unnecessarily - Removed: push-forward function of reformatx has long been obsolete because we can invert any transformation and switch reference and floating image, to achieve the same effect. - Removed: "Binary" data class; this should really be treated as "Labels" anyway. - Removed: for now, we are dropping support for system-installed DCMTK libraries. The configuration of these was a complete hack anyway. For the time being, we will rely exclusively on the bundled, stripped-down version of the toolkit for DICOM support. - Testing: support for baselines local to each build. If a baseline exists in testing/baseline/${TESTNAME}, then it will be used. Otherwise, the global baseline as configured by the CMTK_DATA_ROOT top-level CMake variable is used. - Testing: added tests for manual target grid definition in "reformatx" tool. Release CMTK-1.1.3 (2009-11-18): - Added: new tool, xform2scalar, to extract scalar measures from deformation fields or transformations. This, in combination with "statistics" and "sequence" tools, obsoletes the old (and inappropriately named) "deformation_field" tool. - Added: scripts for multi-NEX motion correction and iterative shape averaging are now configured in build tree and installed in install tree. These (and future scripts) will also be included in installable packages. - Added: B-spline transformation can now be written in ITK file format, thus allowing direct import of "warp" transformations into 3D Slicer. - Added: new binary builds for Solaris on i386 and x86_64 using SunPro CC compiler. - Removed: obsolete "deformation_field" tool. - Fixed: average_images tool was broken. - Fixed: numerous unitialized variables and other things that tripped valgrind memory checker. - Fixed: some more missing tests were added to the configuration of the bundled D(4)CMTK library. - Fixed: cmake file to set up tests when no hash map implementation is found cannot set properties for mcwarp and mcaffine tests. - Fixed: numerous compile errors using Sun CC compiler - Fixed: incomplete Wiki markup of command line help due to incorrectly overloaded virtual member function - Fixed: re-worked CMake build system to eliminate redundant repeated libraries - Fixed: re-arranged "warp" command line parameters to avoid crashing Slicer, as it does not allow optional non-option parameters - Fixed: signal handler functions are now 'extern "C"' to avoid potential problems of conflicting linkage - Fixed: margins for line breaking not correct in Console::FormatText - Improved: better, more data-drive automatic initialization of optimizer step sizes and image sampling parameters in pairwise rigid and nonrigid registration. - Improved: more explicit handling of effective reference and floating volume in pairwise registration classes. - Testing: added tests and baselines for average_images tool. Release CMTK-1.1.2 (2009-10-28): - Added: new multi-threading framework based on pools of continuously running threads. This should reduce thread creation overhead and also improve scheduling by allowing load balancing using over-partitioned tasks, i.e., tasks split into substantially more parts than the number of CPUs. - Added: logit and logistic functions for imagemath tool, for single and multiple images. - Added: imagemath tool "--match-histograms" operation matches the intensity distribution of one image to that of another. - Added: imagemath tool now supports image data padding. - Fixed: basic reformatting in groupwise registration used slightly broken partitioning for SMP computation. This has been fixed. - Fixed: NIFTI reader was not able to read files with detached 348-byte headers, i.e., headers without "extension" field. - Fixed: BioRad header problem resulting from struct field alignment (patch by Greg Jefferis). - Fixed: TemplateArray::GetRangeTemplate() now ignores non-finite values, which also fixes a crash in triplanar viewer when reading images with non-finite values. - Improved: switched long and short options in command line help and wiki markup outputs. Release CMTK-1.1.1 (2009-09-02): - Added: "congeal" and "congeal_warp" now optionally match image histograms to either template data (if template data is used for registration) or first image (if no template is defined or its data is not used). - Added: groupwise, pairwise, and multi-channel registration tools now each support "--delta-f-threshold" option to define termination threshold for optimization based on relative target function change. - Fixed: configuration of type sizes in bundled DICOM library was broken. - Fixed: file system functions were borken on Windows (MS-VS build) due to use of Unix-style directory separators ('/' vs. '\'). - Improved: command line options for "congeal" and "congeal_warp" tools streamlined and grouped. - Improved: help and wiki outputs of command line tool option descriptions now include textual descriptions of the argument expected by each option, e.g., "float", "string", "image-path". - Removed: "Mutual Information" variant of RMI-based groupwise registration deleted; this turned out to be almost zero and thus useless. For now, we're left with optimizing the approximated joint entropy. - Testing: added tests for all registrations using DeltaF threshold. Release CMTK-1.1.0 (2009-08-26): - API: no longer allow short-only options for command lines. - API: cmtk::SmartPointer now supports smart pointers to constant objects for improved const-consistency. - API: begin decoupling the actual transformation classes (e.g., SplineWarpXform) from their reference image grids (used for precomputations). Instead, use classes like the new SplineWarpXformUniformVolume to tie both together, thus allowing more transformation objects to remain const in more situations (see, for example, modified implementation of ReformatVolume::WarpXform). - API: all "...NonVirtual" member functions replaced their virtual counterparts and became virtual themselves. - API: decoupled VTK and non-VTK landmark code so only one tool, align_landmarks, still needs to be linked against the VTKWrapper and VTK libraries. - API: re-worked progress reporting to support multiple-level nested tasks - API: removed some residual legacy preprocessor definitions - Changed: command line tools now need to "opt-in" for XML support. This is to prevent cluttering Slicer's menu with modules that do not actually support it. - Changed: finally eliminated all those affine transformation inversions in the rigid registration framework that have been a pain for over ten years. CAUTION! This changes the outcomes of both affine and warp registrations, usually just slightly, but in unstable cases potentially by a lot. - Fixed: mk_analyze_hdr command line option "--description" was broken. - Fixed: contrary to the Wiki, NMI was the default registration criterion used in "film." This has now been changed to MSD, which is most approriate for within-modality registration. - Fixed: ReformatVolume::CheckerboardMode was not initialized - Fixed: ReformatVolume::CreateInterpoaltor did not support partial volume interpolation. - Fixed: ReformatVolume::MakeTargetImage did not create a grid with proper geometry (i.e., offset, direction matrix, meta data). - Fixed: conversion of non-axial Analyze images to Nrrd produced output file that was missing coordinate space definition. - Fixed: a critical bug that effectively broke all multi-threaded computations on Windows (using MS compilers). - Added: all tools now support "--echo" command line switch, which dumps the command line to stdout before continuing with the program. - Added: the "levelset" and "film" tools now fully support the Slicer3 execution model. Support in "mrbias" tool has also been improved. - Added: new "asegment" tool for atlas-based segmentation - Added: command line class now automatically generates command line syntax description for "--help" output, unless a syntax description string is explicitly set. - Added: cmtk::CommandLine class now permits arbitrary-typed variables to be set by command line enum parameter groups. - Added: new optional "asegment_sri24" tool for atlas-based segmentation using the SRI24 atlas (currently available only separately from http://nitrc.org/projects/sri24/). - Added: "warp" and "registration" tools now support writing of the reformatted floating image without requiring a separate tool, such as refortmatx. This makes integration into Slicer much more convenient. - Added: support Slicer3's XML-based progress reporting, and auto-detect Slicer's presence to switch between standard console progress output and XML-based output. - Added: automatic Wiki markup printing of command line options - Added: export and import of affine transformations in ITK file format for exchange with Slicer. - Added: new class, cmtk::TransformationToNativeSpacesAffine, to change an affine transformation under changes of reference and floating image coordinate spaces. - Added: intensity adjustment of floating image in affine and B-spline registration using histogram matching - Removed: multiple redundant command line parameters of the "warp" tool have been retired. We are only retaining options that differ from the default values. Release CMTK-1.0.2 (2009-07-24): - API: all igsGetSetXXX macros are now cmtkGetSetXXX, and they all define the field name with a "m_" prefix to avoid type/field name collisions. - API: more elegant design of noise estimator classes that work on TypedArray objects. Mike's experimental ML noise estimator implementations moved to "Unstable" library. - API: new wrapper class "cmtk::HashMapSTL" hides the various STL hash map implementations and also adds support for upcoming standard unordered_map class. - Added: source tree now incorporates minimal subset of DCMTK files so we can build DICOM support without external dependencies, even on Windows using VisualStudio. - Added: "mcaffine" tool now accepts user-provided initial transformation from file [Tracker #3925]. - Added: cmtk::CommandLine class now automatically prints parameter defaults in "--help" output. - Fixed: numerous uninitialized data situations identified by valgrind memory checker - Fixed: incomplete computation when using "--sampling-density" option of "congeal" tool. - Fixed: Slicer can now detect CMTK tools as plugins via their response to "--xml"; note that the XML description itself is not fully correct and functional yet. - Fixed: testing and configuration were out of order in top-level CMakeLists.txt, resulting in build problems. - Fixed: "film" tool now uses anisotropic Gaussian kernel for volume injection [Tracker #3945]. - Fixed: volume injection should use reconstructed image pixel size to scale Gaussian kernel, not pass image pixel size. Pass image size depends on number of passes, whereas point spread function does not. - Improved: eliminated explicit offset calculations in cmtk::DataGrid and use existing index-to-offset function instead. - Improved: instead of trying to figure out what STL version provides, or does not provide, a 64bit integer hash function specialization, we simply provide a generic hash function ourselves, which works on all platforms. - Testing: added an experimental build with valgrind/memcheck memory checking to discover further issues with uninitialized data etc. - Testing: support for automated memory testing using valgrind etc. - Testing: added test for multi-channel affine registration using histograms - Testing: added actual multi-channel registration test, and test for mcwarp - Testing: added test for importing deflate-compress DICOM images Release CMTK-1.0.1 (2009-06-25): - API: changed XformList::Apply to XformList::ApplyInPlace - Build: added missing applications "regress" and "probe_xform" to default build again. - Changed: probe_xform tool now uses different command line semantics. All transformations are concatenated rather than applied separately. - Added: normal vector of InfinitePlane can now be set directly. - Added: "sympl" tool can now use different approximated symmetry plane orientations to begin search, supporting non-sagittal symmetry planes [Tracker #3834]. - Added: established infrastructure for optional algorithm validation, beginning with rigid registration. All validation tools must be activated via the "BUILD_VALIDATION" option of the top-level CMakeLists.txt file - Added: tools for rigid registration validation using data from the Retrospective Image Registration Evaluation project. - Added: "registration" tool now supports on-the-fly Sobel edge filtering, and histogram equalization was unified with "warp" tool's. - Fixed: cmtkVoxelMatchingAffineFunctional.h compile error when SMP is turned off. - Fixed: crash of average_affine tool when run with no parameters [Tracker #3841] - Improved: AffineRegistration and ElasticRegistration now use a new, unified image preprocessing class for things like cropping, histogram equalization, etc. - Removed: obsolete cmtk::Array class; replace all uses with std::vector Release CMTK-1.0.0 (2009-06-16): - API: "TemplateHistogram", "LogTemplateHistogram", and "TemplateHistogram2D" renamed to remove "Template" part. Also removed all global histogram typedefs (e.g., "UnsignedIntHistogram") to be more flexible with conditional instantiations - API: "Histogram2D" renamed to "JointHistogram". - API: all "VM_" class prefixes replaced with more explicit "VoxelMatching" - Documentation: added extensive documentation with code examples for several key classes. - SVN: moved test data out of "core" tree and into "data"; allow configuration of separate (shared) data tree from top-level CMake file - Added: MiniXML library included in source tree for all upcoming XML needs. - Added: initial support for XML command line parameter descriptions. Mostly infrastructure work to make sure XML can be generated by CommandLine class with minimal extra programmer involvement - Added: cmtk::Console can now typeset text with given margin and width. - Added: Windows implementation of mutex lock. - Added: cmtk::StdOut stream - Added: "--auto-multi-levels" option for registration tool. Selects automatic resolution and optimization parameters for given number of levels. - Added: new top-level configuration option to switch all numerical algorithms (i.e., AlgLib) to single-precision floats. This can be used to save memory in extreme situations (for example, high-resolution volume reconstruction) - Added: two new operations for imagemath tool: xor-scalar and contract-labels - Added: Windows implementation of dcm2image file scanning - Changed: CMake switches for changing coordinate and data real type are now advanced. - Fixed: numerical instability of correlation coefficient (leading to values outside -1..+1, which in turn produced invalid p-values - Fixed: legacy IGS_MOUNTPOINTS was not considered - Fixed: principal axes registration was broken [Tracker #3743] - Fixed: configure problem with bundled zlib, which broke compile of NrrdIO in Visual Studio. - Fixed: Analyze/Nifti images were not written compressed - Fixed: numerous compiler errors and warnings detected by GCC 4.4 - Improved: simplified code by removing HAVE_ZLIB conditionals. These are now always true since we're bundling zlib. - Improved: use double instead of float for linear model and regression - Improved: use CMake's FindOpenMP to detect and configure OpenMP - Improved: prevent configurations where CMTK_USE_PTHREADS is ON but CMTK_BUILD_SMP is OFF. - Improved: better integration of Doxygen into CMake build system - Improved: better output of stacked image information in dcm2image - Improved: pre-defined "--xml" and "--help" command line options can now be overridden by library user [Tracker #3830]. - Removed: cmtk::MathUtil::CoefficientOfCorrelation was removed because it turned out to be a redundant, numerically unstable variant of MathUtil::Correlation - Removed: deprecated options -m/--metric removed from registration and warp tools. - Removed: obsolete tool skeleton "detect_interleave" - Removed: obsolete "--threads" option from all remaining command lines; using CMTK_NUM_THREADS environment variable is more flexible and versatile. cmtk-3.3.1/CMTKConfig.cmake.in000066400000000000000000000055471276303427400157720ustar00rootroot00000000000000## ## Copyright 1997-2009 Torsten Rohlfing ## ## Copyright 2004-2011 SRI International ## ## This file is part of the Computational Morphometry Toolkit. ## ## http://www.nitrc.org/projects/cmtk/ ## ## The Computational Morphometry Toolkit is free software: you can ## redistribute it and/or modify it under the terms of the GNU General Public ## License as published by the Free Software Foundation, either version 3 of ## the License, or (at your option) any later version. ## ## The Computational Morphometry Toolkit is distributed in the hope that it ## will be useful, but WITHOUT ANY WARRANTY; without even the implied ## warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License along ## with the Computational Morphometry Toolkit. If not, see ## . ## ## $Revision: 3287 $ ## ## $LastChangedDate: 2011-07-26 15:52:15 -0700 (Tue, 26 Jul 2011) $ ## ## $LastChangedBy: torstenrohlfing $ ## #----------------------------------------------------------------------------- # # CMTKConfig.cmake - CMTK CMake configuration file for external projects. # # This file is configured by CMTK and used by the UseCMTK.cmake module # to load CMTK's settings for an external project. # The CMTK install tree. @CMTK_CONFIG_CODE@ # The include directories SET(CMTK_INCLUDE_DIRS "@CMTK_INCLUDE_DIRS_CONFIG@") # The library directories SET(CMTK_LIBRARY_DIRS "@CMTK_LIBRARY_DIRS_CONFIG@") # The binary directory SET(CMTK_BINARY_DIR "@CMTK_BINARY_DIR_CONFIG@") # The C and C++ flags added by CMTK to the cmake-configured flags. SET(CMTK_REQUIRED_C_FLAGS "@CMTK_REQUIRED_C_FLAGS@") SET(CMTK_REQUIRED_CXX_FLAGS "@CMTK_REQUIRED_CXX_FLAGS@") SET(CMTK_REQUIRED_LINK_FLAGS "@CMTK_REQUIRED_LINK_FLAGS@") # The CMTK version number SET(CMTK_VERSION_MAJOR "@CMTK_VERSION_MAJOR@") SET(CMTK_VERSION_MINOR "@CMTK_VERSION_MINOR@") SET(CMTK_VERSION_PATCH "@CMTK_VERSION_PATCH@") # The location of the UseCMTK.cmake file. SET(CMTK_USE_FILE "@CMTK_CONFIG_PREFIX_CONFIG@/@CMTK_USE_FILE@") # The build settings file. SET(CMTK_BUILD_SETTINGS_FILE "@CMTK_CONFIG_PREFIX_CONFIG@/@CMTK_BUILD_SETTINGS_FILE@") # The library dependencies file. SET(CMTK_LIBRARY_DEPENDS_FILE "@CMTK_CONFIG_PREFIX_CONFIG@/@CMTK_LIBRARY_DEPENDS_FILE@") # Whether CMTK was built with shared libraries. SET(CMTK_BUILD_SHARED "@BUILD_SHARED_LIBS@") # A list of all libraries for CMTK. Those listed here should # automatically pull in their dependencies. SET(CMTK_LIBRARIES cmtkQt cmtkPipeline cmtkRegistration cmtkSegmentation cmtkIO cmtkBase cmtkSystem) # The CMTK library dependencies. IF(NOT CMTK_NO_LIBRARY_DEPENDS AND EXISTS "${CMTK_LIBRARY_DEPENDS_FILE}") INCLUDE("${CMTK_LIBRARY_DEPENDS_FILE}") ENDIF(NOT CMTK_NO_LIBRARY_DEPENDS AND EXISTS "${CMTK_LIBRARY_DEPENDS_FILE}") cmtk-3.3.1/CMake/000077500000000000000000000000001276303427400134445ustar00rootroot00000000000000cmtk-3.3.1/CMake/FindCUDA.cmake000066400000000000000000001477001276303427400157740ustar00rootroot00000000000000# - Tools for building CUDA C files: libraries and build dependencies. # This script locates the NVIDIA CUDA C tools. It should work on linux, windows, # and mac and should be reasonably up to date with CUDA C releases. # # This script makes use of the standard find_package arguments of , # REQUIRED and QUIET. CUDA_FOUND will report if an acceptable version of CUDA # was found. # # The script will prompt the user to specify CUDA_TOOLKIT_ROOT_DIR if the prefix # cannot be determined by the location of nvcc in the system path and REQUIRED # is specified to find_package(). To use a different installed version of the # toolkit set the environment variable CUDA_BIN_PATH before running cmake # (e.g. CUDA_BIN_PATH=/usr/local/cuda1.0 instead of the default /usr/local/cuda) # or set CUDA_TOOLKIT_ROOT_DIR after configuring. If you change the value of # CUDA_TOOLKIT_ROOT_DIR, various components that depend on the path will be # relocated. # # It might be necessary to set CUDA_TOOLKIT_ROOT_DIR manually on certain # platforms, or to use a cuda runtime not installed in the default location. In # newer versions of the toolkit the cuda library is included with the graphics # driver- be sure that the driver version matches what is needed by the cuda # runtime version. # # The following variables affect the behavior of the macros in the script (in # alphebetical order). Note that any of these flags can be changed multiple # times in the same directory before calling CUDA_ADD_EXECUTABLE, # CUDA_ADD_LIBRARY, CUDA_COMPILE, CUDA_COMPILE_PTX or CUDA_WRAP_SRCS. # # CUDA_64_BIT_DEVICE_CODE (Default matches host bit size) # -- Set to ON to compile for 64 bit device code, OFF for 32 bit device code. # Note that making this different from the host code when generating object # or C files from CUDA code just won't work, because size_t gets defined by # nvcc in the generated source. If you compile to PTX and then load the # file yourself, you can mix bit sizes between device and host. # # CUDA_ATTACH_VS_BUILD_RULE_TO_CUDA_FILE (Default ON) # -- Set to ON if you want the custom build rule to be attached to the source # file in Visual Studio. Turn OFF if you add the same cuda file to multiple # targets. # # This allows the user to build the target from the CUDA file; however, bad # things can happen if the CUDA source file is added to multiple targets. # When performing parallel builds it is possible for the custom build # command to be run more than once and in parallel causing cryptic build # errors. VS runs the rules for every source file in the target, and a # source can have only one rule no matter how many projects it is added to. # When the rule is run from multiple targets race conditions can occur on # the generated file. Eventually everything will get built, but if the user # is unaware of this behavior, there may be confusion. It would be nice if # this script could detect the reuse of source files across multiple targets # and turn the option off for the user, but no good solution could be found. # # CUDA_BUILD_CUBIN (Default OFF) # -- Set to ON to enable and extra compilation pass with the -cubin option in # Device mode. The output is parsed and register, shared memory usage is # printed during build. # # CUDA_BUILD_EMULATION (Default OFF for device mode) # -- Set to ON for Emulation mode. -D_DEVICEEMU is defined for CUDA C files # when CUDA_BUILD_EMULATION is TRUE. # # CUDA_GENERATED_OUTPUT_DIR (Default CMAKE_CURRENT_BINARY_DIR) # -- Set to the path you wish to have the generated files placed. If it is # blank output files will be placed in CMAKE_CURRENT_BINARY_DIR. # Intermediate files will always be placed in # CMAKE_CURRENT_BINARY_DIR/CMakeFiles. # # CUDA_HOST_COMPILATION_CPP (Default ON) # -- Set to OFF for C compilation of host code. # # CUDA_NVCC_FLAGS # CUDA_NVCC_FLAGS_ # -- Additional NVCC command line arguments. NOTE: multiple arguments must be # semi-colon delimited (e.g. --compiler-options;-Wall) # # CUDA_PROPAGATE_HOST_FLAGS (Default ON) # -- Set to ON to propagate CMAKE_{C,CXX}_FLAGS and their configuration # dependent counterparts (e.g. CMAKE_C_FLAGS_DEBUG) automatically to the # host compiler through nvcc's -Xcompiler flag. This helps make the # generated host code match the rest of the system better. Sometimes # certain flags give nvcc problems, and this will help you turn the flag # propagation off. This does not affect the flags supplied directly to nvcc # via CUDA_NVCC_FLAGS or through the OPTION flags specified through # CUDA_ADD_LIBRARY, CUDA_ADD_EXECUTABLE, or CUDA_WRAP_SRCS. Flags used for # shared library compilation are not affected by this flag. # # CUDA_VERBOSE_BUILD (Default OFF) # -- Set to ON to see all the commands used when building the CUDA file. When # using a Makefile generator the value defaults to VERBOSE (run make # VERBOSE=1 to see output), although setting CUDA_VERBOSE_BUILD to ON will # always print the output. # # The script creates the following macros (in alphebetical order): # # CUDA_ADD_CUFFT_TO_TARGET( cuda_target ) # -- Adds the cufft library to the target (can be any target). Handles whether # you are in emulation mode or not. # # CUDA_ADD_CUBLAS_TO_TARGET( cuda_target ) # -- Adds the cublas library to the target (can be any target). Handles # whether you are in emulation mode or not. # # CUDA_ADD_EXECUTABLE( cuda_target file0 file1 ... # [WIN32] [MACOSX_BUNDLE] [EXCLUDE_FROM_ALL] [OPTIONS ...] ) # -- Creates an executable "cuda_target" which is made up of the files # specified. All of the non CUDA C files are compiled using the standard # build rules specified by CMAKE and the cuda files are compiled to object # files using nvcc and the host compiler. In addition CUDA_INCLUDE_DIRS is # added automatically to include_directories(). Some standard CMake target # calls can be used on the target after calling this macro # (e.g. set_target_properties and target_link_libraries), but setting # properties that adjust compilation flags will not affect code compiled by # nvcc. Such flags should be modified before calling CUDA_ADD_EXECUTABLE, # CUDA_ADD_LIBRARY or CUDA_WRAP_SRCS. # # CUDA_ADD_LIBRARY( cuda_target file0 file1 ... # [STATIC | SHARED | MODULE] [EXCLUDE_FROM_ALL] [OPTIONS ...] ) # -- Same as CUDA_ADD_EXECUTABLE except that a library is created. # # CUDA_BUILD_CLEAN_TARGET() # -- Creates a convience target that deletes all the dependency files # generated. You should make clean after running this target to ensure the # dependency files get regenerated. # # CUDA_COMPILE( generated_files file0 file1 ... [STATIC | SHARED | MODULE] # [OPTIONS ...] ) # -- Returns a list of generated files from the input source files to be used # with ADD_LIBRARY or ADD_EXECUTABLE. # # CUDA_COMPILE_PTX( generated_files file0 file1 ... [OPTIONS ...] ) # -- Returns a list of PTX files generated from the input source files. # # CUDA_INCLUDE_DIRECTORIES( path0 path1 ... ) # -- Sets the directories that should be passed to nvcc # (e.g. nvcc -Ipath0 -Ipath1 ... ). These paths usually contain other .cu # files. # # CUDA_WRAP_SRCS ( cuda_target format generated_files file0 file1 ... # [STATIC | SHARED | MODULE] [OPTIONS ...] ) # -- This is where all the magic happens. CUDA_ADD_EXECUTABLE, # CUDA_ADD_LIBRARY, CUDA_COMPILE, and CUDA_COMPILE_PTX all call this # function under the hood. # # Given the list of files (file0 file1 ... fileN) this macro generates # custom commands that generate either PTX or linkable objects (use "PTX" or # "OBJ" for the format argument to switch). Files that don't end with .cu # or have the HEADER_FILE_ONLY property are ignored. # # The arguments passed in after OPTIONS are extra command line options to # give to nvcc. You can also specify per configuration options by # specifying the name of the configuration followed by the options. General # options must preceed configuration specific options. Not all # configurations need to be specified, only the ones provided will be used. # # OPTIONS -DFLAG=2 "-DFLAG_OTHER=space in flag" # DEBUG -g # RELEASE --use_fast_math # RELWITHDEBINFO --use_fast_math;-g # MINSIZEREL --use_fast_math # # For certain configurations (namely VS generating object files with # CUDA_ATTACH_VS_BUILD_RULE_TO_CUDA_FILE set to ON), no generated file will # be produced for the given cuda file. This is because when you add the # cuda file to Visual Studio it knows that this file produces an object file # and will link in the resulting object file automatically. # # This script will also generate a separate cmake script that is used at # build time to invoke nvcc. This is for serveral reasons. # # 1. nvcc can return negative numbers as return values which confuses # Visual Studio into thinking that the command succeeded. The script now # checks the error codes and produces errors when there was a problem. # # 2. nvcc has been known to not delete incomplete results when it # encounters problems. This confuses build systems into thinking the # target was generated when in fact an unusable file exists. The script # now deletes the output files if there was an error. # # 3. By putting all the options that affect the build into a file and then # make the build rule dependent on the file, the output files will be # regenerated when the options change. # # This script also looks at optional arguments STATIC, SHARED, or MODULE to # determine when to target the object compilation for a shared library. # BUILD_SHARED_LIBS is ignored in CUDA_WRAP_SRCS, but it is respected in # CUDA_ADD_LIBRARY. On some systems special flags are added for building # objects intended for shared libraries. A preprocessor macro, # _EXPORTS is defined when a shared library compilation is # detected. # # Flags passed into add_definitions with -D or /D are passed along to nvcc. # # The script defines the following variables: # # CUDA_VERSION_MAJOR -- The major version of cuda as reported by nvcc. # CUDA_VERSION_MINOR -- The minor version. # CUDA_VERSION # CUDA_VERSION_STRING -- CUDA_VERSION_MAJOR.CUDA_VERSION_MINOR # # CUDA_TOOLKIT_ROOT_DIR -- Path to the CUDA Toolkit (defined if not set). # CUDA_SDK_ROOT_DIR -- Path to the CUDA SDK. Use this to find files in the # SDK. This script will not directly support finding # specific libraries or headers, as that isn't # supported by NVIDIA. If you want to change # libraries when the path changes see the # FindCUDA.cmake script for an example of how to clear # these variables. There are also examples of how to # use the CUDA_SDK_ROOT_DIR to locate headers or # libraries, if you so choose (at your own risk). # CUDA_INCLUDE_DIRS -- Include directory for cuda headers. Added automatically # for CUDA_ADD_EXECUTABLE and CUDA_ADD_LIBRARY. # CUDA_LIBRARIES -- Cuda RT library. # CUDA_CUFFT_LIBRARIES -- Device or emulation library for the Cuda FFT # implementation (alternative to: # CUDA_ADD_CUFFT_TO_TARGET macro) # CUDA_CUBLAS_LIBRARIES -- Device or emulation library for the Cuda BLAS # implementation (alterative to: # CUDA_ADD_CUBLAS_TO_TARGET macro). # # # James Bigler, NVIDIA Corp (nvidia.com - jbigler) # Abe Stephens, SCI Institute -- http://www.sci.utah.edu/~abe/FindCuda.html # # Copyright (c) 2008 - 2009 NVIDIA Corporation. All rights reserved. # # Copyright (c) 2007-2009 # Scientific Computing and Imaging Institute, University of Utah # # This code is licensed under the MIT License. See the FindCUDA.cmake script # for the text of the license. # The MIT License # # License for the specific language governing rights and limitations under # Permission is hereby granted, free of charge, to any person obtaining a # copy of this software and associated documentation files (the "Software"), # to deal in the Software without restriction, including without limitation # the rights to use, copy, modify, merge, publish, distribute, sublicense, # and/or sell copies of the Software, and to permit persons to whom the # Software is furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included # in all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS # OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL # THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER # DEALINGS IN THE SOFTWARE. # ############################################################################### # FindCUDA.cmake # We need to have at least this version to support the VERSION_LESS argument to 'if' (2.6.2) and unset (2.6.3) cmake_policy(PUSH) cmake_minimum_required(VERSION 2.6.3) cmake_policy(POP) # This macro helps us find the location of helper files we will need the full path to macro(CUDA_FIND_HELPER_FILE _name _extension) set(_full_name "${_name}.${_extension}") # CMAKE_CURRENT_LIST_FILE contains the full path to the file currently being # processed. Using this variable, we can pull out the current path, and # provide a way to get access to the other files we need local to here. get_filename_component(CMAKE_CURRENT_LIST_DIR "${CMAKE_CURRENT_LIST_FILE}" PATH) find_file(CUDA_${_name} ${_full_name} PATHS ${CMAKE_CURRENT_LIST_DIR}/FindCUDA NO_DEFAULT_PATH) if(NOT CUDA_${_name}) set(error_message "${_full_name} not found in CMAKE_MODULE_PATH") if(CUDA_FIND_REQUIRED) message(FATAL_ERROR "${error_message}") else(CUDA_FIND_REQUIRED) if(NOT CUDA_FIND_QUIETLY) message(STATUS "${error_message}") endif(NOT CUDA_FIND_QUIETLY) endif(CUDA_FIND_REQUIRED) endif(NOT CUDA_${_name}) # Set this variable as internal, so the user isn't bugged with it. set(CUDA_${_name} ${CUDA_${_name}} CACHE INTERNAL "Location of ${_full_name}" FORCE) endmacro(CUDA_FIND_HELPER_FILE) ##################################################################### ## CUDA_INCLUDE_NVCC_DEPENDENCIES ## # So we want to try and include the dependency file if it exists. If # it doesn't exist then we need to create an empty one, so we can # include it. # If it does exist, then we need to check to see if all the files it # depends on exist. If they don't then we should clear the dependency # file and regenerate it later. This covers the case where a header # file has disappeared or moved. macro(CUDA_INCLUDE_NVCC_DEPENDENCIES dependency_file) set(CUDA_NVCC_DEPEND) set(CUDA_NVCC_DEPEND_REGENERATE FALSE) # Include the dependency file. Create it first if it doesn't exist . The # INCLUDE puts a dependency that will force CMake to rerun and bring in the # new info when it changes. DO NOT REMOVE THIS (as I did and spent a few # hours figuring out why it didn't work. if(NOT EXISTS ${dependency_file}) file(WRITE ${dependency_file} "#FindCUDA.cmake generated file. Do not edit.\n") endif() # Always include this file to force CMake to run again next # invocation and rebuild the dependencies. #message("including dependency_file = ${dependency_file}") include(${dependency_file}) # Now we need to verify the existence of all the included files # here. If they aren't there we need to just blank this variable and # make the file regenerate again. # if(DEFINED CUDA_NVCC_DEPEND) # message("CUDA_NVCC_DEPEND set") # else() # message("CUDA_NVCC_DEPEND NOT set") # endif() if(CUDA_NVCC_DEPEND) #message("CUDA_NVCC_DEPEND true") foreach(f ${CUDA_NVCC_DEPEND}) #message("searching for ${f}") if(NOT EXISTS ${f}) #message("file ${f} not found") set(CUDA_NVCC_DEPEND_REGENERATE TRUE) endif() endforeach(f) else(CUDA_NVCC_DEPEND) #message("CUDA_NVCC_DEPEND false") # No dependencies, so regenerate the file. set(CUDA_NVCC_DEPEND_REGENERATE TRUE) endif(CUDA_NVCC_DEPEND) #message("CUDA_NVCC_DEPEND_REGENERATE = ${CUDA_NVCC_DEPEND_REGENERATE}") # No incoming dependencies, so we need to generate them. Make the # output depend on the dependency file itself, which should cause the # rule to re-run. if(CUDA_NVCC_DEPEND_REGENERATE) file(WRITE ${dependency_file} "#FindCUDA.cmake generated file. Do not edit.\n") endif(CUDA_NVCC_DEPEND_REGENERATE) endmacro(CUDA_INCLUDE_NVCC_DEPENDENCIES) ############################################################################### ############################################################################### # Setup variables' defaults ############################################################################### ############################################################################### # Allow the user to specify if the device code is supposed to be 32 or 64 bit. if(CMAKE_SIZEOF_VOID_P EQUAL 8) set(CUDA_64_BIT_DEVICE_CODE_DEFAULT ON) else() set(CUDA_64_BIT_DEVICE_CODE_DEFAULT OFF) endif() option(CUDA_64_BIT_DEVICE_CODE "Compile device code in 64 bit mode" ${CUDA_64_BIT_DEVICE_CODE_DEFAULT}) # Attach the build rule to the source file in VS. This option option(CUDA_ATTACH_VS_BUILD_RULE_TO_CUDA_FILE "Attach the build rule to the CUDA source file. Enable only when the CUDA source file is added to at most one target." ON) # Prints out extra information about the cuda file during compilation option(CUDA_BUILD_CUBIN "Generate and parse .cubin files in Device mode." OFF) # Set whether we are using emulation or device mode. option(CUDA_BUILD_EMULATION "Build in Emulation mode" OFF) # Where to put the generated output. set(CUDA_GENERATED_OUTPUT_DIR "" CACHE PATH "Directory to put all the output files. If blank it will default to the CMAKE_CURRENT_BINARY_DIR") # Parse HOST_COMPILATION mode. option(CUDA_HOST_COMPILATION_CPP "Generated file extension" ON) # Extra user settable flags set(CUDA_NVCC_FLAGS "" CACHE STRING "Semi-colon delimit multiple arguments.") # Propagate the host flags to the host compiler via -Xcompiler option(CUDA_PROPAGATE_HOST_FLAGS "Propage C/CXX_FLAGS and friends to the host compiler via -Xcompile" ON) # Specifies whether the commands used when compiling the .cu file will be printed out. option(CUDA_VERBOSE_BUILD "Print out the commands run while compiling the CUDA source file. With the Makefile generator this defaults to VERBOSE variable specified on the command line, but can be forced on with this option." OFF) mark_as_advanced( CUDA_64_BIT_DEVICE_CODE CUDA_ATTACH_VS_BUILD_RULE_TO_CUDA_FILE CUDA_GENERATED_OUTPUT_DIR CUDA_HOST_COMPILATION_CPP CUDA_NVCC_FLAGS CUDA_PROPAGATE_HOST_FLAGS ) # Makefile and similar generators don't define CMAKE_CONFIGURATION_TYPES, so we # need to add another entry for the CMAKE_BUILD_TYPE. We also need to add the # standerd set of 4 build types (Debug, MinSizeRel, Release, and RelWithDebInfo) # for completeness. We need run this loop in order to accomodate the addition # of extra configuration types. Duplicate entries will be removed by # REMOVE_DUPLICATES. set(CUDA_configuration_types ${CMAKE_CONFIGURATION_TYPES} ${CMAKE_BUILD_TYPE} Debug MinSizeRel Release RelWithDebInfo) list(REMOVE_DUPLICATES CUDA_configuration_types) foreach(config ${CUDA_configuration_types}) string(TOUPPER ${config} config_upper) set(CUDA_NVCC_FLAGS_${config_upper} "" CACHE STRING "Semi-colon delimit multiple arguments.") mark_as_advanced(CUDA_NVCC_FLAGS_${config_upper}) endforeach() ############################################################################### ############################################################################### # Locate CUDA, Set Build Type, etc. ############################################################################### ############################################################################### # Check to see if the CUDA_TOOLKIT_ROOT_DIR and CUDA_SDK_ROOT_DIR have changed, # if they have then clear the cache variables, so that will be detected again. if(NOT "${CUDA_TOOLKIT_ROOT_DIR}" STREQUAL "${CUDA_TOOLKIT_ROOT_DIR_INTERNAL}") unset(CUDA_NVCC_EXECUTABLE CACHE) unset(CUDA_VERSION CACHE) unset(CUDA_TOOLKIT_INCLUDE CACHE) unset(CUDA_CUDART_LIBRARY CACHE) unset(CUDA_CUDA_LIBRARY CACHE) unset(CUDA_cublas_LIBRARY CACHE) unset(CUDA_cublasemu_LIBRARY CACHE) unset(CUDA_cufft_LIBRARY CACHE) unset(CUDA_cufftemu_LIBRARY CACHE) endif() if(NOT "${CUDA_SDK_ROOT_DIR}" STREQUAL "${CUDA_SDK_ROOT_DIR_INTERNAL}") # No specific variables to catch. Use this kind of code before calling # find_package(CUDA) to clean up any variables that may depend on this path. # unset(MY_SPECIAL_CUDA_SDK_INCLUDE_DIR CACHE) # unset(MY_SPECIAL_CUDA_SDK_LIBRARY CACHE) endif() # Search for the cuda distribution. if(NOT CUDA_TOOLKIT_ROOT_DIR) # Search in the CUDA_BIN_PATH first. find_path(CUDA_TOOLKIT_ROOT_DIR NAMES nvcc nvcc.exe PATHS ENV CUDA_BIN_PATH DOC "Toolkit location." NO_DEFAULT_PATH ) # Now search default paths find_path(CUDA_TOOLKIT_ROOT_DIR NAMES nvcc nvcc.exe PATHS /usr/local/bin /usr/local/cuda/bin DOC "Toolkit location." ) if (CUDA_TOOLKIT_ROOT_DIR) string(REGEX REPLACE "[/\\\\]?bin[64]*[/\\\\]?$" "" CUDA_TOOLKIT_ROOT_DIR ${CUDA_TOOLKIT_ROOT_DIR}) # We need to force this back into the cache. set(CUDA_TOOLKIT_ROOT_DIR ${CUDA_TOOLKIT_ROOT_DIR} CACHE PATH "Toolkit location." FORCE) endif(CUDA_TOOLKIT_ROOT_DIR) if (NOT EXISTS ${CUDA_TOOLKIT_ROOT_DIR}) if(CUDA_FIND_REQUIRED) message(FATAL_ERROR "Specify CUDA_TOOLKIT_ROOT_DIR") elseif(NOT CUDA_FIND_QUIETLY) message("CUDA_TOOLKIT_ROOT_DIR not found or specified") endif() endif (NOT EXISTS ${CUDA_TOOLKIT_ROOT_DIR}) endif (NOT CUDA_TOOLKIT_ROOT_DIR) # CUDA_NVCC_EXECUTABLE find_program(CUDA_NVCC_EXECUTABLE NAMES nvcc PATHS "${CUDA_TOOLKIT_ROOT_DIR}/bin" "${CUDA_TOOLKIT_ROOT_DIR}/bin64" ENV CUDA_BIN_PATH NO_DEFAULT_PATH ) # Search default search paths, after we search our own set of paths. find_program(CUDA_NVCC_EXECUTABLE nvcc) mark_as_advanced(CUDA_NVCC_EXECUTABLE) if(CUDA_NVCC_EXECUTABLE AND NOT CUDA_VERSION) # Compute the version. execute_process (COMMAND ${CUDA_NVCC_EXECUTABLE} "--version" OUTPUT_VARIABLE NVCC_OUT) string(REGEX REPLACE ".*release ([0-9]+)\\.([0-9]+).*" "\\1" CUDA_VERSION_MAJOR ${NVCC_OUT}) string(REGEX REPLACE ".*release ([0-9]+)\\.([0-9]+).*" "\\2" CUDA_VERSION_MINOR ${NVCC_OUT}) set(CUDA_VERSION "${CUDA_VERSION_MAJOR}.${CUDA_VERSION_MINOR}" CACHE STRING "Version of CUDA as computed from nvcc.") mark_as_advanced(CUDA_VERSION) endif() # Always set this convenience variable set(CUDA_VERSION_STRING "${CUDA_VERSION}") # Here we need to determine if the version we found is acceptable. We will # assume that is unless CUDA_FIND_VERSION_EXACT or CUDA_FIND_VERSION is # specified. The presence of either of these options checks the version # string and signals if the version is acceptable or not. set(_cuda_version_acceptable TRUE) # if(CUDA_FIND_VERSION_EXACT AND NOT CUDA_VERSION VERSION_EQUAL CUDA_FIND_VERSION) set(_cuda_version_acceptable FALSE) endif() # if(CUDA_FIND_VERSION AND CUDA_VERSION VERSION_LESS CUDA_FIND_VERSION) set(_cuda_version_acceptable FALSE) endif() # if(NOT _cuda_version_acceptable) set(_cuda_error_message "Requested CUDA version ${CUDA_FIND_VERSION}, but found unacceptable version ${CUDA_VERSION}") if(CUDA_FIND_REQUIRED) message("${_cuda_error_message}") elseif(NOT CUDA_FIND_QUIETLY) message("${_cuda_error_message}") endif() endif() # CUDA_TOOLKIT_INCLUDE find_path(CUDA_TOOLKIT_INCLUDE device_functions.h # Header included in toolkit PATHS "${CUDA_TOOLKIT_ROOT_DIR}/include" ENV CUDA_INC_PATH NO_DEFAULT_PATH ) # Search default search paths, after we search our own set of paths. find_path(CUDA_TOOLKIT_INCLUDE device_functions.h) mark_as_advanced(CUDA_TOOLKIT_INCLUDE) # Set the user list of include dir to nothing to initialize it. set (CUDA_NVCC_INCLUDE_ARGS_USER "") set (CUDA_INCLUDE_DIRS ${CUDA_TOOLKIT_INCLUDE}) macro(FIND_LIBRARY_LOCAL_FIRST _var _names _doc) if(CMAKE_SIZEOF_VOID_P EQUAL 8) set(_cuda_64bit_lib_dir "${CUDA_TOOLKIT_ROOT_DIR}/lib64") endif() find_library(${_var} NAMES ${_names} PATHS ${_cuda_64bit_lib_dir} "${CUDA_TOOLKIT_ROOT_DIR}/lib" ENV CUDA_LIB_PATH DOC ${_doc} NO_DEFAULT_PATH ) # Search default search paths, after we search our own set of paths. find_library(${_var} NAMES ${_names} DOC ${_doc}) endmacro() # CUDA_LIBRARIES find_library_local_first(CUDA_CUDART_LIBRARY cudart "\"cudart\" library") set(CUDA_LIBRARIES ${CUDA_CUDART_LIBRARY}) if(APPLE) # We need to add the path to cudart to the linker using rpath, since the # library name for the cuda libraries is prepended with @rpath. get_filename_component(_cuda_path_to_cudart "${CUDA_CUDART_LIBRARY}" PATH) if(_cuda_path_to_cudart) list(APPEND CUDA_LIBRARIES -Wl,-rpath "-Wl,${_cuda_path_to_cudart}") endif() endif() # 1.1 toolkit on linux doesn't appear to have a separate library on # some platforms. find_library_local_first(CUDA_CUDA_LIBRARY cuda "\"cuda\" library (older versions only).") # Add cuda library to the link line only if it is found. if (CUDA_CUDA_LIBRARY) set(CUDA_LIBRARIES ${CUDA_LIBRARIES} ${CUDA_CUDA_LIBRARY}) endif(CUDA_CUDA_LIBRARY) mark_as_advanced( CUDA_CUDA_LIBRARY CUDA_CUDART_LIBRARY ) ####################### # Look for some of the toolkit helper libraries macro(FIND_CUDA_HELPER_LIBS _name) find_library_local_first(CUDA_${_name}_LIBRARY ${_name} "\"${_name}\" library") mark_as_advanced(CUDA_${_name}_LIBRARY) endmacro(FIND_CUDA_HELPER_LIBS) # Search for cufft and cublas libraries. find_cuda_helper_libs(cufftemu) find_cuda_helper_libs(cublasemu) find_cuda_helper_libs(cufft) find_cuda_helper_libs(cublas) if (CUDA_BUILD_EMULATION) set(CUDA_CUFFT_LIBRARIES ${CUDA_cufftemu_LIBRARY}) set(CUDA_CUBLAS_LIBRARIES ${CUDA_cublasemu_LIBRARY}) else() set(CUDA_CUFFT_LIBRARIES ${CUDA_cufft_LIBRARY}) set(CUDA_CUBLAS_LIBRARIES ${CUDA_cublas_LIBRARY}) endif() ######################## # Look for the SDK stuff find_path(CUDA_SDK_ROOT_DIR common/inc/cutil.h "$ENV{NVSDKCUDA_ROOT}" "[HKEY_LOCAL_MACHINE\\SOFTWARE\\NVIDIA Corporation\\Installed Products\\NVIDIA SDK 10\\Compute;InstallDir]" "/Developer/GPU\ Computing/C" ) # Keep the CUDA_SDK_ROOT_DIR first in order to be able to override the # environment variables. set(CUDA_SDK_SEARCH_PATH "${CUDA_SDK_ROOT_DIR}" "${CUDA_TOOLKIT_ROOT_DIR}/local/NVSDK0.2" "${CUDA_TOOLKIT_ROOT_DIR}/NVSDK0.2" "${CUDA_TOOLKIT_ROOT_DIR}/NV_CUDA_SDK" "$ENV{HOME}/NVIDIA_CUDA_SDK" "$ENV{HOME}/NVIDIA_CUDA_SDK_MACOSX" "/Developer/CUDA" ) # Example of how to find an include file from the CUDA_SDK_ROOT_DIR # find_path(CUDA_CUT_INCLUDE_DIR # cutil.h # PATHS ${CUDA_SDK_SEARCH_PATH} # PATH_SUFFIXES "common/inc" # DOC "Location of cutil.h" # NO_DEFAULT_PATH # ) # # Now search system paths # find_path(CUDA_CUT_INCLUDE_DIR cutil.h DOC "Location of cutil.h") # mark_as_advanced(CUDA_CUT_INCLUDE_DIR) # Example of how to find a library in the CUDA_SDK_ROOT_DIR # # cutil library is called cutil64 for 64 bit builds on windows. We don't want # # to get these confused, so we are setting the name based on the word size of # # the build. # if(CMAKE_SIZEOF_VOID_P EQUAL 8) # set(cuda_cutil_name cutil64) # else(CMAKE_SIZEOF_VOID_P EQUAL 8) # set(cuda_cutil_name cutil32) # endif(CMAKE_SIZEOF_VOID_P EQUAL 8) # find_library(CUDA_CUT_LIBRARY # NAMES cutil ${cuda_cutil_name} # PATHS ${CUDA_SDK_SEARCH_PATH} # # The new version of the sdk shows up in common/lib, but the old one is in lib # PATH_SUFFIXES "common/lib" "lib" # DOC "Location of cutil library" # NO_DEFAULT_PATH # ) # # Now search system paths # find_library(CUDA_CUT_LIBRARY NAMES cutil ${cuda_cutil_name} DOC "Location of cutil library") # mark_as_advanced(CUDA_CUT_LIBRARY) # set(CUDA_CUT_LIBRARIES ${CUDA_CUT_LIBRARY}) ############################# # Check for required components set(CUDA_FOUND TRUE) set(CUDA_TOOLKIT_ROOT_DIR_INTERNAL "${CUDA_TOOLKIT_ROOT_DIR}" CACHE INTERNAL "This is the value of the last time CUDA_TOOLKIT_ROOT_DIR was set successfully." FORCE) set(CUDA_SDK_ROOT_DIR_INTERNAL "${CUDA_SDK_ROOT_DIR}" CACHE INTERNAL "This is the value of the last time CUDA_SDK_ROOT_DIR was set successfully." FORCE) include(FindPackageHandleStandardArgs) find_package_handle_standard_args(CUDA DEFAULT_MSG CUDA_TOOLKIT_ROOT_DIR CUDA_NVCC_EXECUTABLE CUDA_INCLUDE_DIRS CUDA_CUDART_LIBRARY _cuda_version_acceptable ) ############################################################################### ############################################################################### # Macros ############################################################################### ############################################################################### ############################################################################### # Add include directories to pass to the nvcc command. macro(CUDA_INCLUDE_DIRECTORIES) foreach(dir ${ARGN}) list(APPEND CUDA_NVCC_INCLUDE_ARGS_USER "-I${dir}") endforeach(dir ${ARGN}) endmacro(CUDA_INCLUDE_DIRECTORIES) ############################################################################## cuda_find_helper_file(parse_cubin cmake) cuda_find_helper_file(make2cmake cmake) cuda_find_helper_file(run_nvcc cmake) ############################################################################## # Separate the OPTIONS out from the sources # macro(CUDA_GET_SOURCES_AND_OPTIONS _sources _cmake_options _options) set( ${_sources} ) set( ${_cmake_options} ) set( ${_options} ) set( _found_options FALSE ) foreach(arg ${ARGN}) if(arg STREQUAL "OPTIONS") set( _found_options TRUE ) elseif( arg STREQUAL "WIN32" OR arg STREQUAL "MACOSX_BUNDLE" OR arg STREQUAL "EXCLUDE_FROM_ALL" OR arg STREQUAL "STATIC" OR arg STREQUAL "SHARED" OR arg STREQUAL "MODULE" ) list(APPEND ${_cmake_options} "${arg}") else() if ( _found_options ) list(APPEND ${_options} "${arg}") else() # Assume this is a file list(APPEND ${_sources} "${arg}") endif() endif() endforeach() endmacro() ############################################################################## # Parse the OPTIONS from ARGN and set the variables prefixed by _option_prefix # macro(CUDA_PARSE_NVCC_OPTIONS _option_prefix) set( _found_config ) foreach(arg ${ARGN}) # Determine if we are dealing with a perconfiguration flag foreach(config ${CUDA_configuration_types}) string(TOUPPER ${config} config_upper) if (arg STREQUAL "${config_upper}") set( _found_config _${arg}) # Set arg to nothing to keep it from being processed further set( arg ) endif() endforeach() if ( arg ) list(APPEND ${_option_prefix}${_found_config} "${arg}") endif() endforeach() endmacro() ############################################################################## # Helper to add the include directory for CUDA only once function(CUDA_ADD_CUDA_INCLUDE_ONCE) get_directory_property(_include_directories INCLUDE_DIRECTORIES) set(_add TRUE) if(_include_directories) foreach(dir ${_include_directories}) if("${dir}" STREQUAL "${CUDA_INCLUDE_DIRS}") set(_add FALSE) endif() endforeach() endif() if(_add) include_directories(${CUDA_INCLUDE_DIRS}) endif() endfunction() function(CUDA_BUILD_SHARED_LIBRARY shared_flag) set(cmake_args ${ARGN}) # If SHARED, MODULE, or STATIC aren't already in the list of arguments, then # add SHARED or STATIC based on the value of BUILD_SHARED_LIBS. list(FIND cmake_args SHARED _cuda_found_SHARED) list(FIND cmake_args MODULE _cuda_found_MODULE) list(FIND cmake_args STATIC _cuda_found_STATIC) if( _cuda_found_SHARED GREATER -1 OR _cuda_found_MODULE GREATER -1 OR _cuda_found_STATIC GREATER -1) set(_cuda_build_shared_libs) else() if (BUILD_SHARED_LIBS) set(_cuda_build_shared_libs SHARED) else() set(_cuda_build_shared_libs STATIC) endif() endif() set(${shared_flag} ${_cuda_build_shared_libs} PARENT_SCOPE) endfunction() ############################################################################## # This helper macro populates the following variables and setups up custom # commands and targets to invoke the nvcc compiler to generate C or PTX source # dependant upon the format parameter. The compiler is invoked once with -M # to generate a dependency file and a second time with -cuda or -ptx to generate # a .cpp or .ptx file. # INPUT: # cuda_target - Target name # format - PTX or OBJ # FILE1 .. FILEN - The remaining arguments are the sources to be wrapped. # OPTIONS - Extra options to NVCC # OUTPUT: # generated_files - List of generated files ############################################################################## ############################################################################## macro(CUDA_WRAP_SRCS cuda_target format generated_files) if( ${format} MATCHES "PTX" ) set( compile_to_ptx ON ) elseif( ${format} MATCHES "OBJ") set( compile_to_ptx OFF ) else() message( FATAL_ERROR "Invalid format flag passed to CUDA_WRAP_SRCS: '${format}'. Use OBJ or PTX.") endif() # Set up all the command line flags here, so that they can be overriden on a per target basis. set(nvcc_flags "") # Emulation if the card isn't present. if (CUDA_BUILD_EMULATION) # Emulation. set(nvcc_flags ${nvcc_flags} --device-emulation -D_DEVICEEMU -g) else(CUDA_BUILD_EMULATION) # Device mode. No flags necessary. endif(CUDA_BUILD_EMULATION) if(CUDA_HOST_COMPILATION_CPP) set(CUDA_C_OR_CXX CXX) else(CUDA_HOST_COMPILATION_CPP) if(CUDA_VERSION VERSION_LESS "3.0") set(nvcc_flags ${nvcc_flags} --host-compilation C) else() message(WARNING "--host-compilation flag is deprecated in CUDA version >= 3.0. Removing --host-compilation C flag" ) endif() set(CUDA_C_OR_CXX C) endif(CUDA_HOST_COMPILATION_CPP) set(generated_extension ${CMAKE_${CUDA_C_OR_CXX}_OUTPUT_EXTENSION}) if(CUDA_64_BIT_DEVICE_CODE) set(nvcc_flags ${nvcc_flags} -m64) else() set(nvcc_flags ${nvcc_flags} -m32) endif() # This needs to be passed in at this stage, because VS needs to fill out the # value of VCInstallDir from within VS. if(CMAKE_GENERATOR MATCHES "Visual Studio") if( CMAKE_SIZEOF_VOID_P EQUAL 8 ) # Add nvcc flag for 64b Windows set(ccbin_flags -D "\"CCBIN:PATH=$(VCInstallDir)bin\"" ) endif() endif() # Figure out which configure we will use and pass that in as an argument to # the script. We need to defer the decision until compilation time, because # for VS projects we won't know if we are making a debug or release build # until build time. if(CMAKE_GENERATOR MATCHES "Visual Studio") set( CUDA_build_configuration "$(ConfigurationName)" ) else() set( CUDA_build_configuration "${CMAKE_BUILD_TYPE}") endif() # Initialize our list of includes with the user ones followed by the CUDA system ones. set(CUDA_NVCC_INCLUDE_ARGS ${CUDA_NVCC_INCLUDE_ARGS_USER} "-I${CUDA_INCLUDE_DIRS}") # Get the include directories for this directory and use them for our nvcc command. get_directory_property(CUDA_NVCC_INCLUDE_DIRECTORIES INCLUDE_DIRECTORIES) if(CUDA_NVCC_INCLUDE_DIRECTORIES) foreach(dir ${CUDA_NVCC_INCLUDE_DIRECTORIES}) list(APPEND CUDA_NVCC_INCLUDE_ARGS "-I${dir}") endforeach() endif() # Reset these variables set(CUDA_WRAP_OPTION_NVCC_FLAGS) foreach(config ${CUDA_configuration_types}) string(TOUPPER ${config} config_upper) set(CUDA_WRAP_OPTION_NVCC_FLAGS_${config_upper}) endforeach() CUDA_GET_SOURCES_AND_OPTIONS(_cuda_wrap_sources _cuda_wrap_cmake_options _cuda_wrap_options ${ARGN}) CUDA_PARSE_NVCC_OPTIONS(CUDA_WRAP_OPTION_NVCC_FLAGS ${_cuda_wrap_options}) # Figure out if we are building a shared library. BUILD_SHARED_LIBS is # respected in CUDA_ADD_LIBRARY. set(_cuda_build_shared_libs FALSE) # SHARED, MODULE list(FIND _cuda_wrap_cmake_options SHARED _cuda_found_SHARED) list(FIND _cuda_wrap_cmake_options MODULE _cuda_found_MODULE) if(_cuda_found_SHARED GREATER -1 OR _cuda_found_MODULE GREATER -1) set(_cuda_build_shared_libs TRUE) endif() # STATIC list(FIND _cuda_wrap_cmake_options STATIC _cuda_found_STATIC) if(_cuda_found_STATIC GREATER -1) set(_cuda_build_shared_libs FALSE) endif() # CUDA_HOST_FLAGS if(_cuda_build_shared_libs) # If we are setting up code for a shared library, then we need to add extra flags for # compiling objects for shared libraries. set(CUDA_HOST_SHARED_FLAGS ${CMAKE_SHARED_LIBRARY_${CUDA_C_OR_CXX}_FLAGS}) else() set(CUDA_HOST_SHARED_FLAGS) endif() # Only add the CMAKE_{C,CXX}_FLAGS if we are propagating host flags. We # always need to set the SHARED_FLAGS, though. if(CUDA_PROPAGATE_HOST_FLAGS) set(CUDA_HOST_FLAGS "set(CMAKE_HOST_FLAGS ${CMAKE_${CUDA_C_OR_CXX}_FLAGS} ${CUDA_HOST_SHARED_FLAGS})") else() set(CUDA_HOST_FLAGS "set(CMAKE_HOST_FLAGS ${CUDA_HOST_SHARED_FLAGS})") endif() set(CUDA_NVCC_FLAGS_CONFIG "# Build specific configuration flags") # Loop over all the configuration types to generate appropriate flags for run_nvcc.cmake foreach(config ${CUDA_configuration_types}) string(TOUPPER ${config} config_upper) # CMAKE_FLAGS are strings and not lists. By not putting quotes around CMAKE_FLAGS # we convert the strings to lists (like we want). if(CUDA_PROPAGATE_HOST_FLAGS) # nvcc chokes on -g3, so replace it with -g if(CMAKE_COMPILER_IS_GNUCC) string(REPLACE "-g3" "-g" _cuda_C_FLAGS "${CMAKE_${CUDA_C_OR_CXX}_FLAGS_${config_upper}}") else() set(_cuda_C_FLAGS "${CMAKE_${CUDA_C_OR_CXX}_FLAGS_${config_upper}}") endif() set(CUDA_HOST_FLAGS "${CUDA_HOST_FLAGS}\nset(CMAKE_HOST_FLAGS_${config_upper} ${_cuda_C_FLAGS})") endif() # Note that if we ever want CUDA_NVCC_FLAGS_ to be string (instead of a list # like it is currently), we can remove the quotes around the # ${CUDA_NVCC_FLAGS_${config_upper}} variable like the CMAKE_HOST_FLAGS_ variable. set(CUDA_NVCC_FLAGS_CONFIG "${CUDA_NVCC_FLAGS_CONFIG}\nset(CUDA_NVCC_FLAGS_${config_upper} \"${CUDA_NVCC_FLAGS_${config_upper}};;${CUDA_WRAP_OPTION_NVCC_FLAGS_${config_upper}}\")") endforeach() if(compile_to_ptx) # Don't use any of the host compilation flags for PTX targets. set(CUDA_HOST_FLAGS) set(CUDA_NVCC_FLAGS_CONFIG) endif() # Get the list of definitions from the directory property get_directory_property(CUDA_NVCC_DEFINITIONS COMPILE_DEFINITIONS) if(CUDA_NVCC_DEFINITIONS) foreach(_definition ${CUDA_NVCC_DEFINITIONS}) list(APPEND nvcc_flags "-D${_definition}") endforeach() endif() if(_cuda_build_shared_libs) list(APPEND nvcc_flags "-D${cuda_target}_EXPORTS") endif() # Determine output directory if(CUDA_GENERATED_OUTPUT_DIR) set(cuda_compile_output_dir "${CUDA_GENERATED_OUTPUT_DIR}") else() set(cuda_compile_output_dir "${CMAKE_CURRENT_BINARY_DIR}") endif() # Reset the output variable set(_cuda_wrap_generated_files "") # Iterate over the macro arguments and create custom # commands for all the .cu files. foreach(file ${ARGN}) # Ignore any file marked as a HEADER_FILE_ONLY get_source_file_property(_is_header ${file} HEADER_FILE_ONLY) if(${file} MATCHES ".*\\.cu$" AND NOT _is_header) # Add a custom target to generate a c or ptx file. ###################### get_filename_component( basename ${file} NAME ) if( compile_to_ptx ) set(generated_file_path "${cuda_compile_output_dir}") set(generated_file_basename "${cuda_target}_generated_${basename}.ptx") set(format_flag "-ptx") file(MAKE_DIRECTORY "${cuda_compile_output_dir}") else( compile_to_ptx ) set(generated_file_path "${cuda_compile_output_dir}/${CMAKE_CFG_INTDIR}") set(generated_file_basename "${cuda_target}_generated_${basename}${generated_extension}") set(format_flag "-c") endif( compile_to_ptx ) # Set all of our file names. Make sure that whatever filenames that have # generated_file_path in them get passed in through as a command line # argument, so that the ${CMAKE_CFG_INTDIR} gets expanded at run time # instead of configure time. set(generated_file "${generated_file_path}/${generated_file_basename}") set(cmake_dependency_file "${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/${generated_file_basename}.depend") set(NVCC_generated_dependency_file "${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/${generated_file_basename}.NVCC-depend") set(generated_cubin_file "${generated_file_path}/${generated_file_basename}.cubin.txt") set(custom_target_script "${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/${generated_file_basename}.cmake") # Setup properties for obj files: if( NOT compile_to_ptx ) set_source_files_properties("${generated_file}" PROPERTIES EXTERNAL_OBJECT true # This is an object file not to be compiled, but only be linked. ) endif() # Don't add CMAKE_CURRENT_SOURCE_DIR if the path is already an absolute path. get_filename_component(file_path "${file}" PATH) if(IS_ABSOLUTE "${file_path}") set(source_file "${file}") else() set(source_file "${CMAKE_CURRENT_SOURCE_DIR}/${file}") endif() # Bring in the dependencies. Creates a variable CUDA_NVCC_DEPEND ####### cuda_include_nvcc_dependencies(${cmake_dependency_file}) # Convience string for output ########################################### if(CUDA_BUILD_EMULATION) set(cuda_build_type "Emulation") else(CUDA_BUILD_EMULATION) set(cuda_build_type "Device") endif(CUDA_BUILD_EMULATION) # Build the NVCC made dependency file ################################### set(build_cubin OFF) if ( NOT CUDA_BUILD_EMULATION AND CUDA_BUILD_CUBIN ) if ( NOT compile_to_ptx ) set ( build_cubin ON ) endif( NOT compile_to_ptx ) endif( NOT CUDA_BUILD_EMULATION AND CUDA_BUILD_CUBIN ) # Configure the build script configure_file("${CUDA_run_nvcc}" "${custom_target_script}" @ONLY) # So if a user specifies the same cuda file as input more than once, you # can have bad things happen with dependencies. Here we check an option # to see if this is the behavior they want. if(CUDA_ATTACH_VS_BUILD_RULE_TO_CUDA_FILE) set(main_dep MAIN_DEPENDENCY ${source_file}) else() set(main_dep DEPENDS ${source_file}) endif() if(CUDA_VERBOSE_BUILD) set(verbose_output ON) elseif(CMAKE_GENERATOR MATCHES "Makefiles") set(verbose_output "$(VERBOSE)") else() set(verbose_output OFF) endif() # Create up the comment string file(RELATIVE_PATH generated_file_relative_path "${CMAKE_BINARY_DIR}" "${generated_file}") if(compile_to_ptx) set(cuda_build_comment_string "Building NVCC ptx file ${generated_file_relative_path}") else() set(cuda_build_comment_string "Building NVCC (${cuda_build_type}) object ${generated_file_relative_path}") endif() # Build the generated file and dependency file ########################## add_custom_command( OUTPUT ${generated_file} # These output files depend on the source_file and the contents of cmake_dependency_file ${main_dep} DEPENDS ${CUDA_NVCC_DEPEND} DEPENDS ${custom_target_script} # Make sure the output directory exists before trying to write to it. COMMAND ${CMAKE_COMMAND} -E make_directory "${generated_file_path}" COMMAND ${CMAKE_COMMAND} ARGS -D verbose:BOOL=${verbose_output} ${ccbin_flags} -D build_configuration:STRING=${CUDA_build_configuration} -D "generated_file:STRING=${generated_file}" -D "generated_cubin_file:STRING=${generated_cubin_file}" -P "${custom_target_script}" COMMENT "${cuda_build_comment_string}" ) # Make sure the build system knows the file is generated. set_source_files_properties(${generated_file} PROPERTIES GENERATED TRUE) # Don't add the object file to the list of generated files if we are using # visual studio and we are attaching the build rule to the cuda file. VS # will add our object file to the linker automatically for us. set(cuda_add_generated_file TRUE) if(NOT compile_to_ptx AND CMAKE_GENERATOR MATCHES "Visual Studio" AND CUDA_ATTACH_VS_BUILD_RULE_TO_CUDA_FILE) # Visual Studio 8 crashes when you close the solution when you don't add the object file. if(NOT CMAKE_GENERATOR MATCHES "Visual Studio 8") #message("Not adding ${generated_file}") set(cuda_add_generated_file FALSE) endif() endif() if(cuda_add_generated_file) list(APPEND _cuda_wrap_generated_files ${generated_file}) endif() # Add the other files that we want cmake to clean on a cleanup ########## list(APPEND CUDA_ADDITIONAL_CLEAN_FILES "${cmake_dependency_file}") list(REMOVE_DUPLICATES CUDA_ADDITIONAL_CLEAN_FILES) set(CUDA_ADDITIONAL_CLEAN_FILES ${CUDA_ADDITIONAL_CLEAN_FILES} CACHE INTERNAL "List of intermediate files that are part of the cuda dependency scanning.") endif(${file} MATCHES ".*\\.cu$" AND NOT _is_header) endforeach(file) # Set the return parameter set(${generated_files} ${_cuda_wrap_generated_files}) endmacro(CUDA_WRAP_SRCS) ############################################################################### ############################################################################### # ADD LIBRARY ############################################################################### ############################################################################### macro(CUDA_ADD_LIBRARY cuda_target) CUDA_ADD_CUDA_INCLUDE_ONCE() # Separate the sources from the options CUDA_GET_SOURCES_AND_OPTIONS(_sources _cmake_options _options ${ARGN}) CUDA_BUILD_SHARED_LIBRARY(_cuda_shared_flag ${ARGN}) # Create custom commands and targets for each file. CUDA_WRAP_SRCS( ${cuda_target} OBJ _generated_files ${_sources} ${_cmake_options} ${_cuda_shared_flag} OPTIONS ${_options} ) # Add the library. add_library(${cuda_target} ${_cmake_options} ${_generated_files} ${_sources} ) target_link_libraries(${cuda_target} ${CUDA_LIBRARIES} ) # We need to set the linker language based on what the expected generated file # would be. CUDA_C_OR_CXX is computed based on CUDA_HOST_COMPILATION_CPP. set_target_properties(${cuda_target} PROPERTIES LINKER_LANGUAGE ${CUDA_C_OR_CXX} ) endmacro(CUDA_ADD_LIBRARY cuda_target) ############################################################################### ############################################################################### # ADD EXECUTABLE ############################################################################### ############################################################################### macro(CUDA_ADD_EXECUTABLE cuda_target) CUDA_ADD_CUDA_INCLUDE_ONCE() # Separate the sources from the options CUDA_GET_SOURCES_AND_OPTIONS(_sources _cmake_options _options ${ARGN}) # Create custom commands and targets for each file. CUDA_WRAP_SRCS( ${cuda_target} OBJ _generated_files ${_sources} OPTIONS ${_options} ) # Add the library. add_executable(${cuda_target} ${_cmake_options} ${_generated_files} ${_sources} ) target_link_libraries(${cuda_target} ${CUDA_LIBRARIES} ) # We need to set the linker language based on what the expected generated file # would be. CUDA_C_OR_CXX is computed based on CUDA_HOST_COMPILATION_CPP. set_target_properties(${cuda_target} PROPERTIES LINKER_LANGUAGE ${CUDA_C_OR_CXX} ) endmacro(CUDA_ADD_EXECUTABLE cuda_target) ############################################################################### ############################################################################### # CUDA COMPILE ############################################################################### ############################################################################### macro(CUDA_COMPILE generated_files) # Separate the sources from the options CUDA_GET_SOURCES_AND_OPTIONS(_sources _cmake_options _options ${ARGN}) # Create custom commands and targets for each file. CUDA_WRAP_SRCS( cuda_compile OBJ _generated_files ${_sources} ${_cmake_options} OPTIONS ${_options} ) set( ${generated_files} ${_generated_files}) endmacro(CUDA_COMPILE) ############################################################################### ############################################################################### # CUDA COMPILE PTX ############################################################################### ############################################################################### macro(CUDA_COMPILE_PTX generated_files) # Separate the sources from the options CUDA_GET_SOURCES_AND_OPTIONS(_sources _cmake_options _options ${ARGN}) # Create custom commands and targets for each file. CUDA_WRAP_SRCS( cuda_compile_ptx PTX _generated_files ${_sources} ${_cmake_options} OPTIONS ${_options} ) set( ${generated_files} ${_generated_files}) endmacro(CUDA_COMPILE_PTX) ############################################################################### ############################################################################### # CUDA ADD CUFFT TO TARGET ############################################################################### ############################################################################### macro(CUDA_ADD_CUFFT_TO_TARGET target) if (CUDA_BUILD_EMULATION) target_link_libraries(${target} ${CUDA_cufftemu_LIBRARY}) else() target_link_libraries(${target} ${CUDA_cufft_LIBRARY}) endif() endmacro() ############################################################################### ############################################################################### # CUDA ADD CUBLAS TO TARGET ############################################################################### ############################################################################### macro(CUDA_ADD_CUBLAS_TO_TARGET target) if (CUDA_BUILD_EMULATION) target_link_libraries(${target} ${CUDA_cublasemu_LIBRARY}) else() target_link_libraries(${target} ${CUDA_cublas_LIBRARY}) endif() endmacro() ############################################################################### ############################################################################### # CUDA BUILD CLEAN TARGET ############################################################################### ############################################################################### macro(CUDA_BUILD_CLEAN_TARGET) # Call this after you add all your CUDA targets, and you will get a convience # target. You should also make clean after running this target to get the # build system to generate all the code again. set(cuda_clean_target_name clean_cuda_depends) if (CMAKE_GENERATOR MATCHES "Visual Studio") string(TOUPPER ${cuda_clean_target_name} cuda_clean_target_name) endif() add_custom_target(${cuda_clean_target_name} COMMAND ${CMAKE_COMMAND} -E remove ${CUDA_ADDITIONAL_CLEAN_FILES}) # Clear out the variable, so the next time we configure it will be empty. # This is useful so that the files won't persist in the list after targets # have been removed. set(CUDA_ADDITIONAL_CLEAN_FILES "" CACHE INTERNAL "List of intermediate files that are part of the cuda dependency scanning.") endmacro(CUDA_BUILD_CLEAN_TARGET) cmtk-3.3.1/CMake/FindCUDA/000077500000000000000000000000001276303427400147615ustar00rootroot00000000000000cmtk-3.3.1/CMake/FindCUDA/make2cmake.cmake000066400000000000000000000057621276303427400177750ustar00rootroot00000000000000# James Bigler, NVIDIA Corp (nvidia.com - jbigler) # Abe Stephens, SCI Institute -- http://www.sci.utah.edu/~abe/FindCuda.html # # Copyright (c) 2008 - 2009 NVIDIA Corporation. All rights reserved. # # Copyright (c) 2007-2009 # Scientific Computing and Imaging Institute, University of Utah # # This code is licensed under the MIT License. See the FindCUDA.cmake script # for the text of the license. # The MIT License # # License for the specific language governing rights and limitations under # Permission is hereby granted, free of charge, to any person obtaining a # copy of this software and associated documentation files (the "Software"), # to deal in the Software without restriction, including without limitation # the rights to use, copy, modify, merge, publish, distribute, sublicense, # and/or sell copies of the Software, and to permit persons to whom the # Software is furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included # in all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS # OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL # THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER # DEALINGS IN THE SOFTWARE. # ####################################################################### # This converts a file written in makefile syntax into one that can be included # by CMake. file(READ ${input_file} depend_text) if (${depend_text} MATCHES ".+") # message("FOUND DEPENDS") # Remember, four backslashes is escaped to one backslash in the string. string(REGEX REPLACE "\\\\ " " " depend_text ${depend_text}) # This works for the nvcc -M generated dependency files. string(REGEX REPLACE "^.* : " "" depend_text ${depend_text}) string(REGEX REPLACE "[ \\\\]*\n" ";" depend_text ${depend_text}) set(dependency_list "") foreach(file ${depend_text}) string(REGEX REPLACE "^ +" "" file ${file}) if(NOT IS_DIRECTORY ${file}) # If softlinks start to matter, we should change this to REALPATH. For now we need # to flatten paths, because nvcc can generate stuff like /bin/../include instead of # just /include. get_filename_component(file_absolute "${file}" ABSOLUTE) list(APPEND dependency_list "${file_absolute}") endif(NOT IS_DIRECTORY ${file}) endforeach(file) else() # message("FOUND NO DEPENDS") endif() # Remove the duplicate entries and sort them. list(REMOVE_DUPLICATES dependency_list) list(SORT dependency_list) foreach(file ${dependency_list}) set(cuda_nvcc_depend "${cuda_nvcc_depend} \"${file}\"\n") endforeach() file(WRITE ${output_file} "# Generated by: make2cmake.cmake\nSET(CUDA_NVCC_DEPEND\n ${cuda_nvcc_depend})\n\n") cmtk-3.3.1/CMake/FindCUDA/parse_cubin.cmake000066400000000000000000000073061276303427400202630ustar00rootroot00000000000000# James Bigler, NVIDIA Corp (nvidia.com - jbigler) # Abe Stephens, SCI Institute -- http://www.sci.utah.edu/~abe/FindCuda.html # # Copyright (c) 2008 - 2009 NVIDIA Corporation. All rights reserved. # # Copyright (c) 2007-2009 # Scientific Computing and Imaging Institute, University of Utah # # This code is licensed under the MIT License. See the FindCUDA.cmake script # for the text of the license. # The MIT License # # License for the specific language governing rights and limitations under # Permission is hereby granted, free of charge, to any person obtaining a # copy of this software and associated documentation files (the "Software"), # to deal in the Software without restriction, including without limitation # the rights to use, copy, modify, merge, publish, distribute, sublicense, # and/or sell copies of the Software, and to permit persons to whom the # Software is furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included # in all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS # OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL # THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER # DEALINGS IN THE SOFTWARE. # ####################################################################### # Parses a .cubin file produced by nvcc and reports statistics about the file. file(READ ${input_file} file_text) if (${file_text} MATCHES ".+") # Remember, four backslashes is escaped to one backslash in the string. string(REGEX REPLACE ";" "\\\\;" file_text ${file_text}) string(REGEX REPLACE "\ncode" ";code" file_text ${file_text}) list(LENGTH file_text len) foreach(line ${file_text}) # Only look at "code { }" blocks. if(line MATCHES "^code") # Break into individual lines. string(REGEX REPLACE "\n" ";" line ${line}) foreach(entry ${line}) # Extract kernel names. if (${entry} MATCHES "[^g]name = ([^ ]+)") string(REGEX REPLACE ".* = ([^ ]+)" "\\1" entry ${entry}) # Check to see if the kernel name starts with "_" set(skip FALSE) # if (${entry} MATCHES "^_") # Skip the rest of this block. # message("Skipping ${entry}") # set(skip TRUE) # else (${entry} MATCHES "^_") message("Kernel: ${entry}") # endif (${entry} MATCHES "^_") endif(${entry} MATCHES "[^g]name = ([^ ]+)") # Skip the rest of the block if necessary if(NOT skip) # Registers if (${entry} MATCHES "reg([ ]+)=([ ]+)([^ ]+)") string(REGEX REPLACE ".*([ ]+)=([ ]+)([^ ]+)" "\\3" entry ${entry}) message("Registers: ${entry}") endif() # Local memory if (${entry} MATCHES "lmem([ ]+)=([ ]+)([^ ]+)") string(REGEX REPLACE ".*([ ]+)=([ ]+)([^ ]+)" "\\3" entry ${entry}) message("Local: ${entry}") endif() # Shared memory if (${entry} MATCHES "smem([ ]+)=([ ]+)([^ ]+)") string(REGEX REPLACE ".*([ ]+)=([ ]+)([^ ]+)" "\\3" entry ${entry}) message("Shared: ${entry}") endif() if (${entry} MATCHES "^}") message("") endif() endif(NOT skip) endforeach(entry) endif(line MATCHES "^code") endforeach(line) else() # message("FOUND NO DEPENDS") endif() cmtk-3.3.1/CMake/FindCUDA/run_nvcc.cmake000066400000000000000000000246371276303427400176140ustar00rootroot00000000000000# James Bigler, NVIDIA Corp (nvidia.com - jbigler) # # Copyright (c) 2008 - 2009 NVIDIA Corporation. All rights reserved. # # This code is licensed under the MIT License. See the FindCUDA.cmake script # for the text of the license. # The MIT License # # License for the specific language governing rights and limitations under # Permission is hereby granted, free of charge, to any person obtaining a # copy of this software and associated documentation files (the "Software"), # to deal in the Software without restriction, including without limitation # the rights to use, copy, modify, merge, publish, distribute, sublicense, # and/or sell copies of the Software, and to permit persons to whom the # Software is furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included # in all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS # OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL # THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER # DEALINGS IN THE SOFTWARE. ########################################################################## # This file runs the nvcc commands to produce the desired output file along with # the dependency file needed by CMake to compute dependencies. In addition the # file checks the output of each command and if the command fails it deletes the # output files. # Input variables # # verbose:BOOL=<> OFF: Be as quiet as possible (default) # ON : Describe each step # # build_configuration:STRING=<> Typically one of Debug, MinSizeRel, Release, or # RelWithDebInfo, but it should match one of the # entries in CUDA_HOST_FLAGS. This is the build # configuration used when compiling the code. If # blank or unspecified Debug is assumed as this is # what CMake does. # # generated_file:STRING=<> File to generate. This argument must be passed in. # # generated_cubin_file:STRING=<> File to generate. This argument must be passed # in if build_cubin is true. if(NOT generated_file) message(FATAL_ERROR "You must specify generated_file on the command line") endif() # Set these up as variables to make reading the generated file easier set(CMAKE_COMMAND "@CMAKE_COMMAND@") set(source_file "@source_file@") set(NVCC_generated_dependency_file "@NVCC_generated_dependency_file@") set(cmake_dependency_file "@cmake_dependency_file@") set(CUDA_make2cmake "@CUDA_make2cmake@") set(CUDA_parse_cubin "@CUDA_parse_cubin@") set(build_cubin @build_cubin@) # We won't actually use these variables for now, but we need to set this, in # order to force this file to be run again if it changes. set(generated_file_path "@generated_file_path@") set(generated_file_internal "@generated_file@") set(generated_cubin_file_internal "@generated_cubin_file@") set(CUDA_NVCC_EXECUTABLE "@CUDA_NVCC_EXECUTABLE@") set(CUDA_NVCC_FLAGS "@CUDA_NVCC_FLAGS@;;@CUDA_WRAP_OPTION_NVCC_FLAGS@") @CUDA_NVCC_FLAGS_CONFIG@ set(nvcc_flags "@nvcc_flags@") set(CUDA_NVCC_INCLUDE_ARGS "@CUDA_NVCC_INCLUDE_ARGS@") set(format_flag "@format_flag@") if(build_cubin AND NOT generated_cubin_file) message(FATAL_ERROR "You must specify generated_cubin_file on the command line") endif() # This is the list of host compilation flags. It C or CXX should already have # been chosen by FindCUDA.cmake. @CUDA_HOST_FLAGS@ # Take the compiler flags and package them up to be sent to the compiler via -Xcompiler set(nvcc_host_compiler_flags "") # If we weren't given a build_configuration, use Debug. if(NOT build_configuration) set(build_configuration Debug) endif() string(TOUPPER "${build_configuration}" build_configuration) #message("CUDA_NVCC_HOST_COMPILER_FLAGS = ${CUDA_NVCC_HOST_COMPILER_FLAGS}") foreach(flag ${CMAKE_HOST_FLAGS} ${CMAKE_HOST_FLAGS_${build_configuration}}) # Extra quotes are added around each flag to help nvcc parse out flags with spaces. set(nvcc_host_compiler_flags "${nvcc_host_compiler_flags},\"${flag}\"") endforeach() if (nvcc_host_compiler_flags) set(nvcc_host_compiler_flags "-Xcompiler" ${nvcc_host_compiler_flags}) endif() #message("nvcc_host_compiler_flags = \"${nvcc_host_compiler_flags}\"") # Add the build specific configuration flags list(APPEND CUDA_NVCC_FLAGS ${CUDA_NVCC_FLAGS_${build_configuration}}) if(DEFINED CCBIN) set(CCBIN -ccbin "${CCBIN}") endif() # cuda_execute_process - Executes a command with optional command echo and status message. # # status - Status message to print if verbose is true # command - COMMAND argument from the usual execute_process argument structure # ARGN - Remaining arguments are the command with arguments # # CUDA_result - return value from running the command # # Make this a macro instead of a function, so that things like RESULT_VARIABLE # and other return variables are present after executing the process. macro(cuda_execute_process status command) set(_command ${command}) if(NOT _command STREQUAL "COMMAND") message(FATAL_ERROR "Malformed call to cuda_execute_process. Missing COMMAND as second argument. (command = ${command})") endif() if(verbose) execute_process(COMMAND "${CMAKE_COMMAND}" -E echo -- ${status}) # Now we need to build up our command string. We are accounting for quotes # and spaces, anything else is left up to the user to fix if they want to # copy and paste a runnable command line. set(cuda_execute_process_string) foreach(arg ${ARGN}) # If there are quotes, excape them, so they come through. string(REPLACE "\"" "\\\"" arg ${arg}) # Args with spaces need quotes around them to get them to be parsed as a single argument. if(arg MATCHES " ") list(APPEND cuda_execute_process_string "\"${arg}\"") else() list(APPEND cuda_execute_process_string ${arg}) endif() endforeach() # Echo the command execute_process(COMMAND ${CMAKE_COMMAND} -E echo ${cuda_execute_process_string}) endif(verbose) # Run the command execute_process(COMMAND ${ARGN} RESULT_VARIABLE CUDA_result ) endmacro() # Delete the target file cuda_execute_process( "Removing ${generated_file}" COMMAND "${CMAKE_COMMAND}" -E remove "${generated_file}" ) # For CUDA 2.3 and below, -G -M doesn't work, so remove the -G flag # for dependency generation and hope for the best. set(depends_CUDA_NVCC_FLAGS "${CUDA_NVCC_FLAGS}") set(CUDA_VERSION @CUDA_VERSION@) if(CUDA_VERSION VERSION_LESS "3.0") cmake_policy(PUSH) # CMake policy 0007 NEW states that empty list elements are not # ignored. I'm just setting it to avoid the warning that's printed. cmake_policy(SET CMP0007 NEW) # Note that this will remove all occurances of -G. list(REMOVE_ITEM depends_CUDA_NVCC_FLAGS "-G") cmake_policy(POP) endif() # nvcc doesn't define __CUDACC__ for some reason when generating dependency files. This # can cause incorrect dependencies when #including files based on this macro which is # defined in the generating passes of nvcc invokation. We will go ahead and manually # define this for now until a future version fixes this bug. set(CUDACC_DEFINE -D__CUDACC__) # Generate the dependency file cuda_execute_process( "Generating dependency file: ${NVCC_generated_dependency_file}" COMMAND "${CUDA_NVCC_EXECUTABLE}" -M ${CUDACC_DEFINE} "${source_file}" -o "${NVCC_generated_dependency_file}" ${CCBIN} ${nvcc_flags} ${nvcc_host_compiler_flags} ${depends_CUDA_NVCC_FLAGS} -DNVCC ${CUDA_NVCC_INCLUDE_ARGS} ) if(CUDA_result) message(FATAL_ERROR "Error generating ${generated_file}") endif() # Generate the cmake readable dependency file to a temp file. Don't put the # quotes just around the filenames for the input_file and output_file variables. # CMake will pass the quotes through and not be able to find the file. cuda_execute_process( "Generating temporary cmake readable file: ${cmake_dependency_file}.tmp" COMMAND "${CMAKE_COMMAND}" -D "input_file:FILEPATH=${NVCC_generated_dependency_file}" -D "output_file:FILEPATH=${cmake_dependency_file}.tmp" -P "${CUDA_make2cmake}" ) if(CUDA_result) message(FATAL_ERROR "Error generating ${generated_file}") endif() # Copy the file if it is different cuda_execute_process( "Copy if different ${cmake_dependency_file}.tmp to ${cmake_dependency_file}" COMMAND "${CMAKE_COMMAND}" -E copy_if_different "${cmake_dependency_file}.tmp" "${cmake_dependency_file}" ) if(CUDA_result) message(FATAL_ERROR "Error generating ${generated_file}") endif() # Delete the temporary file cuda_execute_process( "Removing ${cmake_dependency_file}.tmp and ${NVCC_generated_dependency_file}" COMMAND "${CMAKE_COMMAND}" -E remove "${cmake_dependency_file}.tmp" "${NVCC_generated_dependency_file}" ) if(CUDA_result) message(FATAL_ERROR "Error generating ${generated_file}") endif() # Generate the code cuda_execute_process( "Generating ${generated_file}" COMMAND "${CUDA_NVCC_EXECUTABLE}" "${source_file}" ${format_flag} -o "${generated_file}" ${CCBIN} ${nvcc_flags} ${nvcc_host_compiler_flags} ${CUDA_NVCC_FLAGS} -DNVCC ${CUDA_NVCC_INCLUDE_ARGS} ) if(CUDA_result) # Since nvcc can sometimes leave half done files make sure that we delete the output file. cuda_execute_process( "Removing ${generated_file}" COMMAND "${CMAKE_COMMAND}" -E remove "${generated_file}" ) message(FATAL_ERROR "Error generating file ${generated_file}") else() if(verbose) message("Generated ${generated_file} successfully.") endif() endif() # Cubin resource report commands. if( build_cubin ) # Run with -cubin to produce resource usage report. cuda_execute_process( "Generating ${generated_cubin_file}" COMMAND "${CUDA_NVCC_EXECUTABLE}" "${source_file}" ${CUDA_NVCC_FLAGS} ${nvcc_flags} ${CCBIN} ${nvcc_host_compiler_flags} -DNVCC -cubin -o "${generated_cubin_file}" ${CUDA_NVCC_INCLUDE_ARGS} ) # Execute the parser script. cuda_execute_process( "Executing the parser script" COMMAND "${CMAKE_COMMAND}" -D "input_file:STRING=${generated_cubin_file}" -P "${CUDA_parse_cubin}" ) endif( build_cubin ) cmtk-3.3.1/CMake/FindConfigDCMTK.cmake000066400000000000000000000221361276303427400172430ustar00rootroot00000000000000# - find DCMTK libraries and applications # ## ## THIS FILE WAS MODIFIED TO MATCH THE REQUIREMENTS OF CMTK ## ## - only DCMTK libraries are tested and configured that are actually used by CMTK ## ## # DCMTK_INCLUDE_DIRS - Directories to include to use DCMTK # DCMTK_LIBRARIES - Files to link against to use DCMTK # DCMTK_FOUND - If false, don't try to use DCMTK # DCMTK_DIR - (optional) Source directory for DCMTK # DCMTK_DCMDICTPATH - Path to dicom.dic data dictionary # # DCMTK_DIR can be used to make it simpler to find the various include # directories and compiled libraries if you've just compiled it in the # source tree. Just set it to the root of the tree where you extracted # the source (default to /usr/include/dcmtk/) #============================================================================= # Copyright 2004-2009 Kitware, Inc. # Copyright 2009-2010 Mathieu Malaterre # Copyright 2010 Thomas Sondergaard # Copyright 2011-2012 SRI International # # CMake - Cross Platform Makefile Generator # Copyright 2000-2009 Kitware, Inc., Insight Software Consortium # All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # # * Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # # * Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # # * Neither the names of Kitware, Inc., the Insight Software Consortium, # nor the names of their contributors may be used to endorse or promote # products derived from this software without specific prior written # permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT # HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # # ------------------------------------------------------------------------------ # # The above copyright and license notice applies to distributions of # CMake in source and binary form. Some source files contain additional # notices of original copyright by their contributors; see each source # for details. Third-party software packages supplied with CMake under # compatible licenses provide their own copyright notices documented in # corresponding subdirectories. # # ------------------------------------------------------------------------------ # # CMake was initially developed by Kitware with the following sponsorship: # # * National Library of Medicine at the National Institutes of Health # as part of the Insight Segmentation and Registration Toolkit (ITK). # # * US National Labs (Los Alamos, Livermore, Sandia) ASC Parallel # Visualization Initiative. # # * National Alliance for Medical Image Computing (NAMIC) is funded by the # National Institutes of Health through the NIH Roadmap for Medical Research, # Grant U54 EB005149. # # * Kitware, Inc. # #============================================================================= # # Written for VXL by Amitha Perera. # Upgraded for GDCM by Mathieu Malaterre. # Modified for EasyViz by Thomas Sondergaard. # # Wed Jul 27 2011 Ankur Sinha # - Add all dcmtk libs # - Add usr/lib to paths # if(NOT DCMTK_FOUND AND NOT DCMTK_DIR) set(DCMTK_DIR "/usr/include/dcmtk/" CACHE PATH "Root of DCMTK source tree (optional).") mark_as_advanced(DCMTK_DIR) endif() foreach(lib dcmdata ## dcmdsig dcmimage dcmimgle dcmjpeg dcmjpls ## dcmnet ## dcmpstat ## dcmqrdb ## dcmsr ## dcmtls ## dcmwlm ijg12 ijg16 ijg8 ## libi2d oflog ofstd) find_library(DCMTK_${lib}_LIBRARY ${lib} PATHS ${DCMTK_DIR}/${lib}/libsrc ${DCMTK_DIR}/${lib}/libsrc/Release ${DCMTK_DIR}/${lib}/libsrc/Debug ${DCMTK_DIR}/${lib}/Release ${DCMTK_DIR}/${lib}/Debug ${DCMTK_DIR}/lib /usr/lib/dcmtk) mark_as_advanced(DCMTK_${lib}_LIBRARY) if(DCMTK_${lib}_LIBRARY) list(APPEND DCMTK_LIBRARIES ${DCMTK_${lib}_LIBRARY}) endif() endforeach() set(DCMTK_config_TEST_HEADER osconfig.h) set(DCMTK_dcmdata_TEST_HEADER dctypes.h) set(DCMTK_dcmimage_TEST_HEADER dicoimg.h) set(DCMTK_dcmimgle_TEST_HEADER dcmimage.h) set(DCMTK_dcmjpeg_TEST_HEADER djdecode.h) set(DCMTK_dcmjpls_TEST_HEADER djcodecd.h) ##set(DCMTK_dcmnet_TEST_HEADER assoc.h) ##set(DCMTK_dcmpstat_TEST_HEADER dcmpstat.h) ##set(DCMTK_dcmqrdb_TEST_HEADER dcmqrdba.h) ##set(DCMTK_dcmsign_TEST_HEADER sicert.h) ##set(DCMTK_dcmsr_TEST_HEADER dsrtree.h) ##set(DCMTK_dcmtls_TEST_HEADER tlslayer.h) ##set(DCMTK_dcmwlm_TEST_HEADER wldsfs.h) set(DCMTK_ofstd_TEST_HEADER ofstdinc.h) set(DCMTK_oflog_TEST_HEADER oflog.h) foreach(dir config dcmdata dcmimage dcmimgle dcmjpeg dcmjpls ## dcmnet ## dcmpstat ## dcmqrdb ## dcmsign ## dcmsr ## dcmtls ## dcmwlm ofstd oflog) find_path(DCMTK_${dir}_INCLUDE_DIR ${DCMTK_${dir}_TEST_HEADER} PATHS ${DCMTK_DIR}/${dir}/include ${DCMTK_DIR}/${dir} ${DCMTK_DIR}/include/${dir} /usr/include/dcmtk) mark_as_advanced(DCMTK_${dir}_INCLUDE_DIR) if(DCMTK_${dir}_INCLUDE_DIR) list(APPEND DCMTK_INCLUDE_DIRS ${DCMTK_${dir}_INCLUDE_DIR}) endif() endforeach() if(WIN32) list(APPEND DCMTK_LIBRARIES netapi32 wsock32) endif() if(DCMTK_ofstd_INCLUDE_DIR) get_filename_component(DCMTK_dcmtk_INCLUDE_DIR "${DCMTK_ofstd_INCLUDE_DIR}" PATH) get_filename_component(DCMTK_dcmtk_INCLUDE_DIR "${DCMTK_dcmtk_INCLUDE_DIR}" PATH) set(DCMTK_dcmtk_INCLUDE_DIR "${DCMTK_dcmtk_INCLUDE_DIR}" CACHE PATH "dcmtk root include dir") list(APPEND DCMTK_INCLUDE_DIRS ${DCMTK_dcmtk_INCLUDE_DIR}) mark_as_advanced(DCMTK_dcmtk_INCLUDE_DIR) endif() include(${CMAKE_ROOT}/Modules/FindPackageHandleStandardArgs.cmake) find_package_handle_standard_args(DCMTK DEFAULT_MSG DCMTK_config_INCLUDE_DIR DCMTK_ofstd_INCLUDE_DIR DCMTK_ofstd_LIBRARY DCMTK_dcmdata_INCLUDE_DIR DCMTK_dcmdata_LIBRARY DCMTK_dcmimgle_INCLUDE_DIR DCMTK_dcmimgle_LIBRARY) # Compatibility: This variable is deprecated set(DCMTK_INCLUDE_DIR ${DCMTK_INCLUDE_DIRS}) ##foreach(executable dcmdump dcmdjpeg dcmdrle) ## string(TOUPPER ${executable} EXECUTABLE) ## find_program(DCMTK_${EXECUTABLE}_EXECUTABLE ${executable} ${DCMTK_DIR}/bin) ## mark_as_advanced(DCMTK_${EXECUTABLE}_EXECUTABLE) ##endforeach() # The following using pieces contributed by Yaroslav Halchenko based on CMake's own CHECK_CXX_SOURCE_COMPILES FUNCTION(CheckLibraryDependency _required _lib _key) IF(NOT DEFINED REQUIRED_LIBRARY_${_lib}) SET(CHECK_CXX_SOURCE_COMPILES_ADD_LIBRARIES "-DLINK_LIBRARIES:STRING=${${_required}}") FILE(WRITE "${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/src.cxx" "int main(void) { return 0; }\n") #MESSAGE( "D: Checking for ${_lib}" ) TRY_COMPILE(_VAR_IGNORE_ ${CMAKE_BINARY_DIR} ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/src.cxx COMPILE_DEFINITIONS ${CMAKE_REQUIRED_DEFINITIONS} CMAKE_FLAGS -DCOMPILE_DEFINITIONS:STRING=${MACRO_CHECK_FUNCTION_DEFINITIONS} "${CHECK_CXX_SOURCE_COMPILES_ADD_LIBRARIES}" OUTPUT_VARIABLE OUTPUT) #MESSAGE( "D: Output was ${OUTPUT}" ) IF("${OUTPUT}" MATCHES ".*undefined reference to.*${_key}.*") MESSAGE(STATUS "+ required library ${_lib}" ) SET(REQUIRED_LIBRARY_${_lib} TRUE CACHE BOOL "Library ${_lib} is required to resolve dependencies.") ELSE() MESSAGE(STATUS "- tested library ${_lib}" ) SET(REQUIRED_LIBRARY_${_lib} FALSE CACHE BOOL "Library ${_lib} is not required to resolve dependencies.") ENDIF() ENDIF(NOT DEFINED REQUIRED_LIBRARY_${_lib}) IF( REQUIRED_LIBRARY_${_lib} ) SET(${_required} ${${_required}};${_lib} PARENT_SCOPE) ENDIF( REQUIRED_LIBRARY_${_lib} ) ENDFUNCTION() IF(DCMTK_FOUND) # Detect missing DCMTK library dependencies by testing the "usual suspects" ## CheckLibraryDependency(DCMTK_LIBRARIES wrap hosts_access) CheckLibraryDependency(DCMTK_LIBRARIES png png_write_image) CheckLibraryDependency(DCMTK_LIBRARIES tiff TIFFGetVersion) CheckLibraryDependency(DCMTK_LIBRARIES CharLS JpegLsReadHeader) ## CheckLibraryDependency(DCMTK_LIBRARIES xml2 xmlGetProp) FIND_PATH(DCMTK_DCMDICTPATH dicom.dic PATHS ${DCMTK_DIR}/lib ${DCMTK_DIR}/share /usr/include/dcmtk /usr/share/dcmtk) ENDIF(DCMTK_FOUND) cmtk-3.3.1/CMake/FindFFTW.cmake000066400000000000000000000062341276303427400160220ustar00rootroot00000000000000## ## Copyright 2012 SRI International ## ## This file is part of the Computational Morphometry Toolkit. ## ## http://www.nitrc.org/projects/cmtk/ ## ## The Computational Morphometry Toolkit is free software: you can ## redistribute it and/or modify it under the terms of the GNU General Public ## License as published by the Free Software Foundation, either version 3 of ## the License, or (at your option) any later version. ## ## The Computational Morphometry Toolkit is distributed in the hope that it ## will be useful, but WITHOUT ANY WARRANTY; without even the implied ## warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License along ## with the Computational Morphometry Toolkit. If not, see ## . ## ## $Revision: 4296 $ ## ## $LastChangedDate: 2012-05-01 15:21:41 -0700 (Tue, 01 May 2012) $ ## ## $LastChangedBy: torstenrohlfing $ ## ## ## This file borrows heavily from the analogous InsightToolkit file ## ## FFTW can be compiled and subsequently linked against ## various data types. ## There is a single set of include files, and then muttiple libraries, ## One for each type. I.e. libfftw.a-->double, libfftwf.a-->float ## The following logic belongs in the individual package ## mark_as_advanced(USE_FFTWD) ## option(USE_FFTWD "Use double precision FFTW if found" ON) ## mark_as_advanced(USE_FFTWF) ## option(USE_FFTWF "Use single precision FFTW if found" ON) set(FFTW_INC_SEARCHPATH /sw/include /usr/include /usr/local/include /usr/include/fftw /usr/local/include/fftw ) find_path(FFTW_INCLUDE_PATH fftw3.h ${FFTW_INC_SEARCHPATH}) if(FFTW_INCLUDE_PATH) set(FFTW_INCLUDE ${FFTW_INCLUDE_PATH}) endif(FFTW_INCLUDE_PATH) if(FFTW_INCLUDE) include_directories( ${FFTW_INCLUDE}) endif(FFTW_INCLUDE) get_filename_component(FFTW_INSTALL_BASE_PATH ${FFTW_INCLUDE_PATH} PATH) set(FFTW_LIB_SEARCHPATH ${FFTW_INSTALL_BASE_PATH}/lib /usr/lib/fftw /usr/local/lib/fftw ) mark_as_advanced(FFTWD_LIB) find_library(FFTWD_LIB fftw3 ${FFTW_LIB_SEARCHPATH}) #Double Precision Lib find_library(FFTWD_THREADS_LIB fftw3_threads ${FFTW_LIB_SEARCHPATH}) #Double Precision Lib only if compiled with threads support find_library(FFTWD_OMP_LIB fftw3_omp ${FFTW_LIB_SEARCHPATH}) #Double Precision Lib only if compiled with OpenMP support if(FFTWD_LIB) set(FFTWD_FOUND 1) get_filename_component(FFTW_LIBDIR ${FFTWD_LIB} PATH) if(FFTWD_THREADS_LIB) set(FFTWD_LIB ${FFTWD_LIB} ${FFTWD_THREADS_LIB} ) endif(FFTWD_THREADS_LIB) endif(FFTWD_LIB) mark_as_advanced(FFTWF_LIB) find_library(FFTWF_LIB fftw3f ${FFTW_LIB_SEARCHPATH}) #Single Precision Lib find_library(FFTWF_THREADS_LIB fftw3f_threads ${FFTW_LIB_SEARCHPATH}) #Single Precision Lib only if compiled with threads support find_library(FFTWF_OMP_LIB fftw3f_omp ${FFTW_LIB_SEARCHPATH}) #Single Precision Lib only if compiled with OpenMP support if(FFTWF_LIB) set(FFTWF_FOUND 1) get_filename_component(FFTW_LIBDIR ${FFTWF_LIB} PATH) if(FFTWF_THREADS_LIB) set(FFTWF_LIB ${FFTWF_LIB} ${FFTWF_THREADS_LIB} ) endif(FFTWF_THREADS_LIB) endif(FFTWF_LIB) cmtk-3.3.1/CMake/FindLZMA.cmake000066400000000000000000000055141276303427400160170ustar00rootroot00000000000000# - Find lzma # Find the native LZMA includes and library # # LZMA_INCLUDE_DIRS - where to find lzma.h, etc. # LZMA_LIBRARIES - List of libraries when using lzma. # LZMA_FOUND - True if lzma found. #============================================================================= # Copyright 2001-2009 Kitware, Inc. # # Distributed under the OSI-approved BSD License (the "License"); # see accompanying file Copyright.txt for details. # # This software is distributed WITHOUT ANY WARRANTY; without even the # implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # #============================================================================= # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # # * Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # # * Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # # * Neither the names of Kitware, Inc., the Insight Software Consortium, # nor the names of their contributors may be used to endorse or promote # products derived from this software without specific prior written # permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT # HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # #============================================================================= IF (LZMA_INCLUDE_DIR) # Already in cache, be silent SET(LZMA_FIND_QUIETLY TRUE) ENDIF (LZMA_INCLUDE_DIR) FIND_PATH(LZMA_INCLUDE_DIR lzmadec.h) SET(LZMA_NAMES lzmadec) FIND_LIBRARY(LZMA_LIBRARY NAMES ${LZMA_NAMES} ) MARK_AS_ADVANCED( LZMA_LIBRARY LZMA_INCLUDE_DIR ) # Per-recommendation SET(LZMA_INCLUDE_DIRS "${LZMA_INCLUDE_DIR}") SET(LZMA_LIBRARIES "${LZMA_LIBRARY}") # handle the QUIETLY and REQUIRED arguments and set LZMA_FOUND to TRUE if # all listed variables are TRUE INCLUDE(FindPackageHandleStandardArgs) FIND_PACKAGE_HANDLE_STANDARD_ARGS(LZMA DEFAULT_MSG LZMA_LIBRARIES LZMA_INCLUDE_DIRS) cmtk-3.3.1/CMake/FindMXML.cmake000066400000000000000000000070461276303427400160330ustar00rootroot00000000000000# - Find mxml # Find the native MXML includes and library # # MXML_INCLUDE_DIRS - where to find mxml.h, etc. # MXML_LIBRARIES - List of libraries when using mxml. # MXMLDOC_EXECUTABLE - Path to "mxmldoc" executable # MXML_VERSION - Version string extracted from output of "mxmldoc" # MXML_FOUND - True if mxml found. #============================================================================= # Copyright 2001-2009 Kitware, Inc. # # Distributed under the OSI-approved BSD License (the "License"); # see accompanying file Copyright.txt for details. # # This software is distributed WITHOUT ANY WARRANTY; without even the # implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # #============================================================================= # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # # * Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # # * Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # # * Neither the names of Kitware, Inc., the Insight Software Consortium, # nor the names of their contributors may be used to endorse or promote # products derived from this software without specific prior written # permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT # HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # #============================================================================= IF (MXML_INCLUDE_DIR) # Already in cache, be silent SET(MXML_FIND_QUIETLY TRUE) ENDIF (MXML_INCLUDE_DIR) FIND_PATH(MXML_INCLUDE_DIR mxml.h) SET(MXML_NAMES mxml) FIND_LIBRARY(MXML_LIBRARY NAMES ${MXML_NAMES} ) FIND_PROGRAM(MXMLDOC_EXECUTABLE NAMES mxmldoc) # Extract version from header if not defined otherwise IF(NOT MXML_VERSION) IF(MXMLDOC_EXECUTABLE) EXECUTE_PROCESS(COMMAND ${MXMLDOC_EXECUTABLE} OUTPUT_VARIABLE MXML_HDR) STRING(REGEX MATCH "meta name=\"creator\".*$" CREATOR ${MXML_HDR}) STRING(REGEX REPLACE "meta name=\"creator\" content=\"Mini-XML v([^\"]*)\".*" "\\1" VERSION ${CREATOR}) ELSE(MXMLDOC_EXECUTABLE) SET(VERSION "0.0") ENDIF(MXMLDOC_EXECUTABLE) SET(MXML_VERSION ${VERSION} CACHE INTERNAL "MXML version number") ENDIF(NOT MXML_VERSION) MARK_AS_ADVANCED( MXMLDOC_EXECUTABLE MXML_LIBRARY MXML_INCLUDE_DIR ) # Per-recommendation SET(MXML_INCLUDE_DIRS "${MXML_INCLUDE_DIR}") SET(MXML_LIBRARIES "${MXML_LIBRARY}") # handle the QUIETLY and REQUIRED arguments and set MXML_FOUND to TRUE if # all listed variables are TRUE INCLUDE(FindPackageHandleStandardArgs) FIND_PACKAGE_HANDLE_STANDARD_ARGS(MXML DEFAULT_MSG MXML_LIBRARIES MXML_INCLUDE_DIRS) cmtk-3.3.1/CMake/FindSQLite3.cmake000066400000000000000000000063701276303427400165010ustar00rootroot00000000000000# - Try to find SQLITE3 # Once done this will define # # SQLITE3_FOUND - system has SQLITE3 # SQLITE3_INCLUDE_DIR - the SQLITE3 include directory # SQLITE3_LIBRARIES - Link these to use SQLITE3 # SQLITE3_DEFINITIONS - Compiler switches required for using SQLITE3 # SQLITE_EXECUTABLE - Comamnd line tool sqlite3 # # Modified from http://openlibraries.org/browser/trunk/FindSQLITE3.cmake # #============================================================================= # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # # * Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # # * Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # # * Neither the names of Kitware, Inc., the Insight Software Consortium, # nor the names of their contributors may be used to endorse or promote # products derived from this software without specific prior written # permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT # HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # #============================================================================= if ( SQLITE3_INCLUDE_DIR AND SQLITE3_LIBRARIES ) # in cache already SET(SQLITE3_FIND_QUIETLY TRUE) endif ( SQLITE3_INCLUDE_DIR AND SQLITE3_LIBRARIES ) # use pkg-config to get the directories and then use these values # in the FIND_PATH() and FIND_LIBRARY() calls if( NOT WIN32 ) INCLUDE(FindPkgConfig) pkg_check_modules(SQLITE3 sqlite3) endif( NOT WIN32 ) FIND_PATH(SQLITE3_INCLUDE_DIR NAMES sqlite3.h PATHS ${_SQLITE3IncDir} ) FIND_LIBRARY(SQLITE3_LIBRARIES NAMES sqlite3 PATHS ${_SQLITE3LinkDir} ) INCLUDE(FindPackageHandleStandardArgs) # handle the QUIETLY and REQUIRED arguments and set SQLITE3_FOUND to TRUE if # all listed variables are TRUE FIND_PACKAGE_HANDLE_STANDARD_ARGS(SQLite3 DEFAULT_MSG SQLITE3_LIBRARIES SQLITE3_INCLUDE_DIR) # Extract version from header if not defined otherwise IF(NOT SQLITE3_VERSION) FILE(READ ${SQLITE3_INCLUDE_DIR}/sqlite3.h SQLITE_HDR) STRING(REGEX MATCH "#define SQLITE_VERSION[ \t]*\"[^ \"]*\"" VERSION ${SQLITE_HDR}) STRING(REGEX REPLACE "[^\"]*\"([^\"]*)\".*" "\\1" VERSION ${VERSION}) SET(SQLITE3_VERSION ${VERSION} CACHE INTERNAL "SQLite3 version number") ENDIF(NOT SQLITE3_VERSION ) MARK_AS_ADVANCED(SQLITE3_INCLUDE_DIR SQLITE3_LIBRARIES SQLITE3_EXECUTABLE) cmtk-3.3.1/CMake/TestQnanhibit.c000066400000000000000000000013531276303427400163670ustar00rootroot00000000000000#include #include #if defined(__BORLANDC__) # include # include #endif int main(int argc, char *argv[]) { const char * const me=argv[0]; const float zero=0.0F; union { float flt32bit; int int32bit; } qnan; #if defined(__BORLANDC__) // Disable floating point exceptions in Borland _control87(MCW_EM, MCW_EM); #endif // defined(__BORLANDC__) if (sizeof(float) != sizeof(int)) { fprintf(stderr, "%s: MADNESS: sizeof(float)=%d != sizeof(int)=%d\n", me, (int)sizeof(float), (int)sizeof(int)); return -1; } qnan.flt32bit=zero/zero; printf("-DTEEM_QNANHIBIT=%d\n", (qnan.int32bit >> 22) & 1); return (int)((qnan.int32bit >> 22) & 1); } cmtk-3.3.1/CMake/TestQnanhibit.cmake000066400000000000000000000036431276303427400172310ustar00rootroot00000000000000# # Checks whether the 22nd bit of a 32-bit quiet-NaN is 1 (1) or 0 (0). This # distinction is needed in handling of IEEE floating point special values. # This quantity is independent of endian-ness. # # VARIABLE - variable to store the result to # MACRO(TEST_QNANHIBIT VARIABLE LOCAL_TEST_DIR) IF("HAVE_${VARIABLE}" MATCHES "^HAVE_${VARIABLE}$") TRY_RUN(${VARIABLE} HAVE_${VARIABLE} ${CMAKE_BINARY_DIR} ${LOCAL_TEST_DIR}/TestQnanhibit.c OUTPUT_VARIABLE OUTPUT) MESSAGE(STATUS "Check the value of the 22nd bit of a 32-bit quiet-NaN") IF(HAVE_${VARIABLE}) IF(${VARIABLE} LESS 0) MESSAGE(ERROR " A test (qnanhibit.c) necessary for NrrdIO configuration returned error code. NrrdIO may not properly handle NaN's.") ENDIF(${VARIABLE} LESS 0) IF(${VARIABLE}) FILE(APPEND ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeError.log "Value of the 22nd bit of a 32-bit quiet-NaN is 1") MESSAGE(STATUS "Check the value of the 22nd bit of a 32-bit quiet-NaN - 1") ELSE(${VARIABLE}) FILE(APPEND ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeError.log "Value of the 22nd bit of a 32-bit quiet-NaN is 0") MESSAGE(STATUS "Check the value of the 22nd bit of a 32-bit quiet-NaN - 0") ENDIF(${VARIABLE}) ELSE(HAVE_${VARIABLE}) FILE(APPEND ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeError.log "\tFailed to compile a test (TestQnanhibit.c) necessary to configure for proper handling of IEEE floating point NaN's.\n") MESSAGE(STATUS "Failed to compile a test (TestQnanhibit.c) necessary to configure for proper handling of IEEE floating point NaN's") ENDIF(HAVE_${VARIABLE}) FILE(APPEND ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeError.log "TestQnanhibit.c produced following output:\n${OUTPUT}\n\n") ENDIF("HAVE_${VARIABLE}" MATCHES "^HAVE_${VARIABLE}$") ENDMACRO(TEST_QNANHIBIT) cmtk-3.3.1/CMakeLists.txt000066400000000000000000000670061276303427400152350ustar00rootroot00000000000000## ## Copyright 1997-2010 Torsten Rohlfing ## ## Copyright 2004-2014 SRI International ## ## Copyright 2015, 2016 Google, Inc. ## ## This file is part of the Computational Morphometry Toolkit. ## ## http://www.nitrc.org/projects/cmtk/ ## ## The Computational Morphometry Toolkit is free software: you can ## redistribute it and/or modify it under the terms of the GNU General Public ## License as published by the Free Software Foundation, either version 3 of ## the License, or (at your option) any later version. ## ## The Computational Morphometry Toolkit is distributed in the hope that it ## will be useful, but WITHOUT ANY WARRANTY; without even the implied ## warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License along ## with the Computational Morphometry Toolkit. If not, see ## . ## ## $Revision: 5429 $ ## ## $LastChangedDate: 2016-01-24 16:14:23 -0800 (Sun, 24 Jan 2016) $ ## ## $LastChangedBy: torstenrohlfing $ ## ## ## This file used to borrow heavily from the analogous InsightToolkit file. ## It may have evolved away since then, however. ## CMAKE_MINIMUM_REQUIRED(VERSION 2.8.0) SET(CMAKE_LEGACY_CYGWIN_WIN32 0) # Remove when CMake >= 2.8.4 is required IF(COMMAND cmake_policy) cmake_policy(SET CMP0003 NEW) ## libraries with full paths vs. search paths ENDIF(COMMAND cmake_policy) SET(CMAKE_OVERRIDE_COMPILER_MISMATCH 1) PROJECT(CMTK) SET(CMTK_VERSION_MAJOR "3") SET(CMTK_VERSION_MINOR "3") SET(CMTK_VERSION_PATCH "1") SET(CMTK_VERSION_STRING "${CMTK_VERSION_MAJOR}.${CMTK_VERSION_MINOR}.${CMTK_VERSION_PATCH}") #----------------------------------------------------------------------------- # Test for some required system information. INCLUDE (${CMAKE_ROOT}/Modules/CMakeDetermineSystem.cmake) INCLUDE (${CMAKE_ROOT}/Modules/CMakeBackwardCompatibilityC.cmake) INCLUDE (${CMAKE_ROOT}/Modules/CMakeBackwardCompatibilityCXX.cmake) #----------------------------------------------------------------------------- # On Mac, if we have MacPorts installed, we need to look for libraries in # /opt/local. MESSAGE(STATUS "System is " ${CMAKE_SYSTEM}) IF(CMAKE_SYSTEM MATCHES "Darwin.*") IF(EXISTS /opt/local) OPTION(CMTK_USE_MACPORTS "Build CMTK with libraries from MacPorts." OFF) IF(CMTK_USE_MACPORTS) LIST(INSERT CMAKE_PREFIX_PATH 0 "/opt/local") ENDIF(CMTK_USE_MACPORTS) ENDIF(EXISTS /opt/local) ENDIF(CMAKE_SYSTEM MATCHES "Darwin.*") #----------------------------------------------------------------------------- # Output directories. IF(NOT LIBRARY_OUTPUT_PATH) SET (LIBRARY_OUTPUT_PATH ${CMTK_BINARY_DIR}/bin CACHE INTERNAL "Single output directory for building all libraries.") ENDIF(NOT LIBRARY_OUTPUT_PATH) IF(NOT EXECUTABLE_OUTPUT_PATH) SET (EXECUTABLE_OUTPUT_PATH ${CMTK_BINARY_DIR}/bin CACHE INTERNAL "Single output directory for building all executables.") ENDIF(NOT EXECUTABLE_OUTPUT_PATH) MARK_AS_ADVANCED(LIBRARY_OUTPUT_PATH EXECUTABLE_OUTPUT_PATH) IF(NOT CMTK_BUILD_LIB_DIR) # variable used for generating CMTKConfig.cmake for the build tree SET(CMTK_BUILD_LIB_DIR "/bin") ENDIF(NOT CMTK_BUILD_LIB_DIR) SET(CMTK_LIBRARY_PATH "${LIBRARY_OUTPUT_PATH}") SET(CMTK_EXECUTABLE_PATH "${EXECUTABLE_OUTPUT_PATH}") SET(CXX_TEST_PATH ${EXECUTABLE_OUTPUT_PATH}) # set CMTK_DIR so it can be used by subprojects SET(CMTK_DIR "${CMAKE_BINARY_DIR}" CACHE INTERNAL "CMTK dir to be used by subprojects") #----------------------------------------------------------------------------- # Setup install target directories IF(NOT CMTK_INSTALL_BIN_DIR) SET(CMTK_INSTALL_BIN_DIR bin CACHE PATH "Binary files will be installed here") ENDIF(NOT CMTK_INSTALL_BIN_DIR) IF(NOT CMTK_INSTALL_LIB_DIR) # 64 bit libraries are not installed in the same directories on solaris # Ref: http://docs.sun.com/app/docs/doc/816-5138/6mba6ua5m?a=view SET(sun64 ) IF(CMAKE_SYSTEM MATCHES "SunOS.*" AND CMAKE_SIZEOF_VOID_P EQUAL 8) EXEC_PROGRAM("uname" ARGS "-p" OUTPUT_VARIABLE sunproc ) IF("${sunproc}" STREQUAL "sparc") SET(sun64 "/sparcv9") ENDIF("${sunproc}" STREQUAL "sparc") IF("${sunproc}" STREQUAL "i386") SET(sun64 "/amd64") ENDIF("${sunproc}" STREQUAL "i386") IF("${sun64}" STREQUAL "") # sun64 is not set - solaris has been ported to a new architecture? MESSAGE("Warning: Unknown processor '${sunproc}'. The libraries may not be installed in the right place.") ENDIF("${sun64}" STREQUAL "") ENDIF(CMAKE_SYSTEM MATCHES "SunOS.*" AND CMAKE_SIZEOF_VOID_P EQUAL 8) SET(CMTK_INSTALL_LIB_DIR "lib${sun64}/cmtk" CACHE PATH "Library files will be installed here") ENDIF(NOT CMTK_INSTALL_LIB_DIR) IF(NOT CMTK_INSTALL_DATA_DIR) SET(CMTK_INSTALL_DATA_DIR "share/cmtk" CACHE PATH "Data files will be installed here") ENDIF(NOT CMTK_INSTALL_DATA_DIR) IF(NOT CMTK_INSTALL_INCLUDE_DIR) SET(CMTK_INSTALL_INCLUDE_DIR "include/cmtk" CACHE PATH "Include files will be installed here") ENDIF(NOT CMTK_INSTALL_INCLUDE_DIR) #----------------------------------------------------------------------------- # Check for system headers. INCLUDE (CheckIncludeFiles) CHECK_INCLUDE_FILES (dirent.h HAVE_DIRENT_H) CHECK_INCLUDE_FILES (execinfo.h HAVE_EXECINFO_H) CHECK_INCLUDE_FILES (fcntl.h HAVE_FCNTL_H) CHECK_INCLUDE_FILES (ieeefp.h HAVE_IEEEFP_H) CHECK_INCLUDE_FILES (inttypes.h HAVE_INTTYPES_H) CHECK_INCLUDE_FILES (malloc.h HAVE_MALLOC_H) CHECK_INCLUDE_FILES (pthread.h HAVE_PTHREAD_H) CHECK_INCLUDE_FILES (stdint.h HAVE_STDINT_H) CHECK_INCLUDE_FILES (termios.h HAVE_TERMIOS_H) CHECK_INCLUDE_FILES (unistd.h HAVE_UNISTD_H) CHECK_INCLUDE_FILES (values.h HAVE_VALUES_H) CHECK_INCLUDE_FILES (sys/ioctl.h HAVE_SYS_IOCTL_H) CHECK_INCLUDE_FILES (sys/procfs.h HAVE_SYS_PROCFS_H) CHECK_INCLUDE_FILES (sys/stat.h HAVE_SYS_STAT_H) CHECK_INCLUDE_FILES (sys/time.h HAVE_SYS_TIME_H) CHECK_INCLUDE_FILES (sys/times.h HAVE_SYS_TIMES_H) CHECK_INCLUDE_FILES (sys/types.h HAVE_SYS_TYPES_H) CHECK_INCLUDE_FILES (sys/utsname.h HAVE_SYS_UTSNAME_H) IF(HAVE_SYS_STAT_H) SET(CMAKE_EXTRA_INCLUDE_FILES sys/stat.h) ENDIF(HAVE_SYS_STAT_H) CHECK_TYPE_SIZE("((struct stat*)0)->st_mode" SIZEOF_ST_SIZE) IF(SIZEOF_ST_SIZE EQUAL 4) CHECK_TYPE_SIZE("struct stat64" CMTK_USE_STAT64) ENDIF(SIZEOF_ST_SIZE EQUAL 4) SET(CMAKE_EXTRA_INCLUDE_FILES) INCLUDE(TestBigEndian) TEST_BIG_ENDIAN(WORDS_BIGENDIAN) #----------------------------------------------------------------------------- # Test NaN high bit INCLUDE(${CMAKE_CURRENT_SOURCE_DIR}/CMake/TestQnanhibit.cmake) TEST_QNANHIBIT(QNANHIBIT_VALUE ${CMAKE_CURRENT_SOURCE_DIR}/CMake) IF(QNANHIBIT_VALUE) SET(QNANHIBIT 1 CACHE INTERNAL "The 22nd bit of 32-bit floating-point quiet NaN.") ELSE(QNANHIBIT_VALUE) SET(QNANHIBIT 0 CACHE INTERNAL "The 22nd bit of 32-bit floating-point quiet NaN.") ENDIF(QNANHIBIT_VALUE) #----------------------------------------------------------------------------- # Check for at least one of the typical STL hash map implementations INCLUDE (CheckIncludeFileCXX) CHECK_INCLUDE_FILE_CXX(unordered_map HAVE_UNORDERED_MAP) IF(NOT HAVE_UNORDERED_MAP) CHECK_INCLUDE_FILE_CXX(hash_map HAVE_HASH_MAP) IF(NOT HAVE_HASH_MAP) CHECK_INCLUDE_FILE_CXX(hash_map.h HAVE_HASH_MAP_H) ENDIF(NOT HAVE_HASH_MAP) ENDIF(NOT HAVE_UNORDERED_MAP) # set a single flag is we have ANY hash map implementation in STL SET(HAVE_STL_HASH_MAP ${HAVE_UNORDERED_MAP}${HAVE_HASH_MAP}${HAVE_HASH_MAP_H} CACHE INTERNAL "Flag for presence of ANY hash map implementation in the C++ STL") #----------------------------------------------------------------------------- # build configuration options. OPTION(BUILD_SHARED_LIBS "Build CMTK with shared libraries." OFF) SET(CMTK_BUILD_SHARED_LIBS ${BUILD_SHARED_LIBS}) OPTION(CMTK_BUILD_WRAPPER "Build 'cmtk' wrapper script and install all actual executables in a private directory to avoid name collisions" ON) MARK_AS_ADVANCED(CMTK_BUILD_WRAPPER) OPTION(CMTK_BUILD_UNSTABLE "Build 'unstable' library and tools" OFF) MARK_AS_ADVANCED(CMTK_BUILD_UNSTABLE) OPTION(CMTK_BUILD_STACKTRACE "Build tools with stack trace printing in case of crash (increases build time)" ON) MARK_AS_ADVANCED(CMTK_BUILD_STACKTRACE) OPTION(CMTK_BUILD_DEMO "Build demonstration code. Strange things will happen if you turn this on." OFF) MARK_AS_ADVANCED(CMTK_BUILD_DEMO) IF(CMTK_BUILD_DEMO) MESSAGE( WARNING "Demo mode is enabled. Make sure not to use this build for production use!") ENDIF(CMTK_BUILD_DEMO) #----------------------------------------------------------------------------- # Add an option to use or not to use SMP multi-core parallelism OPTION(CMTK_USE_SMP "Enable shared-memory parallelism (requires at least POSIX Threads, OpenMP, or Windows threads)" ON) #----------------------------------------------------------------------------- # Add an option to enable or disable POSIX threads if pthread.h header exists IF(HAVE_PTHREAD_H) OPTION(CMTK_USE_PTHREADS "Use POSIX Threads for SMP parallelism" ON) MARK_AS_ADVANCED(CMTK_USE_PTHREADS) IF(NOT CMTK_USE_SMP) SET(CMTK_USE_PTHREADS OFF) ENDIF(NOT CMTK_USE_SMP) ENDIF(HAVE_PTHREAD_H) #----------------------------------------------------------------------------- # for the gnu compiler a -D_PTHREADS is needed on sun # for the native compiler a -mt flag is needed on the sun IF(CMTK_USE_PTHREADS) IF(${CMAKE_SYSTEM} MATCHES "SunOS.*") IF(CMAKE_COMPILER_IS_GNUCXX) SET(CMTK_REQUIRED_CXX_FLAGS "${CMTK_REQUIRED_CXX_FLAGS} -D_PTHREADS") ELSE(CMAKE_COMPILER_IS_GNUCXX) SET(CMTK_REQUIRED_CXX_FLAGS "${CMTK_REQUIRED_CXX_FLAGS} -mt") SET(CMTK_REQUIRED_C_FLAGS "${CMTK_REQUIRED_C_FLAGS} -mt") ENDIF(CMAKE_COMPILER_IS_GNUCXX) ENDIF(${CMAKE_SYSTEM} MATCHES "SunOS.*") ENDIF(CMTK_USE_PTHREADS) # mingw thread support IF(MINGW) SET(CMTK_REQUIRED_CXX_FLAGS "${CMTK_REQUIRED_CXX_FLAGS} -mthreads") SET(CMTK_REQUIRED_C_FLAGS "${CMTK_REQUIRED_C_FLAGS} -mthreads") SET(CMTK_REQUIRED_LINK_FLAGS "${CMTK_REQUIRED_LINK_FLAGS} -mthreads") ENDIF(MINGW) #----------------------------------------------------------------------------- # Check for, and configure, OpenMP IF(NOT DEFINED CMTK_USE_OPENMP) FIND_PACKAGE(OpenMP) IF(OPENMP_FOUND) OPTION(CMTK_USE_OPENMP "Use OpenMP for SMP parallelism" ON) MARK_AS_ADVANCED(CMTK_USE_OPENMP) IF(NOT CMTK_USE_SMP) SET(CMTK_USE_OPENMP OFF) ENDIF(NOT CMTK_USE_SMP) ENDIF(OPENMP_FOUND) ENDIF(NOT DEFINED CMTK_USE_OPENMP) IF(CMTK_USE_OPENMP) SET(CMTK_REQUIRED_CXX_FLAGS "${CMTK_REQUIRED_CXX_FLAGS} ${OpenMP_CXX_FLAGS}") SET(CMTK_REQUIRED_LINK_FLAGS "${CMTK_REQUIRED_LINK_FLAGS} ${OpenMP_C_FLAGS}") ENDIF(CMTK_USE_OPENMP) #----------------------------------------------------------------------------- # Check for, and configure, Grand Central Dispatch CHECK_INCLUDE_FILES(dispatch/dispatch.h HAVE_DISPATCH_H) IF(HAVE_DISPATCH_H) OPTION(CMTK_USE_GCD "Use Grand Central Dispatch for SMP parallelism with system-level load balancing" ON) IF(NOT CMTK_USE_SMP) SET(CMTK_USE_GCD OFF) ENDIF(NOT CMTK_USE_SMP) IF(CMTK_USE_GCD) ENDIF(CMTK_USE_GCD) ENDIF(HAVE_DISPATCH_H) #----------------------------------------------------------------------------- # Check for, and configure, CUDA OPTION(CMTK_USE_CUDA "Use CUDA for GPU computing" OFF) IF(CMTK_USE_CUDA) # check using cmake's own FindCUDA above v2.8 or use our own for earlier versions IF (${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION} STRLESS "2.8") MESSAGE("${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION} STRLESS 2.8") INCLUDE(${CMAKE_CURRENT_SOURCE_DIR}/CMake/FindCUDA.cmake) ELSE (${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION} STRLESS "2.8") FIND_PACKAGE(CUDA) ENDIF(${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION} STRLESS "2.8") ENDIF(CMTK_USE_CUDA) #----------------------------------------------------------------------------- # Check whether compiler supports variable-size automatic arrays, i.e., # things like "const int size = 10; const char string[size];" rather than # "const char string[10];" INCLUDE(${CMAKE_ROOT}/Modules/CheckCXXSourceCompiles.cmake) CHECK_CXX_SOURCE_COMPILES("class A {}; class B : public A {}; int main() { int size = 10; B array[size]; }" CMTK_COMPILER_VAR_AUTO_ARRAYSIZE) #----------------------------------------------------------------------------- # Configure Dart testing support. This should be done before any # MESSAGE(FATAL_ERROR ...) commands are invoked. INCLUDE(${CMAKE_ROOT}/Modules/CTest.cmake) MARK_AS_ADVANCED(TCL_TCLSH DART_ROOT) ENABLE_TESTING() IF(BUILD_TESTING) SET(BUILDNAME "${BUILDNAME}" CACHE STRING "Name of build on the dashboard") MARK_AS_ADVANCED(BUILDNAME) OPTION(CMTK_TESTING_MEMORYCHECK "Test using memory checking" OFF) MARK_AS_ADVANCED(CMTK_TESTING_MEMORYCHECK) IF(NOT CMTK_DATA_ROOT) GET_FILENAME_COMPONENT(CMTK_SVN_ROOT ${CMAKE_CURRENT_SOURCE_DIR} PATH) SET(CMTK_DATA_ROOT ${CMTK_SVN_ROOT}/data CACHE PATH "Root directory of the CMTK data tree") ENDIF(NOT CMTK_DATA_ROOT) MARK_AS_ADVANCED(CMTK_DATA_ROOT) ENDIF(BUILD_TESTING) SET(CMTK_ROOT_PATH_SRI24 "CMTK_ROOT_PATH_SRI24-NOTFOUND" CACHE PATH "Root path of the SRI24 atlas data (usually ends with \"sri24\")" ) #----------------------------------------------------------------------------- # Configure Doxygen support FIND_PACKAGE(Doxygen) IF(DOXYGEN_FOUND) OPTION(BUILD_DOCUMENTATION "Build html-based library API documentation using Doxygen" OFF) CONFIGURE_FILE(${CMAKE_CURRENT_SOURCE_DIR}/Doxyfile.in ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile) IF(BUILD_DOCUMENTATION) ADD_CUSTOM_TARGET(doc COMMAND doxygen DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile) INSTALL(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/html/ DESTINATION ${CMTK_INSTALL_DATA_DIR}/doc/html/ COMPONENT documentation) ENDIF(BUILD_DOCUMENTATION) ENDIF(DOXYGEN_FOUND) #----------------------------------------------------------------------------- # Add an option to use double-precision floats for coordinates. OPTION(CMTK_COORDINATES_DOUBLE "Use double precision floats for coordinates (turn off to save memory)" ON) MARK_AS_ADVANCED(CMTK_COORDINATES_DOUBLE) OPTION(CMTK_DATA_DOUBLE "Use double precision floats for data exchange between typed arrays" ON) MARK_AS_ADVANCED(CMTK_DATA_DOUBLE) OPTION(CMTK_NUMERICS_DOUBLE "Use double precision floats for numerical algorithms" ON) MARK_AS_ADVANCED(CMTK_NUMERICS_DOUBLE) #----------------------------------------------------------------------------- # Add an option to use or not use zlib library OPTION(CMTK_BUILD_ZLIB "Build bundled zlib library, even if system zlib exists" OFF) FIND_PACKAGE(ZLIB) IF(CMTK_BUILD_ZLIB) MESSAGE( STATUS "Building bundled zlib by user configuration" ) SET(CMTK_BUILD_ZLIB 1) SET(ZLIB_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/Utilities/zlib") SET(ZCONF_INCLUDE_DIR "${CMAKE_CURRENT_BINARY_DIR}/Utilities/zlib") SET(ZLIB_LIBRARIES "cmtkZlib" CACHE INTERNAL "") SET(ZLIB_INCLUDE_DIRS ${ZCONF_INCLUDE_DIR} ${ZLIB_INCLUDE_DIR}) ELSE(CMTK_BUILD_ZLIB) IF(ZLIB_FOUND) MESSAGE( STATUS "Using system zlib" ) ELSE(ZLIB_FOUND) MESSAGE( STATUS "Building bundled zlib due to missing system zlib" ) SET(CMTK_BUILD_ZLIB 1) SET(ZLIB_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/Utilities/zlib") SET(ZCONF_INCLUDE_DIR "${CMAKE_CURRENT_BINARY_DIR}/Utilities/zlib") SET(ZLIB_LIBRARIES "cmtkZlib" CACHE INTERNAL "") SET(ZLIB_INCLUDE_DIRS ${ZCONF_INCLUDE_DIR} ${ZLIB_INCLUDE_DIR}) ENDIF(ZLIB_FOUND) ENDIF(CMTK_BUILD_ZLIB) INCLUDE_DIRECTORIES(BEFORE ${ZLIB_INCLUDE_DIRS}) #----------------------------------------------------------------------------- # Add an option to use or not use mxml library OPTION(CMTK_BUILD_MXML "Build bundled mxml library, even if system library exists" OFF) INCLUDE(${CMAKE_CURRENT_SOURCE_DIR}/CMake/FindMXML.cmake) IF(CMTK_BUILD_MXML) MESSAGE( STATUS "Building bundled mxml by user configuration" ) SET(CMTK_BUILD_MXML 1) SET(MXML_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/Utilities/mxml") INCLUDE_DIRECTORIES(BEFORE ${MXML_INCLUDE_DIR}) SET(MXML_LIBRARIES "cmtkMxml" CACHE INTERNAL "") ELSE(CMTK_BUILD_MXML) IF(NOT MXML_FOUND OR ${MXML_VERSION} STRLESS 2.7 OR ${MXML_VERSION} STREQUAL 2.8) IF(NOT MXML_FOUND) MESSAGE( STATUS "Building bundled mxml due to missing system mxml" ) ELSE(NOT MXML_FOUND) MESSAGE( STATUS "Building bundled mxml due to outdated or broken system mxml (minimum version required is 2.7, version 2.8 is broken; installed version is ${MXML_VERSION})" ) ENDIF(NOT MXML_FOUND) SET(CMTK_BUILD_MXML 1) SET(MXML_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/Utilities/mxml") INCLUDE_DIRECTORIES(BEFORE ${MXML_INCLUDE_DIR}) SET(MXML_LIBRARIES "cmtkMxml" CACHE INTERNAL "") ELSE(NOT MXML_FOUND OR ${MXML_VERSION} STRLESS 2.7 OR ${MXML_VERSION} STREQUAL 2.8) MESSAGE( STATUS "Using system mxml version ${MXML_VERSION}" ) ENDIF(NOT MXML_FOUND OR ${MXML_VERSION} STRLESS 2.7 OR ${MXML_VERSION} STREQUAL 2.8) ENDIF(CMTK_BUILD_MXML) #----------------------------------------------------------------------------- # Add an option to use or not use bzip2 library OPTION(CMTK_USE_BZIP2 "Compile support for on-the-fly BZip2 decompression" ON) IF(CMTK_USE_BZIP2) FIND_PACKAGE(BZip2) IF(BZIP2_FOUND) MESSAGE( STATUS "Found system libbz2" ) SET(CMTK_BZIP2_LIBS ${BZIP2_LIBRARIES}) ELSE(BZIP2_FOUND) SET(CMTK_USE_BZIP2 OFF) ENDIF(BZIP2_FOUND) ENDIF(CMTK_USE_BZIP2) #----------------------------------------------------------------------------- # Add an option to use or not use lzma decompression library OPTION(CMTK_USE_LZMA "Compile support for on-the-fly LZMA decompression" ON) IF(CMTK_USE_LZMA) INCLUDE(${CMAKE_CURRENT_SOURCE_DIR}/CMake/FindLZMA.cmake) IF(LZMA_FOUND) MESSAGE( STATUS "Found system lzma library" ) SET(CMTK_LZMA_LIBS ${LZMA_LIBRARY}) ELSE(LZMA_FOUND) SET(CMTK_USE_LZMA OFF) ENDIF(LZMA_FOUND) ENDIF(CMTK_USE_LZMA) IF(CMTK_USE_LZMA) INCLUDE_DIRECTORIES(${LZMA_INCLUDE_DIR}) ENDIF(CMTK_USE_LZMA) #----------------------------------------------------------------------------- # Add an option to build with DICOM support OPTION(CMTK_USE_DCMTK "Build with optional DICOM support" ON) IF(CMTK_USE_DCMTK) INCLUDE(${CMAKE_CURRENT_SOURCE_DIR}/CMake/FindConfigDCMTK.cmake) OPTION(CMTK_BUILD_DCMTK "Build bundled DCMTK library, even if system library exists" OFF) IF(NOT DCMTK_FOUND) MESSAGE( STATUS "DCMTK library not found - defaulting to bundled version." ) SET(CMTK_BUILD_DCMTK ON CACHE BOOL "Build bundled DCMTK library, even if system library exists" FORCE ) ENDIF(NOT DCMTK_FOUND) IF(CMTK_BUILD_DCMTK) MESSAGE( STATUS "Building bundled DCMTK library for DICOM support." ) SET(DCMTK_SOURCE_ROOT ${CMAKE_CURRENT_SOURCE_DIR}/Utilities/dcmtk) SET(DCMTK_BINARY_ROOT ${CMAKE_CURRENT_BINARY_DIR}/Utilities/dcmtk) SET(DCMTK_INCLUDE_DIR "${DCMTK_SOURCE_ROOT}") SET(DCMTK_INCLUDE_DIRS "${DCMTK_BINARY_ROOT};${DCMTK_INCLUDE_DIR};${DCMTK_INCLUDE_DIR}/dcmtk/dcmjpeg;${DCMTK_INCLUDE_DIR}/dcmtk/dcmimage;${DCMTK_INCLUDE_DIR}/dcmtk/dcmimgle;${DCMTK_INCLUDE_DIR}/dcmtk/dcmdata;${DCMTK_INCLUDE_DIR}/dcmtk/ofstd") INCLUDE_DIRECTORIES(BEFORE ${DCMTK_INCLUDE_DIRS}) SET(DCMTK_LIBRARIES "D4CMTK") ELSE(CMTK_BUILD_DCMTK) MESSAGE( STATUS "Using system DCMTK library." ) # system DCMTK should support jpeg ADD_DEFINITIONS(-DCMTK_USE_DCMTK_JPEG) ENDIF(CMTK_BUILD_DCMTK) IF(NOT ${CMAKE_SYSTEM_NAME} MATCHES "Windows") ADD_DEFINITIONS(-DHAVE_CONFIG_H) SET(CMTK_REQUIRED_CXX_FLAGS "${CMTK_REQUIRED_CXX_FLAGS} -DHAVE_CONFIG_H") ENDIF(NOT ${CMAKE_SYSTEM_NAME} MATCHES "Windows") ELSE(CMTK_USE_DCMTK) MESSAGE( STATUS "Building without DICOM support." ) SET(CMTK_BUILD_DCMTK OFF) UNSET(DCMTK_INCLUDE_DIRS CACHE) UNSET(DCMTK_LIBRARIES CACHE) ENDIF(CMTK_USE_DCMTK) #----------------------------------------------------------------------------- # Add an option to use or not use sqlite3 library OPTION(CMTK_USE_SQLITE "Use sqlite3 library for optional database support" OFF) IF(CMTK_USE_SQLITE) OPTION(CMTK_BUILD_SQLITE "Build bundled sqlite3 library, even if system library exists" OFF) IF(CMTK_BUILD_SQLITE) MESSAGE( STATUS "Building bundled sqlite3 library by user configuration" ) SET(CMTK_BUILD_SQLITE 1) SET(SQLITE_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/Utilities/sqlite3") SET(CMTK_SQLITE_LIB "cmtksqlite3" CACHE INTERNAL "") INCLUDE_DIRECTORIES(BEFORE ${SQLITE_INCLUDE_DIR}) ELSE(CMTK_BUILD_SQLITE) INCLUDE(${CMAKE_CURRENT_SOURCE_DIR}/CMake/FindSQLite3.cmake) IF(SQLITE3_FOUND AND SQLITE3_VERSION STRGREATER "3.5.0") MESSAGE( STATUS "Using system sqlite3 library" ) SET(SQLITE_INCLUDE_DIR "${SQLITE3_INCLUDE_DIR}") SET(CMTK_SQLITE_LIB ${SQLITE3_LIBRARIES} CACHE INTERNAL "") ELSE(SQLITE3_FOUND AND SQLITE3_VERSION STRGREATER "3.5.0") MESSAGE( STATUS "Building bundled sqlite3 library due to missing or outdated system library" ) SET(CMTK_BUILD_SQLITE 1) SET(SQLITE_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/Utilities/sqlite3") SET(CMTK_SQLITE_LIB "cmtksqlite3" CACHE INTERNAL "") INCLUDE_DIRECTORIES(BEFORE ${SQLITE_INCLUDE_DIR}) ENDIF(SQLITE3_FOUND AND SQLITE3_VERSION STRGREATER "3.5.0") ENDIF(CMTK_BUILD_SQLITE) # if we're using SQLite and building with test support, then we need the "sqlite3" tool. IF(BUILD_TESTING) FIND_PROGRAM(SQLITE3_EXECUTABLE sqlite3) ENDIF(BUILD_TESTING) ENDIF(CMTK_USE_SQLITE) #----------------------------------------------------------------------------- # Add an option to use or not use TEEM (for NrrdIO support) OPTION(CMTK_BUILD_NRRD "Build NrrdIO library for NRRD import/export" ON) IF(CMTK_BUILD_NRRD) ADD_DEFINITIONS(-DTEEM_ZLIB) SET(CMTK_REQUIRED_CXX_FLAGS "${CMTK_REQUIRED_CXX_FLAGS} -DTEEM_ZLIB") SET(NRRD_LIBRARIES "NrrdIO" CACHE INTERNAL "The library name for the NrrdIO library") SET(NRRD_INCLUDE_DIRS ${CMAKE_CURRENT_BINARY_DIR}/Utilities/NrrdIO ${CMAKE_CURRENT_SOURCE_DIR}/Utilities/NrrdIO) ENDIF(CMTK_BUILD_NRRD) #----------------------------------------------------------------------------- # Set up CPack support, to be able to distribute binary packages. # INCLUDE(${CMAKE_CURRENT_SOURCE_DIR}/cmtkConfigureCPack.cmake) #----------------------------------------------------------------------------- # Configure bundled utilities. ADD_SUBDIRECTORY(Utilities) #----------------------------------------------------------------------------- # Add an option to enable or disable QT support and GUI applications OPTION(CMTK_USE_QT "Use Qt (GUI applications need this)" OFF) IF(CMTK_USE_QT) SET (QT_MT_REQUIRED true) SET (QT_MIN_VERSION "4.3.0") FIND_PACKAGE(Qt4 COMPONENTS QtCore QtGui REQUIRED) IF (QT_FOUND) MESSAGE("Qt4 Found OK (${qt_version_str})") INCLUDE(${QT_USE_FILE}) ELSE(QT_FOUND) MESSAGE(FATAL_ERROR "No Qt4") ENDIF(QT_FOUND) ENDIF(CMTK_USE_QT) #----------------------------------------------------------------------------- # Add options to enable or disable different compoments OPTION(BUILD_APPS "Build command line tools" ON) IF(BUILD_APPS) SUBDIRS(apps) ENDIF(BUILD_APPS) IF(CMTK_USE_QT AND QT_FOUND) OPTION(BUILD_GUI "Build GUI applications" ON) ENDIF(CMTK_USE_QT AND QT_FOUND) #----------------------------------------------------------------------------- # Add an option to use FFTW library OPTION(CMTK_USE_FFTW "Use FFTW library for fast Fourier transform (required for some specialized tools)" ON) IF(CMTK_USE_FFTW) INCLUDE(${CMAKE_CURRENT_SOURCE_DIR}/CMake/FindFFTW.cmake) IF(FFTWD_FOUND) SET(FFTW_LIBS ${FFTWD_LIB}) IF(CMTK_USE_SMP) IF(CMTK_USE_OPENMP AND FFTWD_OMP_LIB) SET(FFTW_LIBS ${FFTWD_OMP_LIB} ${FFTWD_LIB}) ELSE(CMTK_USE_OPENMP AND FFTWD_OMP_LIB) IF(FFTWD_THREADS_LIB) SET(FFTW_LIBS ${FFTWD_THREADS_LIB} ${FFTWD_LIB}) ENDIF(FFTWD_THREADS_LIB) ENDIF(CMTK_USE_OPENMP AND FFTWD_OMP_LIB) ENDIF(CMTK_USE_SMP) SET(CMTK_FFTW_LIBRARIES ${FFTW_LIBS} CACHE INTERNAL "Link libraries for FFTW support.") SET(CMTK_USE_FFTW_FOUND ON) ELSE(FFTWD_FOUND) MESSAGE( WARNING "Cannot find FFTW3 library - disabling FFT support.") SET(CMTK_USE_FFTW_FOUND OFF) UNSET(CMTK_FFTW_LIBRARIES CACHE) ENDIF(FFTWD_FOUND) ENDIF(CMTK_USE_FFTW) #----------------------------------------------------------------------------- # Add an option to enable building of test code OPTION(BUILD_TESTING "Build test code" ON) IF (BUILD_TESTING) SUBDIRS(testing) ENDIF (BUILD_TESTING) #----------------------------------------------------------------------------- # Create the list of include directories needed for header files. INCLUDE(${CMTK_SOURCE_DIR}/cmtkIncludeDirectories.cmake) INCLUDE_DIRECTORIES( ${CMTK_INCLUDE_DIRS_BUILD_TREE} ${CMTK_INCLUDE_DIRS_SYSTEM} ) SUBDIRS(libs) LINK_DIRECTORIES( ${CMAKE_CURRENT_BINARY_DIR} ${DCMTK_LIBRARY_DIR} ) SET(CMTK_SYSTEM_LIBRARIES ${CMAKE_THREAD_LIBS}) #----------------------------------------------------------------------------- # Dispatch the build into the proper subdirectories. IF(BUILD_GUI) SUBDIRS(gui) ENDIF(BUILD_GUI) SUBDIRS(scripts) #----------------------------------------------------------------------------- # Configure this tree CONFIGURE_FILE(${CMAKE_CURRENT_SOURCE_DIR}/CTestCustom.ctest.in ${CMAKE_CURRENT_BINARY_DIR}/CTestCustom.ctest @ONLY) #----------------------------------------------------------------------------- # Help other projects use CMTK. # Copy the UseCMTK.cmake file to the binary tree for backward compatability. CONFIGURE_FILE(${CMTK_SOURCE_DIR}/UseCMTK.cmake.in ${CMTK_BINARY_DIR}/UseCMTK.cmake COPYONLY IMMEDIATE) INSTALL(FILES ${CMAKE_CURRENT_BINARY_DIR}/UseCMTK.cmake DESTINATION ${CMTK_INSTALL_LIB_DIR} COMPONENT libraries) # Create the CMTKConfig.cmake file containing the CMTK configuration. INCLUDE(${CMTK_SOURCE_DIR}/cmtkGenerateCMTKConfig.cmake) #----------------------------------------------------------------------------- # Add compiler flags CMTK needs to work on this platform. This must be # done AFTER the call to CMAKE_EXPORT_BUILD_SETTINGS. SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${CMTK_REQUIRED_C_FLAGS}") SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${CMTK_REQUIRED_CXX_FLAGS}") SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${CMTK_REQUIRED_LINK_FLAGS}") SET(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} ${CMTK_REQUIRED_LINK_FLAGS}") SET(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} ${CMTK_REQUIRED_LINK_FLAGS}") #----------------------------------------------------------------------------- # Settings for rpath when building shared libraries # Disable rpath altogether if we don't use shared libraries IF(CMTK_BUILD_SHARED_LIBS) # use, i.e. don't skip the full RPATH for the build tree SET(CMAKE_SKIP_BUILD_RPATH FALSE) # when building, don't use the install RPATH already # (but later on when installing) SET(CMAKE_BUILD_WITH_INSTALL_RPATH FALSE) # the RPATH to be used when installing SET(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/${CMTK_INSTALL_LIB_DIR}") # add the automatically determined parts of the RPATH # which point to directories outside the build tree to the install RPATH SET(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE) ELSE(CMTK_BUILD_SHARED_LIBS) SET(CMAKE_SKIP_RPATH TRUE) ENDIF(CMTK_BUILD_SHARED_LIBS) # Save library dependencies. EXPORT_LIBRARY_DEPENDENCIES(${CMTK_BINARY_DIR}/CMTKLibraryDepends.cmake) INSTALL(FILES ${CMTK_BINARY_DIR}/CMTKLibraryDepends.cmake DESTINATION ${CMTK_INSTALL_LIB_DIR} COMPONENT libraries) #----------------------------------------------------------------------------- # After everything else is done, do we want to build man pages? OPTION(BUILD_MANPAGES "Create application manpages" OFF) #----------------------------------------------------------------------------- # Configure third-party contributions. OPTION(BUILD_CONTRIB "Build, install, and package third-party contributions" OFF) IF(BUILD_CONTRIB) ADD_SUBDIRECTORY(contrib) ENDIF(BUILD_CONTRIB) cmtk-3.3.1/COPYING.txt000066400000000000000000000000731276303427400143350ustar00rootroot00000000000000 See licenses per code source in the Licenses/ directory. cmtk-3.3.1/CTestConfig.cmake000066400000000000000000000033301276303427400156350ustar00rootroot00000000000000## ## Copyright 1997-2010 Torsten Rohlfing ## ## Copyright 2004-2011 SRI International ## ## This file is part of the Computational Morphometry Toolkit. ## ## http://www.nitrc.org/projects/cmtk/ ## ## The Computational Morphometry Toolkit is free software: you can ## redistribute it and/or modify it under the terms of the GNU General Public ## License as published by the Free Software Foundation, either version 3 of ## the License, or (at your option) any later version. ## ## The Computational Morphometry Toolkit is distributed in the hope that it ## will be useful, but WITHOUT ANY WARRANTY; without even the implied ## warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License along ## with the Computational Morphometry Toolkit. If not, see ## . ## ## $Revision: 5377 $ ## ## $LastChangedDate: 2015-01-16 18:42:56 -0800 (Fri, 16 Jan 2015) $ ## ## $LastChangedBy: torstenrohlfing $ ## set(CTEST_PROJECT_NAME "CMTK") set(CTEST_NIGHTLY_START_TIME "00:00:00 PST") set(CTEST_DROP_METHOD "http") set(CTEST_DROP_SITE "my.cdash.org") set(CTEST_DROP_LOCATION "/submit.php?project=CMTK") set(CTEST_DROP_SITE_CDASH TRUE) SET(CTEST_CUSTOM_WARNING_EXCEPTION ${CTEST_CUSTOM_WARNING_EXCEPTION} "comparison is always (true|false) due to limited range of data type" "warning: iteration variable .* is unsigned" "Warning: String literal converted to char. in initialization" "sqlite3.c:.*: warning: cast from pointer to integer of different size" ".*/Utilities/.*" ) SET(CTEST_CUSTOM_COVERAGE_EXCLUDE ${CTEST_CUSTOM_COVERAGE_EXCLUDE} ".*/Utilities/.*" ) cmtk-3.3.1/CTestCustom.ctest.in000066400000000000000000000033371276303427400163600ustar00rootroot00000000000000## ## Copyright 1997-2010 Torsten Rohlfing ## ## Copyright 2004-2011 SRI International ## ## This file is part of the Computational Morphometry Toolkit. ## ## http://www.nitrc.org/projects/cmtk/ ## ## The Computational Morphometry Toolkit is free software: you can ## redistribute it and/or modify it under the terms of the GNU General Public ## License as published by the Free Software Foundation, either version 3 of ## the License, or (at your option) any later version. ## ## The Computational Morphometry Toolkit is distributed in the hope that it ## will be useful, but WITHOUT ANY WARRANTY; without even the implied ## warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License along ## with the Computational Morphometry Toolkit. If not, see ## . ## ## $Revision: 4664 $ ## ## $LastChangedDate: 2012-12-11 15:02:59 -0800 (Tue, 11 Dec 2012) $ ## ## $LastChangedBy: torstenrohlfing $ ## set(CTEST_PROJECT_NAME "CMTK") set(CTEST_NIGHTLY_START_TIME "00:00:00 PST") set(CTEST_DROP_METHOD "http") set(CTEST_DROP_SITE "neuro.sri.com") set(CTEST_DROP_LOCATION "/CDash/submit.php?project=CMTK") set(CTEST_DROP_SITE_CDASH TRUE) SET(CTEST_CUSTOM_WARNING_EXCEPTION ${CTEST_CUSTOM_WARNING_EXCEPTION} "comparison is always (true|false) due to limited range of data type" "warning: iteration variable .* is unsigned" "Warning: String literal converted to char. in initialization" "sqlite3.c:.*: warning: cast from pointer to integer of different size" ".*/Utilities/.*" ) SET(CTEST_CUSTOM_COVERAGE_EXCLUDE ${CTEST_CUSTOM_COVERAGE_EXCLUDE} ".*/Utilities/.*" ) cmtk-3.3.1/DISCLAIMER000066400000000000000000000007541276303427400140310ustar00rootroot00000000000000This software is provided as is, with no warranty whatsoever. It may or may not compile on your system. Should you get it to compile, it may or may not run. It may crash at any time. It may destroy your data, format your harddisk, set your CPU on fire, and give you herpes. So make sure you have backups of all your valuable data. Be sure not to run the software as root. And please please PLEASE, do not use it for clinical purposes, because none of this is approved. Otherwise, have fun! cmtk-3.3.1/Doxyfile.in000066400000000000000000001716301276303427400146070ustar00rootroot00000000000000# Doxyfile 1.5.7.1 # This file describes the settings to be used by the documentation system # doxygen (www.doxygen.org) for a project # # All text after a hash (#) is considered a comment and will be ignored # The format is: # TAG = value [value, ...] # For lists items can also be appended using: # TAG += value [value, ...] # Values that contain spaces should be placed between quotes (" ") #--------------------------------------------------------------------------- # Project related configuration options #--------------------------------------------------------------------------- # This tag specifies the encoding used for all characters in the config file # that follow. The default is UTF-8 which is also the encoding used for all # text before the first occurrence of this tag. Doxygen uses libiconv (or the # iconv built into libc) for the transcoding. See # http://www.gnu.org/software/libiconv for the list of possible encodings. DOXYFILE_ENCODING = UTF-8 # The PROJECT_NAME tag is a single word (or a sequence of words surrounded # by quotes) that should identify the project. PROJECT_NAME = "Computational Morphometry Toolkit" # The PROJECT_NUMBER tag can be used to enter a project or revision number. # This could be handy for archiving the generated documentation or # if some version control system is used. PROJECT_NUMBER = @CMTK_VERSION_STRING@ # The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) # base path where the generated documentation will be put. # If a relative path is entered, it will be relative to the location # where doxygen was started. If left blank the current directory will be used. OUTPUT_DIRECTORY = ./ # If the CREATE_SUBDIRS tag is set to YES, then doxygen will create # 4096 sub-directories (in 2 levels) under the output directory of each output # format and will distribute the generated files over these directories. # Enabling this option can be useful when feeding doxygen a huge amount of # source files, where putting all generated files in the same directory would # otherwise cause performance problems for the file system. CREATE_SUBDIRS = NO # The OUTPUT_LANGUAGE tag is used to specify the language in which all # documentation generated by doxygen is written. Doxygen will use this # information to generate all constant output in the proper language. # The default language is English, other supported languages are: # Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional, # Croatian, Czech, Danish, Dutch, Farsi, Finnish, French, German, Greek, # Hungarian, Italian, Japanese, Japanese-en (Japanese with English messages), # Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian, Polish, # Portuguese, Romanian, Russian, Serbian, Serbian-Cyrilic, Slovak, Slovene, # Spanish, Swedish, and Ukrainian. OUTPUT_LANGUAGE = English # If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will # include brief member descriptions after the members that are listed in # the file and class documentation (similar to JavaDoc). # Set to NO to disable this. BRIEF_MEMBER_DESC = YES # If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend # the brief description of a member or function before the detailed description. # Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the # brief descriptions will be completely suppressed. REPEAT_BRIEF = YES # This tag implements a quasi-intelligent brief description abbreviator # that is used to form the text in various listings. Each string # in this list, if found as the leading text of the brief description, will be # stripped from the text and the result after processing the whole list, is # used as the annotated text. Otherwise, the brief description is used as-is. # If left blank, the following values are used ("$name" is automatically # replaced with the name of the entity): "The $name class" "The $name widget" # "The $name file" "is" "provides" "specifies" "contains" # "represents" "a" "an" "the" ABBREVIATE_BRIEF = # If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then # Doxygen will generate a detailed section even if there is only a brief # description. ALWAYS_DETAILED_SEC = NO # If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all # inherited members of a class in the documentation of that class as if those # members were ordinary class members. Constructors, destructors and assignment # operators of the base classes will not be shown. INLINE_INHERITED_MEMB = NO # If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full # path before files name in the file list and in the header files. If set # to NO the shortest path that makes the file name unique will be used. FULL_PATH_NAMES = YES # If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag # can be used to strip a user-defined part of the path. Stripping is # only done if one of the specified strings matches the left-hand part of # the path. The tag can be used to show relative paths in the file list. # If left blank the directory from which doxygen is run is used as the # path to strip. STRIP_FROM_PATH = @CMAKE_CURRENT_SOURCE_DIR@/ # The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of # the path mentioned in the documentation of a class, which tells # the reader which header file to include in order to use a class. # If left blank only the name of the header file containing the class # definition is used. Otherwise one should specify the include paths that # are normally passed to the compiler using the -I flag. STRIP_FROM_INC_PATH = # If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter # (but less readable) file names. This can be useful is your file systems # doesn't support long names like on DOS, Mac, or CD-ROM. SHORT_NAMES = NO # If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen # will interpret the first line (until the first dot) of a JavaDoc-style # comment as the brief description. If set to NO, the JavaDoc # comments will behave just like regular Qt-style comments # (thus requiring an explicit @brief command for a brief description.) JAVADOC_AUTOBRIEF = YES # If the QT_AUTOBRIEF tag is set to YES then Doxygen will # interpret the first line (until the first dot) of a Qt-style # comment as the brief description. If set to NO, the comments # will behave just like regular Qt-style comments (thus requiring # an explicit \brief command for a brief description.) QT_AUTOBRIEF = NO # The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen # treat a multi-line C++ special comment block (i.e. a block of //! or /// # comments) as a brief description. This used to be the default behaviour. # The new default is to treat a multi-line C++ comment block as a detailed # description. Set this tag to YES if you prefer the old behaviour instead. MULTILINE_CPP_IS_BRIEF = NO # If the INHERIT_DOCS tag is set to YES (the default) then an undocumented # member inherits the documentation from any documented member that it # re-implements. INHERIT_DOCS = YES # If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce # a new page for each member. If set to NO, the documentation of a member will # be part of the file/class/namespace that contains it. SEPARATE_MEMBER_PAGES = NO # The TAB_SIZE tag can be used to set the number of spaces in a tab. # Doxygen uses this value to replace tabs by spaces in code fragments. TAB_SIZE = 8 # This tag can be used to specify a number of aliases that acts # as commands in the documentation. An alias has the form "name=value". # For example adding "sideeffect=\par Side Effects:\n" will allow you to # put the command \sideeffect (or @sideeffect) in the documentation, which # will result in a user-defined paragraph with heading "Side Effects:". # You can put \n's in the value part of an alias to insert newlines. ALIASES = # Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C # sources only. Doxygen will then generate output that is more tailored for C. # For instance, some of the names that are used will be different. The list # of all members will be omitted, etc. OPTIMIZE_OUTPUT_FOR_C = NO # Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java # sources only. Doxygen will then generate output that is more tailored for # Java. For instance, namespaces will be presented as packages, qualified # scopes will look different, etc. OPTIMIZE_OUTPUT_JAVA = NO # Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran # sources only. Doxygen will then generate output that is more tailored for # Fortran. OPTIMIZE_FOR_FORTRAN = NO # Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL # sources. Doxygen will then generate output that is tailored for # VHDL. OPTIMIZE_OUTPUT_VHDL = NO # If you use STL classes (i.e. std::string, std::vector, etc.) but do not want # to include (a tag file for) the STL sources as input, then you should # set this tag to YES in order to let doxygen match functions declarations and # definitions whose arguments contain STL classes (e.g. func(std::string); v.s. # func(std::string) {}). This also make the inheritance and collaboration # diagrams that involve STL classes more complete and accurate. BUILTIN_STL_SUPPORT = YES # If you use Microsoft's C++/CLI language, you should set this option to YES to # enable parsing support. CPP_CLI_SUPPORT = NO # Set the SIP_SUPPORT tag to YES if your project consists of sip sources only. # Doxygen will parse them like normal C++ but will assume all classes use public # instead of private inheritance when no explicit protection keyword is present. SIP_SUPPORT = NO # For Microsoft's IDL there are propget and propput attributes to indicate getter # and setter methods for a property. Setting this option to YES (the default) # will make doxygen to replace the get and set methods by a property in the # documentation. This will only work if the methods are indeed getting or # setting a simple type. If this is not the case, or you want to show the # methods anyway, you should set this option to NO. IDL_PROPERTY_SUPPORT = YES # If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC # tag is set to YES, then doxygen will reuse the documentation of the first # member in the group (if any) for the other members of the group. By default # all members of a group must be documented explicitly. DISTRIBUTE_GROUP_DOC = NO # Set the SUBGROUPING tag to YES (the default) to allow class member groups of # the same type (for instance a group of public functions) to be put as a # subgroup of that type (e.g. under the Public Functions section). Set it to # NO to prevent subgrouping. Alternatively, this can be done per class using # the \nosubgrouping command. SUBGROUPING = YES # When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum # is documented as struct, union, or enum with the name of the typedef. So # typedef struct TypeS {} TypeT, will appear in the documentation as a struct # with name TypeT. When disabled the typedef will appear as a member of a file, # namespace, or class. And the struct will be named TypeS. This can typically # be useful for C code in case the coding convention dictates that all compound # types are typedef'ed and only the typedef is referenced, never the tag name. TYPEDEF_HIDES_STRUCT = NO # The SYMBOL_CACHE_SIZE determines the size of the internal cache use to # determine which symbols to keep in memory and which to flush to disk. # When the cache is full, less often used symbols will be written to disk. # For small to medium size projects (<1000 input files) the default value is # probably good enough. For larger projects a too small cache size can cause # doxygen to be busy swapping symbols to and from disk most of the time # causing a significant performance penality. # If the system has enough physical memory increasing the cache will improve the # performance by keeping more symbols in memory. Note that the value works on # a logarithmic scale so increasing the size by one will rougly double the # memory usage. The cache size is given by this formula: # 2^(16+SYMBOL_CACHE_SIZE). The valid range is 0..9, the default is 0, # corresponding to a cache size of 2^16 = 65536 symbols SYMBOL_CACHE_SIZE = 0 #--------------------------------------------------------------------------- # Build related configuration options #--------------------------------------------------------------------------- # If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in # documentation are documented, even if no documentation was available. # Private class members and static file members will be hidden unless # the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES EXTRACT_ALL = YES # If the EXTRACT_PRIVATE tag is set to YES all private members of a class # will be included in the documentation. EXTRACT_PRIVATE = YES # If the EXTRACT_STATIC tag is set to YES all static members of a file # will be included in the documentation. EXTRACT_STATIC = YES # If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) # defined locally in source files will be included in the documentation. # If set to NO only classes defined in header files are included. EXTRACT_LOCAL_CLASSES = YES # This flag is only useful for Objective-C code. When set to YES local # methods, which are defined in the implementation section but not in # the interface are included in the documentation. # If set to NO (the default) only methods in the interface are included. EXTRACT_LOCAL_METHODS = NO # If this flag is set to YES, the members of anonymous namespaces will be # extracted and appear in the documentation as a namespace called # 'anonymous_namespace{file}', where file will be replaced with the base # name of the file that contains the anonymous namespace. By default # anonymous namespace are hidden. EXTRACT_ANON_NSPACES = NO # If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all # undocumented members of documented classes, files or namespaces. # If set to NO (the default) these members will be included in the # various overviews, but no documentation section is generated. # This option has no effect if EXTRACT_ALL is enabled. HIDE_UNDOC_MEMBERS = NO # If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all # undocumented classes that are normally visible in the class hierarchy. # If set to NO (the default) these classes will be included in the various # overviews. This option has no effect if EXTRACT_ALL is enabled. HIDE_UNDOC_CLASSES = NO # If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all # friend (class|struct|union) declarations. # If set to NO (the default) these declarations will be included in the # documentation. HIDE_FRIEND_COMPOUNDS = NO # If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any # documentation blocks found inside the body of a function. # If set to NO (the default) these blocks will be appended to the # function's detailed documentation block. HIDE_IN_BODY_DOCS = NO # The INTERNAL_DOCS tag determines if documentation # that is typed after a \internal command is included. If the tag is set # to NO (the default) then the documentation will be excluded. # Set it to YES to include the internal documentation. INTERNAL_DOCS = NO # If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate # file names in lower-case letters. If set to YES upper-case letters are also # allowed. This is useful if you have classes or files whose names only differ # in case and if your file system supports case sensitive file names. Windows # and Mac users are advised to set this option to NO. CASE_SENSE_NAMES = NO # If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen # will show members with their full class and namespace scopes in the # documentation. If set to YES the scope will be hidden. HIDE_SCOPE_NAMES = NO # If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen # will put a list of the files that are included by a file in the documentation # of that file. SHOW_INCLUDE_FILES = YES # If the INLINE_INFO tag is set to YES (the default) then a tag [inline] # is inserted in the documentation for inline members. INLINE_INFO = YES # If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen # will sort the (detailed) documentation of file and class members # alphabetically by member name. If set to NO the members will appear in # declaration order. SORT_MEMBER_DOCS = YES # If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the # brief documentation of file, namespace and class members alphabetically # by member name. If set to NO (the default) the members will appear in # declaration order. SORT_BRIEF_DOCS = NO # If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the # hierarchy of group names into alphabetical order. If set to NO (the default) # the group names will appear in their defined order. SORT_GROUP_NAMES = NO # If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be # sorted by fully-qualified names, including namespaces. If set to # NO (the default), the class list will be sorted only by class name, # not including the namespace part. # Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. # Note: This option applies only to the class list, not to the # alphabetical list. SORT_BY_SCOPE_NAME = NO # The GENERATE_TODOLIST tag can be used to enable (YES) or # disable (NO) the todo list. This list is created by putting \todo # commands in the documentation. GENERATE_TODOLIST = YES # The GENERATE_TESTLIST tag can be used to enable (YES) or # disable (NO) the test list. This list is created by putting \test # commands in the documentation. GENERATE_TESTLIST = YES # The GENERATE_BUGLIST tag can be used to enable (YES) or # disable (NO) the bug list. This list is created by putting \bug # commands in the documentation. GENERATE_BUGLIST = YES # The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or # disable (NO) the deprecated list. This list is created by putting # \deprecated commands in the documentation. GENERATE_DEPRECATEDLIST= YES # The ENABLED_SECTIONS tag can be used to enable conditional # documentation sections, marked by \if sectionname ... \endif. ENABLED_SECTIONS = # The MAX_INITIALIZER_LINES tag determines the maximum number of lines # the initial value of a variable or define consists of for it to appear in # the documentation. If the initializer consists of more lines than specified # here it will be hidden. Use a value of 0 to hide initializers completely. # The appearance of the initializer of individual variables and defines in the # documentation can be controlled using \showinitializer or \hideinitializer # command in the documentation regardless of this setting. MAX_INITIALIZER_LINES = 30 # Set the SHOW_USED_FILES tag to NO to disable the list of files generated # at the bottom of the documentation of classes and structs. If set to YES the # list will mention the files that were used to generate the documentation. SHOW_USED_FILES = YES # If the sources in your project are distributed over multiple directories # then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy # in the documentation. The default is NO. SHOW_DIRECTORIES = YES # Set the SHOW_FILES tag to NO to disable the generation of the Files page. # This will remove the Files entry from the Quick Index and from the # Folder Tree View (if specified). The default is YES. SHOW_FILES = YES # Set the SHOW_NAMESPACES tag to NO to disable the generation of the # Namespaces page. This will remove the Namespaces entry from the Quick Index # and from the Folder Tree View (if specified). The default is YES. SHOW_NAMESPACES = YES # The FILE_VERSION_FILTER tag can be used to specify a program or script that # doxygen should invoke to get the current version for each file (typically from # the version control system). Doxygen will invoke the program by executing (via # popen()) the command , where is the value of # the FILE_VERSION_FILTER tag, and is the name of an input file # provided by doxygen. Whatever the program writes to standard output # is used as the file version. See the manual for examples. FILE_VERSION_FILTER = # The LAYOUT_FILE tag can be used to specify a layout file which will be parsed by # doxygen. The layout file controls the global structure of the generated output files # in an output format independent way. The create the layout file that represents # doxygen's defaults, run doxygen with the -l option. You can optionally specify a # file name after the option, if omitted DoxygenLayout.xml will be used as the name # of the layout file. LAYOUT_FILE = #--------------------------------------------------------------------------- # configuration options related to warning and progress messages #--------------------------------------------------------------------------- # The QUIET tag can be used to turn on/off the messages that are generated # by doxygen. Possible values are YES and NO. If left blank NO is used. QUIET = YES # The WARNINGS tag can be used to turn on/off the warning messages that are # generated by doxygen. Possible values are YES and NO. If left blank # NO is used. WARNINGS = YES # If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings # for undocumented members. If EXTRACT_ALL is set to YES then this flag will # automatically be disabled. WARN_IF_UNDOCUMENTED = YES # If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for # potential errors in the documentation, such as not documenting some # parameters in a documented function, or documenting parameters that # don't exist or using markup commands wrongly. WARN_IF_DOC_ERROR = YES # This WARN_NO_PARAMDOC option can be abled to get warnings for # functions that are documented, but have no documentation for their parameters # or return value. If set to NO (the default) doxygen will only warn about # wrong or incomplete parameter documentation, but not about the absence of # documentation. WARN_NO_PARAMDOC = YES # The WARN_FORMAT tag determines the format of the warning messages that # doxygen can produce. The string should contain the $file, $line, and $text # tags, which will be replaced by the file and line number from which the # warning originated and the warning text. Optionally the format may contain # $version, which will be replaced by the version of the file (if it could # be obtained via FILE_VERSION_FILTER) WARN_FORMAT = "$file:$line: $text" # The WARN_LOGFILE tag can be used to specify a file to which warning # and error messages should be written. If left blank the output is written # to stderr. WARN_LOGFILE = #--------------------------------------------------------------------------- # configuration options related to the input files #--------------------------------------------------------------------------- # The INPUT tag can be used to specify the files and/or directories that contain # documented source files. You may enter file names like "myfile.cpp" or # directories like "/usr/src/myproject". Separate the files or directories # with spaces. INPUT = @CMAKE_CURRENT_SOURCE_DIR@/libs/ # This tag can be used to specify the character encoding of the source files # that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is # also the default input encoding. Doxygen uses libiconv (or the iconv built # into libc) for the transcoding. See http://www.gnu.org/software/libiconv for # the list of possible encodings. INPUT_ENCODING = UTF-8 # If the value of the INPUT tag contains directories, you can use the # FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp # and *.h) to filter out the source-files in the directories. If left # blank the following patterns are tested: # *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx # *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.py *.f90 FILE_PATTERNS = *.h \ *.cxx \ *.txx # The RECURSIVE tag can be used to turn specify whether or not subdirectories # should be searched for input files as well. Possible values are YES and NO. # If left blank NO is used. RECURSIVE = YES # The EXCLUDE tag can be used to specify files and/or directories that should # excluded from the INPUT source files. This way you can easily exclude a # subdirectory from a directory tree whose root is specified with the INPUT tag. EXCLUDE = # The EXCLUDE_SYMLINKS tag can be used select whether or not files or # directories that are symbolic links (a Unix filesystem feature) are excluded # from the input. EXCLUDE_SYMLINKS = NO # If the value of the INPUT tag contains directories, you can use the # EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude # certain files from those directories. Note that the wildcards are matched # against the file with absolute path, so to exclude all test directories # for example use the pattern */test/* EXCLUDE_PATTERNS = */nifti1.h */.svn/* */Numerics/* # The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names # (namespaces, classes, functions, etc.) that should be excluded from the # output. The symbol name can be a fully qualified name, a word, or if the # wildcard * is used, a substring. Examples: ANamespace, AClass, # AClass::ANamespace, ANamespace::*Test EXCLUDE_SYMBOLS = # The EXAMPLE_PATH tag can be used to specify one or more files or # directories that contain example code fragments that are included (see # the \include command). EXAMPLE_PATH = # If the value of the EXAMPLE_PATH tag contains directories, you can use the # EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp # and *.h) to filter out the source-files in the directories. If left # blank all files are included. EXAMPLE_PATTERNS = # If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be # searched for input files to be used with the \include or \dontinclude # commands irrespective of the value of the RECURSIVE tag. # Possible values are YES and NO. If left blank NO is used. EXAMPLE_RECURSIVE = NO # The IMAGE_PATH tag can be used to specify one or more files or # directories that contain image that are included in the documentation (see # the \image command). IMAGE_PATH = # The INPUT_FILTER tag can be used to specify a program that doxygen should # invoke to filter for each input file. Doxygen will invoke the filter program # by executing (via popen()) the command , where # is the value of the INPUT_FILTER tag, and is the name of an # input file. Doxygen will then use the output that the filter program writes # to standard output. If FILTER_PATTERNS is specified, this tag will be # ignored. INPUT_FILTER = # The FILTER_PATTERNS tag can be used to specify filters on a per file pattern # basis. Doxygen will compare the file name with each pattern and apply the # filter if there is a match. The filters are a list of the form: # pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further # info on how filters are used. If FILTER_PATTERNS is empty, INPUT_FILTER # is applied to all files. FILTER_PATTERNS = # If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using # INPUT_FILTER) will be used to filter the input files when producing source # files to browse (i.e. when SOURCE_BROWSER is set to YES). FILTER_SOURCE_FILES = NO #--------------------------------------------------------------------------- # configuration options related to source browsing #--------------------------------------------------------------------------- # If the SOURCE_BROWSER tag is set to YES then a list of source files will # be generated. Documented entities will be cross-referenced with these sources. # Note: To get rid of all source code in the generated output, make sure also # VERBATIM_HEADERS is set to NO. SOURCE_BROWSER = YES # Setting the INLINE_SOURCES tag to YES will include the body # of functions and classes directly in the documentation. INLINE_SOURCES = NO # Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct # doxygen to hide any special comment blocks from generated source code # fragments. Normal C and C++ comments will always remain visible. STRIP_CODE_COMMENTS = YES # If the REFERENCED_BY_RELATION tag is set to YES # then for each documented function all documented # functions referencing it will be listed. REFERENCED_BY_RELATION = YES # If the REFERENCES_RELATION tag is set to YES # then for each documented function all documented entities # called/used by that function will be listed. REFERENCES_RELATION = YES # If the REFERENCES_LINK_SOURCE tag is set to YES (the default) # and SOURCE_BROWSER tag is set to YES, then the hyperlinks from # functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will # link to the source code. Otherwise they will link to the documentstion. REFERENCES_LINK_SOURCE = YES # If the USE_HTAGS tag is set to YES then the references to source code # will point to the HTML generated by the htags(1) tool instead of doxygen # built-in source browser. The htags tool is part of GNU's global source # tagging system (see http://www.gnu.org/software/global/global.html). You # will need version 4.8.6 or higher. USE_HTAGS = NO # If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen # will generate a verbatim copy of the header file for each class for # which an include is specified. Set to NO to disable this. VERBATIM_HEADERS = YES #--------------------------------------------------------------------------- # configuration options related to the alphabetical class index #--------------------------------------------------------------------------- # If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index # of all compounds will be generated. Enable this if the project # contains a lot of classes, structs, unions or interfaces. ALPHABETICAL_INDEX = YES # If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then # the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns # in which this list will be split (can be a number in the range [1..20]) COLS_IN_ALPHA_INDEX = 3 # In case all classes in a project start with a common prefix, all # classes will be put under the same header in the alphabetical index. # The IGNORE_PREFIX tag can be used to specify one or more prefixes that # should be ignored while generating the index headers. IGNORE_PREFIX = #--------------------------------------------------------------------------- # configuration options related to the HTML output #--------------------------------------------------------------------------- # If the GENERATE_HTML tag is set to YES (the default) Doxygen will # generate HTML output. GENERATE_HTML = YES # The HTML_OUTPUT tag is used to specify where the HTML docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `html' will be used as the default path. HTML_OUTPUT = html/ # The HTML_FILE_EXTENSION tag can be used to specify the file extension for # each generated HTML page (for example: .htm,.php,.asp). If it is left blank # doxygen will generate files with .html extension. HTML_FILE_EXTENSION = .html # The HTML_HEADER tag can be used to specify a personal HTML header for # each generated HTML page. If it is left blank doxygen will generate a # standard header. HTML_HEADER = # The HTML_FOOTER tag can be used to specify a personal HTML footer for # each generated HTML page. If it is left blank doxygen will generate a # standard footer. HTML_FOOTER = # The HTML_STYLESHEET tag can be used to specify a user-defined cascading # style sheet that is used by each HTML page. It can be used to # fine-tune the look of the HTML output. If the tag is left blank doxygen # will generate a default style sheet. Note that doxygen will try to copy # the style sheet file to the HTML output directory, so don't put your own # stylesheet in the HTML output directory as well, or it will be erased! HTML_STYLESHEET = # If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes, # files or namespaces will be aligned in HTML using tables. If set to # NO a bullet list will be used. HTML_ALIGN_MEMBERS = YES # If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML # documentation will contain sections that can be hidden and shown after the # page has loaded. For this to work a browser that supports # JavaScript and DHTML is required (for instance Mozilla 1.0+, Firefox # Netscape 6.0+, Internet explorer 5.0+, Konqueror, or Safari). HTML_DYNAMIC_SECTIONS = NO # If the GENERATE_DOCSET tag is set to YES, additional index files # will be generated that can be used as input for Apple's Xcode 3 # integrated development environment, introduced with OSX 10.5 (Leopard). # To create a documentation set, doxygen will generate a Makefile in the # HTML output directory. Running make will produce the docset in that # directory and running "make install" will install the docset in # ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find # it at startup. # See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html for more information. GENERATE_DOCSET = NO # When GENERATE_DOCSET tag is set to YES, this tag determines the name of the # feed. A documentation feed provides an umbrella under which multiple # documentation sets from a single provider (such as a company or product suite) # can be grouped. DOCSET_FEEDNAME = "Doxygen generated docs" # When GENERATE_DOCSET tag is set to YES, this tag specifies a string that # should uniquely identify the documentation set bundle. This should be a # reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen # will append .docset to the name. DOCSET_BUNDLE_ID = org.doxygen.Project # If the GENERATE_HTMLHELP tag is set to YES, additional index files # will be generated that can be used as input for tools like the # Microsoft HTML help workshop to generate a compiled HTML help file (.chm) # of the generated HTML documentation. GENERATE_HTMLHELP = NO # If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can # be used to specify the file name of the resulting .chm file. You # can add a path in front of the file if the result should not be # written to the html output directory. CHM_FILE = # If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can # be used to specify the location (absolute path including file name) of # the HTML help compiler (hhc.exe). If non-empty doxygen will try to run # the HTML help compiler on the generated index.hhp. HHC_LOCATION = # If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag # controls if a separate .chi index file is generated (YES) or that # it should be included in the master .chm file (NO). GENERATE_CHI = NO # If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING # is used to encode HtmlHelp index (hhk), content (hhc) and project file # content. CHM_INDEX_ENCODING = # If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag # controls whether a binary table of contents is generated (YES) or a # normal table of contents (NO) in the .chm file. BINARY_TOC = NO # The TOC_EXPAND flag can be set to YES to add extra items for group members # to the contents of the HTML help documentation and to the tree view. TOC_EXPAND = NO # If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and QHP_VIRTUAL_FOLDER # are set, an additional index file will be generated that can be used as input for # Qt's qhelpgenerator to generate a Qt Compressed Help (.qch) of the generated # HTML documentation. GENERATE_QHP = NO # If the QHG_LOCATION tag is specified, the QCH_FILE tag can # be used to specify the file name of the resulting .qch file. # The path specified is relative to the HTML output folder. QCH_FILE = # The QHP_NAMESPACE tag specifies the namespace to use when generating # Qt Help Project output. For more information please see # Qt Help Project / Namespace. QHP_NAMESPACE = org.doxygen.Project # The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating # Qt Help Project output. For more information please see # Qt Help Project / Virtual Folders. QHP_VIRTUAL_FOLDER = doc # If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can # be used to specify the location of Qt's qhelpgenerator. # If non-empty doxygen will try to run qhelpgenerator on the generated # .qhp file . QHG_LOCATION = # The DISABLE_INDEX tag can be used to turn on/off the condensed index at # top of each HTML page. The value NO (the default) enables the index and # the value YES disables it. DISABLE_INDEX = NO # This tag can be used to set the number of enum values (range [1..20]) # that doxygen will group on one line in the generated HTML documentation. ENUM_VALUES_PER_LINE = 4 # The GENERATE_TREEVIEW tag is used to specify whether a tree-like index # structure should be generated to display hierarchical information. # If the tag value is set to FRAME, a side panel will be generated # containing a tree-like index structure (just like the one that # is generated for HTML Help). For this to work a browser that supports # JavaScript, DHTML, CSS and frames is required (for instance Mozilla 1.0+, # Netscape 6.0+, Internet explorer 5.0+, or Konqueror). Windows users are # probably better off using the HTML help feature. Other possible values # for this tag are: HIERARCHIES, which will generate the Groups, Directories, # and Class Hierarchy pages using a tree view instead of an ordered list; # ALL, which combines the behavior of FRAME and HIERARCHIES; and NONE, which # disables this behavior completely. For backwards compatibility with previous # releases of Doxygen, the values YES and NO are equivalent to FRAME and NONE # respectively. GENERATE_TREEVIEW = NO # If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be # used to set the initial width (in pixels) of the frame in which the tree # is shown. TREEVIEW_WIDTH = 250 # Use this tag to change the font size of Latex formulas included # as images in the HTML documentation. The default is 10. Note that # when you change the font size after a successful doxygen run you need # to manually remove any form_*.png images from the HTML output directory # to force them to be regenerated. FORMULA_FONTSIZE = 10 #--------------------------------------------------------------------------- # configuration options related to the LaTeX output #--------------------------------------------------------------------------- # If the GENERATE_LATEX tag is set to YES (the default) Doxygen will # generate Latex output. GENERATE_LATEX = NO # The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `latex' will be used as the default path. LATEX_OUTPUT = tex/ # The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be # invoked. If left blank `latex' will be used as the default command name. LATEX_CMD_NAME = latex # The MAKEINDEX_CMD_NAME tag can be used to specify the command name to # generate index for LaTeX. If left blank `makeindex' will be used as the # default command name. MAKEINDEX_CMD_NAME = makeindex # If the COMPACT_LATEX tag is set to YES Doxygen generates more compact # LaTeX documents. This may be useful for small projects and may help to # save some trees in general. COMPACT_LATEX = NO # The PAPER_TYPE tag can be used to set the paper type that is used # by the printer. Possible values are: a4, a4wide, letter, legal and # executive. If left blank a4wide will be used. PAPER_TYPE = letter # The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX # packages that should be included in the LaTeX output. EXTRA_PACKAGES = # The LATEX_HEADER tag can be used to specify a personal LaTeX header for # the generated latex document. The header should contain everything until # the first chapter. If it is left blank doxygen will generate a # standard header. Notice: only use this tag if you know what you are doing! LATEX_HEADER = # If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated # is prepared for conversion to pdf (using ps2pdf). The pdf file will # contain links (just like the HTML output) instead of page references # This makes the output suitable for online browsing using a pdf viewer. PDF_HYPERLINKS = NO # If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of # plain latex in the generated Makefile. Set this option to YES to get a # higher quality PDF documentation. USE_PDFLATEX = NO # If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. # command to the generated LaTeX files. This will instruct LaTeX to keep # running if errors occur, instead of asking the user for help. # This option is also used when generating formulas in HTML. LATEX_BATCHMODE = NO # If LATEX_HIDE_INDICES is set to YES then doxygen will not # include the index chapters (such as File Index, Compound Index, etc.) # in the output. LATEX_HIDE_INDICES = NO #--------------------------------------------------------------------------- # configuration options related to the RTF output #--------------------------------------------------------------------------- # If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output # The RTF output is optimized for Word 97 and may not look very pretty with # other RTF readers or editors. GENERATE_RTF = NO # The RTF_OUTPUT tag is used to specify where the RTF docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `rtf' will be used as the default path. RTF_OUTPUT = # If the COMPACT_RTF tag is set to YES Doxygen generates more compact # RTF documents. This may be useful for small projects and may help to # save some trees in general. COMPACT_RTF = NO # If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated # will contain hyperlink fields. The RTF file will # contain links (just like the HTML output) instead of page references. # This makes the output suitable for online browsing using WORD or other # programs which support those fields. # Note: wordpad (write) and others do not support links. RTF_HYPERLINKS = NO # Load stylesheet definitions from file. Syntax is similar to doxygen's # config file, i.e. a series of assignments. You only have to provide # replacements, missing definitions are set to their default value. RTF_STYLESHEET_FILE = # Set optional variables used in the generation of an rtf document. # Syntax is similar to doxygen's config file. RTF_EXTENSIONS_FILE = #--------------------------------------------------------------------------- # configuration options related to the man page output #--------------------------------------------------------------------------- # If the GENERATE_MAN tag is set to YES (the default) Doxygen will # generate man pages GENERATE_MAN = NO # The MAN_OUTPUT tag is used to specify where the man pages will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `man' will be used as the default path. MAN_OUTPUT = # The MAN_EXTENSION tag determines the extension that is added to # the generated man pages (default is the subroutine's section .3) MAN_EXTENSION = .3 # If the MAN_LINKS tag is set to YES and Doxygen generates man output, # then it will generate one additional man file for each entity # documented in the real man page(s). These additional files # only source the real man page, but without them the man command # would be unable to find the correct page. The default is NO. MAN_LINKS = NO #--------------------------------------------------------------------------- # configuration options related to the XML output #--------------------------------------------------------------------------- # If the GENERATE_XML tag is set to YES Doxygen will # generate an XML file that captures the structure of # the code including all documentation. GENERATE_XML = NO # The XML_OUTPUT tag is used to specify where the XML pages will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `xml' will be used as the default path. XML_OUTPUT = xml # The XML_SCHEMA tag can be used to specify an XML schema, # which can be used by a validating XML parser to check the # syntax of the XML files. XML_SCHEMA = # The XML_DTD tag can be used to specify an XML DTD, # which can be used by a validating XML parser to check the # syntax of the XML files. XML_DTD = # If the XML_PROGRAMLISTING tag is set to YES Doxygen will # dump the program listings (including syntax highlighting # and cross-referencing information) to the XML output. Note that # enabling this will significantly increase the size of the XML output. XML_PROGRAMLISTING = YES #--------------------------------------------------------------------------- # configuration options for the AutoGen Definitions output #--------------------------------------------------------------------------- # If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will # generate an AutoGen Definitions (see autogen.sf.net) file # that captures the structure of the code including all # documentation. Note that this feature is still experimental # and incomplete at the moment. GENERATE_AUTOGEN_DEF = NO #--------------------------------------------------------------------------- # configuration options related to the Perl module output #--------------------------------------------------------------------------- # If the GENERATE_PERLMOD tag is set to YES Doxygen will # generate a Perl module file that captures the structure of # the code including all documentation. Note that this # feature is still experimental and incomplete at the # moment. GENERATE_PERLMOD = NO # If the PERLMOD_LATEX tag is set to YES Doxygen will generate # the necessary Makefile rules, Perl scripts and LaTeX code to be able # to generate PDF and DVI output from the Perl module output. PERLMOD_LATEX = NO # If the PERLMOD_PRETTY tag is set to YES the Perl module output will be # nicely formatted so it can be parsed by a human reader. This is useful # if you want to understand what is going on. On the other hand, if this # tag is set to NO the size of the Perl module output will be much smaller # and Perl will parse it just the same. PERLMOD_PRETTY = YES # The names of the make variables in the generated doxyrules.make file # are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. # This is useful so different doxyrules.make files included by the same # Makefile don't overwrite each other's variables. PERLMOD_MAKEVAR_PREFIX = #--------------------------------------------------------------------------- # Configuration options related to the preprocessor #--------------------------------------------------------------------------- # If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will # evaluate all C-preprocessor directives found in the sources and include # files. ENABLE_PREPROCESSING = YES # If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro # names in the source code. If set to NO (the default) only conditional # compilation will be performed. Macro expansion can be done in a controlled # way by setting EXPAND_ONLY_PREDEF to YES. MACRO_EXPANSION = YES # If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES # then the macro expansion is limited to the macros specified with the # PREDEFINED and EXPAND_AS_DEFINED tags. EXPAND_ONLY_PREDEF = YES # If the SEARCH_INCLUDES tag is set to YES (the default) the includes files # in the INCLUDE_PATH (see below) will be search if a #include is found. SEARCH_INCLUDES = NO # The INCLUDE_PATH tag can be used to specify one or more directories that # contain include files that are not input files but should be processed by # the preprocessor. INCLUDE_PATH = # You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard # patterns (like *.h and *.hpp) to filter out the header-files in the # directories. If left blank, the patterns specified with FILE_PATTERNS will # be used. INCLUDE_FILE_PATTERNS = # The PREDEFINED tag can be used to specify one or more macro names that # are defined before the preprocessor is started (similar to the -D option of # gcc). The argument of the tag is a list of macros of the form: name # or name=definition (no spaces). If the definition and the = are # omitted =1 is assumed. To prevent a macro definition from being # undefined via #undef or recursively expanded use the := operator # instead of the = operator. PREDEFINED = # If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then # this tag can be used to specify a list of macro names that should be expanded. # The macro definition that is found in the sources will be used. # Use the PREDEFINED tag if you want to use a different macro definition. EXPAND_AS_DEFINED = # If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then # doxygen's preprocessor will remove all function-like macros that are alone # on a line, have an all uppercase name, and do not end with a semicolon. Such # function macros are typically used for boiler-plate code, and will confuse # the parser if not removed. SKIP_FUNCTION_MACROS = YES #--------------------------------------------------------------------------- # Configuration::additions related to external references #--------------------------------------------------------------------------- # The TAGFILES option can be used to specify one or more tagfiles. # Optionally an initial location of the external documentation # can be added for each tagfile. The format of a tag file without # this location is as follows: # TAGFILES = file1 file2 ... # Adding location for the tag files is done as follows: # TAGFILES = file1=loc1 "file2 = loc2" ... # where "loc1" and "loc2" can be relative or absolute paths or # URLs. If a location is present for each tag, the installdox tool # does not have to be run to correct the links. # Note that each tag file must have a unique name # (where the name does NOT include the path) # If a tag file is not located in the directory in which doxygen # is run, you must also specify the path to the tagfile here. TAGFILES = # When a file name is specified after GENERATE_TAGFILE, doxygen will create # a tag file that is based on the input files it reads. GENERATE_TAGFILE = # If the ALLEXTERNALS tag is set to YES all external classes will be listed # in the class index. If set to NO only the inherited external classes # will be listed. ALLEXTERNALS = NO # If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed # in the modules index. If set to NO, only the current project's groups will # be listed. EXTERNAL_GROUPS = YES # The PERL_PATH should be the absolute path and name of the perl script # interpreter (i.e. the result of `which perl'). PERL_PATH = /usr/bin #--------------------------------------------------------------------------- # Configuration options related to the dot tool #--------------------------------------------------------------------------- # If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will # generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base # or super classes. Setting the tag to NO turns the diagrams off. Note that # this option is superseded by the HAVE_DOT option below. This is only a # fallback. It is recommended to install and use dot, since it yields more # powerful graphs. CLASS_DIAGRAMS = YES # You can define message sequence charts within doxygen comments using the \msc # command. Doxygen will then run the mscgen tool (see # http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the # documentation. The MSCGEN_PATH tag allows you to specify the directory where # the mscgen tool resides. If left empty the tool is assumed to be found in the # default search path. MSCGEN_PATH = # If set to YES, the inheritance and collaboration graphs will hide # inheritance and usage relations if the target is undocumented # or is not a class. HIDE_UNDOC_RELATIONS = YES # If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is # available from the path. This tool is part of Graphviz, a graph visualization # toolkit from AT&T and Lucent Bell Labs. The other options in this section # have no effect if this option is set to NO (the default) HAVE_DOT = YES # By default doxygen will write a font called FreeSans.ttf to the output # directory and reference it in all dot files that doxygen generates. This # font does not include all possible unicode characters however, so when you need # these (or just want a differently looking font) you can specify the font name # using DOT_FONTNAME. You need need to make sure dot is able to find the font, # which can be done by putting it in a standard location or by setting the # DOTFONTPATH environment variable or by setting DOT_FONTPATH to the directory # containing the font. DOT_FONTNAME = FreeSans # The DOT_FONTSIZE tag can be used to set the size of the font of dot graphs. # The default size is 10pt. DOT_FONTSIZE = 10 # By default doxygen will tell dot to use the output directory to look for the # FreeSans.ttf font (which doxygen will put there itself). If you specify a # different font using DOT_FONTNAME you can set the path where dot # can find it using this tag. DOT_FONTPATH = # If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen # will generate a graph for each documented class showing the direct and # indirect inheritance relations. Setting this tag to YES will force the # the CLASS_DIAGRAMS tag to NO. CLASS_GRAPH = YES # If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen # will generate a graph for each documented class showing the direct and # indirect implementation dependencies (inheritance, containment, and # class references variables) of the class with other documented classes. COLLABORATION_GRAPH = YES # If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen # will generate a graph for groups, showing the direct groups dependencies GROUP_GRAPHS = YES # If the UML_LOOK tag is set to YES doxygen will generate inheritance and # collaboration diagrams in a style similar to the OMG's Unified Modeling # Language. UML_LOOK = NO # If set to YES, the inheritance and collaboration graphs will show the # relations between templates and their instances. TEMPLATE_RELATIONS = NO # If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT # tags are set to YES then doxygen will generate a graph for each documented # file showing the direct and indirect include dependencies of the file with # other documented files. INCLUDE_GRAPH = YES # If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and # HAVE_DOT tags are set to YES then doxygen will generate a graph for each # documented header file showing the documented files that directly or # indirectly include this file. INCLUDED_BY_GRAPH = NO # If the CALL_GRAPH and HAVE_DOT options are set to YES then # doxygen will generate a call dependency graph for every global function # or class method. Note that enabling this option will significantly increase # the time of a run. So in most cases it will be better to enable call graphs # for selected functions only using the \callgraph command. CALL_GRAPH = NO # If the CALLER_GRAPH and HAVE_DOT tags are set to YES then # doxygen will generate a caller dependency graph for every global function # or class method. Note that enabling this option will significantly increase # the time of a run. So in most cases it will be better to enable caller # graphs for selected functions only using the \callergraph command. CALLER_GRAPH = NO # If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen # will graphical hierarchy of all classes instead of a textual one. GRAPHICAL_HIERARCHY = YES # If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES # then doxygen will show the dependencies a directory has on other directories # in a graphical way. The dependency relations are determined by the #include # relations between the files in the directories. DIRECTORY_GRAPH = YES # The DOT_IMAGE_FORMAT tag can be used to set the image format of the images # generated by dot. Possible values are png, jpg, or gif # If left blank png will be used. DOT_IMAGE_FORMAT = png # The tag DOT_PATH can be used to specify the path where the dot tool can be # found. If left blank, it is assumed the dot tool can be found in the path. DOT_PATH = /usr/bin # The DOTFILE_DIRS tag can be used to specify one or more directories that # contain dot files that are included in the documentation (see the # \dotfile command). DOTFILE_DIRS = # The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of # nodes that will be shown in the graph. If the number of nodes in a graph # becomes larger than this value, doxygen will truncate the graph, which is # visualized by representing a node as a red box. Note that doxygen if the # number of direct children of the root node in a graph is already larger than # DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note # that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH. DOT_GRAPH_MAX_NODES = 50 # The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the # graphs generated by dot. A depth value of 3 means that only nodes reachable # from the root by following a path via at most 3 edges will be shown. Nodes # that lay further from the root node will be omitted. Note that setting this # option to 1 or 2 may greatly reduce the computation time needed for large # code bases. Also note that the size of a graph can be further restricted by # DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction. MAX_DOT_GRAPH_DEPTH = 0 # Set the DOT_TRANSPARENT tag to YES to generate images with a transparent # background. This is disabled by default, because dot on Windows does not # seem to support this out of the box. Warning: Depending on the platform used, # enabling this option may lead to badly anti-aliased labels on the edges of # a graph (i.e. they become hard to read). DOT_TRANSPARENT = NO # Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output # files in one run (i.e. multiple -o and -T options on the command line). This # makes dot run faster, but since only newer versions of dot (>1.8.10) # support this, this feature is disabled by default. DOT_MULTI_TARGETS = NO # If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will # generate a legend page explaining the meaning of the various boxes and # arrows in the dot generated graphs. GENERATE_LEGEND = YES # If the DOT_CLEANUP tag is set to YES (the default) Doxygen will # remove the intermediate dot files that are used to generate # the various graphs. DOT_CLEANUP = YES #--------------------------------------------------------------------------- # Configuration::additions related to the search engine #--------------------------------------------------------------------------- # The SEARCHENGINE tag specifies whether or not a search engine should be # used. If set to NO the values of all tags below this one will be ignored. SEARCHENGINE = YES cmtk-3.3.1/LICENSE000066400000000000000000001045131276303427400134750ustar00rootroot00000000000000 GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The GNU General Public License is a free, copyleft license for software and other kinds of works. The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things. To protect your rights, we need to prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others. For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it. For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions. Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users. Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free. The precise terms and conditions for copying, distribution and modification follow. TERMS AND CONDITIONS 0. Definitions. "This License" refers to version 3 of the GNU General Public License. "Copyright" also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. "The Program" refers to any copyrightable work licensed under this License. Each licensee is addressed as "you". "Licensees" and "recipients" may be individuals or organizations. To "modify" a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a "modified version" of the earlier work or a work "based on" the earlier work. A "covered work" means either the unmodified Program or a work based on the Program. To "propagate" a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. To "convey" a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. An interactive user interface displays "Appropriate Legal Notices" to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. 1. Source Code. The "source code" for a work means the preferred form of the work for making modifications to it. "Object code" means any non-source form of a work. A "Standard Interface" means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. The "System Libraries" of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A "Major Component", in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. The "Corresponding Source" for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. The Corresponding Source for a work in source code form is that same work. 2. Basic Permissions. All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. 3. Protecting Users' Legal Rights From Anti-Circumvention Law. No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. 4. Conveying Verbatim Copies. You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. 5. Conveying Modified Source Versions. You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: a) The work must carry prominent notices stating that you modified it, and giving a relevant date. b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to "keep intact all notices". c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an "aggregate" if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. 6. Conveying Non-Source Forms. You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. A "User Product" is either (1) a "consumer product", which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, "normally used" refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. "Installation Information" for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. 7. Additional Terms. "Additional permissions" are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or d) Limiting the use for publicity purposes of names of licensors or authors of the material; or e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. All other non-permissive additional terms are considered "further restrictions" within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. 8. Termination. You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. 9. Acceptance Not Required for Having Copies. You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. 10. Automatic Licensing of Downstream Recipients. Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. An "entity transaction" is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. 11. Patents. A "contributor" is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's "contributor version". A contributor's "essential patent claims" are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, "control" includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. In the following three paragraphs, a "patent license" is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To "grant" such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. "Knowingly relying" means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. A patent license is "discriminatory" if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. 12. No Surrender of Others' Freedom. If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. 13. Use with the GNU Affero General Public License. Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such. 14. Revised Versions of this License. The Free Software Foundation may publish revised and/or new versions of the GNU General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation. If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. 15. Disclaimer of Warranty. THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. Limitation of Liability. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 17. Interpretation of Sections 15 and 16. If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . Also add information on how to contact you by electronic and paper mail. If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: Copyright (C) This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, your program's commands might be different; for a GUI interface, you would use an "about box". You should also get your employer (if you work as a programmer) or school, if any, to sign a "copyright disclaimer" for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see . The GNU General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. But first, please read . cmtk-3.3.1/Licenses/000077500000000000000000000000001276303427400142315ustar00rootroot00000000000000cmtk-3.3.1/Licenses/LICENSE.NrrdIO000066400000000000000000000020031276303427400163650ustar00rootroot00000000000000 NrrdIO: stand-alone code for basic nrrd functionality Copyright (C) 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. cmtk-3.3.1/Licenses/LICENSE.alglib000066400000000000000000000030461276303427400164720ustar00rootroot00000000000000Copyright (c) 2003-2007, Sergey Bochkanov (ALGLIB project). See www.alglib.net or alglib.sources.ru for details. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer listed in this license in the documentation and/or other materials provided with the distribution. - Neither the name of the copyright holders nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. cmtk-3.3.1/Licenses/LICENSE.cmtk000066400000000000000000001045151276303427400162010ustar00rootroot00000000000000 GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The GNU General Public License is a free, copyleft license for software and other kinds of works. The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things. To protect your rights, we need to prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others. For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it. For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions. Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users. Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free. The precise terms and conditions for copying, distribution and modification follow. TERMS AND CONDITIONS 0. Definitions. "This License" refers to version 3 of the GNU General Public License. "Copyright" also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. "The Program" refers to any copyrightable work licensed under this License. Each licensee is addressed as "you". "Licensees" and "recipients" may be individuals or organizations. To "modify" a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a "modified version" of the earlier work or a work "based on" the earlier work. A "covered work" means either the unmodified Program or a work based on the Program. To "propagate" a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. To "convey" a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. An interactive user interface displays "Appropriate Legal Notices" to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. 1. Source Code. The "source code" for a work means the preferred form of the work for making modifications to it. "Object code" means any non-source form of a work. A "Standard Interface" means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. The "System Libraries" of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A "Major Component", in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. The "Corresponding Source" for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. The Corresponding Source for a work in source code form is that same work. 2. Basic Permissions. All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. 3. Protecting Users' Legal Rights From Anti-Circumvention Law. No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. 4. Conveying Verbatim Copies. You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. 5. Conveying Modified Source Versions. You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: a) The work must carry prominent notices stating that you modified it, and giving a relevant date. b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to "keep intact all notices". c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an "aggregate" if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. 6. Conveying Non-Source Forms. You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. A "User Product" is either (1) a "consumer product", which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, "normally used" refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. "Installation Information" for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. 7. Additional Terms. "Additional permissions" are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or d) Limiting the use for publicity purposes of names of licensors or authors of the material; or e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. All other non-permissive additional terms are considered "further restrictions" within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. 8. Termination. You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. 9. Acceptance Not Required for Having Copies. You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. 10. Automatic Licensing of Downstream Recipients. Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. An "entity transaction" is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. 11. Patents. A "contributor" is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's "contributor version". A contributor's "essential patent claims" are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, "control" includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. In the following three paragraphs, a "patent license" is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To "grant" such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. "Knowingly relying" means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. A patent license is "discriminatory" if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. 12. No Surrender of Others' Freedom. If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. 13. Use with the GNU Affero General Public License. Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such. 14. Revised Versions of this License. The Free Software Foundation may publish revised and/or new versions of the GNU General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation. If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. 15. Disclaimer of Warranty. THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. Limitation of Liability. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 17. Interpretation of Sections 15 and 16. If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . Also add information on how to contact you by electronic and paper mail. If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: Copyright (C) This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, your program's commands might be different; for a GUI interface, you would use an "about box". You should also get your employer (if you work as a programmer) or school, if any, to sign a "copyright disclaimer" for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see . The GNU General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. But first, please read . cmtk-3.3.1/Licenses/LICENSE.dcmtk000066400000000000000000000345661276303427400163550ustar00rootroot00000000000000 COPYRIGHT Unless otherwise specified, the DCMTK software package has the following copyright: /* * Copyright (C) 1994-2004, OFFIS * * This software and supporting documentation were developed by * * Kuratorium OFFIS e.V. * Healthcare Information and Communication Systems * Escherweg 2 * D-26121 Oldenburg, Germany * * THIS SOFTWARE IS MADE AVAILABLE, AS IS, AND OFFIS MAKES NO WARRANTY * REGARDING THE SOFTWARE, ITS PERFORMANCE, ITS MERCHANTABILITY OR * FITNESS FOR ANY PARTICULAR USE, FREEDOM FROM ANY COMPUTER DISEASES OR * ITS CONFORMITY TO ANY SPECIFICATION. THE ENTIRE RISK AS TO QUALITY AND * PERFORMANCE OF THE SOFTWARE IS WITH THE USER. * * Copyright of the software and supporting documentation is, unless * otherwise stated, owned by OFFIS, and free access is hereby granted as * a license to use this software, copy this software and prepare * derivative works based upon this software. However, any distribution * of this software source code or supporting documentation or derivative * works (source code and supporting documentation) must include the * three paragraphs of this copyright notice. * */ Some portions of the DCMTK software package are derived from earlier versions of this software with the following copyright, and can be identifed by the following copyright notice located in each source file: /* * Copyright (C) 1993/1994, OFFIS, Oldenburg University and CERIUM * * This software and supporting documentation were * developed by * * Institut OFFIS * Bereich Kommunikationssysteme * Westerstr. 10-12 * 26121 Oldenburg, Germany * * Fachbereich Informatik * Abteilung Prozessinformatik * Carl von Ossietzky Universitaet Oldenburg * Ammerlaender Heerstr. 114-118 * 26111 Oldenburg, Germany * * CERIUM * Laboratoire SIM * Faculte de Medecine * 2 Avenue du Pr. Leon Bernard * 35043 Rennes Cedex, France * * for CEN/TC251/WG4 as a contribution to the Radiological * Society of North America (RSNA) 1993 Digital Imaging and * Communications in Medicine (DICOM) Demonstration. * * THIS SOFTWARE IS MADE AVAILABLE, AS IS, AND NEITHER OFFIS, * OLDENBURG UNIVERSITY NOR CERIUM MAKE ANY WARRANTY REGARDING * THE SOFTWARE, ITS PERFORMANCE, ITS MERCHANTABILITY OR * FITNESS FOR ANY PARTICULAR USE, FREEDOM FROM ANY COMPUTER * DISEASES OR ITS CONFORMITY TO ANY SPECIFICATION. THE * ENTIRE RISK AS TO QUALITY AND PERFORMANCE OF THE SOFTWARE * IS WITH THE USER. * * Copyright of the software and supporting documentation * is, unless otherwise stated, jointly owned by OFFIS, * Oldenburg University and CERIUM and free access is hereby * granted as a license to use this software, copy this * software and prepare derivative works based upon this * software. However, any distribution of this software * source code or supporting documentation or derivative * works (source code and supporting documentation) must * include the three paragraphs of this copyright notice. * */ Some other parts of this software within the dcmtk/dcmnet sub-package related to the DICOM Upper Layer Protocol are derived from software developed for the RSNA'93 DICOM demonstration and kindly made available to us by the Mallinckrodt Institute of Radiology. Such software can be identifed by the following copyright notice located in each affected source file: /* * Copyright (C) 1993, RSNA and Washington University * * The software and supporting documentation for the Radiological * Society of North America (RSNA) 1993 Digital Imaging and * Communications in Medicine (DICOM) Demonstration were developed * at the * Electronic Radiology Laboratory * Mallinckrodt Institute of Radiology * Washington University School of Medicine * 510 S. Kingshighway Blvd. * St. Louis, MO 63110 * as part of the 1993 DICOM Central Test Node project for, and * under contract with, the Radiological Society of North America. * * THIS SOFTWARE IS MADE AVAILABLE, AS IS, AND NEITHER RSNA NOR * WASHINGTON UNIVERSITY MAKE ANY WARRANTY ABOUT THE SOFTWARE, ITS * PERFORMANCE, ITS MERCHANTABILITY OR FITNESS FOR ANY PARTICULAR * USE, FREEDOM FROM ANY COMPUTER DISEASES OR ITS CONFORMITY TO ANY * SPECIFICATION. THE ENTIRE RISK AS TO QUALITY AND PERFORMANCE OF * THE SOFTWARE IS WITH THE USER. * * Copyright of the software and supporting documentation is * jointly owned by RSNA and Washington University, and free access * is hereby granted as a license to use this software, copy this * software and prepare derivative works based upon this software. * However, any distribution of this software source code or * supporting documentation or derivative works (source code and * supporting documentation) must include the three paragraphs of * the copyright notice. */ The dcmjpeg sub-package includes an adapted version of the Independent JPEG Group Toolkit Version 6b, which is contained in dcmjpeg/libijg8, dcmjpeg/libijg12 and dcmjpeg/libijg16. This toolkit is covered by the following copyright. The original README file for the Independent JPEG Group Toolkit is located in dcmjpeg/docs/ijg_readme.txt. /* * The authors make NO WARRANTY or representation, either express or implied, * with respect to this software, its quality, accuracy, merchantability, or * fitness for a particular purpose. This software is provided "AS IS", and you, * its user, assume the entire risk as to its quality and accuracy. * * This software is copyright (C) 1991-1998, Thomas G. Lane. * All Rights Reserved except as specified below. * * Permission is hereby granted to use, copy, modify, and distribute this * software (or portions thereof) for any purpose, without fee, subject to these * conditions: * (1) If any part of the source code for this software is distributed, then this * README file must be included, with this copyright and no-warranty notice * unaltered; and any additions, deletions, or changes to the original files * must be clearly indicated in accompanying documentation. * (2) If only executable code is distributed, then the accompanying * documentation must state that "this software is based in part on the work of * the Independent JPEG Group". * (3) Permission for use of this software is granted only if the user accepts * full responsibility for any undesirable consequences; the authors accept * NO LIABILITY for damages of any kind. * * These conditions apply to any software derived from or based on the IJG code, * not just to the unmodified library. If you use our work, you ought to * acknowledge us. * * Permission is NOT granted for the use of any IJG author's name or company name * in advertising or publicity relating to this software or products derived from * it. This software may be referred to only as "the Independent JPEG Group's * software". * * We specifically permit and encourage the use of this software as the basis of * commercial products, provided that all warranty or liability claims are * assumed by the product vendor. */ The color quantization code in module dcmimage (dcmquant and the related classes) is derived from code written by Jef Poskanzer for the NetPBM toolkit which has the following copyright: /* * Copyright (C) 1989, 1991 by Jef Poskanzer. * * Permission to use, copy, modify, and distribute this software and its * documentation for any purpose and without fee is hereby granted, provided * that the above copyright notice appear in all copies and that both that * copyright notice and this permission notice appear in supporting * documentation. This software is provided "as is" without express or * implied warranty. */ The code for the OFStandard::strlcpy and OFStandard::strlcat helper functions in ofstd/libsrc/ofstd.cc has been derived from the BSD implementation of strlcpy() and strlcat() and which carries the following copyright notice: /* * Copyright (c) 1998 Todd C. Miller * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ The code for the OFStandard::atof helper function in ofstd/libsrc/ofstd.cc has been derived from an implementation which carries the following copyright notice: /* * Copyright 1988 Regents of the University of California * Permission to use, copy, modify, and distribute this software and * its documentation for any purpose and without fee is hereby granted, * provided that the above copyright notice appear in all copies. The * University of California makes no representations about the * suitability of this software for any purpose. It is provided "as * is" without express or implied warranty. * * * The code for OFStandard::ftoa has been derived * from an implementation which carries the following copyright notice: * * Copyright (c) 1988 Regents of the University of California. * All rights reserved. * * Redistribution and use in source and binary forms are permitted * provided that the above copyright notice and this paragraph are * duplicated in all such forms and that any documentation, * advertising materials, and other materials related to such * distribution and use acknowledge that the software was developed * by the University of California, Berkeley. The name of the * University may not be used to endorse or promote products derived * from this software without specific prior written permission. * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. */ The "Base64" encoder/decoder in ofstd/libsrc/ofstd.cc has been derived from an implementation which carries the following copyright notice: /* * Copyright (c) 1999, Bob Withers - bwit(at)pobox.com * * This code may be freely used for any purpose, either personal or commercial, * provided the authors copyright notice remains intact. */ The dcmjp2k sub-package (which is currently not part of the free toolkit) includes an adapted version of the JasPer JPEG 2000 toolkit, which is contained in dcmjp2k/libjaspr. This toolkit is covered by the following copyright. /* JasPer License Version 2.0 * * Copyright (c) 1999-2000 Image Power, Inc. * Copyright (c) 1999-2000 The University of British Columbia * Copyright (c) 2001-2003 Michael David Adams * * All rights reserved. * * Permission is hereby granted, free of charge, to any person (the * "User") obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without restriction, * including without limitation the rights to use, copy, modify, merge, * publish, distribute, and/or sell copies of the Software, and to permit * persons to whom the Software is furnished to do so, subject to the * following conditions: * * 1. The above copyright notices and this permission notice (which * includes the disclaimer below) shall be included in all copies or * substantial portions of the Software. * * 2. The name of a copyright holder shall not be used to endorse or * promote products derived from the Software without specific prior * written permission. * * THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS * LICENSE. NO USE OF THE SOFTWARE IS AUTHORIZED HEREUNDER EXCEPT UNDER * THIS DISCLAIMER. THE SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS * "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING * BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. IN NO * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL * INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. NO ASSURANCES ARE * PROVIDED BY THE COPYRIGHT HOLDERS THAT THE SOFTWARE DOES NOT INFRINGE * THE PATENT OR OTHER INTELLECTUAL PROPERTY RIGHTS OF ANY OTHER ENTITY. * EACH COPYRIGHT HOLDER DISCLAIMS ANY LIABILITY TO THE USER FOR CLAIMS * BROUGHT BY ANY OTHER ENTITY BASED ON INFRINGEMENT OF INTELLECTUAL * PROPERTY RIGHTS OR OTHERWISE. AS A CONDITION TO EXERCISING THE RIGHTS * GRANTED HEREUNDER, EACH USER HEREBY ASSUMES SOLE RESPONSIBILITY TO SECURE * ANY OTHER INTELLECTUAL PROPERTY RIGHTS NEEDED, IF ANY. THE SOFTWARE * IS NOT FAULT-TOLERANT AND IS NOT INTENDED FOR USE IN MISSION-CRITICAL * SYSTEMS, SUCH AS THOSE USED IN THE OPERATION OF NUCLEAR FACILITIES, * AIRCRAFT NAVIGATION OR COMMUNICATION SYSTEMS, AIR TRAFFIC CONTROL * SYSTEMS, DIRECT LIFE SUPPORT MACHINES, OR WEAPONS SYSTEMS, IN WHICH * THE FAILURE OF THE SOFTWARE OR SYSTEM COULD LEAD DIRECTLY TO DEATH, * PERSONAL INJURY, OR SEVERE PHYSICAL OR ENVIRONMENTAL DAMAGE ("HIGH * RISK ACTIVITIES"). THE COPYRIGHT HOLDERS SPECIFICALLY DISCLAIM ANY * EXPRESS OR IMPLIED WARRANTY OF FITNESS FOR HIGH RISK ACTIVITIES. */ cmtk-3.3.1/Licenses/LICENSE.mxml000066400000000000000000000632131276303427400162170ustar00rootroot00000000000000 Mini-XML License October 18, 2005 The Mini-XML library and included programs are provided under the terms of the GNU Library General Public License (LGPL) with the following exceptions: 1. Static linking of applications to the Mini-XML library does not constitute a derivative work and does not require the author to provide source code for the application, use the shared Mini-XML libraries, or link their applications against a user-supplied version of Mini-XML. If you link the application to a modified version of Mini-XML, then the changes to Mini-XML must be provided under the terms of the LGPL in sections 1, 2, and 4. 2. You do not have to provide a copy of the Mini-XML license with programs that are linked to the Mini-XML library, nor do you have to identify the Mini-XML license in your program or documentation as required by section 6 of the LGPL. GNU LIBRARY GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1991 Free Software Foundation, Inc. 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. [This is the first released version of the library GPL. It is numbered 2 because it goes with version 2 of the ordinary GPL.] Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public Licenses are intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This license, the Library General Public License, applies to some specially designated Free Software Foundation software, and to any other libraries whose authors decide to use it. You can use it for your libraries, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the library, or if you modify it. For example, if you distribute copies of the library, whether gratis or for a fee, you must give the recipients all the rights that we gave you. You must make sure that they, too, receive or can get the source code. If you link a program with the library, you must provide complete object files to the recipients so that they can relink them with the library, after making changes to the library and recompiling it. And you must show them these terms so they know their rights. Our method of protecting your rights has two steps: (1) copyright the library, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the library. Also, for each distributor's protection, we want to make certain that everyone understands that there is no warranty for this free library. If the library is modified by someone else and passed on, we want its recipients to know that what they have is not the original version, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that companies distributing free software will individually obtain patent licenses, thus in effect transforming the program into proprietary software. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. Most GNU software, including some libraries, is covered by the ordinary GNU General Public License, which was designed for utility programs. This license, the GNU Library General Public License, applies to certain designated libraries. This license is quite different from the ordinary one; be sure to read it in full, and don't assume that anything in it is the same as in the ordinary license. The reason we have a separate public license for some libraries is that they blur the distinction we usually make between modifying or adding to a program and simply using it. Linking a program with a library, without changing the library, is in some sense simply using the library, and is analogous to running a utility program or application program. However, in a textual and legal sense, the linked executable is a combined work, a derivative of the original library, and the ordinary General Public License treats it as such. Because of this blurred distinction, using the ordinary General Public License for libraries did not effectively promote software sharing, because most developers did not use the libraries. We concluded that weaker conditions might promote sharing better. However, unrestricted linking of non-free programs would deprive the users of those programs of all benefit from the free status of the libraries themselves. This Library General Public License is intended to permit developers of non-free programs to use free libraries, while preserving your freedom as a user of such programs to change the free libraries that are incorporated in them. (We have not seen how to achieve this as regards changes in header files, but we have achieved it as regards changes in the actual functions of the Library.) The hope is that this will lead to faster development of free libraries. The precise terms and conditions for copying, distribution and modification follow. Pay close attention to the difference between a "work based on the library" and a "work that uses the library". The former contains code derived from the library, while the latter only works together with the library. Note that it is possible for a library to be covered by the ordinary General Public License rather than by this special one. GNU LIBRARY GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License Agreement applies to any software library which contains a notice placed by the copyright holder or other authorized party saying it may be distributed under the terms of this Library General Public License (also called "this License"). Each licensee is addressed as "you". A "library" means a collection of software functions and/or data prepared so as to be conveniently linked with application programs (which use some of those functions and data) to form executables. The "Library", below, refers to any such software library or work which has been distributed under these terms. A "work based on the Library" means either the Library or any derivative work under copyright law: that is to say, a work containing the Library or a portion of it, either verbatim or with modifications and/or translated straightforwardly into another language. (Hereinafter, translation is included without limitation in the term "modification".) "Source code" for a work means the preferred form of the work for making modifications to it. For a library, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the library. Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running a program using the Library is not restricted, and output from such a program is covered only if its contents constitute a work based on the Library (independent of the use of the Library in a tool for writing it). Whether that is true depends on what the Library does and what the program that uses the Library does. 1. You may copy and distribute verbatim copies of the Library's complete source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and distribute a copy of this License along with the Library. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Library or any portion of it, thus forming a work based on the Library, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) The modified work must itself be a software library. b) You must cause the files modified to carry prominent notices stating that you changed the files and the date of any change. c) You must cause the whole of the work to be licensed at no charge to all third parties under the terms of this License. d) If a facility in the modified Library refers to a function or a table of data to be supplied by an application program that uses the facility, other than as an argument passed when the facility is invoked, then you must make a good faith effort to ensure that, in the event an application does not supply such function or table, the facility still operates, and performs whatever part of its purpose remains meaningful. (For example, a function in a library to compute square roots has a purpose that is entirely well-defined independent of the application. Therefore, Subsection 2d requires that any application-supplied function or table used by this function must be optional: if the application does not supply it, the square root function must still compute square roots.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Library, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Library, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Library. In addition, mere aggregation of another work not based on the Library with the Library (or with a work based on the Library) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may opt to apply the terms of the ordinary GNU General Public License instead of this License to a given copy of the Library. To do this, you must alter all the notices that refer to this License, so that they refer to the ordinary GNU General Public License, version 2, instead of to this License. (If a newer version than version 2 of the ordinary GNU General Public License has appeared, then you can specify that version instead if you wish.) Do not make any other change in these notices. Once this change is made in a given copy, it is irreversible for that copy, so the ordinary GNU General Public License applies to all subsequent copies and derivative works made from that copy. This option is useful when you wish to copy part of the code of the Library into a program that is not a library. 4. You may copy and distribute the Library (or a portion or derivative of it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange. If distribution of object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place satisfies the requirement to distribute the source code, even though third parties are not compelled to copy the source along with the object code. 5. A program that contains no derivative of any portion of the Library, but is designed to work with the Library by being compiled or linked with it, is called a "work that uses the Library". Such a work, in isolation, is not a derivative work of the Library, and therefore falls outside the scope of this License. However, linking a "work that uses the Library" with the Library creates an executable that is a derivative of the Library (because it contains portions of the Library), rather than a "work that uses the library". The executable is therefore covered by this License. Section 6 states terms for distribution of such executables. When a "work that uses the Library" uses material from a header file that is part of the Library, the object code for the work may be a derivative work of the Library even though the source code is not. Whether this is true is especially significant if the work can be linked without the Library, or if the work is itself a library. The threshold for this to be true is not precisely defined by law. If such an object file uses only numerical parameters, data structure layouts and accessors, and small macros and small inline functions (ten lines or less in length), then the use of the object file is unrestricted, regardless of whether it is legally a derivative work. (Executables containing this object code plus portions of the Library will still fall under Section 6.) Otherwise, if the work is a derivative of the Library, you may distribute the object code for the work under the terms of Section 6. Any executables containing that work also fall under Section 6, whether or not they are linked directly with the Library itself. 6. As an exception to the Sections above, you may also compile or link a "work that uses the Library" with the Library to produce a work containing portions of the Library, and distribute that work under terms of your choice, provided that the terms permit modification of the work for the customer's own use and reverse engineering for debugging such modifications. You must give prominent notice with each copy of the work that the Library is used in it and that the Library and its use are covered by this License. You must supply a copy of this License. If the work during execution displays copyright notices, you must include the copyright notice for the Library among them, as well as a reference directing the user to the copy of this License. Also, you must do one of these things: a) Accompany the work with the complete corresponding machine-readable source code for the Library including whatever changes were used in the work (which must be distributed under Sections 1 and 2 above); and, if the work is an executable linked with the Library, with the complete machine-readable "work that uses the Library", as object code and/or source code, so that the user can modify the Library and then relink to produce a modified executable containing the modified Library. (It is understood that the user who changes the contents of definitions files in the Library will not necessarily be able to recompile the application to use the modified definitions.) b) Accompany the work with a written offer, valid for at least three years, to give the same user the materials specified in Subsection 6a, above, for a charge no more than the cost of performing this distribution. c) If distribution of the work is made by offering access to copy from a designated place, offer equivalent access to copy the above specified materials from the same place. d) Verify that the user has already received a copy of these materials or that you have already sent this user a copy. For an executable, the required form of the "work that uses the Library" must include any data and utility programs needed for reproducing the executable from it. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. It may happen that this requirement contradicts the license restrictions of other proprietary libraries that do not normally accompany the operating system. Such a contradiction means you cannot use both them and the Library together in an executable that you distribute. 7. You may place library facilities that are a work based on the Library side-by-side in a single library together with other library facilities not covered by this License, and distribute such a combined library, provided that the separate distribution of the work based on the Library and of the other library facilities is otherwise permitted, and provided that you do these two things: a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities. This must be distributed under the terms of the Sections above. b) Give prominent notice with the combined library of the fact that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. 8. You may not copy, modify, sublicense, link with, or distribute the Library except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense, link with, or distribute the Library is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 9. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Library or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Library (or any work based on the Library), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Library or works based on it. 10. Each time you redistribute the Library (or any work based on the Library), the recipient automatically receives a license from the original licensor to copy, distribute, link with or modify the Library subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 11. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Library at all. For example, if a patent license would not permit royalty-free redistribution of the Library by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Library. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply, and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 12. If the distribution and/or use of the Library is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Library under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 13. The Free Software Foundation may publish revised and/or new versions of the Library General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Library specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Library does not specify a license version number, you may choose any version ever published by the Free Software Foundation. 14. If you wish to incorporate parts of the Library into other free programs whose distribution conditions are incompatible with these, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS Appendix: How to Apply These Terms to Your New Libraries If you develop a new library, and you want it to be of the greatest possible use to the public, we recommend making it free software that everyone can redistribute and change. You can do so by permitting redistribution under these terms (or, alternatively, under the terms of the ordinary General Public License). To apply these terms, attach the following notices to the library. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. Also add information on how to contact you by electronic and paper mail. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the library, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the library `Frob' (a library for tweaking knobs) written by James Random Hacker. , 1 April 1990 Ty Coon, President of Vice That's all there is to it! cmtk-3.3.1/Licenses/LICENSE.numdiff000066400000000000000000001045131276303427400166710ustar00rootroot00000000000000 GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The GNU General Public License is a free, copyleft license for software and other kinds of works. The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things. To protect your rights, we need to prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others. For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it. For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions. Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users. Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free. The precise terms and conditions for copying, distribution and modification follow. TERMS AND CONDITIONS 0. Definitions. "This License" refers to version 3 of the GNU General Public License. "Copyright" also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. "The Program" refers to any copyrightable work licensed under this License. Each licensee is addressed as "you". "Licensees" and "recipients" may be individuals or organizations. To "modify" a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a "modified version" of the earlier work or a work "based on" the earlier work. A "covered work" means either the unmodified Program or a work based on the Program. To "propagate" a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. To "convey" a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. An interactive user interface displays "Appropriate Legal Notices" to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. 1. Source Code. The "source code" for a work means the preferred form of the work for making modifications to it. "Object code" means any non-source form of a work. A "Standard Interface" means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. The "System Libraries" of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A "Major Component", in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. The "Corresponding Source" for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. The Corresponding Source for a work in source code form is that same work. 2. Basic Permissions. All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. 3. Protecting Users' Legal Rights From Anti-Circumvention Law. No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. 4. Conveying Verbatim Copies. You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. 5. Conveying Modified Source Versions. You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: a) The work must carry prominent notices stating that you modified it, and giving a relevant date. b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to "keep intact all notices". c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an "aggregate" if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. 6. Conveying Non-Source Forms. You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. A "User Product" is either (1) a "consumer product", which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, "normally used" refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. "Installation Information" for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. 7. Additional Terms. "Additional permissions" are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or d) Limiting the use for publicity purposes of names of licensors or authors of the material; or e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. All other non-permissive additional terms are considered "further restrictions" within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. 8. Termination. You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. 9. Acceptance Not Required for Having Copies. You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. 10. Automatic Licensing of Downstream Recipients. Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. An "entity transaction" is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. 11. Patents. A "contributor" is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's "contributor version". A contributor's "essential patent claims" are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, "control" includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. In the following three paragraphs, a "patent license" is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To "grant" such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. "Knowingly relying" means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. A patent license is "discriminatory" if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. 12. No Surrender of Others' Freedom. If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. 13. Use with the GNU Affero General Public License. Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such. 14. Revised Versions of this License. The Free Software Foundation may publish revised and/or new versions of the GNU General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation. If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. 15. Disclaimer of Warranty. THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. Limitation of Liability. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 17. Interpretation of Sections 15 and 16. If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . Also add information on how to contact you by electronic and paper mail. If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: Copyright (C) This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, your program's commands might be different; for a GUI interface, you would use an "about box". You should also get your employer (if you work as a programmer) or school, if any, to sign a "copyright disclaimer" for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see . The GNU General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. But first, please read . cmtk-3.3.1/Licenses/LICENSE.sqlite3000066400000000000000000000026721276303427400166300ustar00rootroot00000000000000SQLite Copyright SQLite is in the Public Domain All of the deliverable code in SQLite has been dedicated to the public domain by the authors. All code authors, and representatives of the companies they work for, have signed affidavits dedicating their contributions to the public domain and originals of those signed affidavits are stored in a firesafe at the main offices of Hwaci. Anyone is free to copy, modify, publish, use, compile, sell, or distribute the original SQLite code, either in source code form or as a compiled binary, for any purpose, commercial or non-commercial, and by any means. The previous paragraph applies to the deliverable code in SQLite - those parts of the SQLite library that you actually bundle and ship with a larger application. Portions of the documentation and some code used as part of the build process might fall under other licenses. The details here are unclear. We do not worry about the licensing of the documentation and build code so much because none of these things are part of the core deliverable SQLite library. All of the deliverable code in SQLite has been written from scratch. No code has been taken from other projects or from the open internet. Every line of code can be traced back to its original author, and all of those authors have public domain dedications on file. So the SQLite code base is clean and is uncontaminated with licensed code from other projects. https://www.sqlite.org/copyright.html cmtk-3.3.1/Licenses/LICENSE.vtk000066400000000000000000000033761276303427400160520ustar00rootroot00000000000000 Program: Visualization Toolkit Language: C++ Thanks: Thanks to David G. Gobbi who developed this class. Copyright (c) 1993-2001 Ken Martin, Will Schroeder, Bill Lorensen All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither name of Ken Martin, Will Schroeder, or Bill Lorensen nor the names of any contributors may be used to endorse or promote products derived from this software without specific prior written permission. * Modified source versions must be plainly marked as such, and must not be misrepresented as being the original software. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. cmtk-3.3.1/Licenses/LICENSE.zlib000066400000000000000000000027731276303427400162060ustar00rootroot00000000000000Copyright notice: (C) 1995-2010 Jean-loup Gailly and Mark Adler This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. Jean-loup Gailly Mark Adler jloup@gzip.org madler@alumni.caltech.edu If you use the zlib library in a product, we would appreciate *not* receiving lengthy legal documents to sign. The sources are provided for free but without warranty of any kind. The library has been entirely written by Jean-loup Gailly and Mark Adler; it does not include third-party code. If you redistribute modified sources, we would appreciate that you include in the file ChangeLog history information documenting your changes. Please read the FAQ for more information on the distribution of modified source versions. cmtk-3.3.1/README.txt000066400000000000000000000047631276303427400141740ustar00rootroot00000000000000 The Computational Morphometry Toolkit ========================================================================== Release Notes -- CMTK 3.3.0 =========================== This is a minor bugfix release that addresses an issue with polynomial transformations that could cause out-of-bounds memory access. Release Notes -- CMTK 3.3.0 =========================== This release replaces signed 32bit integers for pixel indexing with signed 64bit integers, thus removing the previous limitation of allowing no more than 2B pixels per image. Platform Support ================ CMTK has been built and tested on the following platforms: - Linux 64bit (Fedora 23), gcc 5.3.1 - Cygwin, gcc 4.9.3 - Windows7 x64, VisualStudio 2013 Express for Desktop Platform-Specific Issues ======================== MacOS-X ------- - Code coverage tests are only supported with gcc compiler and SDK 10.6. Older SDKs or the clang and llvm compiler front-ends do not support code coverage. http://www.nitrc.org/tracker/index.php?func=detail&aid=5450&group_id=212&atid=877 - To use a pre-combiled binary version of CMTK that was compiled on MacOS with MacPorts compilers, the following MacPorts packages have to be installed under /opt/local: - qt4 - sqlite3 - dcmtk SolarisStudio Compiler, Linux/Intel ----------------------------------- - SolarisStudio cannot build CMTK due to a non-standard, incompatible C++ STL. - SolarisStudio C++ 12.2 crashes when compiling the "Numerics" library with full optimization, -O3. A bug report has been filed with, and accepted by, Oracle: http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6989625 Workaround: build "MinSizeRel" configuration, which sets optimization level to O2. Note that OpenMP must be disabled, because otherwise optimization is bumped back to O3 by default. This problem is also fixed in SolarisStudio 12.3, but see next issue. - SolarisStudio C++ 12.3 crashes when compiling the file "cmtkEchoPlanarUnwarpFunctional.cxx" with OpenMP support turned on. Workaround: set "CMTK_USE_OPENMP" configuration option to "OFF"; this will lead to much of CMTK's functionality executing single-threaded. Open64 Compiler --------------- - CMTK does not build in Release mode with the Open64 compiler due to internal compiler errors. ========================================================================== This software is available from http://www.nitrc.org/projects/cmtk/ ========================================================================== cmtk-3.3.1/UseCMTK.cmake.in000066400000000000000000000041011276303427400153020ustar00rootroot00000000000000## ## Copyright 1997-2009 Torsten Rohlfing ## ## Copyright 2004-2009 SRI International ## ## This file is part of the Computational Morphometry Toolkit. ## ## http://www.nitrc.org/projects/cmtk/ ## ## The Computational Morphometry Toolkit is free software: you can ## redistribute it and/or modify it under the terms of the GNU General Public ## License as published by the Free Software Foundation, either version 3 of ## the License, or (at your option) any later version. ## ## The Computational Morphometry Toolkit is distributed in the hope that it ## will be useful, but WITHOUT ANY WARRANTY; without even the implied ## warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License along ## with the Computational Morphometry Toolkit. If not, see ## . ## ## $Revision: 2610 $ ## ## $LastChangedDate: 2010-12-08 12:45:57 -0800 (Wed, 08 Dec 2010) $ ## ## $LastChangedBy: torstenrohlfing $ ## # # This file sets up include directories, link directories, and # compiler settings for a project to use CMTK. It should not be # included directly, but rather through the CMTK_USE_FILE setting # obtained from CMTKConfig.cmake. # IF(CMTK_BUILD_SETTINGS_FILE) INCLUDE(${CMAKE_ROOT}/Modules/CMakeImportBuildSettings.cmake) CMAKE_IMPORT_BUILD_SETTINGS(${CMTK_BUILD_SETTINGS_FILE}) ENDIF(CMTK_BUILD_SETTINGS_FILE) # Add compiler flags needed to use CMTK. SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${CMTK_REQUIRED_C_FLAGS}") SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${CMTK_REQUIRED_CXX_FLAGS}") SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${CMTK_REQUIRED_LINK_FLAGS}") SET(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} ${CMTK_REQUIRED_LINK_FLAGS}") SET(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} ${CMTK_REQUIRED_LINK_FLAGS}") # Add include directories needed to use CMTK. INCLUDE_DIRECTORIES(BEFORE ${CMTK_INCLUDE_DIRS}) # Add link directories needed to use CMTK. LINK_DIRECTORIES(${CMTK_LIBRARY_DIRS}) cmtk-3.3.1/Utilities/000077500000000000000000000000001276303427400144375ustar00rootroot00000000000000cmtk-3.3.1/Utilities/CMakeLists.txt000066400000000000000000000047351276303427400172100ustar00rootroot00000000000000## ## Copyright 1997-2009 Torsten Rohlfing ## ## Copyright 2004-2011, 2014 SRI International ## ## This file is part of the Computational Morphometry Toolkit. ## ## http://www.nitrc.org/projects/cmtk/ ## ## The Computational Morphometry Toolkit is free software: you can ## redistribute it and/or modify it under the terms of the GNU General Public ## License as published by the Free Software Foundation, either version 3 of ## the License, or (at your option) any later version. ## ## The Computational Morphometry Toolkit is distributed in the hope that it ## will be useful, but WITHOUT ANY WARRANTY; without even the implied ## warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License along ## with the Computational Morphometry Toolkit. If not, see ## . ## ## $Revision: 5172 $ ## ## $LastChangedDate: 2014-01-16 12:49:48 -0800 (Thu, 16 Jan 2014) $ ## ## $LastChangedBy: torstenrohlfing $ ## # turn off Windows security warnings for bundled code IF(${CMAKE_SYSTEM_NAME} MATCHES "Windows") ADD_DEFINITIONS(-D_CRT_SECURE_NO_WARNINGS) ENDIF(${CMAKE_SYSTEM_NAME} MATCHES "Windows") IF(CMTK_BUILD_ZLIB) SUBDIRS(zlib) ENDIF(CMTK_BUILD_ZLIB) IF(CMTK_BUILD_NRRD) SUBDIRS(NrrdIO) ENDIF(CMTK_BUILD_NRRD) IF(CMTK_BUILD_DCMTK) SUBDIRS(dcmtk) ENDIF(CMTK_BUILD_DCMTK) IF(CMTK_BUILD_MXML) SUBDIRS(mxml) ENDIF(CMTK_BUILD_MXML) IF(CMTK_BUILD_SQLITE) SUBDIRS(sqlite3) ENDIF(CMTK_BUILD_SQLITE) # # If building test code, we need numdiff to tolerantly compare numerical results against baselines # IF(BUILD_TESTING) # Tets whether numdiff is already available FIND_PROGRAM(NUMDIFF_EXECUTABLE_SYSTEM numdiff PATHS /usr/bin /usr/local/bin /opt/local/bin ENV PATH) # If not available, build our own IF(EXISTS ${NUMDIFF_EXECUTABLE_SYSTEM}) MESSAGE(STATUS "Found numdiff executable at ${NUMDIFF_EXECUTABLE_SYSTEM}") SET(NUMDIFF_EXECUTABLE ${NUMDIFF_EXECUTABLE_SYSTEM} CACHE PATH "Path to numdiff executable" FORCE) ELSE(EXISTS ${NUMDIFF_EXECUTABLE_SYSTEM}) MESSAGE(STATUS "BUILD_TESTING is active - building numdiff for tolerant numerical comparisons") SUBDIRS(numdiff-5.2.1) SET(NUMDIFF_EXECUTABLE ${CMAKE_BINARY_DIR}/bin/numdiff CACHE PATH "Path to numdiff executable" FORCE) ENDIF(EXISTS ${NUMDIFF_EXECUTABLE_SYSTEM}) MARK_AS_ADVANCED(NUMDIFF_EXECUTABLE NUMDIFF_EXECUTABLE_SYSTEM) ENDIF(BUILD_TESTING) cmtk-3.3.1/Utilities/NrrdIO/000077500000000000000000000000001276303427400155745ustar00rootroot00000000000000cmtk-3.3.1/Utilities/NrrdIO/000-README.txt000066400000000000000000000135301276303427400175710ustar00rootroot00000000000000--------------------------------------------------------------------------- License ------------------------------------------------------------------- --------------------------------------------------------------------------- NrrdIO: stand-alone code for basic nrrd functionality Copyright (C) 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. --------------------------------------------------------------------------- General information ------------------------------------------------------- --------------------------------------------------------------------------- ** NOTE: These source files have been copied and/or modified from Teem, ** . Teem is licensed under a weakened GNU Lesser Public ** License (the weakening is to remove burdens on those releasing binaries ** that statically link against Teem) . The non-reciprocal licensing defined ** above applies to only the source files in the NrrdIO distribution, and not ** to Teem. NrrdIO is a modified and highly abbreviated version of the Teem. NrrdIO contains only the source files (or portions thereof) required for creating and destroying nrrds, and for getting them into and out of files. The NrrdIO sources are created from the Teem sources by using GNU Make (pre-GNUmakefile in the NrrdIO distribution). NrrdIO makes it very easy to add support for the NRRD file format to your program, which is a good thing considering and design and flexibility of the NRRD file format, and the existence of the "unu" command-line tool for operating on nrrds. Using NrrdIO requires exactly one header file, "NrrdIO.h", and exactly one library, libNrrdIO. Currently, the API presented by NrrdIO is a strict subset of the Teem API. There is no additional encapsulation or abstraction. This could be annoying in the sense that you still have to deal with the biff (for error messages) and the air (for utilities) library function calls. Or it could be good and sane in the sense that code which uses NrrdIO can be painlessly "upgraded" to use more of Teem. Also, the API documentation for the same functionality in Teem will apply directly to NrrdIO. NrrdIO was originally created with the help of Josh Cates in order to add support for the NRRD file format to the Insight Toolkit (ITK). --------------------------------------------------------------------------- NrrdIO API crash course --------------------------------------------------- --------------------------------------------------------------------------- Please read . The functions that are explained in detail are all present in NrrdIO. Be aware, however, that NrrdIO currently supports ONLY the NRRD file format, and not: PNG, PNM, VTK, or EPS. The functionality in Teem's nrrd library which is NOT in NrrdIO is basically all those non-trivial manipulations of the values in the nrrd, or their ordering in memory. Still, NrrdIO can do a fair amount, namely all the functions listed in these sections of the "Overview of rest of API" in the above web page: - Basic "methods" - Manipulation of per-axis meta-information - Utility functions - Comments in nrrd - Key/value pairs - Endianness (byte ordering) - Getting/Setting values (crude!) - Input from, Output to files --------------------------------------------------------------------------- Files comprising NrrdIO --------------------------------------------------- --------------------------------------------------------------------------- NrrdIO.h: The single header file that declares all the functions and variables that NrrdIO provides. sampleIO.c: Tiny little command-line program demonstrating the basic NrrdIO API. Read this for examples of how NrrdIO is used to read and write NRRD files. CMakeLists.txt: to build NrrdIO with CMake pre-GNUmakefile: how NrrdIO sources are created from the Teem sources. Requires that TEEM_SRC_ROOT be set, and uses the following two files. tail.pl, unteem.pl: used to make small modifications to the source files to convert them from Teem to NrrdIO sources mangle.pl: used to generate a #include file for name-mangling the external symbols in the NrrdIO library, to avoid possible problems with programs that link with both NrrdIO and the rest of Teem. preamble.c: the preamble describing the non-copyleft licensing of NrrdIO. qnanhibit.c: discover a variable which, like endianness, is architecture dependent and which is required for building NrrdIO (as well as Teem), but unlike endianness, is completely obscure and unheard of. encodingBzip2.c, formatEPS.c, formatPNG.c, formatPNM.c, formatText.c, formatVTK.c: These files create stubs for functionality which is fully present in Teem, but which has been removed from NrrdIO in the interest of simplicity. The filenames are in fact unfortunately misleading, but they should be understood as listing the functionality that is MISSING in NrrdIO. All other files: copied/modified from the air, biff, and nrrd libraries of Teem. cmtk-3.3.1/Utilities/NrrdIO/001-README.txt000066400000000000000000000004101276303427400175630ustar00rootroot00000000000000This version of NrrdIO has been adapted by Torsten Rohlfing for inclusion in CMTK, The Computational Morphometry Toolkit. All modifiations are (C) SRI International, but are licensed under the same terms as the original NrrdIO code taken from the Insight Toolkit. cmtk-3.3.1/Utilities/NrrdIO/754.c000066400000000000000000000433271276303427400162700ustar00rootroot00000000000000/* NrrdIO: stand-alone code for basic nrrd functionality Copyright (C) 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. */ #include "NrrdIO.h" #include "privateAir.h" #include "teemQnanhibit.h" /* ** all this is based on a reading of ** Hennessy + Patterson "Computer Architecture, A Quantitative Approach" ** pages A-13 - A-17 ** ** and some assorted web pages, such as: ** http://en.wikipedia.org/wiki/NaN#Encoding ** which explains what Teem calls qnanhibit, and ** http://grouper.ieee.org/groups/754/email/msg04192.html ** which includes some discussion on signal-vs-quiet nan */ /* ** _airFloatEndian{Little,Big}, _airDoubleEndian{Little,Big} ** ** these unions facilitate converting amongst ** i: unsigned integral type ** c: (sign,exp,frac) triples of unsigned integral components ** v: the floating point numbers these bit-patterns represent */ typedef union { unsigned int i; struct { unsigned int mant : 23; unsigned int expo : 8; unsigned int sign : 1; } c; float v; } _airFloatEndianLittle; /* HEY COPY AND PASTE */ typedef union { unsigned int i; struct { unsigned int sign : 1; unsigned int expo : 8; unsigned int mant : 23; } c; float v; } _airFloatEndianBig; typedef union { airULLong i; /* these next two members are used for printing in airFPFprintf_d */ struct { /* access to whole double as two unsigned ints */ unsigned int half0 : 32; unsigned int half1 : 32; } h; struct { /* access to fraction with two unsigned ints */ unsigned int mant1 : 32; unsigned int mant0 : 20; unsigned int expo : 11; unsigned int sign : 1; } c; double v; } _airDoubleEndianLittle; /* HEY COPY AND PASTE */ typedef union { airULLong i; /* these next two members are used for printing in airFPFprintf_d */ struct { /* access to whole double as two unsigned ints */ unsigned int half1 : 32; unsigned int half0 : 32; } h; struct { /* access to fraction with two unsigned ints */ unsigned int sign : 1; unsigned int expo : 11; unsigned int mant0 : 20; unsigned int mant1 : 32; } c; double v; } _airDoubleEndianBig; /* ** The hex numbers in braces are examples of C's "initial member of a union" ** aggregate initialization. */ #if TEEM_QNANHIBIT == 1 const int airMyQNaNHiBit = 1; const airFloat airFloatQNaN = {0x7fffffff}; const airFloat airFloatSNaN = {0x7fbfffff}; #else const int airMyQNaNHiBit = 0; const airFloat airFloatQNaN = {0x7fbfffff}; const airFloat airFloatSNaN = {0x7fffffff}; #endif const airFloat airFloatPosInf = {0x7f800000}; const airFloat airFloatNegInf = {0xff800000}; /* why does solaris whine? */ /* ** these shouldn't be needed, but here they are if need be: in this file: const airFloat airFloatMax = {0x7f7fffff}; const airFloat airFloatMin = {0x00800000}; const airDouble airDoubleMax = {AIR_ULLONG(0x7fefffffffffffff)}; const airDouble airDoubleMin = {AIR_ULLONG(0x0010000000000000)}; in air.h: extern air_export const airFloat airFloatMax; extern air_export const airFloat airFloatMin; extern air_export const airDouble airDoubleMax; extern air_export const airDouble airDoubleMin; #define AIR_FLT_MIN (airFloatMin.f) #define AIR_FLT_MAX (airFloatMax.f) #define AIR_DBL_MIN (airDoubleMin.d) #define AIR_DBL_MAX (airDoubleMax.d) */ /* the bit-masking done here quiets gcc -Wconversion warnings */ #define FP_SET_F(flit, fbig, s, e, m) \ flit.c.sign = 1u & (s); \ flit.c.expo = ((1u<8)-1) & (e); \ flit.c.mant = ((1u<23)-1) & (m); \ fbig.c.sign = 1u & (s); \ fbig.c.expo = ((1u<8)-1) & (e); \ fbig.c.mant = ((1u<23)-1) & (m) #define FP_GET_F(s, e, m, flit, fbig) \ if (airEndianLittle == airMyEndian()) { \ (s) = flit.c.sign; \ (e) = flit.c.expo; \ (m) = flit.c.mant; \ } else { \ (s) = fbig.c.sign; \ (e) = fbig.c.expo; \ (m) = fbig.c.mant; \ } #define FP_SET_D(dlit, dbig, s, e, m0, m1) \ dlit.c.sign = 1u & (s); \ dlit.c.expo = ((1u<<11)-1) & (e); \ dlit.c.mant0 = ((1u<<20)-1) & (m0); \ dlit.c.mant1 = (m1); \ dbig.c.sign = 1u & (s); \ dbig.c.expo = ((1u<<11)-1) & (e); \ dbig.c.mant0 = ((1u<<20)-1) & (m0); \ dbig.c.mant1 = (m1) #define FP_GET_D(s, e, m0, m1, dlit, dbig) \ if (airEndianLittle == airMyEndian()) { \ (s) = dlit.c.sign; \ (e) = dlit.c.expo; \ (m0) = dlit.c.mant0; \ (m1) = dlit.c.mant1; \ } else { \ (s) = dbig.c.sign; \ (e) = dbig.c.expo; \ (m0) = dbig.c.mant0; \ (m1) = dbig.c.mant1; \ } float airFPPartsToVal_f(unsigned int sign, unsigned int expo, unsigned int mant) { _airFloatEndianLittle flit; _airFloatEndianBig fbig; FP_SET_F(flit, fbig, sign, expo, mant); return (airEndianLittle == airMyEndian() ? flit.v : fbig.v); } void airFPValToParts_f(unsigned int *signP, unsigned int *expoP, unsigned int *mantP, float v) { _airFloatEndianLittle flit; _airFloatEndianBig fbig; flit.v = fbig.v = v; FP_GET_F(*signP, *expoP, *mantP, flit, fbig); } double airFPPartsToVal_d(unsigned int sign, unsigned int expo, unsigned int mant0, unsigned int mant1) { _airDoubleEndianLittle dlit; _airDoubleEndianBig dbig; FP_SET_D(dlit, dbig, sign, expo, mant0, mant1); return (airEndianLittle == airMyEndian() ? dlit.v : dbig.v); } /* ** Disable the 'local variable used without having been initialized' ** warning produced by the MSVC compiler */ #ifdef _MSC_VER #pragma warning(push) #pragma warning(disable : 4700) #endif void airFPValToParts_d(unsigned int *signP, unsigned int *expoP, unsigned int *mant0P, unsigned int *mant1P, double v) { _airDoubleEndianLittle dlit; _airDoubleEndianBig dbig; dlit.v = dbig.v = v; FP_GET_D(*signP, *expoP, *mant0P, *mant1P, dlit, dbig); } #ifdef _MSC_VER #pragma warning(pop) #endif /* ******** airFPGen_f() ** ** generates a floating point value which is a member of the given class */ float airFPGen_f(int cls) { _airFloatEndianLittle flit; _airFloatEndianBig fbig; switch(cls) { case airFP_SNAN: /* sgn: anything, mant: anything non-zero with high bit !TEEM_QNANHIBIT */ FP_SET_F(flit, fbig, 0, 0xff, (!TEEM_QNANHIBIT << 22) | 0x3fffff); break; case airFP_QNAN: /* sgn: anything, mant: anything non-zero with high bit TEEM_QNANHIBIT */ FP_SET_F(flit, fbig, 0, 0xff, (TEEM_QNANHIBIT << 22) | 0x3fffff); break; case airFP_POS_INF: FP_SET_F(flit, fbig, 0, 0xff, 0); break; case airFP_NEG_INF: FP_SET_F(flit, fbig, 1, 0xff, 0); break; case airFP_POS_NORM: /* exp: anything non-zero but < 0xff, mant: anything */ FP_SET_F(flit, fbig, 0, 0x80, 0x7ff000); break; case airFP_NEG_NORM: /* exp: anything non-zero but < 0xff, mant: anything */ FP_SET_F(flit, fbig, 1, 0x80, 0x7ff000); break; case airFP_POS_DENORM: /* mant: anything non-zero */ FP_SET_F(flit, fbig, 0, 0, 0xff); break; case airFP_NEG_DENORM: /* mant: anything non-zero */ FP_SET_F(flit, fbig, 1, 0, 0xff); break; case airFP_NEG_ZERO: FP_SET_F(flit, fbig, 1, 0, 0); break; case airFP_POS_ZERO: default: FP_SET_F(flit, fbig, 0, 0, 0); break; } return (airEndianLittle == airMyEndian() ? flit.v : fbig.v); } /* ******** airFPGen_d() ** ** generates a floating point value which is a member of the given class */ double airFPGen_d(int cls) { _airDoubleEndianLittle dlit; _airDoubleEndianBig dbig; switch(cls) { case airFP_SNAN: /* sgn: anything, mant: anything non-zero with high bit !TEEM_QNANHIBIT */ FP_SET_D(dlit, dbig, 0, 0x7ff, (!TEEM_QNANHIBIT << 19) | 0x7ffff, 0xffffffff); break; case airFP_QNAN: /* sgn: anything, mant anything non-zero with high bit TEEM_QNANHIBIT */ FP_SET_D(dlit, dbig, 0, 0x7ff, (TEEM_QNANHIBIT << 19) | 0x7ffff, 0xffffffff); break; case airFP_POS_INF: FP_SET_D(dlit, dbig, 0, 0x7ff, 0, 0); break; case airFP_NEG_INF: FP_SET_D(dlit, dbig, 1, 0x7ff, 0, 0); break; case airFP_POS_NORM: /* exp: anything non-zero but < 0xff, mant: anything */ FP_SET_D(dlit, dbig, 0, 0x400, 0x0ff00, 0); break; case airFP_NEG_NORM: /* exp: anything non-zero but < 0xff, mant: anything */ FP_SET_D(dlit, dbig, 1, 0x400, 0x0ff00, 0); break; case airFP_POS_DENORM: /* mant: anything non-zero */ FP_SET_D(dlit, dbig, 0, 0, 0xff, 0); break; case airFP_NEG_DENORM: /* mant: anything non-zero */ FP_SET_D(dlit, dbig, 1, 0, 0xff, 0); break; case airFP_NEG_ZERO: FP_SET_D(dlit, dbig, 1, 0, 0, 0); break; case airFP_POS_ZERO: default: FP_SET_D(dlit, dbig, 0, 0, 0, 0); break; } return (airEndianLittle == airMyEndian() ? dlit.v : dbig.v); } /* ******** airFPClass_f() ** ** given a floating point number, tells which class its in */ int airFPClass_f(float val) { _airFloatEndianLittle flit; _airFloatEndianBig fbig; unsigned int sign, expv, mant; int indexv, ret = 0; flit.v = fbig.v = val; FP_GET_F(sign, expv, mant, flit, fbig); indexv = ((!!sign) << 2) | ((!!expv) << 1) | (!!mant); switch(indexv) { case 0: /* all fields are zero */ ret = airFP_POS_ZERO; break; case 1: /* only mantissa is non-zero */ ret = airFP_POS_DENORM; break; case 2: /* only exponent field is non-zero */ if (0xff == expv) { ret = airFP_POS_INF; } else { ret = airFP_POS_NORM; } break; case 3: /* exponent and mantissa fields are non-zero */ if (0xff == expv) { if (TEEM_QNANHIBIT == mant >> 22) { ret = airFP_QNAN; } else { ret = airFP_SNAN; } } else { ret = airFP_POS_NORM; } break; case 4: /* only sign field is non-zero */ ret = airFP_NEG_ZERO; break; case 5: /* sign and mantissa fields are non-zero */ ret = airFP_NEG_DENORM; break; case 6: /* sign and exponent fields are non-zero */ if (0xff > expv) { ret = airFP_NEG_NORM; } else { ret = airFP_NEG_INF; } break; case 7: /* all fields are non-zero */ if (0xff > expv) { ret = airFP_NEG_NORM; } else { if (TEEM_QNANHIBIT == mant >> 22) { ret = airFP_QNAN; } else { ret = airFP_SNAN; } } break; } return ret; } /* ** Disable the 'local variable used without having been initialized' ** warning produced by the MSVC compiler */ #ifdef _MSC_VER #pragma warning(push) #pragma warning(disable : 4700) #endif /* ******** airFPClass_d() ** ** given a double, tells which class its in */ int airFPClass_d(double val) { _airDoubleEndianLittle dlit; _airDoubleEndianBig dbig; unsigned int sign, expo, mant0, mant1; int indexv, ret=0; unsigned char hibit; dlit.v = dbig.v = val; /* "expo = d.c.expo" had been annotated with: "this seems to be a WIN32 bug: on a quiet-NaN, d.c.exp should be non-zero, but it was completely zero, so that this function returned airFP_NEG_DENORM instead of airFP_QNAN" */ FP_GET_D(sign, expo, mant0, mant1, dlit, dbig); hibit = AIR_CAST(unsigned char, mant0 >> 19); /* mant0 20 bits wide: ok */ indexv = ((!!sign) << 2) | ((!!expo) << 1) | (!!mant0 || !!mant1); switch(indexv) { case 0: /* all fields are zero */ ret = airFP_POS_ZERO; break; case 1: /* only fractional field is non-zero */ ret = airFP_POS_DENORM; break; case 2: /* only exponent field is non-zero */ if (0x7ff > expo) { ret = airFP_POS_NORM; } else { ret = airFP_POS_INF; } break; case 3: /* exponent and fractional fields are non-zero */ if (0x7ff > expo) { ret = airFP_POS_NORM; } else { if (TEEM_QNANHIBIT == hibit) { ret = airFP_QNAN; } else { ret = airFP_SNAN; } } break; case 4: /* only sign field is non-zero */ ret = airFP_NEG_ZERO; break; case 5: /* sign and fractional fields are non-zero */ ret = airFP_NEG_DENORM; break; case 6: /* sign and exponent fields are non-zero */ if (0x7ff > expo) { ret = airFP_NEG_NORM; } else { ret = airFP_NEG_INF; } break; case 7: /* all fields are non-zero */ if (0x7ff > expo) ret = airFP_NEG_NORM; else { if (TEEM_QNANHIBIT == hibit) { ret = airFP_QNAN; } else { ret = airFP_SNAN; } } break; } return ret; } #ifdef _MSC_VER #pragma warning(pop) #endif /* ******** airIsNaN() ** ** returns 1 if input is either kind of NaN, 0 otherwise. It is okay ** to only have a double version of this function, as opposed to ** having one for float and one for double, because Section 6.2 of the ** 754 spec tells us that that NaN is to be preserved across precision ** changes (and airSanity() explicitly checks for this). */ int airIsNaN(double g) { _airFloatEndianLittle flit; _airFloatEndianBig fbig; unsigned int sign, expo, mant; flit.v = fbig.v = AIR_CAST(float, g); FP_GET_F(sign, expo, mant, flit, fbig); AIR_UNUSED(sign); return (0xff == expo && mant); } /* ******** airIsInf_f(), airIsInf_d() ** ** returns 1 if input is positive infinity, ** -1 if negative infinity, ** or 0 otherwise (including NaN) ** ** thus the non-zero-ness of the return is an easy way to do a ** boolean check of whether the value is infinite */ int airIsInf_f(float f) { int c, ret; c = airFPClass_f(f); if (airFP_POS_INF == c) { ret = 1; } else if (airFP_NEG_INF == c) { ret = -1; } else { ret = 0; } return ret; } int airIsInf_d(double d) { int c, ret; c = airFPClass_d(d); if (airFP_POS_INF == c) { ret = 1; } else if (airFP_NEG_INF == c) { ret = -1; } else { ret = 0; } return ret; } /* airExists_f() airExists_d() were nixed because they weren't used- you can just use AIR_EXISTS_F and AIR_EXISTS_D directly */ /* ******** airExists() ** ** an optimization-proof alternative to AIR_EXISTS */ int airExists(double val) { _airDoubleEndianLittle dlit; _airDoubleEndianBig dbig; dbig.v = dlit.v = val; return (airEndianLittle == airMyEndian() ? 0x7ff != dlit.c.expo : 0x7ff != dbig.c.expo); } /* ******** airNaN() ** ** returns a float quiet NaN */ float airNaN(void) { return airFPGen_f(airFP_QNAN); } /* ******** airFPFprintf_f() ** ** prints out the bits of a "float", indicating the three different fields */ void airFPFprintf_f(FILE *file, float val) { int i; unsigned int sign, expo, mant; _airFloatEndianLittle flit; _airFloatEndianBig fbig; if (file) { flit.v = fbig.v = val; FP_GET_F(sign, expo, mant, flit, fbig); fprintf(file, "%f: class %d; 0x%08x = ",val, airFPClass_f(val), airEndianLittle == airMyEndian() ? flit.i : fbig.i); fprintf(file, "sign:0x%x, expo:0x%02x, mant:0x%06x = \n", sign, expo, mant); fprintf(file, " S [ . . Exp . . ] " "[ . . . . . . . . . Mant. . . . . . . . . . ]\n"); fprintf(file, " %d ", sign); for (i=7; i>=0; i--) { fprintf(file, "%d ", (expo >> i) & 1); } for (i=22; i>=0; i--) { fprintf(file, "%d ", (mant >> i) & 1); } fprintf(file, "\n"); } } /* ******** airFPFprintf_d() ** ** prints out the bits of a "double", indicating the three different fields */ void airFPFprintf_d(FILE *file, double val) { int i; _airDoubleEndianLittle dlit; _airDoubleEndianBig dbig; unsigned int sign, expo, mant0, mant1; if (file) { dlit.v = dbig.v = val; fprintf(file, "%f: class %d; 0x%08x %08x = \n", val, airFPClass_d(val), (airEndianLittle == airMyEndian() ? dlit.h.half1 : dbig.h.half1), (airEndianLittle == airMyEndian() ? dlit.h.half0 : dbig.h.half0)); FP_GET_D(sign, expo, mant0, mant1, dlit, dbig); fprintf(file, "sign:0x%x, expo:0x%03x, mant:0x%05x %08x = \n", sign, expo, mant0, mant1); fprintf(file, "S[...Exp...][.......................Mant.......................]\n"); fprintf(file, "%d", sign); for (i=10; i>=0; i--) { fprintf(file, "%d", (expo >> i) & 1); } for (i=19; i>=0; i--) { fprintf(file, "%d", (mant0 >> i) & 1); } for (i=31; i>=0; i--) { fprintf(file, "%d", (mant1 >> i) & 1); } fprintf(file, "\n"); } } cmtk-3.3.1/Utilities/NrrdIO/CMakeLists.txt000066400000000000000000000071131276303427400203360ustar00rootroot00000000000000# # NrrdIO: stand-alone code for basic nrrd functionality # Copyright (C) 2011, 2010, 2009 University of Chicago # Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann # Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah # # This software is provided 'as-is', without any express or implied # warranty. In no event will the authors be held liable for any # damages arising from the use of this software. # # Permission is granted to anyone to use this software for any # purpose, including commercial applications, and to alter it and # redistribute it freely, subject to the following restrictions: # # 1. The origin of this software must not be misrepresented; you must # not claim that you wrote the original software. If you use this # software in a product, an acknowledgment in the product # documentation would be appreciated but is not required. # # 2. Altered source versions must be plainly marked as such, and must # not be misrepresented as being the original software. # # 3. This notice may not be removed or altered from any source distribution. # # # Modified 23-Oct-2012 by Torsten Rohlfing for CMTK, # (C) 2012 SRI International # CMAKE_MINIMUM_REQUIRED(VERSION 2.4) PROJECT(NrrdIO) INCLUDE_REGULAR_EXPRESSION("^.*.h$") # # This CMake file configures the NrrdIO library build. NrrdIO # is used by Insight/Code/IO/itkNrrdIO for reading/writing # "Nearly Raw Raster Data" within the open-source Teem software # package. See http://teem.sourceforge.net for more information. # SET(nrrdio_SRCS comment.c enumsNrrd.c mop.c string.c 754.c defaultsNrrd.c parseAir.c dio.c format.c parseNrrd.c formatEPS.c encoding.c formatNRRD.c encodingAscii.c formatPNG.c encodingBzip2.c formatPNM.c accessors.c encodingGzip.c formatText.c array.c encodingHex.c formatVTK.c read.c arraysNrrd.c encodingRaw.c gzio.c reorder.c write.c axis.c endianAir.c keyvalue.c biffbiff.c biffmsg.c endianNrrd.c methodsNrrd.c sane.c enum.c miscAir.c simple.c ) # Turn on TEEM_BUILD so that the proper dll export def's are # used on windows builds. ADD_DEFINITIONS(-DTEEM_BUILD=1) # Configure files with settings for use by the build. CONFIGURE_FILE(${NrrdIO_SOURCE_DIR}/NrrdConfigure.h.in ${NrrdIO_BINARY_DIR}/NrrdConfigure.h) # Need to be able to find NrrdConfig.h INCLUDE_DIRECTORIES(${NrrdIO_BINARY_DIR}) #The QNANHIBIT variable is configured by the root level CMakeLists.txt IF(QNANHIBIT) ADD_DEFINITIONS(-DTEEM_QNANHIBIT=1) ELSE(QNANHIBIT) ADD_DEFINITIONS(-DTEEM_QNANHIBIT=0) ENDIF(QNANHIBIT) #DirectIO is the fast way to do multi-gigabyte I/O and currently only available #for SGI platforms. Use of DirectIO is enabled manually for now. #OPTION(USE_DIRECTIO "Use DirectIO for Nrrd file IO. Only valid on SGI systems." 0) #MARK_AS_ADVANCED(USE_DIRECTIO) #IF(USE_DIRECTIO) # ADD_DEFINITIONS(-DTEEM_DIO=1) #ELSE(USE_DIRECTIO) ADD_DEFINITIONS(-DTEEM_DIO=0) #ENDIF(USE_DIRECTIO) # Possibly turn on usage of zlib compression (requires linking with libz) # (i.e., programs compiled with ITKNrrdIO must also be compiled with zlib) ADD_DEFINITIONS(-DTEEM_ZLIB=1) ## ## CMTK-speficic add-ons ## INCLUDE_DIRECTORIES(BEFORE ${ZCONF_INCLUDE_DIR} ${ZLIB_INCLUDE_DIR}) ADD_LIBRARY(NrrdIO ${nrrdio_SRCS} ) TARGET_LINK_LIBRARIES(NrrdIO ${ZLIB_LIBRARIES}) INSTALL(TARGETS NrrdIO RUNTIME DESTINATION ${CMTK_INSTALL_BIN_DIR} COMPONENT runtime LIBRARY DESTINATION ${CMTK_INSTALL_LIB_DIR} COMPONENT runtime ARCHIVE DESTINATION ${CMTK_INSTALL_LIB_DIR} COMPONENT libraries) FILE(GLOB files "${CMAKE_CURRENT_SOURCE_DIR}/*.h") INSTALL(FILES ${files} DESTINATION ${CMTK_INSTALL_INCLUDE_DIR} COMPONENT headers) cmtk-3.3.1/Utilities/NrrdIO/NrrdConfigure.h.in000066400000000000000000000023241276303427400211220ustar00rootroot00000000000000// Configure compile time dependent code // BUG: 0005904 shows that special action must be taken for Mac 64 bit systems. // See: http://public.kitware.com/Bug/view.php?id=5904 /* 32 or 64 bits. */ /* All compilers that support Mac OS X define __LP64__ if the architecture is 64 bits. */ #define CMAKE_SIZEOF_VOID_P @CMAKE_SIZEOF_VOID_P@ #if !defined(__APPLE__) #if CMAKE_SIZEOF_VOID_P == 8 #define TEEM_32BIT 0 #else #define TEEM_32BIT 1 #endif #elif defined(__LP64__) && __LP64__ #define TEEM_32BIT 0 #else #define TEEM_32BIT 1 #endif /* what byte order */ /* All compilers that support Mac OS X define either __BIG_ENDIAN__ or __LITTLE_ENDIAN__ to match the endianness of the architecture being compiled for. This is not necessarily the same as the architecture of the machine doing the building. In order to support Universal Binaries on Mac OS X, we prefer those defines to decide the endianness. On other platform, we use the result of the TRY_RUN. */ #if !defined(__APPLE__) #if @CMAKE_WORDS_BIGENDIAN@ #define TEEM_ENDIAN 4321 #else #define TEEM_ENDIAN 1234 #endif #else #if defined(__BIG_ENDIAN__) #define TEEM_ENDIAN 4321 #else #define TEEM_ENDIAN 1234 #endif #endif cmtk-3.3.1/Utilities/NrrdIO/NrrdIO.h000066400000000000000000002756661276303427400171300ustar00rootroot00000000000000/* NrrdIO: stand-alone code for basic nrrd functionality Copyright (C) 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. */ /* * Modified 23-Oct-2010 by Torsten Rohlfing for CMTK, * (C) 2012 SRI International */ #include "NrrdConfigure.h" #include #include #include #include #include #include #include /* ******** TEEM_VERSION ** ** TEEM_VERSION is a single (decimal) number that will always increase ** monotically, and the _MAJOR, _MINOR, _PATCH are also numbers that ** can be used to implement pre-processor logic about specifc ** versions. The TEEM_VERSION_STRING is used in the (existing) char ** *airTeemVersion (added in version 1.9.0). Yes, keeping these in ** sync is currently a manual operation. ** ** NOTE: Significant API changes (aside from API additions) should NOT ** occur with changes in patch level, only with major or minor version ** changes. ** ** NOTE: ../../CMakeLists.txt's Teem_VERSION variables must be in sync */ #define TEEM_VERSION_MAJOR 1 /* must be 1 digit */ #define TEEM_VERSION_MINOR 11 /* 1 or 2 digits */ #define TEEM_VERSION_PATCH 00 /* 1 or 2 digits */ #define TEEM_VERSION 11100 /* must be 5 digits, to facilitate easy numerical comparison */ #define TEEM_VERSION_STRING "1.11.0" /* cannot be so easily compared */ /* THE FOLLOWING INCLUDE IS ONLY FOR THE CMTK DISTRIBUTION. This header mangles the symbols in the NrrdIO library, preventing conflicts in applications linked against two versions of NrrdIO. */ #include "cmtk_NrrdIO_mangle.h" #ifdef __cplusplus extern "C" { #endif #define TEEM_BUILD 1 #if defined(_WIN32) && !defined(__CYGWIN__) && !defined(TEEM_STATIC) # if defined(TEEM_BUILD) || defined(air_EXPORTS) || defined(teem_EXPORTS) # define NRRDIO_EXPORT extern __declspec(dllexport) # else # define NRRDIO_EXPORT extern __declspec(dllimport) # endif #else /* TEEM_STATIC || UNIX */ # define NRRDIO_EXPORT extern #endif #if defined(_WIN32) && !defined(__CYGWIN__) && !defined(__MINGW32__) typedef signed __int64 airLLong; typedef unsigned __int64 airULLong; # define AIR_LLONG_FMT "%I64d" # define AIR_ULLONG_FMT "%I64u" # define AIR_LLONG(x) x##i64 # define AIR_ULLONG(x) x##ui64 #else typedef signed long long airLLong; typedef unsigned long long airULLong; # define AIR_LLONG_FMT "%lld" # define AIR_ULLONG_FMT "%llu" # define AIR_LLONG(x) x##ll # define AIR_ULLONG(x) x##ull #endif /* ** These serve as conservative estimates on how large various strings ** might end up being. It would be theoretically better to completely ** avoid the use of fixed-size buffers, but in many contexts the ** implementation complexity of handling them reliably is distracts ** from more urgent implementation goals. In the mean time, these can ** be used safely as long as the lengths are used consistently. ** ** The possibly unfortunate convention that has become established in ** Teem is code using these tends to NOT add the "+1", to explicitly ** indicate the space for 0-termination, and instead assumes it is ** part of the numbers below, even though this is at the cost of ** confusion about how the maximal strlen() will be less than each of ** these numbers. This will be addressed in Teem 2.0. */ #define AIR_STRLEN_SMALL (128+1) /* has to be big enough to hold: - printed value of size_t and ptrdiff_t, - line of text that should contain file format "magic" */ #define AIR_STRLEN_MED (256+1) #define AIR_STRLEN_LARGE (512+1) #define AIR_STRLEN_HUGE (1024+1) /* has to be big enough to hold a biff error message (one line of it) */ /* ******** airPtrPtrUnion ** ** union of addresses of pointers to various types, to deal with strict ** aliasing warnings, especially with the first argument to airArrayNew(). ** Unfortunately this can't meet the needs of all such cases because some ** libraries need to manage addresses of arrays of other kinds of ** library-specific objects (about which air is ignorant). */ typedef union { unsigned char **uc; signed char **sc; char **c; char ***cp; unsigned short **us; short **s; unsigned int **ui; int **i; float **f; double **d; void **v; } airPtrPtrUnion; /* ******** airEnum struct ** ** The airEnum provides the basic mechanism of mapping from a ** string to an int enum value, and back. */ typedef struct { const char *name; /* what are these things? */ unsigned int M; /* str[0]: string for the unknown/invalid value; * str[1] .. str[M]: canonical strings for the enum values; * "val" NULL: unknown/invalid = 0; * valid values are 1 .. M * "val" non-NULL: unknown/invalid = val[0]; * valid are val[1].. val[M] */ const char **str; /* see above */ const int *val; /* see above */ const char **desc; /* desc[i] is a short description of the enum values represented by str[i] (thereby starting with the unknown value), to be used to by things like hest */ const char **strEqv; /* If non-NULL, all the variations in strings recognized in mapping from string to value (the values in valEqv). This **MUST** be terminated by a zero-length string ("") so as to signify the end of the list. This should *not* contain the string for unknown/invalid. If "strEqv" is NULL, then mapping from string to value is done only by traversing "str", and "valEqv" is ignored. */ const int *valEqv; /* If strEqv non-NULL, valEqv holds the values corresponding to the strings in strEqv, with one integer for each non-zero-length string in strEqv: strEqv[i] is a valid string representation for value valEqv[i]. This should *not* contain the value for unknown/invalid. This "valEqv" is ignored if "strEqv" is NULL. */ int sense; /* require case matching on strings */ } airEnum; NRRDIO_EXPORT int airEnumUnknown(const airEnum *enm); NRRDIO_EXPORT int airEnumValCheck(const airEnum *enm, int val); NRRDIO_EXPORT const char *airEnumStr(const airEnum *enm, int val); NRRDIO_EXPORT const char *airEnumDesc(const airEnum *enm, int val); NRRDIO_EXPORT int airEnumVal(const airEnum *enm, const char *str); NRRDIO_EXPORT char *airEnumFmtDesc(const airEnum *enm, int val, int canon, const char *fmt); NRRDIO_EXPORT void airEnumPrint(FILE *file, const airEnum *enm); /* ******** airEndian enum ** ** for identifying how a file was written to disk, for those encodings ** where the raw data on disk is dependent on the endianness of the ** architecture. */ enum { airEndianUnknown, /* 0: nobody knows */ airEndianLittle = 1234, /* 1234: Intel and friends */ airEndianBig = 4321, /* 4321: the rest */ airEndianLast }; /* endianAir.c */ NRRDIO_EXPORT const airEnum *const airEndian; NRRDIO_EXPORT int airMyEndian(void); /* array.c: poor-man's dynamically resizable arrays */ typedef struct { void *data, /* where the data is */ **dataP; /* (possibly NULL) address of user's data variable, kept in sync with internal "data" variable */ unsigned int len, /* length of array: # units for which there is considered to be data (which is <= total # units allocated). The # bytes which contain data is len*unit. Always updated (unlike "*lenP") */ *lenP, /* (possibly NULL) address of user's length variable, kept in sync with internal "len" variable */ incr, /* the granularity of the changes in amount of space allocated: when the length reaches a multiple of "incr", then the array is resized */ size; /* array is allocated to have "size" increments, or, size*incr elements, or, size*incr*unit bytes */ size_t unit; /* the size in bytes of one element in the array */ int noReallocWhenSmaller; /* as it says */ /* the following are all callbacks useful for maintaining either an array of pointers (allocCB and freeCB) or array of structs (initCB and doneCB). allocCB or initCB is called when the array length increases, and freeCB or doneCB when it decreases. Any of them can be NULL if no such activity is desired. allocCB sets values in the array (as in storing the return from malloc(); freeCB is called on values in the array (as in calling free()), and the values are cast to void*. allocCB and freeCB don't care about the value of "unit" (though perhaps they should). initCB and doneCB are called on the _addresses_ of elements in the array. allocCB and initCB are called for the elements in ascending order in the array, and freeCB and doneCB are called in descending order. allocCB and initCB are mutually exclusive- they can't both be non-NULL. Same goes for freeCB and doneCB */ void *(*allocCB)(void); /* values of new elements set to return of this */ void *(*freeCB)(void *); /* called on the values of invalidated elements */ void (*initCB)(void *); /* called on addresses of new elements */ void (*doneCB)(void *); /* called on addresses of invalidated elements */ } airArray; NRRDIO_EXPORT airArray *airArrayNew(void **dataP, unsigned int *lenP, size_t unit, unsigned int incr); NRRDIO_EXPORT void airArrayStructCB(airArray *a, void (*initCB)(void *), void (*doneCB)(void *)); NRRDIO_EXPORT void airArrayPointerCB(airArray *a, void *(*allocCB)(void), void *(*freeCB)(void *)); NRRDIO_EXPORT void airArrayLenSet(airArray *a, unsigned int newlen); NRRDIO_EXPORT unsigned int airArrayLenIncr(airArray *a, int delta); NRRDIO_EXPORT airArray *airArrayNix(airArray *a); NRRDIO_EXPORT airArray *airArrayNuke(airArray *a); /* ******** airFP enum ** ** the different kinds of floating point number afforded by IEEE 754, ** and the values returned by airFPClass_f(). ** ** The values probably won't agree with those in #include's like ** ieee.h, ieeefp.h, fp_class.h. This is because IEEE 754 hasn't ** defined standard values for these, so everyone does it differently. ** ** This enum uses underscores (against Teem convention) to help ** legibility while also conforming to the spirit of the somewhat ** standard naming conventions */ enum { airFP_Unknown, /* 0: nobody knows */ airFP_SNAN, /* 1: signalling NaN */ airFP_QNAN, /* 2: quiet NaN */ airFP_POS_INF, /* 3: positive infinity */ airFP_NEG_INF, /* 4: negative infinity */ airFP_POS_NORM, /* 5: positive normalized non-zero */ airFP_NEG_NORM, /* 6: negative normalized non-zero */ airFP_POS_DENORM, /* 7: positive denormalized non-zero */ airFP_NEG_DENORM, /* 8: negative denormalized non-zero */ airFP_POS_ZERO, /* 9: +0.0, positive zero */ airFP_NEG_ZERO, /* 10: -0.0, negative zero */ airFP_Last /* after the last valid one */ }; /* 754.c: IEEE-754 related stuff values */ typedef union { unsigned int i; float f; } airFloat; typedef union { airULLong i; double d; } airDouble; NRRDIO_EXPORT const int airMyQNaNHiBit; NRRDIO_EXPORT float airFPPartsToVal_f(unsigned int sign, unsigned int expo, unsigned int mant); NRRDIO_EXPORT void airFPValToParts_f(unsigned int *signP, unsigned int *expoP, unsigned int *mantP, float v); NRRDIO_EXPORT double airFPPartsToVal_d(unsigned int sign, unsigned int expo, unsigned int mant0, unsigned int mant1); NRRDIO_EXPORT void airFPValToParts_d(unsigned int *signP, unsigned int *expoP, unsigned int *mant0P, unsigned int *mant1P, double v); NRRDIO_EXPORT float airFPGen_f(int cls); NRRDIO_EXPORT double airFPGen_d(int cls); NRRDIO_EXPORT int airFPClass_f(float val); NRRDIO_EXPORT int airFPClass_d(double val); NRRDIO_EXPORT void airFPFprintf_f(FILE *file, float val); NRRDIO_EXPORT void airFPFprintf_d(FILE *file, double val); NRRDIO_EXPORT const airFloat airFloatQNaN; NRRDIO_EXPORT const airFloat airFloatSNaN; NRRDIO_EXPORT const airFloat airFloatPosInf; NRRDIO_EXPORT const airFloat airFloatNegInf; NRRDIO_EXPORT float airNaN(void); NRRDIO_EXPORT int airIsNaN(double d); NRRDIO_EXPORT int airIsInf_f(float f); NRRDIO_EXPORT int airIsInf_d(double d); NRRDIO_EXPORT int airExists(double d); /* ******** airType ** ** Different types which air cares about. ** Currently only used in the command-line parsing, but perhaps will ** be used elsewhere in air later */ enum { airTypeUnknown, /* 0 */ airTypeBool, /* 1 */ airTypeInt, /* 2 */ airTypeUInt, /* 3 */ airTypeLongInt, /* 4 */ airTypeULongInt, /* 5 */ airTypeSize_t, /* 6 */ airTypeFloat, /* 7 */ airTypeDouble, /* 8 */ airTypeChar, /* 9 */ airTypeString, /* 10 */ airTypeEnum, /* 11 */ airTypeOther, /* 12 */ airTypeLast }; #define AIR_TYPE_MAX 12 /* parseAir.c */ NRRDIO_EXPORT double airAtod(const char *str); NRRDIO_EXPORT int airSingleSscanf(const char *str, const char *fmt, void *ptr); NRRDIO_EXPORT const airEnum *const airBool; NRRDIO_EXPORT unsigned int airParseStrB(int *out, const char *s, const char *ct, unsigned int n, ... /* (nothing used) */); NRRDIO_EXPORT unsigned int airParseStrI(int *out, const char *s, const char *ct, unsigned int n, ... /* (nothing used) */); NRRDIO_EXPORT unsigned int airParseStrUI(unsigned int *out, const char *s, const char *ct, unsigned int n, ... /* (nothing used) */); NRRDIO_EXPORT unsigned int airParseStrZ(size_t *out, const char *s, const char *ct, unsigned int n, ... /* (nothing used) */); NRRDIO_EXPORT unsigned int airParseStrF(float *out, const char *s, const char *ct, unsigned int n, ... /* (nothing used) */); NRRDIO_EXPORT unsigned int airParseStrD(double *out, const char *s, const char *ct, unsigned int n, ... /* (nothing used) */); NRRDIO_EXPORT unsigned int airParseStrC(char *out, const char *s, const char *ct, unsigned int n, ... /* (nothing used) */); NRRDIO_EXPORT unsigned int airParseStrS(char **out, const char *s, const char *ct, unsigned int n, ... /* REQ'D even if n>1: int greedy */); NRRDIO_EXPORT unsigned int airParseStrE(int *out, const char *s, const char *ct, unsigned int n, ... /* REQUIRED: airEnum *e */); NRRDIO_EXPORT unsigned int (*airParseStr[AIR_TYPE_MAX+1])(void *, const char *, const char *, unsigned int, ...); /* string.c */ NRRDIO_EXPORT char *airStrdup(const char *s); NRRDIO_EXPORT size_t airStrlen(const char *s); NRRDIO_EXPORT int airStrtokQuoting; NRRDIO_EXPORT char *airStrtok(char *s, const char *ct, char **last); NRRDIO_EXPORT unsigned int airStrntok(const char *s, const char *ct); NRRDIO_EXPORT char *airStrtrans(char *s, char from, char to); NRRDIO_EXPORT char *airStrcpy(char *dst, size_t dstSize, const char *src); NRRDIO_EXPORT int airEndsWith(const char *s, const char *suff); NRRDIO_EXPORT char *airUnescape(char *s); NRRDIO_EXPORT char *airOneLinify(char *s); NRRDIO_EXPORT char *airToLower(char *str); NRRDIO_EXPORT char *airToUpper(char *str); NRRDIO_EXPORT unsigned int airOneLine(FILE *file, char *line, unsigned int size); /* sane.c */ /* ******** airInsane enum ** ** reasons for why airSanity() failed (specifically, the possible ** return values for airSanity() */ enum { airInsane_not, /* 0: actually, all sanity checks passed */ airInsane_endian, /* 1: airMyEndian is wrong */ airInsane_pInfExists, /* 2: AIR_EXISTS(positive infinity) was true */ airInsane_nInfExists, /* 3: AIR_EXISTS(negative infinity) was true */ airInsane_NaNExists, /* 4: AIR_EXISTS(NaN) was true */ airInsane_FltDblFPClass, /* 5: double -> float assignment messed up the airFPClass_f() of the value */ airInsane_QNaNHiBit, /* 6: airMyQNaNHiBit is wrong */ airInsane_AIR_NAN, /* 7: airFPClass_f(AIR_QNAN) wrong (no longer checking on problematic SNAN) */ airInsane_dio, /* 8: airMyDio set to something invalid */ airInsane_UCSize, /* 9: unsigned char isn't 8 bits */ airInsane_FISize, /* 10: sizeof(float), sizeof(int) not 4 */ airInsane_DLSize /* 11: sizeof(double), sizeof(airLLong) not 8 */ }; #define AIR_INSANE_MAX 11 NRRDIO_EXPORT const char *airInsaneErr(int insane); NRRDIO_EXPORT int airSanity(void); /* miscAir.c */ NRRDIO_EXPORT const char *airTeemVersion; NRRDIO_EXPORT const char *airTeemReleaseDate; NRRDIO_EXPORT void *airNull(void); NRRDIO_EXPORT void *airSetNull(void **ptrP); NRRDIO_EXPORT void *airFree(void *ptr); NRRDIO_EXPORT FILE *airFopen(const char *name, FILE *std, const char *mode); NRRDIO_EXPORT FILE *airFclose(FILE *file); NRRDIO_EXPORT int airSinglePrintf(FILE *file, char *str, const char *fmt, ...); NRRDIO_EXPORT char *airSprintSize_t(char str[AIR_STRLEN_SMALL], size_t val); /* dio.c */ /* ******** airNoDio enum ** ** reasons for why direct I/O won't be used with a particular ** file/pointer combination */ enum { airNoDio_okay, /* 0: actually, you CAN do direct I/O */ airNoDio_arch, /* 1: Teem thinks this architecture can't do it */ airNoDio_format, /* 2: Teem thinks given data file format can't use it */ airNoDio_std, /* 3: DIO isn't possible for std{in|out|err} */ airNoDio_fd, /* 4: couldn't get underlying file descriptor */ airNoDio_dioinfo, /* 5: calling fcntl() to get direct I/O info failed */ airNoDio_small, /* 6: requested size is too small */ airNoDio_size, /* 7: requested size not a multiple of d_miniosz */ airNoDio_ptr, /* 8: pointer not multiple of d_mem */ airNoDio_fpos, /* 9: current file position not multiple of d_miniosz */ airNoDio_setfl, /* 10: fcntl(fd, SETFL, FDIRECT) failed */ airNoDio_test, /* 11: couldn't memalign() even a small bit of memory */ airNoDio_disable /* 12: someone disabled it with airDisableDio */ }; #define AIR_NODIO_MAX 12 NRRDIO_EXPORT const char *airNoDioErr(int noDio); NRRDIO_EXPORT const int airMyDio; NRRDIO_EXPORT int airDisableDio; NRRDIO_EXPORT void airDioInfo(int *align, int *min, int *max, int fd); NRRDIO_EXPORT int airDioTest(int fd, const void *ptr, size_t size); NRRDIO_EXPORT void *airDioMalloc(size_t size, int fd); NRRDIO_EXPORT size_t airDioRead(int fd, void *ptr, size_t size); NRRDIO_EXPORT size_t airDioWrite(int fd, const void *ptr, size_t size); /* mop.c: clean-up utilities */ enum { airMopNever, airMopOnError, airMopOnOkay, airMopAlways }; typedef void *(*airMopper)(void *); typedef struct { void *ptr; /* the thing to be processed */ airMopper mop; /* the function to which does the processing */ int when; /* from the airMopWhen enum */ } airMop; NRRDIO_EXPORT airArray *airMopNew(void); NRRDIO_EXPORT int airMopAdd(airArray *arr, void *ptr, airMopper mop, int when); NRRDIO_EXPORT void airMopSub(airArray *arr, void *ptr, airMopper mop); NRRDIO_EXPORT void airMopMem(airArray *arr, void *_ptrP, int when); NRRDIO_EXPORT void airMopUnMem(airArray *arr, void *_ptrP); NRRDIO_EXPORT void airMopPrint(airArray *arr, const void *_str, int when); NRRDIO_EXPORT void airMopDone(airArray *arr, int error); NRRDIO_EXPORT void airMopError(airArray *arr); NRRDIO_EXPORT void airMopOkay(airArray *arr); NRRDIO_EXPORT void airMopDebug(airArray *arr); /******* the interminable sea of defines and macros *******/ #define AIR_TRUE 1 #define AIR_FALSE 0 #define AIR_WHITESPACE " \t\n\r\v\f" /* K+R pg. 157 */ /* ******** AIR_UNUSED ** ** one way of reconciling "warning: unused parameter" with ** C's "error: parameter name omitted" */ #define AIR_UNUSED(x) (void)(x) /* ******** AIR_CAST, AIR_UINT, AIR_INT ** ** just casts, but with the added ability to grep for them more easily, ** since casts should probably always be revisited and reconsidered. */ #define AIR_CAST(t, v) ((t)(v)) #define AIR_UINT(x) AIR_CAST(unsigned int, x) #define AIR_INT(x) AIR_CAST(int, x) /* ******** AIR_VOIDP, AIR_CVOIDP ** ** explicit casting to "void *" (and "const void *") from non-void* pointers ** is strictly speaking needed for the %p format specifier in printf-like ** functions; this is a slightly more convenient form */ #define AIR_VOIDP(x) AIR_CAST(void *, x) #define AIR_CVOIDP(x) AIR_CAST(const void *, x) /* ******** AIR_MALLOC, AIR_CALLOC ** ** slightly simpler wrapper around cast and malloc/calloc ** ** HEY note that "T" is not guarded by parentheses in its first usage, ** as arguments in Teem macros normally are */ #define AIR_MALLOC(N, T) (T*)(malloc((N)*sizeof(T))) #define AIR_CALLOC(N, T) (T*)(calloc((N), sizeof(T))) /* ******** AIR_ENDIAN, AIR_QNANHIBIT, AIR_DIO ** ** These reflect particulars of hardware which we're running on. The ** difference from the things starting with TEEM_ is that the TEEM_ ** values are for passing architecture-specific to compilation of source ** files, and thes AIR_ variables are for advertising that information ** to anyone linking against air (or Teem) and including air.h. */ #define AIR_ENDIAN (airMyEndian()) #define AIR_QNANHIBIT (airMyQNaNHiBit) #define AIR_DIO (airMyDio) /* ******** AIR_NAN, AIR_QNAN, AIR_SNAN, AIR_POS_INF, AIR_NEG_INF ** ** its nice to have these values available without the cost of a ** function call. ** ** NOTE: AIR_POS_INF and AIR_NEG_INF correspond to the _unique_ ** bit-patterns which signify positive and negative infinity. With ** the NaNs, however, they are only one of many possible ** representations. */ #define AIR_NAN (airFloatQNaN.f) #define AIR_QNAN (airFloatQNaN.f) #define AIR_SNAN (airFloatSNaN.f) #define AIR_POS_INF (airFloatPosInf.f) #define AIR_NEG_INF (airFloatNegInf.f) /* ******** AIR_EXISTS ** ** is non-zero (true) only for values which are not NaN or +/-infinity ** ** You'd think that (x == x) might work, but no no no, some optimizing ** compilers (e.g. SGI's cc) say "well of course they're equal, for all ** possible values". Bastards! ** ** One of the benefits of IEEE 754 floating point numbers is that ** gradual underflow means that x = y <==> x - y = 0 for any (positive ** or negative) normalized or denormalized float. Otherwise this ** macro could not be valid; some floating point conventions say that ** a zero-valued exponent means zero, regardless of the mantissa. ** ** However, there MAY be problems on machines which use extended ** (80-bit) floating point registers, such as Intel chips- where the ** same initial value 1) directly read from the register, versus 2) ** saved to memory and loaded back, may end up being different. I ** have yet to produce this behavior, or convince myself it can't happen. ** ** The reason to #define AIR_EXISTS as airExists is that on some ** optimizing compilers, the !((x) - (x)) doesn't work. This has been ** the case on Windows and 64-bit irix6 (64 bit) with -Ofast. If ** airSanity fails because a special value "exists", then use the ** first version of AIR_EXISTS. ** ** There is a performance consequence of using airExists(x), in that it ** is a function call, although (HEY) we should facilitate inline'ing it ** for compilers that know how to. ** ** gcc 4.5.3 -std=c89, at least on cygwin, has problems with ** the type of "!((x) - (x))" when used with bit-wise xor ^, saying ** "invalid operands to binary ^ (have ‘int’ and ‘int’)" but these ** problems oddly went away with the explicit cast to int. */ #if 1 #define AIR_EXISTS(x) (airExists(x)) #else #define AIR_EXISTS(x) (AIR_CAST(int, !((x) - (x)))) #endif /* ******** AIR_MAX(a,b), AIR_MIN(a,b), AIR_ABS(a) ** ** the usual */ #define AIR_MAX(a,b) ((a) > (b) ? (a) : (b)) #define AIR_MIN(a,b) ((a) < (b) ? (a) : (b)) #define AIR_ABS(a) ((a) > 0.0f ? (a) : -(a)) /* ******** AIR_COMPARE(a,b) ** ** the sort of compare that qsort() wants for ascending sort */ #define AIR_COMPARE(a,b) ((a) < (b) \ ? -1 \ : ((a) > (b) \ ? 1 \ : 0)) /* ******** AIR_IN_OP(a,b,c), AIR_IN_CL(a,b,c) ** ** is true if the middle argument is in the open/closed interval ** defined by the first and third arguments ** ** AIR_IN_OP is new name for old AIR_BETWEEN ** AIR_IN_CL is new name for old AIR_INSIDE */ #define AIR_IN_OP(a,b,c) ((a) < (b) && (b) < (c)) /* closed interval */ #define AIR_IN_CL(a,b,c) ((a) <= (b) && (b) <= (c)) /* open interval */ /* ******** AIR_CLAMP(a,b,c) ** ** returns the middle argument, after being clamped to the closed ** interval defined by the first and third arguments */ #define AIR_CLAMP(a,b,c) ((b) < (a) \ ? (a) \ : ((b) > (c) \ ? (c) \ : (b))) /* ******** AIR_MOD(i, N) ** ** returns that integer in [0, N-1] which is i plus a multiple of N. It ** may be unfortunate that the expression (i)%(N) appears three times; ** this should be inlined. Or perhaps the compiler's optimizations ** (common sub-expression elimination) will save us. ** ** Note: integer divisions are not very fast on some modern chips; ** don't go silly using this one. */ #define AIR_MOD(i, N) ((i)%(N) >= 0 ? (i)%(N) : N + (i)%(N)) /* ******** AIR_LERP(w, a, b) ** ** returns a when w=0, and b when w=1, and linearly varies in between */ #define AIR_LERP(w, a, b) ((w)*((b) - (a)) + (a)) /* ******** AIR_AFFINE(i,x,I,o,O) ** ** given intervals [i,I], [o,O] and a value x which may or may not be ** inside [i,I], return the value y such that y stands in the same ** relationship to [o,O] that x does with [i,I]. Or: ** ** y - o x - i ** ------- = ------- ** O - o I - i ** ** It is the callers responsibility to make sure I-i and O-o are ** both non-zero. Strictly speaking, real problems arise only when ** when I-i is zero: division by zero generates either NaN or infinity ** ** NOTE that "x" is evaluated only once (which makes this more useful), ** as is "I" and "O" (usually not so important); "i" and "o" are each ** evaluated twice */ #define AIR_AFFINE(i,x,I,o,O) ( \ ((double)(O)-(o))*((double)(x)-(i)) / ((double)(I)-(i)) + (o)) /* ******** AIR_DELTA(i,x,I,o,O) ** ** given intervals [i,I] and [o,O], calculates the number y such that ** a change of x within [i,I] is proportional to a change of y within ** [o,O]. Or: ** ** y x ** ------- = ------- ** O - o I - i ** ** It is the callers responsibility to make sure I-i and O-o are ** both non-zero ** ** NOTE that all arguments are evaluated only once */ #define AIR_DELTA(i,x,I,o,O) ( \ ((double)(O)-(o))*((double)(x)) / ((double)(I)-(i)) ) /* ******** AIR_ROUNDUP, AIR_ROUNDDOWN ** ** rounds integers up or down; just wrappers around floor and ceil */ #define AIR_ROUNDUP(x) ((int)(floor((x)+0.5))) #define AIR_ROUNDDOWN(x) ((int)(ceil((x)-0.5))) #define AIR_ROUNDUP_UI(x) ((unsigned int)(floor((x)+0.5))) #define AIR_ROUNDDOWN_UI(x) ((unsigned int)(ceil((x)-0.5))) #ifdef __cplusplus } #endif #ifdef __cplusplus extern "C" { #endif /* ** biffMsg struct ** ** externally usable thing for holding error messages */ typedef struct { char *key; /* string for identifying the general source of the error message; set once, at time of biffMsg creation */ char **err; /* array of error strings; the err array itself is NOT null-terminated */ unsigned int errNum; /* length of "err" == # strings stored */ airArray *errArr; /* air array for err and num */ } biffMsg; /* biffmsg.c */ NRRDIO_EXPORT biffMsg *biffMsgNew(const char *key); NRRDIO_EXPORT biffMsg *biffMsgNix(biffMsg *msg); NRRDIO_EXPORT void biffMsgAdd(biffMsg *msg, const char *err); NRRDIO_EXPORT void biffMsgClear(biffMsg *msg); NRRDIO_EXPORT unsigned int biffMsgLineLenMax(const biffMsg *msg); NRRDIO_EXPORT void biffMsgMove(biffMsg *dest, biffMsg *src, const char *err); NRRDIO_EXPORT unsigned int biffMsgErrNum(const biffMsg *msg); NRRDIO_EXPORT unsigned int biffMsgStrlen(const biffMsg *msg); NRRDIO_EXPORT void biffMsgStrSet(char *ret, const biffMsg *msg); NRRDIO_EXPORT biffMsg *biffMsgNoop; /* biffbiff.c */ NRRDIO_EXPORT void biffAdd(const char *key, const char *err); NRRDIO_EXPORT void biffAddf(const char *key, const char *errfmt, ...) #ifdef __GNUC__ __attribute__ ((format(printf,2,3))) #endif ; NRRDIO_EXPORT void biffMaybeAdd(const char *key, const char *err, int useBiff); NRRDIO_EXPORT void biffMaybeAddf(int useBiff, const char *key, const char *errfmt, ... ) #ifdef __GNUC__ __attribute__ ((format(printf,3,4))) #endif ; NRRDIO_EXPORT char *biffGet(const char *key); NRRDIO_EXPORT unsigned int biffGetStrlen(const char *key); NRRDIO_EXPORT void biffSetStr(char *str, const char *key); NRRDIO_EXPORT void biffDone(const char *key); NRRDIO_EXPORT char *biffGetDone(const char *key); #ifdef __cplusplus } #endif #include #ifdef __cplusplus extern "C" { #endif /* feel free to set these to higher values and recompile */ #define NRRD_DIM_MAX 16 /* Max array dimension (nrrd->dim) */ #define NRRD_SPACE_DIM_MAX 8 /* Max dimension of "space" around array (nrrd->spaceDim) */ #define NRRD_EXT_NRRD ".nrrd" #define NRRD_EXT_NHDR ".nhdr" #define NRRD_EXT_PGM ".pgm" #define NRRD_EXT_PPM ".ppm" #define NRRD_EXT_PNG ".png" #define NRRD_EXT_VTK ".vtk" #define NRRD_EXT_TEXT ".txt" #define NRRD_EXT_EPS ".eps" /* HEY: should this be renamed -> MAXNUM ? Would be more consistent with other Teem pound-define names */ #define NRRD_KERNEL_PARMS_NUM 8 /* max # arguments to a kernel- this is weird: it isn't the max of any of the NrrdKernels defined by the nrrd library (that is more like 3), but is the max number of parms of any NrrdKernel used by anyone using Teem, such as in gage. Enforcing one global max simplifies implementation. */ /* ** For the 64-bit integer types (not standard except in C99), we used ** to try to use the names for the _MIN and _MAX values which are used ** in C99 (as well as gcc) such as LLONG_MAX, or those used on SGI ** such as LONGLONG_MAX. However, since the tests (in nrrdSanity) ** were re-written to detect overflow based on manipulation of ** specific values, we might as well also define the _MIN and _MAX in ** terms of explicit values (which agree with those defined by C99). */ #define NRRD_LLONG_MAX AIR_LLONG(9223372036854775807) #define NRRD_LLONG_MIN (-NRRD_LLONG_MAX-AIR_LLONG(1)) #define NRRD_ULLONG_MAX AIR_ULLONG(18446744073709551615) /* ** Chances are, you shouldn't mess with these */ #define NRRD_COMMENT_CHAR '#' #define NRRD_FILENAME_INCR 32 #define NRRD_COMMENT_INCR 16 #define NRRD_KEYVALUE_INCR 32 #define NRRD_LIST_FLAG "LIST" #define NRRD_PNM_COMMENT "# NRRD>" /* this is designed to be robust against the mungling that xv does, but no promises for any other image programs */ #define NRRD_PNG_FIELD_KEY "NRRD" /* this is the key used for getting nrrd fields into/out of png comments */ #define NRRD_PNG_COMMENT_KEY "NRRD#" /* this is the key used for getting nrrd comments into/out of png comments */ #define NRRD_UNKNOWN "???" /* how to represent something unknown in a field of the nrrd header, when it being unknown is not an error */ #define NRRD_NONE "none" /* like NRRD_UNKNOWN, but with an air of certainty */ #ifdef __cplusplus } #endif #ifdef __cplusplus extern "C" { #endif /******* ******** NONE of these enums should have values set explicitly in their ******** definition. The values should simply start at 0 (for Unknown) ******** and increase one integer per value. The _nrrdCheckEnums() ******** sanity check assumes this, and there is no reason to use ******** explicit values for any of the enums. *******/ /* ******** nrrdIoState* enum ** ** the various things it makes sense to get and set in nrrdIoState struct ** via nrrdIoStateGet and nrrdIoStateSet */ enum { nrrdIoStateUnknown, nrrdIoStateDetachedHeader, nrrdIoStateBareText, nrrdIoStateCharsPerLine, nrrdIoStateValsPerLine, nrrdIoStateSkipData, nrrdIoStateKeepNrrdDataFileOpen, nrrdIoStateZlibLevel, nrrdIoStateZlibStrategy, nrrdIoStateBzip2BlockSize, nrrdIoStateLast }; /* ******** nrrdFormatType* enum ** ** the different file formats which nrrd supports */ enum { nrrdFormatTypeUnknown, nrrdFormatTypeNRRD, /* 1: basic nrrd format (associated with any of the magics starting with "NRRD") */ nrrdFormatTypePNM, /* 2: PNM image */ nrrdFormatTypePNG, /* 3: PNG image */ nrrdFormatTypeVTK, /* 4: VTK Structured Points datasets (v1.0 and 2.0) */ nrrdFormatTypeText, /* 5: bare ASCII text for 2D arrays */ nrrdFormatTypeEPS, /* 6: Encapsulated PostScript (write-only) */ nrrdFormatTypeLast }; #define NRRD_FORMAT_TYPE_MAX 6 /* ******** nrrdBoundary* enum ** ** when resampling, how to deal with the ends of a scanline */ enum { nrrdBoundaryUnknown, nrrdBoundaryPad, /* 1: fill with some user-specified value */ nrrdBoundaryBleed, /* 2: copy the last/first value out as needed */ nrrdBoundaryWrap, /* 3: wrap-around */ nrrdBoundaryWeight, /* 4: normalize the weighting on the existing samples; ONLY sensible for a strictly positive kernel which integrates to unity (as in blurring) */ nrrdBoundaryMirror, /* 5: mirror folding */ nrrdBoundaryLast }; #define NRRD_BOUNDARY_MAX 5 /* ******** nrrdType* enum ** ** all the different types, identified by integer ** ** 18 July 03: After some consternation, GLK decided to set ** nrrdTypeUnknown and nrrdTypeDefault to the same thing, with the ** reasoning that the only times that nrrdTypeDefault is used is when ** controlling an *output* type (the type of "nout"), or rather, ** choosing not to control an output type. As output types must be ** known, there is no confusion between being unset/unknown (invalid) ** and being simply default. */ enum { nrrdTypeUnknown=0, /* 0: signifies "type is unset/unknown" */ nrrdTypeDefault=0, /* 0: signifies "determine output type for me" */ nrrdTypeChar, /* 1: signed 1-byte integer */ nrrdTypeUChar, /* 2: unsigned 1-byte integer */ nrrdTypeShort, /* 3: signed 2-byte integer */ nrrdTypeUShort, /* 4: unsigned 2-byte integer */ nrrdTypeInt, /* 5: signed 4-byte integer */ nrrdTypeUInt, /* 6: unsigned 4-byte integer */ nrrdTypeLLong, /* 7: signed 8-byte integer */ nrrdTypeULLong, /* 8: unsigned 8-byte integer */ nrrdTypeFloat, /* 9: 4-byte floating point */ nrrdTypeDouble, /* 10: 8-byte floating point */ nrrdTypeBlock, /* 11: size user defined at run time; MUST BE LAST */ nrrdTypeLast }; #define NRRD_TYPE_MAX 11 #define NRRD_TYPE_SIZE_MAX 8 /* max(sizeof()) over all scalar types */ #define NRRD_TYPE_BIGGEST double /* this should be a basic C type which requires for storage the maximum size of all the basic C types */ /* ******** nrrdEncodingType enum ** ** how data might be encoded into a bytestream */ enum { nrrdEncodingTypeUnknown, nrrdEncodingTypeRaw, /* 1: same as memory layout (modulo endianness) */ nrrdEncodingTypeAscii, /* 2: decimal values are spelled out in ascii */ nrrdEncodingTypeHex, /* 3: hexidecimal (two chars per byte) */ nrrdEncodingTypeGzip, /* 4: gzip'ed raw data */ nrrdEncodingTypeBzip2, /* 5: bzip2'ed raw data */ nrrdEncodingTypeLast }; #define NRRD_ENCODING_TYPE_MAX 5 /* ******** nrrdZlibStrategy enum ** ** how gzipped data is compressed */ enum { nrrdZlibStrategyUnknown, nrrdZlibStrategyDefault, /* 1: default (Huffman + string match) */ nrrdZlibStrategyHuffman, /* 2: Huffman only */ nrrdZlibStrategyFiltered, /* 3: specialized for filtered data */ nrrdZlibStrategyLast }; #define NRRD_ZLIB_STRATEGY_MAX 3 /* ******** nrrdCenter enum ** ** node-centered vs. cell-centered */ enum { nrrdCenterUnknown, /* 0: no centering known for this axis */ nrrdCenterNode, /* 1: samples at corners of things (how "voxels" are usually imagined) |\______/|\______/|\______/| X X X X */ nrrdCenterCell, /* 2: samples at middles of things (characteristic of histogram bins) \___|___/\___|___/\___|___/ X X X */ nrrdCenterLast }; #define NRRD_CENTER_MAX 2 /* ******** nrrdKind enum ** ** For describing the information along one axis of an array. This is ** most important for clarifying the representation of non-scalar ** data, in order to distinguish between axes that are genuine image ** domain axes, and axes that exist just to store the multiple ** attributes per sample. One could argue that this information ** should be per-array and not per-axis, but you still have to ** indicate which one of the axes is the attribute axis. And, if you ** have, say, the gradient of RGB colors, you want the per-pixel 3x3 ** array to have those two attribute axes tagged accordingly. ** ** More of these may be added in the future, such as when nrrd ** supports bricking. Since nrrd is never going to be in the business ** of manipulating the kind information or supporting kind-specific ** semantics, there can be proliferation of nrrdKinds, provided ** pointless redundancy is avoided. ** ** There is a relationship between some of these (nrrdKindSpace is a ** specific nrrdKindDomain), but currently there is no effort to ** record this meta-kind information. ** ** Keep in sync: ** enumsNrrd.c: nrrdKind airEnum ** axis.c: nrrdKindSize() ** axis.c: _nrrdKindAltered() ** ** NOTE: The nrrdKindSize() function returns the valid size for these. ** */ enum { nrrdKindUnknown, nrrdKindDomain, /* 1: any image domain */ nrrdKindSpace, /* 2: a spatial domain */ nrrdKindTime, /* 3: a temporal domain */ /* -------------------------- end domain kinds */ /* -------------------------- begin range kinds */ nrrdKindList, /* 4: any list of values, non-resample-able */ nrrdKindPoint, /* 5: coords of a point */ nrrdKindVector, /* 6: coeffs of (contravariant) vector */ nrrdKindCovariantVector, /* 7: coeffs of covariant vector (eg gradient) */ nrrdKindNormal, /* 8: coeffs of unit-length covariant vector */ /* -------------------------- end arbitrary size kinds */ /* -------------------------- begin size-specific kinds */ nrrdKindStub, /* 9: axis with one sample (a placeholder) */ nrrdKindScalar, /* 10: effectively, same as a stub */ nrrdKindComplex, /* 11: real and imaginary components */ nrrdKind2Vector, /* 12: 2 component vector */ nrrdKind3Color, /* 13: ANY 3-component color value */ nrrdKindRGBColor, /* 14: RGB, no colorimetry */ nrrdKindHSVColor, /* 15: HSV, no colorimetry */ nrrdKindXYZColor, /* 16: perceptual primary colors */ nrrdKind4Color, /* 17: ANY 4-component color value */ nrrdKindRGBAColor, /* 18: RGBA, no colorimetry */ nrrdKind3Vector, /* 19: 3-component vector */ nrrdKind3Gradient, /* 20: 3-component covariant vector */ nrrdKind3Normal, /* 21: 3-component covector, assumed normalized */ nrrdKind4Vector, /* 22: 4-component vector */ nrrdKindQuaternion, /* 23: (w,x,y,z), not necessarily normalized */ nrrdKind2DSymMatrix, /* 24: Mxx Mxy Myy */ nrrdKind2DMaskedSymMatrix, /* 25: mask Mxx Mxy Myy */ nrrdKind2DMatrix, /* 26: Mxx Mxy Myx Myy */ nrrdKind2DMaskedMatrix, /* 27: mask Mxx Mxy Myx Myy */ nrrdKind3DSymMatrix, /* 28: Mxx Mxy Mxz Myy Myz Mzz */ nrrdKind3DMaskedSymMatrix, /* 29: mask Mxx Mxy Mxz Myy Myz Mzz */ nrrdKind3DMatrix, /* 30: Mxx Mxy Mxz Myx Myy Myz Mzx Mzy Mzz */ nrrdKind3DMaskedMatrix, /* 31: mask Mxx Mxy Mxz Myx Myy Myz Mzx Mzy Mzz */ nrrdKindLast }; #define NRRD_KIND_MAX 31 /* ******** nrrdAxisInfo enum ** ** the different pieces of per-axis information recorded in a nrrd */ enum { nrrdAxisInfoUnknown, nrrdAxisInfoSize, /* 1: number of samples along axis */ #define NRRD_AXIS_INFO_SIZE_BIT (1<< 1) nrrdAxisInfoSpacing, /* 2: spacing between samples */ #define NRRD_AXIS_INFO_SPACING_BIT (1<< 2) nrrdAxisInfoThickness, /* 3: thickness of sample region */ #define NRRD_AXIS_INFO_THICKNESS_BIT (1<< 3) nrrdAxisInfoMin, /* 4: min pos. assoc. w/ 1st sample */ #define NRRD_AXIS_INFO_MIN_BIT (1<< 4) nrrdAxisInfoMax, /* 5: max pos. assoc. w/ last sample */ #define NRRD_AXIS_INFO_MAX_BIT (1<< 5) nrrdAxisInfoSpaceDirection, /* 6: inter-sample vector in "space" */ #define NRRD_AXIS_INFO_SPACEDIRECTION_BIT (1<< 6) nrrdAxisInfoCenter, /* 7: cell vs. node */ #define NRRD_AXIS_INFO_CENTER_BIT (1<< 7) nrrdAxisInfoKind, /* 8: from the nrrdKind* enum */ #define NRRD_AXIS_INFO_KIND_BIT (1<< 8) nrrdAxisInfoLabel, /* 9: string describing the axis */ #define NRRD_AXIS_INFO_LABEL_BIT (1<< 9) nrrdAxisInfoUnits, /* 10: from the nrrdUnit* enum */ #define NRRD_AXIS_INFO_UNITS_BIT (1<<10) nrrdAxisInfoLast }; #define NRRD_AXIS_INFO_MAX 10 #define NRRD_AXIS_INFO_ALL \ ((1<<1)|(1<<2)|(1<<3)|(1<<4)|(1<<5)|(1<<6)|(1<<7)|(1<<8)|(1<<9)|(1<<10)) #define NRRD_AXIS_INFO_NONE 0 /* ******** nrrdBasicInfo enum ** ** the non-per-axis (or per-array) pieces of information that could ** meaningfully be copied between nrrds (hence the void *data is not ** included). ** ** "Basic" is named after the "basic field specifications" described ** in the NRRD file format definition */ enum { nrrdBasicInfoUnknown, nrrdBasicInfoData, /* 1 */ #define NRRD_BASIC_INFO_DATA_BIT (1<< 1) nrrdBasicInfoType, /* 2 */ #define NRRD_BASIC_INFO_TYPE_BIT (1<< 2) nrrdBasicInfoBlocksize, /* 3 */ #define NRRD_BASIC_INFO_BLOCKSIZE_BIT (1<< 3) nrrdBasicInfoDimension, /* 4 */ #define NRRD_BASIC_INFO_DIMENSION_BIT (1<< 4) nrrdBasicInfoContent, /* 5 */ #define NRRD_BASIC_INFO_CONTENT_BIT (1<< 5) nrrdBasicInfoSampleUnits, /* 6 */ #define NRRD_BASIC_INFO_SAMPLEUNITS_BIT (1<< 6) nrrdBasicInfoSpace, /* 7 */ #define NRRD_BASIC_INFO_SPACE_BIT (1<< 7) nrrdBasicInfoSpaceDimension, /* 8 */ #define NRRD_BASIC_INFO_SPACEDIMENSION_BIT (1<< 8) nrrdBasicInfoSpaceUnits, /* 9 */ #define NRRD_BASIC_INFO_SPACEUNITS_BIT (1<< 9) nrrdBasicInfoSpaceOrigin, /* 10 */ #define NRRD_BASIC_INFO_SPACEORIGIN_BIT (1<<10) nrrdBasicInfoMeasurementFrame, /* 11 */ #define NRRD_BASIC_INFO_MEASUREMENTFRAME_BIT (1<<11) nrrdBasicInfoOldMin, /* 12 */ #define NRRD_BASIC_INFO_OLDMIN_BIT (1<<12) nrrdBasicInfoOldMax, /* 13 */ #define NRRD_BASIC_INFO_OLDMAX_BIT (1<<13) nrrdBasicInfoComments, /* 14 */ #define NRRD_BASIC_INFO_COMMENTS_BIT (1<<14) nrrdBasicInfoKeyValuePairs, /* 15 */ #define NRRD_BASIC_INFO_KEYVALUEPAIRS_BIT (1<<15) nrrdBasicInfoLast }; #define NRRD_BASIC_INFO_MAX 15 #define NRRD_BASIC_INFO_ALL \ ((1<<1)|(1<<2)|(1<<3)|(1<<4)|(1<<5)|(1<<6)|(1<<7)|(1<<8)|(1<<9)|(1<<10)\ |(1<<11)|(1<<12)|(1<<13)|(1<<14)|(1<<15)) #define NRRD_BASIC_INFO_SPACE (NRRD_BASIC_INFO_SPACE_BIT \ | NRRD_BASIC_INFO_SPACEDIMENSION_BIT \ | NRRD_BASIC_INFO_SPACEUNITS_BIT \ | NRRD_BASIC_INFO_SPACEORIGIN_BIT \ | NRRD_BASIC_INFO_MEASUREMENTFRAME_BIT) #define NRRD_BASIC_INFO_NONE 0 /* ******** nrrdField enum ** ** the various fields we can parse in a NRRD header ** ** other things which must be kept in sync: ** arraysNrrd.c: ** _nrrdFieldValidInImage[] ** _nrrdFieldOnePerAxis[] ** _nrrdFieldValidInText[] ** _nrrdFieldRequired[] ** parseNrrd.c: ** _nrrdReadNrrdParseInfo[] ** enumsNrrd.c: ** nrrdField definition ** simple.c: ** _nrrdFieldCheck[] ** write.c: ** _nrrdFieldInteresting() ** _nrrdSprintFieldInfo() ** to some extent, in this file: ** nrrdAxisInfo and nrrdBasicInfo enums ** axis.c (for per-axis info): ** _nrrdAxisInfoCopy() ** methodsNrrd.c: ** lots of functions, but you knew that . . . */ enum { nrrdField_unknown, nrrdField_comment, /* 1 */ nrrdField_content, /* 2 */ nrrdField_number, /* 3 */ nrrdField_type, /* 4 */ nrrdField_block_size, /* 5 */ nrrdField_dimension, /* 6 */ nrrdField_space, /* 7 */ nrrdField_space_dimension, /* 8 */ nrrdField_sizes, /* 9 ----- begin per-axis ----- */ nrrdField_spacings, /* 10 */ nrrdField_thicknesses, /* 11 */ nrrdField_axis_mins, /* 12 */ nrrdField_axis_maxs, /* 13 */ nrrdField_space_directions, /* 14 */ nrrdField_centers, /* 15 */ nrrdField_kinds, /* 16 */ nrrdField_labels, /* 17 */ nrrdField_units, /* 18 ------ end per-axis ------ */ nrrdField_min, /* 19 */ nrrdField_max, /* 20 */ nrrdField_old_min, /* 21 */ nrrdField_old_max, /* 22 */ nrrdField_endian, /* 23 */ nrrdField_encoding, /* 24 */ nrrdField_line_skip, /* 25 */ nrrdField_byte_skip, /* 26 */ nrrdField_keyvalue, /* 27 */ nrrdField_sample_units, /* 28 */ nrrdField_space_units, /* 29 */ nrrdField_space_origin, /* 30 */ nrrdField_measurement_frame, /* 31 */ nrrdField_data_file, /* 32 */ nrrdField_last }; #define NRRD_FIELD_MAX 32 /* ******** nrrdHasNonExist* enum ** ** oh look, I'm violating my rules outline above for how the enum values ** should be ordered. The reason for this is that its just too bizarro to ** have the logical value of both nrrdHasNonExistFalse and nrrdHasNonExistTrue ** to be (in C) true. For instance, nrrdHasNonExist() should be able to ** return a value from this enum which also functions in a C expressions as ** the expected boolean value. If for some reason (outide the action of ** nrrdHasNonExist(), nrrdHasNonExistUnknown is interpreted as true, that's ** probably harmlessly conservative. Time will tell. */ enum { nrrdHasNonExistFalse, /* 0: no non-existent values were seen */ nrrdHasNonExistTrue, /* 1: some non-existent values were seen */ nrrdHasNonExistOnly, /* 2: NOTHING BUT non-existent values were seen */ nrrdHasNonExistUnknown, /* 3 */ nrrdHasNonExistLast }; #define NRRD_HAS_NON_EXIST_MAX 3 /* ******** nrrdSpace* enum ** ** Identifies the space in which which the origin and direction ** vectors have their coordinates measured. When a direction is named ** here (like "Left" or "Anterior"), that implies a basis vector that ** points in that direction, along which that coordinate becomes *larger* ** (this is the opposite of MetaIO, for example). ** ** All of these spaces have a well-defined expected dimension, as ** determined by nrrdSpaceDimension(), and setting a nrrd to be in ** such a space, by nrrdSpaceSet(), will automatically set nrrd->spaceDim. ** ** The first six spaces here are PATIENT-ORIENTED spaces, which are ** properly speaking aligned with the patient, and not the scanner ** itself. But nrrdSpaceScannerXYZ and nrrdSpaceScannerXYZTime are ** DEVICE-ORIENTED spaces, irrespective of the patient, used in a ** previous version of the DICOM standard. When the two spaces are ** lined up with normal patient orientation in the scanner, ** nrrdSpaceScannerXYZ is the same as nrrdSpaceLeftPosteriorSuperior. ** To quote Part 3 (Information Object Definitions) of the DICOM spec ** (page 275): "If a patient lies parallel to the ground, face-up on ** the table, with his feet-to-head direction same as the ** front-to-back direction of the imaging equipment, the direction of ** the axes of this patient based coordinate system and the equipment ** based coordinate system in previous versions of this Standard will ** coincide." ** ** Keep in sync: ** enumsNrrd.c: nrrdSpace airEnum ** simple.c: int nrrdSpaceDimension(int space) */ enum { nrrdSpaceUnknown, nrrdSpaceRightAnteriorSuperior, /* 1: NIFTI-1 (right-handed) */ nrrdSpaceLeftAnteriorSuperior, /* 2: standard Analyze (left-handed) */ nrrdSpaceLeftPosteriorSuperior, /* 3: DICOM 3.0 (right-handed) */ nrrdSpaceRightAnteriorSuperiorTime, /* 4: */ nrrdSpaceLeftAnteriorSuperiorTime, /* 5: */ nrrdSpaceLeftPosteriorSuperiorTime, /* 6: */ nrrdSpaceScannerXYZ, /* 7: ACR/NEMA 2.0 (pre-DICOM 3.0) */ nrrdSpaceScannerXYZTime, /* 8: */ nrrdSpace3DRightHanded, /* 9: */ nrrdSpace3DLeftHanded, /* 10: */ nrrdSpace3DRightHandedTime, /* 11: */ nrrdSpace3DLeftHandedTime, /* 12: */ nrrdSpaceLast }; #define NRRD_SPACE_MAX 12 /* ******** nrrdSpacingStatus* enum ** ** a way of describing how spacing information is known or not known for a ** given axis, as determined by nrrdSpacingCalculate */ enum { nrrdSpacingStatusUnknown, /* 0: nobody knows, or invalid axis choice */ nrrdSpacingStatusNone, /* 1: neither axis->spacing nor axis->spaceDirection is set */ nrrdSpacingStatusScalarNoSpace, /* 2: axis->spacing set, w/out space info */ nrrdSpacingStatusScalarWithSpace, /* 3: axis->spacing set, but there *is* space info, which means the spacing does *not* live in the surrounding space */ nrrdSpacingStatusDirection, /* 4: axis->spaceDirection set, and measured according to surrounding space */ nrrdSpacingStatusLast }; #define NRRD_SPACING_STATUS_MAX 4 /* ******** nrrdOriginStatus* enum ** ** how origin information was or was not computed by nrrdOriginCalculate */ enum { nrrdOriginStatusUnknown, /* 0: nobody knows, or invalid parms */ nrrdOriginStatusDirection, /* 1: chosen axes have spaceDirections */ nrrdOriginStatusNoMin, /* 2: axis->min doesn't exist */ nrrdOriginStatusNoMaxOrSpacing, /* 3: axis->max or ->spacing doesn't exist */ nrrdOriginStatusOkay, /* 4: all is well */ nrrdOriginStatusLast }; #ifdef __cplusplus } #endif #ifdef __cplusplus extern "C" { #endif /* ******** NRRD_CELL_POS, NRRD_NODE_POS, NRRD_POS ******** NRRD_CELL_IDX, NRRD_NODE_IDX, NRRD_IDX ** ** the guts of nrrdAxisPos() and nrrdAxisIdx(), for converting ** between "index space" location and "position" or "world space" location, ** given the centering, min and max "position", and number of samples. ** ** Unlike nrrdAxisPos() and nrrdAxisIdx(), this assumes that center ** is either nrrdCenterCell or nrrdCenterNode, but not nrrdCenterUnknown. */ /* index to position, cell centering */ #define NRRD_CELL_POS(min, max, size, idx) \ AIR_AFFINE(0, (idx) + 0.5, (size), (min), (max)) /* index to position, node centering */ #define NRRD_NODE_POS(min, max, size, idx) \ AIR_AFFINE(0, (idx), (size)-1, (min), (max)) /* index to position, either centering */ #define NRRD_POS(center, min, max, size, idx) \ (nrrdCenterCell == (center) \ ? NRRD_CELL_POS((min), (max), (size), (idx)) \ : NRRD_NODE_POS((min), (max), (size), (idx))) /* position to index, cell centering */ #define NRRD_CELL_IDX(min, max, size, pos) \ (AIR_AFFINE((min), (pos), (max), 0, (size)) - 0.5) /* position to index, node centering */ #define NRRD_NODE_IDX(min, max, size, pos) \ AIR_AFFINE((min), (pos), (max), 0, (size)-1) /* position to index, either centering */ #define NRRD_IDX(center, min, max, size, pos) \ (nrrdCenterCell == (center) \ ? NRRD_CELL_IDX((min), (max), (size), (pos)) \ : NRRD_NODE_IDX((min), (max), (size), (pos))) /* ******** NRRD_SPACING ** ** the guts of nrrdAxisSpacing(), determines the inter-sample ** spacing, given centering, min and max "position", and number of samples ** ** Unlike nrrdAxisSpacing, this assumes that center is either ** nrrdCenterCell or nrrdCenterNode, but not nrrdCenterUnknown. */ #define NRRD_SPACING(center, min, max, size) \ (nrrdCenterCell == center \ ? ((max) - (min))/AIR_CAST(double, size) \ : ((max) - (min))/(AIR_CAST(double, (size)- 1))) \ /* ******** NRRD_COORD_UPDATE ** ** This is for doing the "carrying" associated with gradually incrementing an ** array of coordinates. Assuming that the given coordinate array "coord" has ** been incremented by adding 1 to coord[0], this macro will propagating the ** change up to higher axes (when the coordinate has reached the size on a ** lower axis.) In addition, the final statement of the macro prevents the ** last index from going past a valid value. ** ** Assumptions: ** -- coord[] and size[] should both be arrays of unsigned integral values, ** presumably size_t ** -- size[i] is >= 1 for all i= (size)[ddd]; \ ddd++) { \ (coord)[ddd] = 0; \ (coord)[ddd+1]++; \ } \ if (dim) { \ (coord)[(dim)-1] = AIR_MIN((coord)[(dim)-1], (size)[(dim)-1]-1); \ } \ } /* ******** NRRD_COORD_INCR ** ** increments coord[idx] (by one) and then calls NRRD_COORD_UPDATE to ** propagate this change as necessary to higher numbered axes. Does ** nothing if idx>=dim, since that would be an invalid index into ** coord[] and size[] */ #define NRRD_COORD_INCR(coord, size, dim, idx) \ if ((idx) < (dim)) { \ (coord)[(idx)]++; \ NRRD_COORD_UPDATE((coord)+(idx), (size)+(idx), (dim)-(idx)); \ } /* ******** NRRD_INDEX_GEN ** ** Given array coordinates "coord" and sizes "size", both of length "dim", ** this calculates the linear index represented by coord (assuming lower ** coordinates are for *faster* axes), and stores it in "I". Has the same ** assumptions as NRRD_COORD_UPDATE. */ #define NRRD_INDEX_GEN(I, coord, size, dim) \ { \ unsigned int ddd = (dim); \ (I) = 0; \ while (ddd) { \ ddd--; \ (I) = (coord)[ddd] + (size)[ddd]*(I); \ } \ } /* ******** NRRD_COORD_GEN ** ** opposite of NRRD_INDEX_GEN: going from linear index "I" to ** coordinate array "coord". */ #define NRRD_COORD_GEN(coord, size, dim, I) \ { \ unsigned int ddd; \ size_t myI = (I); \ for (ddd=0; ddd<(dim); ddd++) { \ (coord)[ddd] = myI % (size)[ddd]; \ myI /= (size)[ddd]; \ } \ } #ifdef __cplusplus } #endif #include #include /* for ptrdiff_t */ #ifdef __cplusplus extern "C" { #endif #define NRRD nrrdBiffKey /* ******** NrrdAxisInfo struct ** ** all the information which can sensibly be associated with ** one axis of a nrrd. The only member which MUST be explicitly ** set to something meaningful is "size". ** ** If an axis lies conceptually along some direction in an enclosing ** space of dimension nrrd->spaceDim, then the first nrrd->spaceDim ** entries of spaceDirection[] must be non-NaN, and min, max, spacing, ** and units must NOT be set; thickness, center, and label can still ** be used. The mutual exclusion between axis-aligned and general ** direction information is enforced per-axis, not per-array. ** ** The min and max values give the range of positions "represented" ** by the samples along this axis. In node-centering, "min" IS the ** position at the lowest index. In cell-centering, the position at ** the lowest index is between min and max (a touch bigger than min, ** assuming min < max). ** ** There needs to be a one-to-one correspondence between these variables ** and the nrrdAxisInfo* enum (nrrdEnums.h), the per-axis header fields ** (see nrrdField* enum in nrrdEnums.h), and the various methods in axis.c */ typedef struct { size_t size; /* number of elements along each axis */ double spacing; /* if non-NaN, distance between samples */ double thickness; /* if non-NaN, nominal thickness of region represented by one sample along the axis. No semantics relative to spacing are assumed or imposed, and unlike spacing, there is no sensible way to alter thickness- it is either copied (as with cropping and slicing) or set to NaN (when resampled). */ double min, max; /* if non-NaN, range of positions spanned by the samples on this axis. Obviously, one can set "spacing" to something incompatible with min and max: the idea is that only one (min and max, or spacing) should be taken to be significant at any time. */ double spaceDirection[NRRD_SPACE_DIM_MAX]; /* the vector, in "space" (as described by nrrd->space and/or nrrd->spaceDim), from one sample to the next sample along this axis. It is the column vector of the transform from index space to "space" space */ int center; /* cell vs. node centering (value should be one of nrrdCenter{Unknown,Node,Cell} */ int kind; /* what kind of information is along this axis (from the nrrdKind* enum) */ char *label, /* short info string for each axis */ *units; /* string identifying the unit */ } NrrdAxisInfo; /* ******** Nrrd struct ** ** The struct used to wrap around the raw data array */ typedef struct { /* ** NECESSARY information describing the main array. This is ** generally set at the same time that either the nrrd is created, ** or at the time that the nrrd is wrapped around an existing array */ void *data; /* the data in memory */ int type; /* a value from the nrrdType enum */ unsigned int dim; /* the dimension (rank) of the array */ /* ** All per-axis specific information */ NrrdAxisInfo axis[NRRD_DIM_MAX]; /* axis[0] is the fastest axis in the scan- line ordering, the one who's coordinates change the fastest as the elements are accessed in the order in which they appear in memory */ /* ** Optional information descriptive of whole array, some of which is ** meaningfuly for only some uses of a nrrd */ char *content; /* brief account of what this data is */ char *sampleUnits; /* units of measurement of the values stored in the array itself (not the array axes and not space coordinates). The logical name might be "dataUnits", but that's perhaps ambiguous. Note that these units may apply to non-scalar kinds (e.g. coefficients of a vector have the same units) */ int space; /* from nrrdSpace* enum, and often implies the value of spaceDim */ unsigned int spaceDim; /* if non-zero, the dimension of the space in which the regular sampling grid conceptually lies. This is a separate variable because this dimension can be different than the array dimension. The non-zero-ness of this value is in fact the primary indicator that space and orientation information is set. This identifies the number of entries in "origin" and the per-axis "direction" vectors that are taken as meaningful */ char *spaceUnits[NRRD_SPACE_DIM_MAX]; /* units for coordinates of space */ double spaceOrigin[NRRD_SPACE_DIM_MAX]; /* the location of the center the first (lowest memory address) array sample, regardless of node-vs-cell centering */ double measurementFrame[NRRD_SPACE_DIM_MAX][NRRD_SPACE_DIM_MAX]; /* if spaceDim is non-zero, this may store a spaceDim-by-spaceDim matrix which transforms vector/matrix coefficients in the "measurement frame" to those in the world space described by spaceDim (and hopefully space). Coeff [i][j] is *column* i & *row* j, which is probably the *transpose* of what you expect. There are no semantics linking this to the "kind" of any axis, for a variety of reasons */ size_t blockSize; /* for nrrdTypeBlock, block byte size */ double oldMin, oldMax; /* if non-NaN, and if nrrd is of integral type, extremal values for the array BEFORE it was quantized */ void *ptr; /* never read or set by nrrd; use/abuse as you see fit */ /* ** Comments. Read from, and written to, header. ** The comment array "cmt" is NOT NULL-terminated. ** The number of comments is cmtArr->len. */ char **cmt; airArray *cmtArr; /* ** Key-value pairs. */ char **kvp; airArray *kvpArr; } Nrrd; struct NrrdIoState_t; struct NrrdEncoding_t; /* ******** NrrdFormat ** ** All information and behavior relevent to one datafile format */ typedef struct { char name[AIR_STRLEN_SMALL]; /* short identifying string */ int isImage, /* this format is intended solely for "2D" images, which controls the invocation of _nrrdReshapeUpGrayscale() if nrrdStateGrayscaleImage3D */ readable, /* we can read as well as write this format */ usesDIO; /* this format can use Direct IO */ /* tests if this format is currently available in this build */ int (*available)(void); /* (for writing) returns non-zero if a given filename could likely be represented by this format */ int (*nameLooksLike)(const char *filename); /* (for writing) returns non-zero if a given nrrd/encoding pair will fit in this format */ int (*fitsInto)(const Nrrd *nrrd, const struct NrrdEncoding_t *encoding, int useBiff); /* (for reading) returns non-zero if what has been read in so far is recognized as the beginning of this format */ int (*contentStartsLike)(struct NrrdIoState_t *nio); /* reader and writer */ int (*read)(FILE *file, Nrrd *nrrd, struct NrrdIoState_t *nio); int (*write)(FILE *file, const Nrrd *nrrd, struct NrrdIoState_t *nio); } NrrdFormat; /* ******** NrrdEncoding ** ** All information and behavior relevent to one way of encoding data ** ** The data readers are responsible for memory allocation. ** This is necessitated by the memory restrictions of direct I/O */ typedef struct NrrdEncoding_t { char name[AIR_STRLEN_SMALL], /* short identifying string */ suffix[AIR_STRLEN_SMALL]; /* costumary filename suffix */ int endianMatters, isCompression; int (*available)(void); /* The "data" and "elementNum" values have to be passed explicitly to read/wrote because they will be different from nrrd->data and nrrdElementNumber(nrrd) in the case of multiple data files. You might think that the only other thing required to be passed is nrrdElementSize(nrrd), but no, it is in fact best to pass the whole Nrrd, instead of just certain attributes. The stupid details: nrrd->dim: needed to know whether to put one value per line in case of 1-D nrrdEncodingAscii nrrd->axis[0].size: need for proper formatting of nrrdEncodingAscii nrrd->type: needed for nrrdEncodingAscii, since its action is entirely parameterized by type nrrd->blockSize: needed for nrrdElementSize in case of nrrdTypeBlock */ int (*read)(FILE *file, void *data, size_t elementNum, Nrrd *nrrd, struct NrrdIoState_t *nio); int (*write)(FILE *file, const void *data, size_t elementNum, const Nrrd *nrrd, struct NrrdIoState_t *nio); } NrrdEncoding; /* ******** NrrdIoState struct ** ** Everything relating to how the nrrd is read and written. ** Multiple parameters for writing are set here (like format, encoding, ** zlib parameters). Also, this is the place where those few parameters ** of reading are stored (like skipData and keepNrrdDataFileOpen). Also, ** after the nrrd has been read, it is a potentially useful record of what ** it took to read it in. */ typedef struct NrrdIoState_t { char *path, /* allows us to remember the directory from whence this nrrd was "load"ed, or to whence this nrrd is "save"ed, MINUS the trailing "/", so as to facilitate games with header-relative data files */ *base, /* when "save"ing a nrrd into separate header and data, the name of the header file (e.g. "output.nhdr") MINUS the ".nhdr". This is massaged to produce a header- relative data filename. */ *line, /* buffer for saving one line from file */ *dataFNFormat, /* if non-NULL, the format string (containing something like "%d" as a substring) to be used to identify multiple detached datafiles. NB: This is "format" in the sense of a printf- style format string, not in the sense of a file format. This may need header-relative path processing. */ **dataFN, /* ON READ + WRITE: array of data filenames. These are not passed directly to fopen, they may need header-relative path processing. Like the cmtArr in the Nrrd, this array is not NULL- terminated */ *headerStringWrite; /* ON WRITE: string from to which the header can be written. On write, it is assumed allocated for as long as it needs to be (probably via a first pass with learningHeaderStrlen). NOTE: It is the non-NULL-ity of this which signifies the intent to do string-based writing */ const char *headerStringRead; /* ON READ: like headerStringWrite, but for reading the header from. NOTE: It is the non-NULL-ity of this which signifies the intent to do string-based reading */ airArray *dataFNArr; /* for managing the above */ FILE *headerFile, /* if non-NULL, the file from which the NRRD header is being read */ *dataFile; /* this used to be a central part of how the I/O code worked, but now it is simply the place to store the dataFile in the case of keepNrrdDataFileOpen */ unsigned int dataFileDim, /* The dimension of the data in each data file. Together with dataFNArr->len, this determines how many bytes should be in each data file */ lineLen, /* allocated size of line, including the last character for \0 */ charsPerLine, /* when writing ASCII data in which we intend only to write a huge long list of numbers whose text formatting implies nothing, then how many characters do we limit ourselves to per line */ valsPerLine, /* when writing ASCII data in which we DO intend to sigify (or at least hint at) something with the formatting, then what is the max number of values to write on a line */ lineSkip, /* if dataFile non-NULL, the number of lines in dataFile that should be skipped over (so as to bypass another form of ASCII header preceeding raw data) */ headerStrlen, /* ON WRITE, for NRRDs, if learningHeaderStrlen, the learned strlen of the header so far */ headerStrpos; /* ON READ, for NRRDs, if headerStringRead is non-NULL, the current location of reading in the header */ long int byteSkip; /* exactly like lineSkip, but bytes instead of lines. First the lines are skipped, then the bytes */ /* Note that the NRRD0004 and NRRD0005 file formats indicate that a numbered sequence of data filenames should be indexed via a "%d" format specification, and that the format doc says nothing about the "min" and "max" fields of "data file" being only positive. So the following three dataFN* fields are appropriately (signed) ints, even if all normal usage could also be represented with unsigned ints. Nonetheless, the return from _nrrdDataFNNumber(), which gives the total number of file names, is still appropriately an unsigned int. This may be revisited if the file format itself is adjusted. */ int dataFNMin, /* used with dataFNFormat to identify .. */ dataFNMax, /* .. all the multiple detached datafiles */ dataFNStep; /* how to step from max to min */ /* On the other hand, dataFNIndex ranges from 0 to (#datafiles-1), and not dataFNMin to dataFNMax, so it really should be unsigned */ unsigned int dataFNIndex; /* which of the data files are being read */ int pos, /* line[pos] is beginning of stuff which still has yet to be parsed */ endian, /* endian-ness of the data in file, for those encoding/type combinations for which it matters (from nrrdEndian) */ seen[NRRD_FIELD_MAX+1], /* for error checking in header parsing */ detachedHeader, /* ON WRITE: request for file (NRRD format only) to be split into distinct header and data. This only has an effect if detaching the header is not already necessary, as it is with multiple data files */ bareText, /* when writing a plain text file, is there any effort made to record the nrrd struct info in the text file */ skipData, /* if non-zero (all formats): ON READ: don't allocate memory for, and don't read in, the data portion of the file (but we do verify that for nrrds, detached datafiles can be opened). Note: Does NOT imply keepNrrdDataFileOpen. Warning: resulting nrrd struct will have "data" pointer NULL. ON WRITE: don't write data portion of file (for nrrds, don't even try to open detached datafiles). Warning: can result in broken noncomformant files. (be careful with this) */ skipFormatURL, /* if non-zero for NRRD format ON WRITE: skip the comment lines that document where to find the NRRD file format specs */ keepNrrdDataFileOpen, /* ON READ: when there is only a single dataFile, don't close nio->dataFile when you otherwise would, when reading the nrrd format. Probably used in conjunction with skipData. (currently for "unu data") ON WRITE: no semantics */ zlibLevel, /* zlib compression level (0-9, -1 for default[6], 0 for no compression). */ zlibStrategy, /* zlib compression strategy, can be one of the nrrdZlibStrategy enums, default is nrrdZlibStrategyDefault. */ bzip2BlockSize, /* block size used for compression, roughly equivalent to better but slower (1-9, -1 for default[9]). */ learningHeaderStrlen; /* ON WRITE, for nrrds, learn and save the total length of header into headerStrlen. This is used to allocate a buffer for header */ void *oldData; /* ON READ: if non-NULL, pointer to space that has already been allocated for oldDataSize */ size_t oldDataSize; /* ON READ: size of mem pointed to by oldData */ /* The format and encoding. These are initialized to nrrdFormatUnknown and nrrdEncodingUnknown, respectively. USE THESE VALUES for any kind of initialization or flagging; DO NOT USE NULL */ const NrrdFormat *format; const NrrdEncoding *encoding; } NrrdIoState; /******** defaults (nrrdDefault..) and state (nrrdState..) */ /* defaultsNrrd.c */ NRRDIO_EXPORT int nrrdDefaultWriteEncodingType; NRRDIO_EXPORT int nrrdDefaultWriteBareText; NRRDIO_EXPORT unsigned int nrrdDefaultWriteCharsPerLine; NRRDIO_EXPORT unsigned int nrrdDefaultWriteValsPerLine; NRRDIO_EXPORT int nrrdDefaultCenter; NRRDIO_EXPORT double nrrdDefaultSpacing; NRRDIO_EXPORT int nrrdStateVerboseIO; NRRDIO_EXPORT int nrrdStateKeyValuePairsPropagate; NRRDIO_EXPORT int nrrdStateAlwaysSetContent; NRRDIO_EXPORT int nrrdStateDisableContent; NRRDIO_EXPORT const char *nrrdStateUnknownContent; NRRDIO_EXPORT int nrrdStateGrayscaleImage3D; NRRDIO_EXPORT int nrrdStateKeyValueReturnInternalPointers; NRRDIO_EXPORT int nrrdStateKindNoop; /******** all the airEnums used through-out nrrd */ /* ** the actual C enums are in nrrdEnums.h; experience has shown that it ** is not particularly useful to name those enums, since the shortest ** name is best used for the airEnums here */ /* enumsNrrd.c */ NRRDIO_EXPORT const airEnum *const nrrdFormatType; NRRDIO_EXPORT const airEnum *const nrrdType; NRRDIO_EXPORT const airEnum *const nrrdEncodingType; NRRDIO_EXPORT const airEnum *const nrrdCenter; NRRDIO_EXPORT const airEnum *const nrrdKind; NRRDIO_EXPORT const airEnum *const nrrdField; NRRDIO_EXPORT const airEnum *const nrrdSpace; NRRDIO_EXPORT const airEnum *const nrrdSpacingStatus; /******** arrays of things (poor-man's functions/predicates) */ /* arraysNrrd.c */ NRRDIO_EXPORT const char nrrdTypePrintfStr[NRRD_TYPE_MAX+1][AIR_STRLEN_SMALL]; NRRDIO_EXPORT const size_t nrrdTypeSize[NRRD_TYPE_MAX+1]; NRRDIO_EXPORT const double nrrdTypeMin[NRRD_TYPE_MAX+1]; NRRDIO_EXPORT const double nrrdTypeMax[NRRD_TYPE_MAX+1]; NRRDIO_EXPORT const int nrrdTypeIsIntegral[NRRD_TYPE_MAX+1]; NRRDIO_EXPORT const int nrrdTypeIsUnsigned[NRRD_TYPE_MAX+1]; /******** pseudo-constructors, pseudo-destructors, and such */ /* methodsNrrd.c */ NRRDIO_EXPORT NrrdIoState *nrrdIoStateNew(void); NRRDIO_EXPORT void nrrdIoStateInit(NrrdIoState *nio); NRRDIO_EXPORT NrrdIoState *nrrdIoStateNix(NrrdIoState *nio); NRRDIO_EXPORT void nrrdInit(Nrrd *nrrd); NRRDIO_EXPORT Nrrd *nrrdNew(void); NRRDIO_EXPORT Nrrd *nrrdNix(Nrrd *nrrd); NRRDIO_EXPORT Nrrd *nrrdEmpty(Nrrd *nrrd); NRRDIO_EXPORT Nrrd *nrrdNuke(Nrrd *nrrd); NRRDIO_EXPORT int nrrdWrap_nva(Nrrd *nrrd, void *data, int type, unsigned int dim, const size_t *size); NRRDIO_EXPORT int nrrdWrap_va(Nrrd *nrrd, void *data, int type, unsigned int dim, ... /* size_t sx, sy, .., axis(dim-1) size */); NRRDIO_EXPORT void nrrdBasicInfoInit(Nrrd *nrrd, int excludeBitflag); NRRDIO_EXPORT int nrrdBasicInfoCopy(Nrrd *nout, const Nrrd *nin, int excludeBitflag); NRRDIO_EXPORT int nrrdCopy(Nrrd *nout, const Nrrd *nin); NRRDIO_EXPORT int nrrdAlloc_nva(Nrrd *nrrd, int type, unsigned int dim, const size_t *size); NRRDIO_EXPORT int nrrdAlloc_va(Nrrd *nrrd, int type, unsigned int dim, ... /* size_t sx, sy, .., axis(dim-1) size */); NRRDIO_EXPORT int nrrdMaybeAlloc_nva(Nrrd *nrrd, int type, unsigned int dim, const size_t *size); NRRDIO_EXPORT int nrrdMaybeAlloc_va(Nrrd *nrrd, int type, unsigned int dim, ... /* size_t sx, sy, .., ax(dim-1) size */); /******** axis info related */ /* axis.c */ NRRDIO_EXPORT int nrrdKindIsDomain(int kind); NRRDIO_EXPORT unsigned int nrrdKindSize(int kind); NRRDIO_EXPORT int nrrdAxisInfoCopy(Nrrd *nout, const Nrrd *nin, const int *axmap, int excludeBitflag); NRRDIO_EXPORT void nrrdAxisInfoSet_nva(Nrrd *nin, int axInfo, const void *info); NRRDIO_EXPORT void nrrdAxisInfoSet_va(Nrrd *nin, int axInfo, ... /* const void* */); NRRDIO_EXPORT void nrrdAxisInfoGet_nva(const Nrrd *nrrd, int axInfo, void *info); NRRDIO_EXPORT void nrrdAxisInfoGet_va(const Nrrd *nrrd, int axInfo, ... /* ??? */); NRRDIO_EXPORT double nrrdAxisInfoPos(const Nrrd *nrrd, unsigned int ax, double idx); NRRDIO_EXPORT double nrrdAxisInfoIdx(const Nrrd *nrrd, unsigned int ax, double pos); NRRDIO_EXPORT void nrrdAxisInfoPosRange(double *loP, double *hiP, const Nrrd *nrrd, unsigned int ax, double loIdx, double hiIdx); NRRDIO_EXPORT void nrrdAxisInfoIdxRange(double *loP, double *hiP, const Nrrd *nrrd, unsigned int ax, double loPos, double hiPos); NRRDIO_EXPORT void nrrdAxisInfoSpacingSet(Nrrd *nrrd, unsigned int ax); NRRDIO_EXPORT void nrrdAxisInfoMinMaxSet(Nrrd *nrrd, unsigned int ax, int defCenter); NRRDIO_EXPORT unsigned int nrrdDomainAxesGet(const Nrrd *nrrd, unsigned int axisIdx[NRRD_DIM_MAX]); NRRDIO_EXPORT unsigned int nrrdRangeAxesGet(const Nrrd *nrrd, unsigned int axisIdx[NRRD_DIM_MAX]); NRRDIO_EXPORT unsigned int nrrdSpatialAxesGet(const Nrrd *nrrd, unsigned int axisIdx[NRRD_DIM_MAX]); NRRDIO_EXPORT unsigned int nrrdNonSpatialAxesGet(const Nrrd *nrrd, unsigned int axisIdx[NRRD_DIM_MAX]); NRRDIO_EXPORT int nrrdSpacingCalculate(const Nrrd *nrrd, unsigned int ax, double *spacing, double vector[NRRD_SPACE_DIM_MAX]); NRRDIO_EXPORT int nrrdOrientationReduce(Nrrd *nout, const Nrrd *nin, int setMinsFromOrigin); /******** simple things */ /* simple.c */ NRRDIO_EXPORT const char *nrrdBiffKey; NRRDIO_EXPORT unsigned int nrrdSpaceDimension(int space); NRRDIO_EXPORT int nrrdSpaceSet(Nrrd *nrrd, int space); NRRDIO_EXPORT int nrrdSpaceDimensionSet(Nrrd *nrrd, unsigned int spaceDim); NRRDIO_EXPORT unsigned int nrrdSpaceOriginGet(const Nrrd *nrrd, double vector[NRRD_SPACE_DIM_MAX]); NRRDIO_EXPORT int nrrdSpaceOriginSet(Nrrd *nrrd, double vector[NRRD_SPACE_DIM_MAX]); NRRDIO_EXPORT int nrrdOriginCalculate(const Nrrd *nrrd, unsigned int *axisIdx, unsigned int axisIdxNum, int defaultCenter, double *origin); NRRDIO_EXPORT int nrrdContentSet_va(Nrrd *nout, const char *func, const Nrrd *nin, const char *format, ... /* printf-style arg list */ ); NRRDIO_EXPORT void nrrdDescribe(FILE *file, const Nrrd *nrrd); NRRDIO_EXPORT int nrrdCheck(const Nrrd *nrrd); NRRDIO_EXPORT int _nrrdCheck(const Nrrd *nrrd, int checkData, int useBiff); NRRDIO_EXPORT size_t nrrdElementSize(const Nrrd *nrrd); NRRDIO_EXPORT size_t nrrdElementNumber(const Nrrd *nrrd); NRRDIO_EXPORT int nrrdSanity(void); NRRDIO_EXPORT int nrrdSameSize(const Nrrd *n1, const Nrrd *n2, int useBiff); NRRDIO_EXPORT void nrrdSpaceVecCopy(double dst[NRRD_SPACE_DIM_MAX], const double src[NRRD_SPACE_DIM_MAX]); NRRDIO_EXPORT void nrrdSpaceVecScaleAdd2(double sum[NRRD_SPACE_DIM_MAX], double sclA, const double vecA[NRRD_SPACE_DIM_MAX], double sclB, const double vecB[NRRD_SPACE_DIM_MAX]); NRRDIO_EXPORT void nrrdSpaceVecScale(double out[NRRD_SPACE_DIM_MAX], double scl, const double vec[NRRD_SPACE_DIM_MAX]); NRRDIO_EXPORT double nrrdSpaceVecNorm(unsigned int sdim, const double vec[NRRD_SPACE_DIM_MAX]); NRRDIO_EXPORT int nrrdSpaceVecExists(unsigned int sdim, double vec[NRRD_SPACE_DIM_MAX]); NRRDIO_EXPORT void nrrdSpaceVecSetNaN(double vec[NRRD_SPACE_DIM_MAX]); /******** comments related */ /* comment.c */ NRRDIO_EXPORT int nrrdCommentAdd(Nrrd *nrrd, const char *str); NRRDIO_EXPORT void nrrdCommentClear(Nrrd *nrrd); NRRDIO_EXPORT int nrrdCommentCopy(Nrrd *nout, const Nrrd *nin); /******** key/value pairs */ /* keyvalue.c */ NRRDIO_EXPORT unsigned int nrrdKeyValueSize(const Nrrd *nrrd); NRRDIO_EXPORT int nrrdKeyValueAdd(Nrrd *nrrd, const char *key, const char *value); NRRDIO_EXPORT char *nrrdKeyValueGet(const Nrrd *nrrd, const char *key); NRRDIO_EXPORT void nrrdKeyValueIndex(const Nrrd *nrrd, char **keyP, char **valueP, unsigned int ki); NRRDIO_EXPORT int nrrdKeyValueErase(Nrrd *nrrd, const char *key); NRRDIO_EXPORT void nrrdKeyValueClear(Nrrd *nrrd); NRRDIO_EXPORT int nrrdKeyValueCopy(Nrrd *nout, const Nrrd *nin); /******** endian related */ /* endianNrrd.c */ NRRDIO_EXPORT void nrrdSwapEndian(Nrrd *nrrd); /******** getting information to and from files */ /* formatXXX.c */ NRRDIO_EXPORT const NrrdFormat *const nrrdFormatNRRD; NRRDIO_EXPORT const NrrdFormat *const nrrdFormatPNM; NRRDIO_EXPORT const NrrdFormat *const nrrdFormatPNG; NRRDIO_EXPORT const NrrdFormat *const nrrdFormatVTK; NRRDIO_EXPORT const NrrdFormat *const nrrdFormatText; NRRDIO_EXPORT const NrrdFormat *const nrrdFormatEPS; /* format.c */ NRRDIO_EXPORT const NrrdFormat *const nrrdFormatUnknown; NRRDIO_EXPORT const NrrdFormat * const nrrdFormatArray[NRRD_FORMAT_TYPE_MAX+1]; /* encodingXXX.c */ NRRDIO_EXPORT const NrrdEncoding *const nrrdEncodingRaw; NRRDIO_EXPORT const NrrdEncoding *const nrrdEncodingAscii; NRRDIO_EXPORT const NrrdEncoding *const nrrdEncodingHex; NRRDIO_EXPORT const NrrdEncoding *const nrrdEncodingGzip; NRRDIO_EXPORT const NrrdEncoding *const nrrdEncodingBzip2; /* encoding.c */ NRRDIO_EXPORT const NrrdEncoding *const nrrdEncodingUnknown; NRRDIO_EXPORT const NrrdEncoding * const nrrdEncodingArray[NRRD_ENCODING_TYPE_MAX+1]; /* parseNrrd.c */ /* this needs the "FILE *file" first arg for the sole reason that parsing a "data file: " field which identifies a LIST must then read in all the data filenames from the same file */ NRRDIO_EXPORT int (*nrrdFieldInfoParse[NRRD_FIELD_MAX+1])(FILE *file, Nrrd *nrrd, NrrdIoState *nio, int useBiff); NRRDIO_EXPORT unsigned int _nrrdDataFNNumber(NrrdIoState *nio); NRRDIO_EXPORT int _nrrdContainsPercentThisAndMore(const char *str, char thss); NRRDIO_EXPORT int _nrrdDataFNCheck(NrrdIoState *nio, Nrrd *nrrd, int useBiff); /* read.c */ NRRDIO_EXPORT int _nrrdOneLine(unsigned int *lenP, NrrdIoState *nio, FILE *file); NRRDIO_EXPORT int nrrdLineSkip(FILE *dataFile, NrrdIoState *nio); NRRDIO_EXPORT int nrrdByteSkip(FILE *dataFile, Nrrd *nrrd, NrrdIoState *nio); NRRDIO_EXPORT int nrrdLoad(Nrrd *nrrd, const char *filename, NrrdIoState *nio); NRRDIO_EXPORT int nrrdLoadMulti(Nrrd *const *nin, unsigned int ninLen, const char *fnameFormat, unsigned int numStart, NrrdIoState *nio); NRRDIO_EXPORT int nrrdRead(Nrrd *nrrd, FILE *file, NrrdIoState *nio); NRRDIO_EXPORT int nrrdStringRead(Nrrd *nrrd, const char *string, NrrdIoState *nio); /* write.c */ NRRDIO_EXPORT int nrrdIoStateSet(NrrdIoState *nio, int parm, int value); NRRDIO_EXPORT int nrrdIoStateEncodingSet(NrrdIoState *nio, const NrrdEncoding *encoding); NRRDIO_EXPORT int nrrdIoStateFormatSet(NrrdIoState *nio, const NrrdFormat *format); NRRDIO_EXPORT int nrrdIoStateGet(NrrdIoState *nio, int parm); NRRDIO_EXPORT const NrrdEncoding *nrrdIoStateEncodingGet(NrrdIoState *nio); NRRDIO_EXPORT const NrrdFormat *nrrdIoStateFormatGet(NrrdIoState *nio); NRRDIO_EXPORT int nrrdSave(const char *filename, const Nrrd *nrrd, NrrdIoState *nio); NRRDIO_EXPORT int nrrdSaveMulti(const char *fnameFormat, const Nrrd *const *nin, unsigned int ninLen, unsigned int numStart, NrrdIoState *nio); NRRDIO_EXPORT int nrrdWrite(FILE *file, const Nrrd *nrrd, NrrdIoState *nio); NRRDIO_EXPORT int nrrdStringWrite(char **stringP, const Nrrd *nrrd, NrrdIoState *nio); /******** getting value into and out of an array of general type, and all other simplistic functionality pseudo-parameterized by type */ /* accessors.c */ NRRDIO_EXPORT double (*nrrdDLoad[NRRD_TYPE_MAX+1])(const void *v); NRRDIO_EXPORT float (*nrrdFLoad[NRRD_TYPE_MAX+1])(const void *v); NRRDIO_EXPORT int (*nrrdILoad[NRRD_TYPE_MAX+1])(const void *v); NRRDIO_EXPORT unsigned int (*nrrdUILoad[NRRD_TYPE_MAX+1])(const void *v); NRRDIO_EXPORT double (*nrrdDStore[NRRD_TYPE_MAX+1])(void *v, double d); NRRDIO_EXPORT float (*nrrdFStore[NRRD_TYPE_MAX+1])(void *v, float f); NRRDIO_EXPORT int (*nrrdIStore[NRRD_TYPE_MAX+1])(void *v, int j); NRRDIO_EXPORT unsigned int (*nrrdUIStore[NRRD_TYPE_MAX+1])(void *v, unsigned int j); NRRDIO_EXPORT double (*nrrdDLookup[NRRD_TYPE_MAX+1])(const void *v, size_t I); NRRDIO_EXPORT float (*nrrdFLookup[NRRD_TYPE_MAX+1])(const void *v, size_t I); NRRDIO_EXPORT int (*nrrdILookup[NRRD_TYPE_MAX+1])(const void *v, size_t I); NRRDIO_EXPORT unsigned int (*nrrdUILookup[NRRD_TYPE_MAX+1])(const void *v, size_t I); NRRDIO_EXPORT double (*nrrdDInsert[NRRD_TYPE_MAX+1])(void *v, size_t I, double d); NRRDIO_EXPORT float (*nrrdFInsert[NRRD_TYPE_MAX+1])(void *v, size_t I, float f); NRRDIO_EXPORT int (*nrrdIInsert[NRRD_TYPE_MAX+1])(void *v, size_t I, int j); NRRDIO_EXPORT unsigned int (*nrrdUIInsert[NRRD_TYPE_MAX+1])(void *v, size_t I, unsigned int j); NRRDIO_EXPORT int (*nrrdSprint[NRRD_TYPE_MAX+1])(char *, const void *); /******** permuting, shuffling, and all flavors of reshaping */ /* reorder.c */ NRRDIO_EXPORT int nrrdAxesInsert(Nrrd *nout, const Nrrd *nin, unsigned int ax); NRRDIO_EXPORT int nrrdInvertPerm(unsigned int *invp, const unsigned int *perm, unsigned int n); NRRDIO_EXPORT int nrrdAxesPermute(Nrrd *nout, const Nrrd *nin, const unsigned int *axes); NRRDIO_EXPORT int nrrdShuffle(Nrrd *nout, const Nrrd *nin, unsigned int axis, const size_t *perm); /******** sampling, slicing, cropping */ /* subset.c */ NRRDIO_EXPORT int nrrdSlice(Nrrd *nout, const Nrrd *nin, unsigned int axis, size_t pos); NRRDIO_EXPORT int nrrdCrop(Nrrd *nout, const Nrrd *nin, size_t *min, size_t *max); #ifdef __cplusplus } #endif cmtk-3.3.1/Utilities/NrrdIO/NrrdIO_Srcs.txt000066400000000000000000000006711276303427400204700ustar00rootroot00000000000000754.c mop.c array.c parseAir.c dio.c sane.c endianAir.c string.c enum.c miscAir.c biffbiff.c biffmsg.c accessors.c defaultsNrrd.c enumsNrrd.c arraysNrrd.c methodsNrrd.c reorder.c axis.c simple.c comment.c keyvalue.c endianNrrd.c parseNrrd.c gzio.c read.c write.c format.c formatNRRD.c encoding.c encodingRaw.c encodingAscii.c encodingHex.c encodingGzip.c subset.c encodingBzip2.c formatEPS.c formatPNG.c formatPNM.c formatText.c formatVTK.c cmtk-3.3.1/Utilities/NrrdIO/accessors.c000066400000000000000000000165041276303427400177330ustar00rootroot00000000000000/* NrrdIO: stand-alone code for basic nrrd functionality Copyright (C) 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. */ #include "NrrdIO.h" #include "privateNrrd.h" #include "float.h" /* ** making these typedefs here allows us to used one token for both ** constructing function names, and for specifying argument types */ typedef signed char CH; typedef unsigned char UC; typedef signed short SH; typedef unsigned short US; /* Microsoft apparently uses 'IN' as a keyword, so we changed 'IN' to 'JN'. */ typedef signed int JN; typedef unsigned int UI; typedef airLLong LL; /* ui64 to double conversion is not implemented, sorry */ #if _MSC_VER < 1300 typedef airLLong UL; #else typedef airULLong UL; #endif typedef float FL; typedef double DB; #define MAP(F, A) \ F(A, CH) \ F(A, UC) \ F(A, SH) \ F(A, US) \ F(A, JN) \ F(A, UI) \ F(A, LL) \ F(A, UL) \ F(A, FL) \ F(A, DB) /* ** _nrrdLoad( *v) ** ** Dereferences v as TB*, casts it to TA, returns it. */ #define LOAD_DEF(TA, TB) \ static TA \ _nrrdLoad##TA##TB(TB *v) { \ return (TA)(*v); \ } #define LOAD_LIST(TA, TB) \ (TA (*)(const void *))_nrrdLoad##TA##TB, MAP(LOAD_DEF, UI) MAP(LOAD_DEF, JN) MAP(LOAD_DEF, FL) MAP(LOAD_DEF, DB) unsigned int (* nrrdUILoad[NRRD_TYPE_MAX+1])(const void*) = { NULL, MAP(LOAD_LIST, UI) NULL }; int (* nrrdILoad[NRRD_TYPE_MAX+1])(const void*) = { NULL, MAP(LOAD_LIST, JN) NULL }; float (* nrrdFLoad[NRRD_TYPE_MAX+1])(const void*) = { NULL, MAP(LOAD_LIST, FL) NULL }; double (* nrrdDLoad[NRRD_TYPE_MAX+1])(const void*) = { NULL, MAP(LOAD_LIST, DB) NULL }; /* ** _nrrdStore( *v, j) ** ** Takes a TA j, and stores it in *v, thereby implicitly casting it to TB. ** Returns the result of the assignment, which may not be the same as ** the value that was passed in. */ #define STORE_DEF(TA, TB) \ static TA \ _nrrdStore##TA##TB(TB *v, TA j) { \ return (TA)(*v = (TB)j); \ } #define STORE_LIST(TA, TB) \ (TA (*)(void *, TA))_nrrdStore##TA##TB, MAP(STORE_DEF, UI) MAP(STORE_DEF, JN) MAP(STORE_DEF, FL) MAP(STORE_DEF, DB) unsigned int (* nrrdUIStore[NRRD_TYPE_MAX+1])(void *, unsigned int) = { NULL, MAP(STORE_LIST, UI) NULL }; int (* nrrdIStore[NRRD_TYPE_MAX+1])(void *, int) = { NULL, MAP(STORE_LIST, JN) NULL }; float (* nrrdFStore[NRRD_TYPE_MAX+1])(void *, float) = { NULL, MAP(STORE_LIST, FL) NULL }; double (* nrrdDStore[NRRD_TYPE_MAX+1])(void *, double) = { NULL, MAP(STORE_LIST, DB) NULL }; /* ** _nrrdLookup( *v, size_t I) ** ** Looks up element I of TB array v, and returns it cast to a TA. */ #define LOOKUP_DEF(TA, TB) \ static TA \ _nrrdLookup##TA##TB(TB *v, size_t I) { \ return (TA)v[I]; \ } #define LOOKUP_LIST(TA, TB) \ (TA (*)(const void*, size_t))_nrrdLookup##TA##TB, MAP(LOOKUP_DEF, UI) MAP(LOOKUP_DEF, JN) MAP(LOOKUP_DEF, FL) MAP(LOOKUP_DEF, DB) unsigned int (* nrrdUILookup[NRRD_TYPE_MAX+1])(const void *, size_t) = { NULL, MAP(LOOKUP_LIST, UI) NULL }; int (* nrrdILookup[NRRD_TYPE_MAX+1])(const void *, size_t) = { NULL, MAP(LOOKUP_LIST, JN) NULL }; float (* nrrdFLookup[NRRD_TYPE_MAX+1])(const void *, size_t) = { NULL, MAP(LOOKUP_LIST, FL) NULL }; double (* nrrdDLookup[NRRD_TYPE_MAX+1])(const void *, size_t) = { NULL, MAP(LOOKUP_LIST, DB) NULL }; /* ** _nrrdInsert( *v, size_t I, j) ** ** Given TA j, stores it in v[i] (implicitly casting to TB). ** Returns the result of the assignment, which may not be the same as ** the value that was passed in. */ #define INSERT_DEF(TA, TB) \ static TA \ _nrrdInsert##TA##TB(TB *v, size_t I, TA j) { \ return (TA)(v[I] = (TB)j); \ } #define INSERT_LIST(TA, TB) \ (TA (*)(void*, size_t, TA))_nrrdInsert##TA##TB, MAP(INSERT_DEF, UI) MAP(INSERT_DEF, JN) MAP(INSERT_DEF, FL) MAP(INSERT_DEF, DB) unsigned int (* nrrdUIInsert[NRRD_TYPE_MAX+1])(void *, size_t, unsigned int) = { NULL, MAP(INSERT_LIST, UI) NULL }; int (* nrrdIInsert[NRRD_TYPE_MAX+1])(void *, size_t, int) = { NULL, MAP(INSERT_LIST, JN) NULL }; float (* nrrdFInsert[NRRD_TYPE_MAX+1])(void *, size_t, float) = { NULL, MAP(INSERT_LIST, FL) NULL }; double (* nrrdDInsert[NRRD_TYPE_MAX+1])(void *, size_t, double) = { NULL, MAP(INSERT_LIST, DB) NULL }; /* ******** nrrdSprint ** ** Dereferences pointer v and sprintf()s that value into given string s, ** returns the result of sprintf() ** ** There is obviously no provision for ensuring that the sprint'ing ** doesn't overflow the buffer, which is unfortunate... */ static int _nrrdSprintCH(char *s, const CH *v) { return sprintf(s, "%d", *v); } static int _nrrdSprintUC(char *s, const UC *v) { return sprintf(s, "%u", *v); } static int _nrrdSprintSH(char *s, const SH *v) { return sprintf(s, "%d", *v); } static int _nrrdSprintUS(char *s, const US *v) { return sprintf(s, "%u", *v); } static int _nrrdSprintIN(char *s, const JN *v) { return sprintf(s, "%d", *v); } static int _nrrdSprintUI(char *s, const UI *v) { return sprintf(s, "%u", *v); } static int _nrrdSprintLL(char *s, const LL *v) { return sprintf(s, AIR_LLONG_FMT, *v); } static int _nrrdSprintUL(char *s, const UL *v) { return sprintf(s, AIR_ULLONG_FMT, *v); } /* HEY: sizeof(float) and sizeof(double) assumed here, since we're basing "8" and "17" on 6 == FLT_DIG and 15 == DBL_DIG, which are digits of precision for floats and doubles, respectively */ static int _nrrdSprintFL(char *s, const FL *v) { return airSinglePrintf(NULL, s, "%.8g", (double)(*v)); } static int _nrrdSprintDB(char *s, const DB *v) { return airSinglePrintf(NULL, s, "%.17g", *v); } int (* nrrdSprint[NRRD_TYPE_MAX+1])(char *, const void *) = { NULL, (int (*)(char *, const void *))_nrrdSprintCH, (int (*)(char *, const void *))_nrrdSprintUC, (int (*)(char *, const void *))_nrrdSprintSH, (int (*)(char *, const void *))_nrrdSprintUS, (int (*)(char *, const void *))_nrrdSprintIN, (int (*)(char *, const void *))_nrrdSprintUI, (int (*)(char *, const void *))_nrrdSprintLL, (int (*)(char *, const void *))_nrrdSprintUL, (int (*)(char *, const void *))_nrrdSprintFL, (int (*)(char *, const void *))_nrrdSprintDB, NULL}; cmtk-3.3.1/Utilities/NrrdIO/array.c000066400000000000000000000171051276303427400170620ustar00rootroot00000000000000/* NrrdIO: stand-alone code for basic nrrd functionality Copyright (C) 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. */ #include "NrrdIO.h" static void _airLenSet(airArray *a, unsigned int len) { a->len = len; /* printf(" HEY: len = %d\n", a->len); */ if (a->lenP) { *(a->lenP) = len; /* printf(" HEY: *(a->lenP) = *(%lu) = %d\n", (unsigned long)a->lenP, *(a->lenP)); */ } } static void _airSetData(airArray *a, void *data) { a->data = data; if (a->dataP) { *(a->dataP) = data; } } /* ******** airArrayNew() ** ** creates a new airArray struct and returns a pointer to it. ** dataP is a pointer to the user's data pointer ** lenP is a pointer to the user's array length variable (optional) ** unit is the size (in bytes) of one element in the array ** incr is the number of units by which the array will grow or shrink ** ** returns NULL on error, or the new airArray pointer if okay ** errors: bogus arguments, or couldn't alloc airArray struct ** ** --> The user CAN NOT change the pointer variable (of which *dataP ** is the address) after this is called, or else everything will ** get all bolloxed up. The same goes for the array length ** variable, if its address is passed- though in that case the ** correct value will over-write any other. */ airArray * airArrayNew(void **dataP, unsigned int *lenP, size_t unit, unsigned int incr) { airArray *a; if (unit<=0 || incr<=0) { return NULL; } a = AIR_CALLOC(1, airArray); if (!a) { return NULL; } a->dataP = dataP; _airSetData(a, NULL); a->lenP = lenP; _airLenSet(a, 0); a->incr = incr; a->unit = unit; a->noReallocWhenSmaller = AIR_FALSE; a->allocCB = NULL; a->freeCB = NULL; a->initCB = NULL; a->doneCB = NULL; return a; } /* ******** airArrayStructCB() ** ** set callbacks to maintain array of structs */ void airArrayStructCB(airArray *a, void (*initCB)(void *), void (*doneCB)(void *)) { if (a) { a->initCB = initCB; a->doneCB = doneCB; a->allocCB = NULL; a->freeCB = NULL; } } /* ******** airArrayPointerCB() ** ** set callbacks to maintain array of pointers */ void airArrayPointerCB(airArray *a, void *(*allocCB)(void), void *(*freeCB)(void *)) { if (a) { a->initCB = NULL; a->doneCB = NULL; a->allocCB = allocCB; a->freeCB = freeCB; } } /* ******** airArrayLenSet() ** ** Set the length of the array, allocating or freeing as needed ** ** returns 1 on error, otherwise 0 if okay ** possible errors: bogus arguments, or couldn't allocate new memory segment ** ** In case we can't allocate the new space, the old space is left untouched, ** however if the new length is smaller, the free/done callbacks will ** have been called on invalidated elements ** ** NB: this used to have a "boolean" return to indicate allocation ** error, but almost nothing in Teem actually did the error checking. ** Now conscientious users can look at NULL-ity of a->data to detect ** such an error. */ void airArrayLenSet(airArray *a, unsigned int newlen) { /* char me[]="airArrayLenSet"; */ unsigned int ii, newsize; void *addr, *newdata; if (!a) { /* user is a moron, what can you do */ return; } if (newlen == a->len) { /* nothing to do */ return; } /* call freeCB/doneCB on all the elements which are going bye-bye */ /* Wed Sep 12 14:40:45 EDT 2007: the order in which these called is now ascending, instead of descending (as was the way before) */ if (newlen < a->len && (a->freeCB || a->doneCB)) { for (ii=newlen; iilen; ii++) { addr = (char*)(a->data) + ii*a->unit; if (a->freeCB) { (a->freeCB)(*((void**)addr)); } else { (a->doneCB)(addr); } } } newsize = newlen ? (newlen-1)/a->incr + 1 : 0; if (newsize != a->size) { /* we have to change the size of the array */ if (newsize) { /* array should be bigger or smaller, but not zero-length */ if (newsize > a->size || (newsize < a->size && !(a->noReallocWhenSmaller)) ) { newdata = calloc(newsize*a->incr, a->unit); if (!newdata) { free(a->data); _airSetData(a, NULL); return; } memcpy(newdata, a->data, AIR_MIN(a->len*a->unit, newsize*a->incr*a->unit)); free(a->data); _airSetData(a, newdata); a->size = newsize; } } else { /* array should be zero-length */ free(a->data); _airSetData(a, NULL); a->size = newsize; } } /* else new size is still within current allocated length, and neither "size" nor "data" need to change */ /* call allocCB/initCB on newly created elements */ if (newlen > a->len && (a->allocCB || a->initCB)) { for (ii=a->len; iidata) + ii*a->unit; if (a->allocCB) { *((void**)addr) = (a->allocCB)(); } else { (a->initCB)(addr); } } } _airLenSet(a, newlen); return; } /* ******** airArrayLenIncr() ** ** Like airArrayLenSet, but works with an increment instead of an ** absolute length. Return value is different: ** got NULL: return 0 ** allocation error: return 0, and a->data set to NULL ** no error, delta > 0: return index of 1st element in newly allocated ** segment (a->len before length was increased) ** no error, delta <= 0: return 0, and a->data unchanged ** ** HEY: it is apparently not clear how to do error checking (aside from ** looking at a->data) when there was NO data previously allocated, and the ** first index of the newly allocated data is zero. */ unsigned int airArrayLenIncr(airArray *a, int delta) { /* char me[]="airArrayLenIncr"; */ unsigned int oldlen, ret, negdel; if (!a) { return 0; } negdel = (delta < 0 ? AIR_UINT(-delta) : 0); if (delta < 0 && negdel > a->len) { /* error: asked for newlength to be negative */ airArrayLenSet(a, 0); return 0; } oldlen = a->len; airArrayLenSet(a, (delta >= 0 ? oldlen + AIR_UINT(delta) : oldlen - negdel)); if (!a->data) { /* allocation error */ ret = 0; } else { ret = (delta <= 0 ? 0 : oldlen); } return ret; } /* ******** airArrayNuke() ** ** free both the memory pointed to by the struct and the struct itself */ airArray * airArrayNuke(airArray *a) { if (a) { airArrayLenSet(a, 0); free(a); } return NULL; } /* ******** airArrayNix() ** ** frees just the struct, leaving the memory it points to untouched */ airArray * airArrayNix(airArray *a) { if (a) { free(a); } return NULL; } cmtk-3.3.1/Utilities/NrrdIO/arraysNrrd.c000066400000000000000000000244241276303427400200750ustar00rootroot00000000000000/* NrrdIO: stand-alone code for basic nrrd functionality Copyright (C) 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. */ #include "NrrdIO.h" #include "privateNrrd.h" /* learned: /usr/bin/c++ on mac (at least) won't actually put a const int blah[] array in an object file if it hasn't been declared as "extern" */ const char nrrdTypePrintfStr[NRRD_TYPE_MAX+1][AIR_STRLEN_SMALL] = { "%*d", /* nrrdTypeUnknown: what else? the effect will be "skip" for sscanf, and "minimum precision" for printf */ "%d", /* nrrdTypeChar: char */ "%u", /* nrrdTypeUChar: unsigned char */ "%hd", /* nrrdTypeShort: short */ "%hu", /* nrrdTypeUShort: unsigned short */ "%d", /* nrrdTypeInt: int */ "%u", /* nrrdTypeUInt: unsigned int */ AIR_LLONG_FMT, /* nrrdTypeLLong: long long */ AIR_ULLONG_FMT, /* nrrdTypeULLong: unsigned long long */ "%f", /* nrrdTypeFloat: float */ "%lf", /* nrrdTypeDouble: double */ "%*d" /* nrrdTypeBlock: what else? */ }; /* ** the setting of NRRD_TYPE_BIGGEST has to be in accordance with this */ const size_t nrrdTypeSize[NRRD_TYPE_MAX+1] = { 0, /* nrrdTypeUnknown: unknown */ 1, /* nrrdTypeChar: char */ 1, /* nrrdTypeUChar: unsigned char */ 2, /* nrrdTypeShort: short */ 2, /* nrrdTypeUShort: unsigned short */ 4, /* nrrdTypeInt: int */ 4, /* nrrdTypeUInt: unsigned int */ 8, /* nrrdTypeLLong: long long */ 8, /* nrrdTypeULLong: unsigned long long */ 4, /* nrrdTypeFloat: float */ 8, /* nrrdTypeDouble: double */ 0 /* nrrdTypeBlock: effectively unknown; user has to set explicitly */ }; const int nrrdTypeIsIntegral[NRRD_TYPE_MAX+1] = { 0, /* nrrdTypeUnknown: unknown */ 1, /* nrrdTypeChar: char */ 1, /* nrrdTypeUChar: unsigned char */ 1, /* nrrdTypeShort: short */ 1, /* nrrdTypeUShort: unsigned short */ 1, /* nrrdTypeInt: int */ 1, /* nrrdTypeUInt: unsigned int */ 1, /* nrrdTypeLLong: long long */ 1, /* nrrdTypeULLong: unsigned long long */ 0, /* nrrdTypeFloat: float */ 0, /* nrrdTypeDouble: double */ 1 /* nrrdTypeBlock: for some reason we pretend that blocks are integers */ }; const int nrrdTypeIsUnsigned[NRRD_TYPE_MAX+1] = { 0, /* nrrdTypeUnknown: unknown */ 0, /* nrrdTypeChar: char */ 1, /* nrrdTypeUChar: unsigned char */ 0, /* nrrdTypeShort: short */ 1, /* nrrdTypeUShort: unsigned short */ 0, /* nrrdTypeInt: int */ 1, /* nrrdTypeUInt: unsigned int */ 0, /* nrrdTypeLLong: long long */ 1, /* nrrdTypeULLong: unsigned long long */ 0, /* nrrdTypeFloat: float */ 0, /* nrrdTypeDouble: double */ 0 /* nrrdTypeBlock: for some reason we pretend that blocks are signed */ }; /* ******** nrrdTypeMin[] ******** nrrdTypeMax[] ** ** only intended for small (<= 32 bits) integral types, ** so that we know how to "unquantize" integral values. ** A 64-bit double can correctly store the 32-bit integral ** mins and maxs, but gets the last few places wrong in the ** 64-bit mins and max. */ const double nrrdTypeMin[NRRD_TYPE_MAX+1] = { 0, /* nrrdTypeUnknown: unknown */ SCHAR_MIN, /* nrrdTypeChar: char */ 0, /* nrrdTypeUChar: unsigned char */ SHRT_MIN, /* nrrdTypeShort: short */ 0, /* nrrdTypeUShort: unsigned short */ INT_MIN, /* nrrdTypeInt: int */ 0, /* nrrdTypeUInt: unsigned int */ (double)NRRD_LLONG_MIN, /* nrrdTypeLLong: long long */ 0, /* nrrdTypeULLong: unsigned long long */ 0, /* nrrdTypeFloat: float */ 0, /* nrrdTypeDouble: double */ 0 /* nrrdTypeBlock: punt */ }, nrrdTypeMax[NRRD_TYPE_MAX+1] = { 0, /* nrrdTypeUnknown: unknown */ SCHAR_MAX, /* nrrdTypeChar: char */ UCHAR_MAX, /* nrrdTypeUChar: unsigned char */ SHRT_MAX, /* nrrdTypeShort: short */ USHRT_MAX, /* nrrdTypeUShort: unsigned short */ INT_MAX, /* nrrdTypeInt: int */ UINT_MAX, /* nrrdTypeUInt: unsigned int */ (double)NRRD_LLONG_MAX, /* nrrdTypeLLong: long long */ (double)NRRD_ULLONG_MAX, /* nrrdTypeULLong: unsigned long long */ 0, /* nrrdTypeFloat: float */ 0, /* nrrdTypeDouble: double */ 0 /* nrrdTypeBlock: punt */ }; /* ** _nrrdFieldValidInImage[] ** ** these fields are valid embedded in PNM and PNG comments ** This does NOT include the fields who's values are constrained ** by the image format (and in the case of PNM, magic) itself. */ const int _nrrdFieldValidInImage[NRRD_FIELD_MAX+1] = { 0, /* nrrdField_unknown */ 1, /* nrrdField_comment */ 1, /* nrrdField_content */ 0, /* nrrdField_number */ 0, /* nrrdField_type */ 0, /* nrrdField_block_size */ 0, /* nrrdField_dimension */ 1, /* nrrdField_space */ 1, /* nrrdField_space_dimension */ 0, /* nrrdField_sizes */ 1, /* nrrdField_spacings */ 1, /* nrrdField_thicknesses */ 1, /* nrrdField_axis_mins */ 1, /* nrrdField_axis_maxs */ 1, /* nrrdField_space_directions */ 1, /* nrrdField_centers */ 1, /* nrrdField_kinds */ 1, /* nrrdField_labels */ 1, /* nrrdField_units */ 0, /* nrrdField_min */ 0, /* nrrdField_max */ 1, /* nrrdField_old_min */ 1, /* nrrdField_old_max */ 0, /* nrrdField_endian */ 0, /* nrrdField_encoding */ 0, /* nrrdField_line_skip */ 0, /* nrrdField_byte_skip */ 1, /* nrrdField_keyvalue */ 1, /* nrrdField_sample_units */ 1, /* nrrdField_space_units */ 1, /* nrrdField_space_origin */ 1, /* nrrdField_measurement_frame */ 0 /* nrrdField_data_file */ }; /* ** _nrrdFieldOnePerAxis ** ** whether or not you need one value per axis, like labels and spacings */ const int _nrrdFieldOnePerAxis[NRRD_FIELD_MAX+1] = { 0, /* nrrdField_unknown */ 0, /* nrrdField_comment */ 0, /* nrrdField_content */ 0, /* nrrdField_number */ 0, /* nrrdField_type */ 0, /* nrrdField_block_size */ 0, /* nrrdField_dimension */ 0, /* nrrdField_space */ 0, /* nrrdField_space_dimension */ 1, /* nrrdField_sizes */ 1, /* nrrdField_spacings */ 1, /* nrrdField_thicknesses */ 1, /* nrrdField_axis_mins */ 1, /* nrrdField_axis_maxs */ 1, /* nrrdField_space_directions */ 1, /* nrrdField_centers */ 1, /* nrrdField_kinds */ 1, /* nrrdField_labels */ 1, /* nrrdField_units */ 0, /* nrrdField_min */ 0, /* nrrdField_max */ 0, /* nrrdField_old_min */ 0, /* nrrdField_old_max */ 0, /* nrrdField_endian */ 0, /* nrrdField_encoding */ 0, /* nrrdField_line_skip */ 0, /* nrrdField_byte_skip */ 0, /* nrrdField_keyvalue */ 0, /* nrrdField_sample_units */ 0, /* nrrdField_space_units */ 0, /* nrrdField_space_origin */ 0, /* nrrdField_measurement_frame */ 0 /* nrrdField_data_file */ }; /* ** _nrrdFieldValidInText[] ** ** these fields are valid embedded in plain text comments ** This does NOT include the fields who's values are constrained ** the plain text format itself. */ const int _nrrdFieldValidInText[NRRD_FIELD_MAX+1] = { 0, /* nrrdField_unknown */ 1, /* nrrdField_comment */ 1, /* nrrdField_content */ 0, /* nrrdField_number */ 0, /* nrrdField_type: decided AGAINST plain text holding general type (but I forget why ...) */ 0, /* nrrdField_block_size */ 1, /* nrrdField_dimension: but can only be 1 or 2 */ 0, /* nrrdField_space */ 0, /* nrrdField_space_dimension */ 0, /* nrrdField_sizes */ 1, /* nrrdField_spacings */ 1, /* nrrdField_thicknesses */ 1, /* nrrdField_axis_mins */ 1, /* nrrdField_axis_maxs */ 1, /* nrrdField_space_directions */ 1, /* nrrdField_centers */ 1, /* nrrdField_kinds */ 1, /* nrrdField_labels */ 1, /* nrrdField_units */ 0, /* nrrdField_min */ 0, /* nrrdField_max */ 1, /* nrrdField_old_min */ 1, /* nrrdField_old_max */ 0, /* nrrdField_endian */ 0, /* nrrdField_encoding */ 0, /* nrrdField_line_skip */ 0, /* nrrdField_byte_skip */ 0, /* nrrdField_keyvalue */ 0, /* nrrdField_sample_units */ 0, /* nrrdField_space_units */ 0, /* nrrdField_space_origin */ 0, /* nrrdField_measurement_frame */ 0 /* nrrdField_data_file */ }; /* ** _nrrdFieldRequired[] ** ** regardless of whether its a nrrd, PNM, or plain text, these things ** need to be conveyed, either explicity or implicitly */ const int _nrrdFieldRequired[NRRD_FIELD_MAX+1] = { 0, /* "Ernesto \"Che\" Guevara" */ 0, /* "#" */ 0, /* nrrdField_content */ 0, /* nrrdField_number */ 1, /* nrrdField_type */ 0, /* nrrdField_block size */ 1, /* nrrdField_dimension */ 0, /* nrrdField_space */ 0, /* nrrdField_space_dimension */ 1, /* nrrdField_sizes */ 0, /* nrrdField_spacings */ 0, /* nrrdField_thicknesses */ 0, /* nrrdField_axis mins */ 0, /* nrrdField_axis maxs */ 0, /* nrrdField_space_directions */ 0, /* nrrdField_centers */ 0, /* nrrdField_kinds */ 0, /* nrrdField_labels */ 0, /* nrrdField_units */ 0, /* nrrdField_min */ 0, /* nrrdField_max */ 0, /* nrrdField_old min */ 0, /* nrrdField_old max */ 0, /* nrrdField_endian */ 1, /* nrrdField_encoding */ 0, /* nrrdField_line_skip */ 0, /* nrrdField_byte_skip */ 0, /* nrrdField_keyvalue */ 0, /* nrrdField_sample_units */ 0, /* nrrdField_space_units */ 0, /* nrrdField_space_origin */ 0, /* nrrdField_measurement_frame */ 0 /* nrrdField_data file */ }; cmtk-3.3.1/Utilities/NrrdIO/axis.c000066400000000000000000000767571276303427400167320ustar00rootroot00000000000000/* NrrdIO: stand-alone code for basic nrrd functionality Copyright (C) 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. */ #include "NrrdIO.h" #include "privateNrrd.h" /* ------------------------------------------------------------ */ void _nrrdAxisInfoInit(NrrdAxisInfo *axis) { int dd; if (axis) { axis->size = 0; axis->spacing = axis->thickness = AIR_NAN; axis->min = axis->max = AIR_NAN; for (dd=0; ddspaceDirection[dd] = AIR_NAN; } axis->center = nrrdCenterUnknown; axis->kind = nrrdKindUnknown; axis->label = (char *)airFree(axis->label); axis->units = (char *)airFree(axis->units); } } void _nrrdAxisInfoNewInit(NrrdAxisInfo *axis) { if (axis) { axis->label = NULL; axis->units = NULL; _nrrdAxisInfoInit(axis); } } /* ------------------------------------------------------------ */ /* ******** nrrdKindIsDomain ** ** returns non-zero for kinds (from nrrdKind* enum) that are domain ** axes, or independent variable axes, or resample-able axes, all ** different ways of describing the same thing */ int nrrdKindIsDomain(int kind) { return (nrrdKindDomain == kind || nrrdKindSpace == kind || nrrdKindTime == kind); } /* ******** nrrdKindSize ** ** returns suggested size (length) of an axis with the given kind, or, ** 0 if either (1) there is no suggested size because the axis is the ** kind of an independent or domain variable or (2) the kind is invalid */ unsigned int nrrdKindSize(int kind) { static const char me[]="nrrdKindSize"; unsigned int ret; if (!( AIR_IN_OP(nrrdKindUnknown, kind, nrrdKindLast) )) { /* they gave us invalid or unknown kind */ return 0; } switch (kind) { case nrrdKindDomain: case nrrdKindSpace: case nrrdKindTime: case nrrdKindList: case nrrdKindPoint: case nrrdKindVector: case nrrdKindCovariantVector: case nrrdKindNormal: ret = 0; break; case nrrdKindStub: case nrrdKindScalar: ret = 1; break; case nrrdKindComplex: case nrrdKind2Vector: ret = 2; break; case nrrdKind3Color: case nrrdKindRGBColor: case nrrdKindHSVColor: case nrrdKindXYZColor: ret = 3; break; case nrrdKind4Color: case nrrdKindRGBAColor: ret = 4; break; case nrrdKind3Vector: case nrrdKind3Normal: ret = 3; break; case nrrdKind4Vector: case nrrdKindQuaternion: ret = 4; break; case nrrdKind2DSymMatrix: ret = 3; break; case nrrdKind2DMaskedSymMatrix: ret = 4; break; case nrrdKind2DMatrix: ret = 4; break; case nrrdKind2DMaskedMatrix: ret = 5; break; case nrrdKind3DSymMatrix: ret = 6; break; case nrrdKind3DMaskedSymMatrix: ret = 7; break; case nrrdKind3DMatrix: ret = 9; break; case nrrdKind3DMaskedMatrix: ret = 10; break; default: fprintf(stderr, "%s: PANIC: nrrdKind %d not implemented!\n", me, kind); ret = UINT_MAX; } return ret; } /* ** _nrrdKindAltered: ** ** implements logic for how kind should be updated when samples ** along the axis are altered */ int _nrrdKindAltered(int kindIn, int resampling) { int kindOut; if (nrrdStateKindNoop) { kindOut = nrrdKindUnknown; /* HEY: setting the kindOut to unknown is arguably not a no-op. It is more like pointedly and stubbornly simplistic. So maybe nrrdStateKindNoop could be renamed .. */ } else { if (nrrdKindIsDomain(kindIn) || (0 == nrrdKindSize(kindIn) && !resampling)) { kindOut = kindIn; } else { kindOut = nrrdKindUnknown; } } return kindOut; } /* ** _nrrdAxisInfoCopy ** ** HEY: we have a void return even though this function potentially ** involves calling airStrdup!! */ void _nrrdAxisInfoCopy(NrrdAxisInfo *dest, const NrrdAxisInfo *src, int bitflag) { int ii; if (!(NRRD_AXIS_INFO_SIZE_BIT & bitflag)) { dest->size = src->size; } if (!(NRRD_AXIS_INFO_SPACING_BIT & bitflag)) { dest->spacing = src->spacing; } if (!(NRRD_AXIS_INFO_THICKNESS_BIT & bitflag)) { dest->thickness = src->thickness; } if (!(NRRD_AXIS_INFO_MIN_BIT & bitflag)) { dest->min = src->min; } if (!(NRRD_AXIS_INFO_MAX_BIT & bitflag)) { dest->max = src->max; } if (!(NRRD_AXIS_INFO_SPACEDIRECTION_BIT & bitflag)) { for (ii=0; iispaceDirection[ii] = src->spaceDirection[ii]; } } if (!(NRRD_AXIS_INFO_CENTER_BIT & bitflag)) { dest->center = src->center; } if (!(NRRD_AXIS_INFO_KIND_BIT & bitflag)) { dest->kind = src->kind; } if (!(NRRD_AXIS_INFO_LABEL_BIT & bitflag)) { if (dest->label != src->label) { dest->label = (char *)airFree(dest->label); dest->label = (char *)airStrdup(src->label); } } if (!(NRRD_AXIS_INFO_UNITS_BIT & bitflag)) { if (dest->units != src->units) { dest->units = (char *)airFree(dest->units); dest->units = (char *)airStrdup(src->units); } } return; } /* ******** nrrdAxisInfoCopy() ** ** For copying all the per-axis peripheral information. Takes a ** permutation "map"; map[d] tells from which axis in input should the ** output axis d copy its information. The length of this permutation ** array is nout->dim. If map is NULL, the identity permutation is ** assumed. If map[i]==-1 for any i in [0,dim-1], then nothing is ** copied into axis i of output. The "bitflag" field controls which ** per-axis fields will NOT be copied; if bitflag==0, then all fields ** are copied. The value of bitflag should be |'s of NRRD_AXIS_INFO_* ** defines. ** ** Decided to Not use Biff, since many times map will be NULL, in ** which case the only error is getting a NULL nrrd, or an invalid map ** permutation, which will probably be unlikely given the contexts in ** which this is called. For the paranoid, the integer return value ** indicates error. ** ** Sun Feb 27 21:12:57 EST 2005: decided to allow nout==nin, so now ** use a local array of NrrdAxisInfo as buffer. */ int nrrdAxisInfoCopy(Nrrd *nout, const Nrrd *nin, const int *axmap, int bitflag) { NrrdAxisInfo axisBuffer[NRRD_DIM_MAX]; const NrrdAxisInfo *axis; unsigned int from, axi; if (!(nout && nin)) { return 1; } if (axmap) { for (axi=0; axidim; axi++) { if (-1 == axmap[axi]) { continue; } if (!AIR_IN_CL(0, axmap[axi], (int)nin->dim-1)) { return 3; } } } if (nout == nin) { /* copy axis info to local buffer */ for (axi=0; axidim; axi++) { _nrrdAxisInfoNewInit(axisBuffer + axi); _nrrdAxisInfoCopy(axisBuffer + axi, nin->axis + axi, bitflag); } axis = axisBuffer; } else { axis = nin->axis; } for (axi=0; axidim; axi++) { if (axmap && -1 == axmap[axi]) { /* for this axis, we don't touch a thing */ continue; } from = axmap ? (unsigned int)axmap[axi] : axi; _nrrdAxisInfoCopy(nout->axis + axi, axis + from, bitflag); } if (nout == nin) { /* free dynamically allocated stuff */ for (axi=0; axidim; axi++) { _nrrdAxisInfoInit(axisBuffer + axi); } } return 0; } /* ******** nrrdAxisInfoSet_nva() ** ** Simple means of setting fields of the axis array in the nrrd. ** ** type to pass for third argument: ** nrrdAxisInfoSize: size_t* ** nrrdAxisInfoSpacing: double* ** nrrdAxisInfoThickness: double* ** nrrdAxisInfoMin: double* ** nrrdAxisInfoMax: double* ** nrrdAxisInfoSpaceDirection: double (*var)[NRRD_SPACE_DIM_MAX] ** nrrdAxisInfoCenter: int* ** nrrdAxisInfoKind: int* ** nrrdAxisInfoLabel: char** ** nrrdAxisInfoUnits: char** */ void nrrdAxisInfoSet_nva(Nrrd *nrrd, int axInfo, const void *_info) { _nrrdAxisInfoSetPtrs info; int exists; unsigned int ai, si, minsi; if (!( nrrd && AIR_IN_CL(1, nrrd->dim, NRRD_DIM_MAX) && AIR_IN_OP(nrrdAxisInfoUnknown, axInfo, nrrdAxisInfoLast) && _info )) { return; } info.P = _info; for (ai=0; aidim; ai++) { switch (axInfo) { case nrrdAxisInfoSize: nrrd->axis[ai].size = info.ST[ai]; break; case nrrdAxisInfoSpacing: nrrd->axis[ai].spacing = info.D[ai]; break; case nrrdAxisInfoThickness: nrrd->axis[ai].thickness = info.D[ai]; break; case nrrdAxisInfoMin: nrrd->axis[ai].min = info.D[ai]; break; case nrrdAxisInfoMax: nrrd->axis[ai].max = info.D[ai]; break; case nrrdAxisInfoSpaceDirection: /* we won't allow setting an invalid direction */ exists = AIR_EXISTS(info.V[ai][0]); minsi = nrrd->spaceDim; for (si=0; sispaceDim; si++) { nrrd->axis[ai].spaceDirection[si] = info.V[ai][si]; if (exists ^ AIR_EXISTS(info.V[ai][si])) { minsi = 0; break; } } for (si=minsi; siaxis[ai].spaceDirection[si] = AIR_NAN; } break; case nrrdAxisInfoCenter: nrrd->axis[ai].center = info.I[ai]; break; case nrrdAxisInfoKind: nrrd->axis[ai].kind = info.I[ai]; break; case nrrdAxisInfoLabel: nrrd->axis[ai].label = (char *)airFree(nrrd->axis[ai].label); nrrd->axis[ai].label = (char *)airStrdup(info.CP[ai]); break; case nrrdAxisInfoUnits: nrrd->axis[ai].units = (char *)airFree(nrrd->axis[ai].units); nrrd->axis[ai].units = (char *)airStrdup(info.CP[ai]); break; } } if (nrrdAxisInfoSpaceDirection == axInfo) { for (ai=nrrd->dim; aiaxis[ai].spaceDirection[si] = AIR_NAN; } } } return; } /* ******** nrrdAxisInfoSet_va() ** ** var args front-end for nrrdAxisInfoSet_nva ** ** types to pass, one for each dimension: ** nrrdAxisInfoSize: size_t ** nrrdAxisInfoSpacing: double ** nrrdAxisInfoThickness: double ** nrrdAxisInfoMin: double ** nrrdAxisInfoMax: double ** nrrdAxisInfoSpaceDirection: double* ** nrrdAxisInfoCenter: int ** nrrdAxisInfoKind: int ** nrrdAxisInfoLabel: char* ** nrrdAxisInfoUnits: char* */ void nrrdAxisInfoSet_va(Nrrd *nrrd, int axInfo, ...) { NRRD_TYPE_BIGGEST *buffer[NRRD_DIM_MAX]; _nrrdAxisInfoSetPtrs info; unsigned int ai, si; va_list ap; double *dp, svec[NRRD_DIM_MAX][NRRD_SPACE_DIM_MAX]; if (!( nrrd && AIR_IN_CL(1, nrrd->dim, NRRD_DIM_MAX) && AIR_IN_OP(nrrdAxisInfoUnknown, axInfo, nrrdAxisInfoLast) )) { return; } info.P = buffer; va_start(ap, axInfo); for (ai=0; aidim; ai++) { switch (axInfo) { case nrrdAxisInfoSize: info.ST[ai] = va_arg(ap, size_t); /* printf("!%s: got int[%d] = %d\n", "nrrdAxisInfoSet", d, info.I[ai]); */ break; case nrrdAxisInfoSpaceDirection: dp = va_arg(ap, double*); /* punting on using info enum */ /* printf("!%s: got dp = %lu\n", "nrrdAxisInfoSet", (unsigned long)(dp)); */ for (si=0; sispaceDim; si++) { /* nrrd->axis[ai].spaceDirection[si] = dp[si]; */ svec[ai][si] = dp[si]; } for (si=nrrd->spaceDim; siaxis[ai].spaceDirection[si] = AIR_NAN; */ svec[ai][si] = dp[si]; } break; case nrrdAxisInfoCenter: case nrrdAxisInfoKind: info.I[ai] = va_arg(ap, int); /* printf("!%s: got int[%d] = %d\n", "nrrdAxisInfoSet", d, info.I[ai]); */ break; case nrrdAxisInfoSpacing: case nrrdAxisInfoThickness: case nrrdAxisInfoMin: case nrrdAxisInfoMax: info.D[ai] = va_arg(ap, double); /* printf("!%s: got double[%d] = %g\n", "nrrdAxisInfoSet", d, info.D[ai]); */ break; case nrrdAxisInfoLabel: /* we DO NOT do the airStrdup() here because this pointer value is just going to be handed to nrrdAxisInfoSet_nva(), which WILL do the airStrdup(); we're not violating the rules for axis labels */ info.CP[ai] = va_arg(ap, char *); /* printf("!%s: got char*[%d] = |%s|\n", "nrrdAxisInfoSet", d, info.CP[ai]); */ break; case nrrdAxisInfoUnits: /* see not above */ info.CP[ai] = va_arg(ap, char *); break; } } va_end(ap); if (nrrdAxisInfoSpaceDirection != axInfo) { /* now set the quantities which we've gotten from the var args */ nrrdAxisInfoSet_nva(nrrd, axInfo, info.P); } else { nrrdAxisInfoSet_nva(nrrd, axInfo, svec); } return; } /* ******** nrrdAxisInfoGet_nva() ** ** get any of the axis fields into an array ** ** Note that getting axes labels involves implicitly allocating space ** for them, due to the action of airStrdup(). The user is ** responsible for free()ing these strings when done with them. ** ** type to pass for third argument: ** nrrdAxisInfoSize: size_t* ** nrrdAxisInfoSpacing: double* ** nrrdAxisInfoThickness: double* ** nrrdAxisInfoMin: double* ** nrrdAxisInfoMax: double* ** nrrdAxisInfoSpaceDirection: double (*var)[NRRD_SPACE_DIM_MAX] ** nrrdAxisInfoCenter: int* ** nrrdAxisInfoKind: int* ** nrrdAxisInfoLabel: char** ** nrrdAxisInfoUnits: char** */ void nrrdAxisInfoGet_nva(const Nrrd *nrrd, int axInfo, void *_info) { _nrrdAxisInfoGetPtrs info; unsigned int ai, si; if (!( nrrd && AIR_IN_CL(1, nrrd->dim, NRRD_DIM_MAX) && AIR_IN_OP(nrrdAxisInfoUnknown, axInfo, nrrdAxisInfoLast) )) { return; } info.P = _info; for (ai=0; aidim; ai++) { switch (axInfo) { case nrrdAxisInfoSize: info.ST[ai] = nrrd->axis[ai].size; break; case nrrdAxisInfoSpacing: info.D[ai] = nrrd->axis[ai].spacing; break; case nrrdAxisInfoThickness: info.D[ai] = nrrd->axis[ai].thickness; break; case nrrdAxisInfoMin: info.D[ai] = nrrd->axis[ai].min; break; case nrrdAxisInfoMax: info.D[ai] = nrrd->axis[ai].max; break; case nrrdAxisInfoSpaceDirection: for (si=0; sispaceDim; si++) { info.V[ai][si] = nrrd->axis[ai].spaceDirection[si]; } for (si=nrrd->spaceDim; siaxis[ai].center; break; case nrrdAxisInfoKind: info.I[ai] = nrrd->axis[ai].kind; break; case nrrdAxisInfoLabel: /* note airStrdup()! */ info.CP[ai] = airStrdup(nrrd->axis[ai].label); break; case nrrdAxisInfoUnits: /* note airStrdup()! */ info.CP[ai] = airStrdup(nrrd->axis[ai].units); break; } } if (nrrdAxisInfoSpaceDirection == axInfo) { for (ai=nrrd->dim; aidim, NRRD_DIM_MAX) && AIR_IN_OP(nrrdAxisInfoUnknown, axInfo, nrrdAxisInfoLast) )) { return; } if (nrrdAxisInfoSpaceDirection != axInfo) { info.P = buffer; nrrdAxisInfoGet_nva(nrrd, axInfo, info.P); } else { nrrdAxisInfoGet_nva(nrrd, axInfo, svec); } va_start(ap, axInfo); for (ai=0; aidim; ai++) { ptr = va_arg(ap, void*); /* printf("!%s(%d): ptr = %lu\n", "nrrdAxisInfoGet", d, (unsigned long)ptr); */ switch (axInfo) { case nrrdAxisInfoSize: *((size_t*)ptr) = info.ST[ai]; break; case nrrdAxisInfoSpacing: case nrrdAxisInfoThickness: case nrrdAxisInfoMin: case nrrdAxisInfoMax: *((double*)ptr) = info.D[ai]; /* printf("!%s: got double[%d] = %lg\n", "nrrdAxisInfoGet", d, *((double*)ptr)); */ break; case nrrdAxisInfoSpaceDirection: for (si=0; sispaceDim; si++) { ((double*)ptr)[si] = svec[ai][si]; } for (si=nrrd->spaceDim; sidim-1 )) { return AIR_NAN; } center = _nrrdCenter(nrrd->axis[ax].center); min = nrrd->axis[ax].min; max = nrrd->axis[ax].max; size = nrrd->axis[ax].size; return NRRD_POS(center, min, max, size, idx); } /* ******** nrrdAxisInfoIdx() ** ** given a nrrd, an axis, and a (floating point) world space position, ** return the index implied the axis's min, max, and center. ** Does the opposite of nrrdAxisPos(). ** ** does not use biff */ double nrrdAxisInfoIdx(const Nrrd *nrrd, unsigned int ax, double pos) { int center; size_t size; double min, max; if (!( nrrd && ax <= nrrd->dim-1 )) { return AIR_NAN; } center = _nrrdCenter(nrrd->axis[ax].center); min = nrrd->axis[ax].min; max = nrrd->axis[ax].max; size = nrrd->axis[ax].size; return NRRD_IDX(center, min, max, size, pos); } /* ******** nrrdAxisInfoPosRange() ** ** given a nrrd, an axis, and two (floating point) index space positions, ** return the range of positions implied the axis's min, max, and center ** The opposite of nrrdAxisIdxRange() */ void nrrdAxisInfoPosRange(double *loP, double *hiP, const Nrrd *nrrd, unsigned int ax, double loIdx, double hiIdx) { int center, flip = 0; size_t size; double min, max, tmp; if (!( loP && hiP && nrrd && ax <= nrrd->dim-1 )) { *loP = *hiP = AIR_NAN; return; } center = _nrrdCenter(nrrd->axis[ax].center); min = nrrd->axis[ax].min; max = nrrd->axis[ax].max; size = nrrd->axis[ax].size; if (loIdx > hiIdx) { flip = 1; tmp = loIdx; loIdx = hiIdx; hiIdx = tmp; } if (nrrdCenterCell == center) { *loP = AIR_AFFINE(0, loIdx, size, min, max); *hiP = AIR_AFFINE(0, hiIdx+1, size, min, max); } else { *loP = AIR_AFFINE(0, loIdx, size-1, min, max); *hiP = AIR_AFFINE(0, hiIdx, size-1, min, max); } if (flip) { tmp = *loP; *loP = *hiP; *hiP = tmp; } return; } /* ******** nrrdAxisInfoIdxRange() ** ** given a nrrd, an axis, and two (floating point) world space positions, ** return the range of index space implied the axis's min, max, and center ** The opposite of nrrdAxisPosRange(). ** ** Actually- there are situations where sending an interval through ** nrrdAxisIdxRange -> nrrdAxisPosRange -> nrrdAxisIdxRange ** such as in cell centering, when the range of positions given does ** not even span one sample. Such as: ** axis->size = 4, axis->min = -4, axis->max = 4, loPos = 0, hiPos = 1 ** --> nrrdAxisIdxRange == (2, 1.5) --> nrrdAxisPosRange == (2, -1) ** The basic problem is that because of the 0.5 offset inherent in ** cell centering, there are situations where (in terms of the arguments ** to nrrdAxisIdxRange()) loPos < hiPos, but *loP > *hiP. */ void nrrdAxisInfoIdxRange(double *loP, double *hiP, const Nrrd *nrrd, unsigned int ax, double loPos, double hiPos) { int center, flip = 0; size_t size; double min, max, tmp; if (!( loP && hiP && nrrd && ax <= nrrd->dim-1 )) { *loP = *hiP = AIR_NAN; return; } center = _nrrdCenter(nrrd->axis[ax].center); min = nrrd->axis[ax].min; max = nrrd->axis[ax].max; size = nrrd->axis[ax].size; if (loPos > hiPos) { flip = 1; tmp = loPos; loPos = hiPos; hiPos = tmp; } if (nrrdCenterCell == center) { if (min < max) { *loP = AIR_AFFINE(min, loPos, max, 0, size); *hiP = AIR_AFFINE(min, hiPos, max, -1, size-1); } else { *loP = AIR_AFFINE(min, loPos, max, -1, size-1); *hiP = AIR_AFFINE(min, hiPos, max, 0, size); } } else { *loP = AIR_AFFINE(min, loPos, max, 0, size-1); *hiP = AIR_AFFINE(min, hiPos, max, 0, size-1); } if (flip) { tmp = *loP; *loP = *hiP; *hiP = tmp; } return; } void nrrdAxisInfoSpacingSet(Nrrd *nrrd, unsigned int ax) { int sign; double min, max, tmp; if (!( nrrd && ax <= nrrd->dim-1 )) { return; } min = nrrd->axis[ax].min; max = nrrd->axis[ax].max; if (!( AIR_EXISTS(min) && AIR_EXISTS(max) )) { /* there's no actual basis on which to set the spacing information, but we have to set it something, so here goes .. */ nrrd->axis[ax].spacing = nrrdDefaultSpacing; return; } if (min > max) { tmp = min; min = max; max = tmp; sign = -1; } else { sign = 1; } /* the skinny */ nrrd->axis[ax].spacing = NRRD_SPACING(_nrrdCenter(nrrd->axis[ax].center), min, max, nrrd->axis[ax].size); nrrd->axis[ax].spacing *= sign; return; } void nrrdAxisInfoMinMaxSet(Nrrd *nrrd, unsigned int ax, int defCenter) { int center; double spacing; if (!( nrrd && ax <= nrrd->dim-1 )) { return; } center = _nrrdCenter2(nrrd->axis[ax].center, defCenter); spacing = nrrd->axis[ax].spacing; if (!AIR_EXISTS(spacing)) spacing = nrrdDefaultSpacing; if (nrrdCenterCell == center) { nrrd->axis[ax].min = 0; nrrd->axis[ax].max = spacing*AIR_CAST(double, nrrd->axis[ax].size); } else { nrrd->axis[ax].min = 0; nrrd->axis[ax].max = spacing*AIR_CAST(double, nrrd->axis[ax].size - 1); } return; } /* ******** nrrdDomainAxesGet ** ** Based on the per-axis "kind" field, learns which are the domain ** (resample-able) axes of an image, in other words, the axes which ** correspond to independent variables. The return value is the ** number of domain axes, and that many values are set in the given ** axisIdx[] array ** ** NOTE: this takes a wild guess that an unset (nrrdKindUnknown) kind ** is a domain axis. */ unsigned int nrrdDomainAxesGet(const Nrrd *nrrd, unsigned int axisIdx[NRRD_DIM_MAX]) { unsigned int domAxi, axi; if (!( nrrd && axisIdx )) { return 0; } domAxi = 0; for (axi=0; axidim; axi++) { if (nrrdKindUnknown == nrrd->axis[axi].kind || nrrdKindIsDomain(nrrd->axis[axi].kind)) { axisIdx[domAxi++] = axi; } } return domAxi; } int _nrrdSpaceVecExists(const Nrrd *nrrd, unsigned int axi) { unsigned int sai; int ret; if (!( nrrd && axi < nrrd->dim && nrrd->spaceDim )) { ret = AIR_FALSE; } else { ret = AIR_TRUE; for (sai=0; saispaceDim; sai++) { ret &= AIR_EXISTS(nrrd->axis[axi].spaceDirection[sai]); } } return ret; } unsigned int nrrdSpatialAxesGet(const Nrrd *nrrd, unsigned int axisIdx[NRRD_DIM_MAX]) { unsigned int spcAxi, axi; if (!( nrrd && axisIdx && nrrd->spaceDim)) { return 0; } spcAxi = 0; for (axi=0; axidim; axi++) { if (_nrrdSpaceVecExists(nrrd, axi)) { axisIdx[spcAxi++] = axi; } } return spcAxi; } /* ******** nrrdRangeAxesGet ** ** Based on the per-axis "kind" field, learns which are the range ** (non-resample-able) axes of an image, in other words, the axes ** which correspond to dependent variables. The return value is the ** number of range axes; that number of values are set in the given ** axisIdx[] array ** ** Note: this really is as simple as returning the complement of the ** axis selected by nrrdDomainAxesGet() */ unsigned int nrrdRangeAxesGet(const Nrrd *nrrd, unsigned int axisIdx[NRRD_DIM_MAX]) { unsigned int domNum, domIdx[NRRD_DIM_MAX], rngAxi, axi, ii, isDom; if (!( nrrd && axisIdx )) { return 0; } domNum = nrrdDomainAxesGet(nrrd, domIdx); rngAxi = 0; for (axi=0; axidim; axi++) { isDom = AIR_FALSE; for (ii=0; iidim; axi++) { isSpc = AIR_FALSE; for (ii=0; iispacing, ** vector = all NaNs ** ** nrrdSpacingStatusScalarWithSpace There *is* a surrounding space, but the ** given axis does not live in that space, ** because it has no space direction. Caller ** may want to think about what's going on. ** *spacing = axis->spacing, ** vector = all NaNs ** ** nrrdSpacingStatusDirection There is a surrounding space, in which ** this axis has a direction V: ** *spacing = |V| (length of direction), ** vector = V/|V| (normalized direction) ** NOTE: it is still possible for both ** *spacing and vector to be all NaNs!! */ int nrrdSpacingCalculate(const Nrrd *nrrd, unsigned int ax, double *spacing, double vector[NRRD_SPACE_DIM_MAX]) { int ret; if (!( nrrd && spacing && vector && ax <= nrrd->dim-1 && !_nrrdCheck(nrrd, AIR_FALSE, AIR_FALSE) )) { /* there's a problem with the arguments. Note: the _nrrdCheck() call does not check on non-NULL-ity of nrrd->data */ ret = nrrdSpacingStatusUnknown; if (spacing) { *spacing = AIR_NAN; } if (vector) { nrrdSpaceVecSetNaN(vector); } } else { if (AIR_EXISTS(nrrd->axis[ax].spacing)) { if (nrrd->spaceDim > 0) { ret = nrrdSpacingStatusScalarWithSpace; } else { ret = nrrdSpacingStatusScalarNoSpace; } *spacing = nrrd->axis[ax].spacing; nrrdSpaceVecSetNaN(vector); } else { if (nrrd->spaceDim > 0 && _nrrdSpaceVecExists(nrrd, ax)) { ret = nrrdSpacingStatusDirection; *spacing = nrrdSpaceVecNorm(nrrd->spaceDim, nrrd->axis[ax].spaceDirection); nrrdSpaceVecScale(vector, 1.0/(*spacing), nrrd->axis[ax].spaceDirection); } else { ret = nrrdSpacingStatusNone; *spacing = AIR_NAN; nrrdSpaceVecSetNaN(vector); } } } return ret; } int nrrdOrientationReduce(Nrrd *nout, const Nrrd *nin, int setMinsFromOrigin) { static const char me[]="nrrdOrientationReduce"; unsigned int spatialAxisNum, spatialAxisIdx[NRRD_DIM_MAX], saxii; NrrdAxisInfo *axis; if (!(nout && nin)) { biffAddf(NRRD, "%s: got NULL spacing", me); return 1; } if (nout != nin) { if (nrrdCopy(nout, nin)) { biffAddf(NRRD, "%s: trouble doing initial copying", me); return 1; } } if (!nout->spaceDim) { /* we're done! */ return 0; } spatialAxisNum = nrrdSpatialAxesGet(nout, spatialAxisIdx); for (saxii=0; saxiiaxis + spatialAxisIdx[saxii]; axis->spacing = nrrdSpaceVecNorm(nout->spaceDim, axis->spaceDirection); if (setMinsFromOrigin) { axis->min = (saxii < nout->spaceDim ? nout->spaceOrigin[saxii] : AIR_NAN); } } nrrdSpaceSet(nout, nrrdSpaceUnknown); return 0; } cmtk-3.3.1/Utilities/NrrdIO/biffbiff.c000066400000000000000000000242111276303427400174750ustar00rootroot00000000000000/* NrrdIO: stand-alone code for basic nrrd functionality Copyright (C) 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. */ #include "NrrdIO.h" #include "privateBiff.h" /* ** Until Teem has its own printf implementation, this will have to do; ** it is imperfect because these are not functionally identical. */ #if defined(WIN32) || defined(_WIN32) # define snprintf _snprintf #endif static biffMsg ** _bmsg=NULL; /* master array of biffMsg pointers */ static unsigned int _bmsgNum=0; /* length of _biffErr == # keys maintained */ static airArray * _bmsgArr=NULL; /* air array of _biffErr and _biffNum */ #define __INCR 2 typedef union { biffMsg ***b; void **v; } _beu; /* ** _bmsgStart() ** ** allocates data structers needed by biff. Panics if ** anything goes wrong. ** ** NOTE: Can be harmlessly called multiple times. */ static void _bmsgStart(void) { static const char me[]="[biff] _bmsgStart"; _beu uu; if (_bmsgArr) { /* its non-NULL, must have been called already */ return; } uu.b = &_bmsg; _bmsgArr = airArrayNew(uu.v, &_bmsgNum, sizeof(biffMsg*), __INCR); if (!_bmsgArr) { fprintf(stderr, "%s: PANIC: couldn't allocate internal data\n", me); /* exit(1); */ } /* airArrayPointerCB(_bmsgArr, NULL, (airMopper)biffMsgNix);*/ return; } static void _bmsgFinish(void) { if (_bmsgArr) { /* setting _bmsgArr to NULL is needed to put biff back in initial state so that next calls to biff re-trigger _bmsgStart() */ _bmsgArr = airArrayNuke(_bmsgArr); } return; } /* ** _bmsgFind() ** ** returns the biffMsg (in _bmsg) of the entry with the given key, or ** NULL if it was not found */ static biffMsg * _bmsgFind(const char *key) { static const char me[]="[biff] _bmsgFind"; biffMsg *msg; unsigned int ii; if (!key) { fprintf(stderr, "%s: PANIC got NULL key", me); return NULL; /* exit(1); */ } msg = NULL; if (_bmsgNum) { for (ii=0; ii<_bmsgNum; ii++) { if (!strcmp(_bmsg[ii]->key, key)) { msg = _bmsg[ii]; break; } } } return msg; } /* ** assumes that msg really is in _bmsg[] */ static unsigned int _bmsgFindIdx(biffMsg *msg) { unsigned int ii; for (ii=0; ii<_bmsgNum; ii++) { if (msg == _bmsg[ii]) { break; } } return ii; } /* ** _bmsgAdd() ** ** if given key already has a biffMsg in _bmsg, returns that. ** otherise, adds a new biffMsg for given key to _bmsg, and returns it ** panics if there is a problem */ static biffMsg * _bmsgAdd(const char *key) { static const char me[]="[biff] _bmsgAdd"; unsigned int ii; biffMsg *msg; msg = NULL; /* find if key exists already */ for (ii=0; ii<_bmsgNum; ii++) { if (!strcmp(key, _bmsg[ii]->key)) { msg = _bmsg[ii]; break; } } if (!msg) { /* have to add new biffMsg */ ii = airArrayLenIncr(_bmsgArr, 1); if (!_bmsg) { fprintf(stderr, "%s: PANIC: couldn't accommodate one more key\n", me); return NULL; /* exit(1); */ } msg = _bmsg[ii] = biffMsgNew(key); } return msg; } /***********************************************************************/ /***********************************************************************/ /* ******** biffAdd() ** ** Adds string "err" at key "key", whether or not there are any ** existing messages there. Since biffSet() was killed ** Wed Apr 20 11:11:51 EDT 2005, this has become the main biff ** function. */ void biffAdd(const char *key, const char *err) { biffMsg *msg; _bmsgStart(); msg = _bmsgAdd(key); biffMsgAdd(msg, err); return; } static void _biffAddVL(const char *key, const char *errfmt, va_list args) { biffMsg *msg; _bmsgStart(); msg = _bmsgAdd(key); _biffMsgAddVL(msg, errfmt, args); return; } /* ******** biffAddf() ** ** Adds string "err" at key "key", whether or not there are any ** existing messages there. This version accepts a printf style ** format string as input. */ void biffAddf(const char *key, const char *errfmt, ...) { va_list args; va_start(args, errfmt); _biffAddVL(key, errfmt, args); va_end(args); return; } #if 0 /* ******** biffAddf_e ** ** calls (eventually) biffMsgAdd if msg is non-NULL, otherwise calls ** biffAdd if msg is NULL. */ void biffAddf_e(biffMsg *msg, const char *key, const char *errfmt, ...) { va_list args; va_start(args, errfmt); if (msg) { _biffMsgAddVL(msg, errfmt, args); } else { _biffAddVL(key, errfmt, args); } va_end(args); return; } #endif /* ******** biffMaybeAdd() ** ** wrapper around biffAdd() but doesn't actually do anything if !useBiff */ void biffMaybeAdd(const char *key, const char *err, int useBiff) { if (useBiff) { biffAdd(key, err); } return; } void biffMaybeAddf(int useBiff, const char *key, const char *errfmt, ...) { va_list args; va_start(args, errfmt); if (useBiff) { _biffAddVL(key, errfmt, args); } va_end(args); return; } /* ******** biffGet() ** ** creates a string which records all the errors at given key and ** returns it. Returns NULL in case of error. This function should ** be considered a glorified strdup(): it is the callers responsibility ** to free() this string later */ char * /*Teem: allocates char* */ /* this comment is an experiment */ biffGet(const char *key) { static const char me[]="biffGet"; char *ret; biffMsg *msg; _bmsgStart(); msg = _bmsgFind(key); if (!msg) { static const char err[]="[%s] No information for this key!"; size_t errlen; fprintf(stderr, "%s: WARNING: no information for key \"%s\"\n", me, key); errlen = strlen(err)+strlen(key)+1; ret = AIR_CALLOC(errlen, char); if (!ret) { fprintf(stderr, "%s: PANIC: unable to allocate buffer\n", me); return NULL; /* exit(1); */ } snprintf(ret, errlen, err, key); return ret; } ret = AIR_CALLOC(biffMsgStrlen(msg)+1, char); if (!ret) { fprintf(stderr, "%s: PANIC: unable to allocate buffer\n", me); return NULL; /* exit(1); */ } biffMsgStrSet(ret, msg); return ret; } /* ******** biffGetStrlen() ** ** for when you want to allocate the buffer for the biff string, this is ** how you learn its length */ unsigned int biffGetStrlen(const char *key) { static const char me[]="biffGetStrlen"; biffMsg *msg; unsigned int len; _bmsgStart(); msg = _bmsgFind(key); if (!msg) { fprintf(stderr, "%s: WARNING: no information for key \"%s\"\n", me, key); return 0; } len = biffMsgStrlen(msg); len += 1; /* GLK forgets if the convention is that the caller allocates for one more to include '\0'; this is safer */ return len; } /* ******** biffSetStr() ** ** for when you want to allocate the buffer for the biff string, this is ** how you get the error message itself */ void biffSetStr(char *str, const char *key) { static const char me[]="biffSetStr"; biffMsg *msg; if (!str) { fprintf(stderr, "%s: ERROR: got NULL buffer for \"%s\"\n", me, key); return; } _bmsgStart(); msg = _bmsgFind(key); if (!msg) { fprintf(stderr, "%s: WARNING: no information for key \"%s\"\n", me, key); return; } biffMsgStrSet(str, msg); return; } /* ******** biffCheck() ** ** sees how many messages there are for a given key; ** Note that this is just a simple wrapper around biffMsgErrNum */ unsigned int biffCheck(const char *key) { _bmsgStart(); return biffMsgErrNum(_bmsgFind(key)); } /* ******** biffDone() ** ** frees everything associated with given key, and shrinks list of keys, ** and calls _bmsgFinish() if there are no keys left */ void biffDone(const char *key) { static const char me[]="biffDone"; unsigned int idx; biffMsg *msg; _bmsgStart(); msg = _bmsgFind(key); if (!msg) { fprintf(stderr, "%s: WARNING: no information for key \"%s\"\n", me, key); return; } idx = _bmsgFindIdx(msg); biffMsgNix(msg); if (_bmsgNum > 1) { /* if we have more than one key in action, move the last biffMsg to the position that was just cleared up */ _bmsg[idx] = _bmsg[_bmsgNum-1]; } airArrayLenIncr(_bmsgArr, -1); /* if that was the last key, close shop */ if (!_bmsgArr->len) { _bmsgFinish(); } return; } void biffMove(const char *destKey, const char *err, const char *srcKey) { static const char me[]="biffMove"; biffMsg *dest, *src; _bmsgStart(); dest = _bmsgAdd(destKey); src = _bmsgFind(srcKey); if (!src) { fprintf(stderr, "%s: WARNING: key \"%s\" unknown\n", me, srcKey); return; } biffMsgMove(dest, src, err); return; } static void _biffMoveVL(const char *destKey, const char *srcKey, const char *errfmt, va_list args) { static const char me[]="biffMovev"; biffMsg *dest, *src; _bmsgStart(); dest = _bmsgAdd(destKey); src = _bmsgFind(srcKey); if (!src) { fprintf(stderr, "%s: WARNING: key \"%s\" unknown\n", me, srcKey); return; } _biffMsgMoveVL(dest, src, errfmt, args); return; } void biffMovef(const char *destKey, const char *srcKey, const char *errfmt, ...) { va_list args; va_start(args, errfmt); _biffMoveVL(destKey, srcKey, errfmt, args); va_end(args); return; } char * biffGetDone(const char *key) { char *ret; _bmsgStart(); ret = biffGet(key); biffDone(key); /* will call _bmsgFinish if this is the last key */ return ret; } /* this is the end */ cmtk-3.3.1/Utilities/NrrdIO/biffmsg.c000066400000000000000000000170621276303427400173630ustar00rootroot00000000000000/* NrrdIO: stand-alone code for basic nrrd functionality Copyright (C) 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. */ #include "NrrdIO.h" #include "privateBiff.h" /* ** with the Nov'09 re-write of biff, this sourcefile becomes the only ** place where a static buffer is used for message handling; this ** should eventually be avoided by using things like asprintf and ** vasprintf which allocated the string as needed */ #define _HACK_STRLEN AIR_STRLEN_HUGE #define _MSG_INCR 2 biffMsg * biffMsgNew(const char *key) { static const char me[]="biffMsgNew"; biffMsg *msg; if (!key) { fprintf(stderr, "%s: PANIC got NULL key\n", me); return NULL; /* exit(1); */ } msg = AIR_CALLOC(1, biffMsg); if (msg) { airPtrPtrUnion appu; msg->key = airStrdup(key); msg->err = NULL; msg->errNum = 0; appu.cp = &(msg->err); msg->errArr = airArrayNew(appu.v, &(msg->errNum), sizeof(char*), _MSG_INCR); if (msg->errArr) { airArrayPointerCB(msg->errArr, NULL, airFree); } } if (!( msg && msg->key && msg->errArr )) { fprintf(stderr, "%s: PANIC couldn't calloc new msg\n", me); return NULL; /* exit(1); */ } return msg; } biffMsg * biffMsgNix(biffMsg *msg) { if (msg && msg != biffMsgNoop) { airFree(msg->key); airArrayLenSet(msg->errArr, 0); /* frees all msg->err[i] */ airArrayNuke(msg->errArr); airFree(msg); } return NULL; } /* ** adds a given message to the given entry. The message is processed to ** convert all whitespace into ' ', and to eliminate whitespace at the ** end of the message. */ void biffMsgAdd(biffMsg *msg, const char *err) { static const char me[]="biffMsgAdd"; unsigned int idx; if (biffMsgNoop == msg) { return; } if (!( msg && err )) { fprintf(stderr, "%s: PANIC got NULL msg (%p) or err (%p)\n", me, AIR_VOIDP(msg), AIR_CVOIDP(err)); /* exit(1); */ } idx = airArrayLenIncr(msg->errArr, 1); if (!msg->err) { fprintf(stderr, "%s: PANIC: couldn't add message to %s\n", me, msg->key); /* exit(1); */ } if (!( msg->err[idx] = airOneLinify(airStrdup(err)) )) { fprintf(stderr, "%s: PANIC: couldn't alloc message to %s\n", me, msg->key); /* exit(1); */ } return; } void _biffMsgAddVL(biffMsg *msg, const char *errfmt, va_list args) { char errstr[_HACK_STRLEN]; vsprintf(errstr, errfmt, args); biffMsgAdd(msg, errstr); return; } void biffMsgClear(biffMsg *msg) { if (biffMsgNoop == msg) { return; } airArrayLenSet(msg->errArr, 0); /* frees all msg->err[i] */ /* but msg->key stays allocated */ return; } /* ** max length of line formatted "[] \n" */ unsigned int biffMsgLineLenMax(const biffMsg *msg) { unsigned int ii, len, maxlen; if (biffMsgNoop == msg) { return 0; } maxlen = 0; for (ii=0; iierrNum; ii++) { len = AIR_UINT(strlen(msg->err[ii]) + strlen(msg->key) + strlen("[] \n")); maxlen = AIR_MAX(maxlen, len); } return maxlen; } /* ******** biffMsgMove ** ** "src" is not const because we clear it after moving things out */ void biffMsgMove(biffMsg *dest, biffMsg *src, const char *err) { static const char me[]="biffMsgMove"; unsigned int ii; char *buff; if (biffMsgNoop == dest || biffMsgNoop == src) { return; } if (!( dest && src )) { fprintf(stderr, "%s: PANIC got NULL msg (%p %p)\n", me, AIR_VOIDP(dest), AIR_VOIDP(src)); /* exit(1); */ } /* if src and dest are same, this degenerates to biffMsgAdd */ if (dest == src && airStrlen(err)) { biffMsgAdd(dest, err); return; } buff = AIR_CALLOC(biffMsgLineLenMax(src)+1, char); if (!buff) { fprintf(stderr, "%s: PANIC: can't allocate buffer\n", me); /* exit(1); */ } for (ii=0; iierrNum; ii++) { sprintf(buff, "[%s] %s", src->key, src->err[ii]); biffMsgAdd(dest, buff); } free(buff); biffMsgClear(src); if (airStrlen(err)) { biffMsgAdd(dest, err); } return; } void _biffMsgMoveVL(biffMsg *dest, biffMsg *src, const char *errfmt, va_list args) { char errstr[_HACK_STRLEN]; vsprintf(errstr, errfmt, args); biffMsgMove(dest, src, errstr); return; } void biffMsgMovef(biffMsg *dest, biffMsg *src, const char *errfmt, ...) { va_list args; va_start(args, errfmt); _biffMsgMoveVL(dest, src, errfmt, args); va_end(args); return; } /* ******** biffMsgErrNum ** ** returns number of errors in a message */ unsigned int biffMsgErrNum(const biffMsg *msg) { if (biffMsgNoop == msg) { return 0; } if (!msg) { return 0; } return msg->errNum; } /* ******** biffMsgStrlen ** ** returns length of string (not including null termination, as usual) ** of the error message that will be generated by biffMsgStrSet */ unsigned int biffMsgStrlen(const biffMsg *msg) { static const char me[]="biffMsgStrlen"; unsigned int ii, len; if (biffMsgNoop == msg) { return 0; } if (!( msg )) { fprintf(stderr, "%s: PANIC got NULL msg %p\n", me, AIR_CVOIDP(msg)); return 0; /* exit(1); */ } len = 0; for (ii=0; iierrNum; ii++) { len += AIR_UINT(strlen(msg->key) + strlen(msg->err[ii]) + strlen("[] \n")); } return len+1; } char * biffMsgStrAlloc(const biffMsg *msg) { static const char me[]="biffMsgStrAlloc"; char *ret; unsigned int len; if (biffMsgNoop == msg) { return NULL; } len = biffMsgStrlen(msg); ret = AIR_CALLOC(len+1, char); if (!ret) { fprintf(stderr, "%s: PANIC couldn't alloc string", me); return NULL; /* exit(1); */ } return ret; } /* ** ret is assumed to be allocated for biffMsgStrlen()+1, or is the ** the return from biffMsgStrAlloc */ void biffMsgStrSet(char *ret, const biffMsg *msg) { static const char me[]="biffMsgStrSet"; char *buff; unsigned int ii; if (biffMsgNoop == msg) { return; } buff = AIR_CALLOC(biffMsgLineLenMax(msg)+1, char); if (!buff) { fprintf(stderr, "%s: PANIC couldn't alloc buffer", me); /* exit(1); */ } strcpy(ret, ""); for (ii=msg->errNum; ii>0; ii--) { sprintf(buff, "[%s] %s\n", msg->key, msg->err[ii-1]); strcat(ret, buff); } free(buff); } char * biffMsgStrGet(const biffMsg *msg) { char *ret; if (biffMsgNoop == msg) { return NULL; } ret = biffMsgStrAlloc(msg); biffMsgStrSet(ret, msg); return ret; } biffMsg _biffMsgNoop = { NULL, NULL, 0, NULL }; /* ******** biffMsgNoop ** ** pass this instead of a real biffMsg (allocated by biffMsgNew) as a ** flag to say, "don't bother, really". This turns all the biffMsg ** functions into no-ops (except that var-args are still consumed ** where they are used) */ biffMsg * biffMsgNoop = &_biffMsgNoop; cmtk-3.3.1/Utilities/NrrdIO/cmtk_NrrdIO_mangle.h000066400000000000000000001241131276303427400214450ustar00rootroot00000000000000#ifndef __cmtk_NrrdIO_mangle_h #define __cmtk_NrrdIO_mangle_h /* This header file mangles all symbols exported from the NrrdIO library. It is included in all files while building the NrrdIO library. Due to namespace pollution, no NrrdIO headers should be included in .h files in CMTK. This file was created via the mangle.pl script in the NrrdIO distribution: perl mangle.pl cmtk > cmtk_NrrdIO_mangle.h This uses nm to list all text (T), data (D) symbols, as well read-only (R) things (seen on Linux) and "other" (S) things (seen on Mac). On Macs, the preceeding underscore is removed. */ /* * Modified 11-Apr-2011 by Torsten Rohlfing for CMTK, * (C) 2011 SRI International */ #define airExists cmtk_airExists #define airFPClass_d cmtk_airFPClass_d #define airFPClass_f cmtk_airFPClass_f #define airFPFprintf_d cmtk_airFPFprintf_d #define airFPFprintf_f cmtk_airFPFprintf_f #define airFPGen_d cmtk_airFPGen_d #define airFPGen_f cmtk_airFPGen_f #define airFPPartsToVal_d cmtk_airFPPartsToVal_d #define airFPPartsToVal_f cmtk_airFPPartsToVal_f #define airFPValToParts_d cmtk_airFPValToParts_d #define airFPValToParts_f cmtk_airFPValToParts_f #define airFloatNegInf cmtk_airFloatNegInf #define airFloatPosInf cmtk_airFloatPosInf #define airFloatQNaN cmtk_airFloatQNaN #define airFloatSNaN cmtk_airFloatSNaN #define airIsInf_d cmtk_airIsInf_d #define airIsInf_f cmtk_airIsInf_f #define airIsNaN cmtk_airIsNaN #define airMyQNaNHiBit cmtk_airMyQNaNHiBit #define airNaN cmtk_airNaN #define _airMopPrint cmtk__airMopPrint #define _airMopWhenStr cmtk__airMopWhenStr #define airMopAdd cmtk_airMopAdd #define airMopDebug cmtk_airMopDebug #define airMopDone cmtk_airMopDone #define airMopError cmtk_airMopError #define airMopMem cmtk_airMopMem #define airMopNew cmtk_airMopNew #define airMopOkay cmtk_airMopOkay #define airMopPrint cmtk_airMopPrint #define airMopSub cmtk_airMopSub #define airMopUnMem cmtk_airMopUnMem #define _airLenSet cmtk__airLenSet #define _airSetData cmtk__airSetData #define airArrayLenIncr cmtk_airArrayLenIncr #define airArrayLenPreSet cmtk_airArrayLenPreSet #define airArrayLenSet cmtk_airArrayLenSet #define airArrayNew cmtk_airArrayNew #define airArrayNix cmtk_airArrayNix #define airArrayNuke cmtk_airArrayNuke #define airArrayPointerCB cmtk_airArrayPointerCB #define airArrayStructCB cmtk_airArrayStructCB #define _airBool cmtk__airBool #define _airBoolDesc cmtk__airBoolDesc #define _airBoolStr cmtk__airBoolStr #define _airBoolStrEqv cmtk__airBoolStrEqv #define _airBoolVal cmtk__airBoolVal #define _airBoolValEqv cmtk__airBoolValEqv #define airAtod cmtk_airAtod #define airBool cmtk_airBool #define airParseStr cmtk_airParseStr #define airParseStrB cmtk_airParseStrB #define airParseStrC cmtk_airParseStrC #define airParseStrD cmtk_airParseStrD #define airParseStrE cmtk_airParseStrE #define airParseStrF cmtk_airParseStrF #define airParseStrI cmtk_airParseStrI #define airParseStrLI cmtk_airParseStrLI #define airParseStrS cmtk_airParseStrS #define airParseStrUI cmtk_airParseStrUI #define airParseStrZ cmtk_airParseStrZ #define airSingleSscanf cmtk_airSingleSscanf #define _airNoDioErr cmtk__airNoDioErr #define airDioInfo cmtk_airDioInfo #define airDioMalloc cmtk_airDioMalloc #define airDioRead cmtk_airDioRead #define airDioTest cmtk_airDioTest #define airDioWrite cmtk_airDioWrite #define airDisableDio cmtk_airDisableDio #define airMyDio cmtk_airMyDio #define airNoDioErr cmtk_airNoDioErr #define _airBadInsane cmtk__airBadInsane #define _airInsaneErr cmtk__airInsaneErr #define airInsaneErr cmtk_airInsaneErr #define airSanity cmtk_airSanity #define _airEndian cmtk__airEndian #define _airEndianDesc cmtk__airEndianDesc #define _airEndianStr cmtk__airEndianStr #define _airEndianVal cmtk__airEndianVal #define airEndian cmtk_airEndian #define airMyEndian cmtk_airMyEndian #define airEndsWith cmtk_airEndsWith #define airOneLine cmtk_airOneLine #define airOneLinify cmtk_airOneLinify #define airStrdup cmtk_airStrdup #define airStrlen cmtk_airStrlen #define airStrntok cmtk_airStrntok #define airStrtok cmtk_airStrtok #define airStrtokQuoting cmtk_airStrtokQuoting #define airStrtrans cmtk_airStrtrans #define airToLower cmtk_airToLower #define airToUpper cmtk_airToUpper #define airUnescape cmtk_airUnescape #define _airEnumIndex cmtk__airEnumIndex #define _enumPrintVal cmtk__enumPrintVal #define airEnumDesc cmtk_airEnumDesc #define airEnumFmtDesc cmtk_airEnumFmtDesc #define airEnumLast cmtk_airEnumLast #define airEnumPrint cmtk_airEnumPrint #define airEnumStr cmtk_airEnumStr #define airEnumUnknown cmtk_airEnumUnknown #define airEnumVal cmtk_airEnumVal #define airEnumValCheck cmtk_airEnumValCheck #define _airSanityHelper cmtk__airSanityHelper #define airFclose cmtk_airFclose #define airFopen cmtk_airFopen #define airFree cmtk_airFree #define airMy32Bit cmtk_airMy32Bit #define airNull cmtk_airNull #define airSetNull cmtk_airSetNull #define airSinglePrintf cmtk_airSinglePrintf #define airTeemReleaseDate cmtk_airTeemReleaseDate #define airTeemVersion cmtk_airTeemVersion #define _bmsg cmtk__bmsg #define _bmsgAdd cmtk__bmsgAdd #define _bmsgArr cmtk__bmsgArr #define _bmsgFind cmtk__bmsgFind #define _bmsgFindIdx cmtk__bmsgFindIdx #define _bmsgFinish cmtk__bmsgFinish #define _bmsgNum cmtk__bmsgNum #define _bmsgStart cmtk__bmsgStart #define biffAdd cmtk_biffAdd #define biffAddVL cmtk_biffAddVL #define biffAddf cmtk_biffAddf #define biffCheck cmtk_biffCheck #define biffDone cmtk_biffDone #define biffGet cmtk_biffGet #define biffGetDone cmtk_biffGetDone #define biffGetStrlen cmtk_biffGetStrlen #define biffMaybeAdd cmtk_biffMaybeAdd #define biffMaybeAddf cmtk_biffMaybeAddf #define biffMove cmtk_biffMove #define biffMoveVL cmtk_biffMoveVL #define biffMovef cmtk_biffMovef #define biffSetStr cmtk_biffSetStr #define biffSetStrDone cmtk_biffSetStrDone #define _biffMsgNoop cmtk__biffMsgNoop #define biffMsgAdd cmtk_biffMsgAdd #define biffMsgAddVL cmtk_biffMsgAddVL #define biffMsgAddf cmtk_biffMsgAddf #define biffMsgClear cmtk_biffMsgClear #define biffMsgLineLenMax cmtk_biffMsgLineLenMax #define biffMsgMove cmtk_biffMsgMove #define biffMsgMoveVL cmtk_biffMsgMoveVL #define biffMsgMovef cmtk_biffMsgMovef #define biffMsgNew cmtk_biffMsgNew #define biffMsgNix cmtk_biffMsgNix #define biffMsgNoop cmtk_biffMsgNoop #define biffMsgStrAlloc cmtk_biffMsgStrAlloc #define biffMsgStrGet cmtk_biffMsgStrGet #define biffMsgStrSet cmtk_biffMsgStrSet #define biffMsgStrlen cmtk_biffMsgStrlen #define _nrrdInsertDBCH cmtk__nrrdInsertDBCH #define _nrrdInsertDBDB cmtk__nrrdInsertDBDB #define _nrrdInsertDBFL cmtk__nrrdInsertDBFL #define _nrrdInsertDBJN cmtk__nrrdInsertDBJN #define _nrrdInsertDBLL cmtk__nrrdInsertDBLL #define _nrrdInsertDBSH cmtk__nrrdInsertDBSH #define _nrrdInsertDBUC cmtk__nrrdInsertDBUC #define _nrrdInsertDBUI cmtk__nrrdInsertDBUI #define _nrrdInsertDBUL cmtk__nrrdInsertDBUL #define _nrrdInsertDBUS cmtk__nrrdInsertDBUS #define _nrrdInsertFLCH cmtk__nrrdInsertFLCH #define _nrrdInsertFLDB cmtk__nrrdInsertFLDB #define _nrrdInsertFLFL cmtk__nrrdInsertFLFL #define _nrrdInsertFLJN cmtk__nrrdInsertFLJN #define _nrrdInsertFLLL cmtk__nrrdInsertFLLL #define _nrrdInsertFLSH cmtk__nrrdInsertFLSH #define _nrrdInsertFLUC cmtk__nrrdInsertFLUC #define _nrrdInsertFLUI cmtk__nrrdInsertFLUI #define _nrrdInsertFLUL cmtk__nrrdInsertFLUL #define _nrrdInsertFLUS cmtk__nrrdInsertFLUS #define _nrrdInsertJNCH cmtk__nrrdInsertJNCH #define _nrrdInsertJNDB cmtk__nrrdInsertJNDB #define _nrrdInsertJNFL cmtk__nrrdInsertJNFL #define _nrrdInsertJNJN cmtk__nrrdInsertJNJN #define _nrrdInsertJNLL cmtk__nrrdInsertJNLL #define _nrrdInsertJNSH cmtk__nrrdInsertJNSH #define _nrrdInsertJNUC cmtk__nrrdInsertJNUC #define _nrrdInsertJNUI cmtk__nrrdInsertJNUI #define _nrrdInsertJNUL cmtk__nrrdInsertJNUL #define _nrrdInsertJNUS cmtk__nrrdInsertJNUS #define _nrrdInsertUICH cmtk__nrrdInsertUICH #define _nrrdInsertUIDB cmtk__nrrdInsertUIDB #define _nrrdInsertUIFL cmtk__nrrdInsertUIFL #define _nrrdInsertUIJN cmtk__nrrdInsertUIJN #define _nrrdInsertUILL cmtk__nrrdInsertUILL #define _nrrdInsertUISH cmtk__nrrdInsertUISH #define _nrrdInsertUIUC cmtk__nrrdInsertUIUC #define _nrrdInsertUIUI cmtk__nrrdInsertUIUI #define _nrrdInsertUIUL cmtk__nrrdInsertUIUL #define _nrrdInsertUIUS cmtk__nrrdInsertUIUS #define _nrrdLoadDBCH cmtk__nrrdLoadDBCH #define _nrrdLoadDBDB cmtk__nrrdLoadDBDB #define _nrrdLoadDBFL cmtk__nrrdLoadDBFL #define _nrrdLoadDBJN cmtk__nrrdLoadDBJN #define _nrrdLoadDBLL cmtk__nrrdLoadDBLL #define _nrrdLoadDBSH cmtk__nrrdLoadDBSH #define _nrrdLoadDBUC cmtk__nrrdLoadDBUC #define _nrrdLoadDBUI cmtk__nrrdLoadDBUI #define _nrrdLoadDBUL cmtk__nrrdLoadDBUL #define _nrrdLoadDBUS cmtk__nrrdLoadDBUS #define _nrrdLoadFLCH cmtk__nrrdLoadFLCH #define _nrrdLoadFLDB cmtk__nrrdLoadFLDB #define _nrrdLoadFLFL cmtk__nrrdLoadFLFL #define _nrrdLoadFLJN cmtk__nrrdLoadFLJN #define _nrrdLoadFLLL cmtk__nrrdLoadFLLL #define _nrrdLoadFLSH cmtk__nrrdLoadFLSH #define _nrrdLoadFLUC cmtk__nrrdLoadFLUC #define _nrrdLoadFLUI cmtk__nrrdLoadFLUI #define _nrrdLoadFLUL cmtk__nrrdLoadFLUL #define _nrrdLoadFLUS cmtk__nrrdLoadFLUS #define _nrrdLoadJNCH cmtk__nrrdLoadJNCH #define _nrrdLoadJNDB cmtk__nrrdLoadJNDB #define _nrrdLoadJNFL cmtk__nrrdLoadJNFL #define _nrrdLoadJNJN cmtk__nrrdLoadJNJN #define _nrrdLoadJNLL cmtk__nrrdLoadJNLL #define _nrrdLoadJNSH cmtk__nrrdLoadJNSH #define _nrrdLoadJNUC cmtk__nrrdLoadJNUC #define _nrrdLoadJNUI cmtk__nrrdLoadJNUI #define _nrrdLoadJNUL cmtk__nrrdLoadJNUL #define _nrrdLoadJNUS cmtk__nrrdLoadJNUS #define _nrrdLoadUICH cmtk__nrrdLoadUICH #define _nrrdLoadUIDB cmtk__nrrdLoadUIDB #define _nrrdLoadUIFL cmtk__nrrdLoadUIFL #define _nrrdLoadUIJN cmtk__nrrdLoadUIJN #define _nrrdLoadUILL cmtk__nrrdLoadUILL #define _nrrdLoadUISH cmtk__nrrdLoadUISH #define _nrrdLoadUIUC cmtk__nrrdLoadUIUC #define _nrrdLoadUIUI cmtk__nrrdLoadUIUI #define _nrrdLoadUIUL cmtk__nrrdLoadUIUL #define _nrrdLoadUIUS cmtk__nrrdLoadUIUS #define _nrrdLookupDBCH cmtk__nrrdLookupDBCH #define _nrrdLookupDBDB cmtk__nrrdLookupDBDB #define _nrrdLookupDBFL cmtk__nrrdLookupDBFL #define _nrrdLookupDBJN cmtk__nrrdLookupDBJN #define _nrrdLookupDBLL cmtk__nrrdLookupDBLL #define _nrrdLookupDBSH cmtk__nrrdLookupDBSH #define _nrrdLookupDBUC cmtk__nrrdLookupDBUC #define _nrrdLookupDBUI cmtk__nrrdLookupDBUI #define _nrrdLookupDBUL cmtk__nrrdLookupDBUL #define _nrrdLookupDBUS cmtk__nrrdLookupDBUS #define _nrrdLookupFLCH cmtk__nrrdLookupFLCH #define _nrrdLookupFLDB cmtk__nrrdLookupFLDB #define _nrrdLookupFLFL cmtk__nrrdLookupFLFL #define _nrrdLookupFLJN cmtk__nrrdLookupFLJN #define _nrrdLookupFLLL cmtk__nrrdLookupFLLL #define _nrrdLookupFLSH cmtk__nrrdLookupFLSH #define _nrrdLookupFLUC cmtk__nrrdLookupFLUC #define _nrrdLookupFLUI cmtk__nrrdLookupFLUI #define _nrrdLookupFLUL cmtk__nrrdLookupFLUL #define _nrrdLookupFLUS cmtk__nrrdLookupFLUS #define _nrrdLookupJNCH cmtk__nrrdLookupJNCH #define _nrrdLookupJNDB cmtk__nrrdLookupJNDB #define _nrrdLookupJNFL cmtk__nrrdLookupJNFL #define _nrrdLookupJNJN cmtk__nrrdLookupJNJN #define _nrrdLookupJNLL cmtk__nrrdLookupJNLL #define _nrrdLookupJNSH cmtk__nrrdLookupJNSH #define _nrrdLookupJNUC cmtk__nrrdLookupJNUC #define _nrrdLookupJNUI cmtk__nrrdLookupJNUI #define _nrrdLookupJNUL cmtk__nrrdLookupJNUL #define _nrrdLookupJNUS cmtk__nrrdLookupJNUS #define _nrrdLookupUICH cmtk__nrrdLookupUICH #define _nrrdLookupUIDB cmtk__nrrdLookupUIDB #define _nrrdLookupUIFL cmtk__nrrdLookupUIFL #define _nrrdLookupUIJN cmtk__nrrdLookupUIJN #define _nrrdLookupUILL cmtk__nrrdLookupUILL #define _nrrdLookupUISH cmtk__nrrdLookupUISH #define _nrrdLookupUIUC cmtk__nrrdLookupUIUC #define _nrrdLookupUIUI cmtk__nrrdLookupUIUI #define _nrrdLookupUIUL cmtk__nrrdLookupUIUL #define _nrrdLookupUIUS cmtk__nrrdLookupUIUS #define _nrrdSprintCH cmtk__nrrdSprintCH #define _nrrdSprintDB cmtk__nrrdSprintDB #define _nrrdSprintFL cmtk__nrrdSprintFL #define _nrrdSprintIN cmtk__nrrdSprintIN #define _nrrdSprintLL cmtk__nrrdSprintLL #define _nrrdSprintSH cmtk__nrrdSprintSH #define _nrrdSprintUC cmtk__nrrdSprintUC #define _nrrdSprintUI cmtk__nrrdSprintUI #define _nrrdSprintUL cmtk__nrrdSprintUL #define _nrrdSprintUS cmtk__nrrdSprintUS #define _nrrdStoreDBCH cmtk__nrrdStoreDBCH #define _nrrdStoreDBDB cmtk__nrrdStoreDBDB #define _nrrdStoreDBFL cmtk__nrrdStoreDBFL #define _nrrdStoreDBJN cmtk__nrrdStoreDBJN #define _nrrdStoreDBLL cmtk__nrrdStoreDBLL #define _nrrdStoreDBSH cmtk__nrrdStoreDBSH #define _nrrdStoreDBUC cmtk__nrrdStoreDBUC #define _nrrdStoreDBUI cmtk__nrrdStoreDBUI #define _nrrdStoreDBUL cmtk__nrrdStoreDBUL #define _nrrdStoreDBUS cmtk__nrrdStoreDBUS #define _nrrdStoreFLCH cmtk__nrrdStoreFLCH #define _nrrdStoreFLDB cmtk__nrrdStoreFLDB #define _nrrdStoreFLFL cmtk__nrrdStoreFLFL #define _nrrdStoreFLJN cmtk__nrrdStoreFLJN #define _nrrdStoreFLLL cmtk__nrrdStoreFLLL #define _nrrdStoreFLSH cmtk__nrrdStoreFLSH #define _nrrdStoreFLUC cmtk__nrrdStoreFLUC #define _nrrdStoreFLUI cmtk__nrrdStoreFLUI #define _nrrdStoreFLUL cmtk__nrrdStoreFLUL #define _nrrdStoreFLUS cmtk__nrrdStoreFLUS #define _nrrdStoreJNCH cmtk__nrrdStoreJNCH #define _nrrdStoreJNDB cmtk__nrrdStoreJNDB #define _nrrdStoreJNFL cmtk__nrrdStoreJNFL #define _nrrdStoreJNJN cmtk__nrrdStoreJNJN #define _nrrdStoreJNLL cmtk__nrrdStoreJNLL #define _nrrdStoreJNSH cmtk__nrrdStoreJNSH #define _nrrdStoreJNUC cmtk__nrrdStoreJNUC #define _nrrdStoreJNUI cmtk__nrrdStoreJNUI #define _nrrdStoreJNUL cmtk__nrrdStoreJNUL #define _nrrdStoreJNUS cmtk__nrrdStoreJNUS #define _nrrdStoreUICH cmtk__nrrdStoreUICH #define _nrrdStoreUIDB cmtk__nrrdStoreUIDB #define _nrrdStoreUIFL cmtk__nrrdStoreUIFL #define _nrrdStoreUIJN cmtk__nrrdStoreUIJN #define _nrrdStoreUILL cmtk__nrrdStoreUILL #define _nrrdStoreUISH cmtk__nrrdStoreUISH #define _nrrdStoreUIUC cmtk__nrrdStoreUIUC #define _nrrdStoreUIUI cmtk__nrrdStoreUIUI #define _nrrdStoreUIUL cmtk__nrrdStoreUIUL #define _nrrdStoreUIUS cmtk__nrrdStoreUIUS #define nrrdDInsert cmtk_nrrdDInsert #define nrrdDLoad cmtk_nrrdDLoad #define nrrdDLookup cmtk_nrrdDLookup #define nrrdDStore cmtk_nrrdDStore #define nrrdFInsert cmtk_nrrdFInsert #define nrrdFLoad cmtk_nrrdFLoad #define nrrdFLookup cmtk_nrrdFLookup #define nrrdFStore cmtk_nrrdFStore #define nrrdIInsert cmtk_nrrdIInsert #define nrrdILoad cmtk_nrrdILoad #define nrrdILookup cmtk_nrrdILookup #define nrrdIStore cmtk_nrrdIStore #define nrrdSprint cmtk_nrrdSprint #define nrrdUIInsert cmtk_nrrdUIInsert #define nrrdUILoad cmtk_nrrdUILoad #define nrrdUILookup cmtk_nrrdUILookup #define nrrdUIStore cmtk_nrrdUIStore #define nrrdDefaultCenter cmtk_nrrdDefaultCenter #define nrrdDefaultSpacing cmtk_nrrdDefaultSpacing #define nrrdDefaultWriteBareText cmtk_nrrdDefaultWriteBareText #define nrrdDefaultWriteCharsPerLine cmtk_nrrdDefaultWriteCharsPerLine #define nrrdDefaultWriteEncodingType cmtk_nrrdDefaultWriteEncodingType #define nrrdDefaultWriteValsPerLine cmtk_nrrdDefaultWriteValsPerLine #define nrrdStateAlwaysSetContent cmtk_nrrdStateAlwaysSetContent #define nrrdStateDisableContent cmtk_nrrdStateDisableContent #define nrrdStateGrayscaleImage3D cmtk_nrrdStateGrayscaleImage3D #define nrrdStateKeyValuePairsPropagate cmtk_nrrdStateKeyValuePairsPropagate #define nrrdStateKeyValueReturnInternalPointers cmtk_nrrdStateKeyValueReturnInternalPointers #define nrrdStateKindNoop cmtk_nrrdStateKindNoop #define nrrdStateUnknownContent cmtk_nrrdStateUnknownContent #define nrrdStateVerboseIO cmtk_nrrdStateVerboseIO #define _nrrdCenterDesc cmtk__nrrdCenterDesc #define _nrrdCenterStr cmtk__nrrdCenterStr #define _nrrdCenter_enum cmtk__nrrdCenter_enum #define _nrrdEncodingType cmtk__nrrdEncodingType #define _nrrdEncodingTypeDesc cmtk__nrrdEncodingTypeDesc #define _nrrdEncodingTypeStr cmtk__nrrdEncodingTypeStr #define _nrrdEncodingTypeStrEqv cmtk__nrrdEncodingTypeStrEqv #define _nrrdEncodingTypeValEqv cmtk__nrrdEncodingTypeValEqv #define _nrrdField cmtk__nrrdField #define _nrrdFieldDesc cmtk__nrrdFieldDesc #define _nrrdFieldStr cmtk__nrrdFieldStr #define _nrrdFieldStrEqv cmtk__nrrdFieldStrEqv #define _nrrdFieldValEqv cmtk__nrrdFieldValEqv #define _nrrdFormatType cmtk__nrrdFormatType #define _nrrdFormatTypeDesc cmtk__nrrdFormatTypeDesc #define _nrrdFormatTypeStr cmtk__nrrdFormatTypeStr #define _nrrdFormatTypeStrEqv cmtk__nrrdFormatTypeStrEqv #define _nrrdFormatTypeValEqv cmtk__nrrdFormatTypeValEqv #define _nrrdKindDesc cmtk__nrrdKindDesc #define _nrrdKindStr cmtk__nrrdKindStr #define _nrrdKindStr_Eqv cmtk__nrrdKindStr_Eqv #define _nrrdKindVal_Eqv cmtk__nrrdKindVal_Eqv #define _nrrdKind_enum cmtk__nrrdKind_enum #define _nrrdSpace cmtk__nrrdSpace #define _nrrdSpaceDesc cmtk__nrrdSpaceDesc #define _nrrdSpaceStr cmtk__nrrdSpaceStr #define _nrrdSpaceStrEqv cmtk__nrrdSpaceStrEqv #define _nrrdSpaceValEqv cmtk__nrrdSpaceValEqv #define _nrrdSpacingStatus cmtk__nrrdSpacingStatus #define _nrrdSpacingStatusDesc cmtk__nrrdSpacingStatusDesc #define _nrrdSpacingStatusStr cmtk__nrrdSpacingStatusStr #define _nrrdType cmtk__nrrdType #define _nrrdTypeDesc cmtk__nrrdTypeDesc #define _nrrdTypeStr cmtk__nrrdTypeStr #define _nrrdTypeStrEqv cmtk__nrrdTypeStrEqv #define _nrrdTypeValEqv cmtk__nrrdTypeValEqv #define nrrdCenter cmtk_nrrdCenter #define nrrdEncodingType cmtk_nrrdEncodingType #define nrrdField cmtk_nrrdField #define nrrdFormatType cmtk_nrrdFormatType #define nrrdKind cmtk_nrrdKind #define nrrdSpace cmtk_nrrdSpace #define nrrdSpacingStatus cmtk_nrrdSpacingStatus #define nrrdType cmtk_nrrdType #define _nrrdFieldOnePerAxis cmtk__nrrdFieldOnePerAxis #define _nrrdFieldRequired cmtk__nrrdFieldRequired #define _nrrdFieldValidInImage cmtk__nrrdFieldValidInImage #define _nrrdFieldValidInText cmtk__nrrdFieldValidInText #define nrrdTypeIsIntegral cmtk_nrrdTypeIsIntegral #define nrrdTypeIsUnsigned cmtk_nrrdTypeIsUnsigned #define nrrdTypeMax cmtk_nrrdTypeMax #define nrrdTypeMin cmtk_nrrdTypeMin #define nrrdTypeNumberOfValues cmtk_nrrdTypeNumberOfValues #define nrrdTypePrintfStr cmtk_nrrdTypePrintfStr #define nrrdTypeSize cmtk_nrrdTypeSize #define _nrrdAxisInfoInit cmtk__nrrdAxisInfoInit #define _nrrdAxisInfoNewInit cmtk__nrrdAxisInfoNewInit #define _nrrdCopy cmtk__nrrdCopy #define _nrrdSizeCheck cmtk__nrrdSizeCheck #define nrrdAlloc_nva cmtk_nrrdAlloc_nva #define nrrdAlloc_va cmtk_nrrdAlloc_va #define nrrdAxisInfoCopy cmtk_nrrdAxisInfoCopy #define nrrdAxisInfoGet_nva cmtk_nrrdAxisInfoGet_nva #define nrrdAxisInfoSet_nva cmtk_nrrdAxisInfoSet_nva #define nrrdBasicInfoCopy cmtk_nrrdBasicInfoCopy #define nrrdBasicInfoInit cmtk_nrrdBasicInfoInit #define nrrdBiffKey cmtk_nrrdBiffKey #define nrrdCommentClear cmtk_nrrdCommentClear #define nrrdCommentCopy cmtk_nrrdCommentCopy #define nrrdCopy cmtk_nrrdCopy #define nrrdDefaultWriteBareText cmtk_nrrdDefaultWriteBareText #define nrrdDefaultWriteCharsPerLine cmtk_nrrdDefaultWriteCharsPerLine #define nrrdDefaultWriteValsPerLine cmtk_nrrdDefaultWriteValsPerLine #define nrrdElementNumber cmtk_nrrdElementNumber #define nrrdElementSize cmtk_nrrdElementSize #define nrrdEmpty cmtk_nrrdEmpty #define nrrdEncodingUnknown cmtk_nrrdEncodingUnknown #define nrrdFormatUnknown cmtk_nrrdFormatUnknown #define nrrdInit cmtk_nrrdInit #define nrrdIoStateInit cmtk_nrrdIoStateInit #define nrrdIoStateNew cmtk_nrrdIoStateNew #define nrrdIoStateNix cmtk_nrrdIoStateNix #define nrrdKeyValueClear cmtk_nrrdKeyValueClear #define nrrdKeyValueCopy cmtk_nrrdKeyValueCopy #define nrrdMaybeAlloc_nva cmtk_nrrdMaybeAlloc_nva #define nrrdMaybeAlloc_va cmtk_nrrdMaybeAlloc_va #define nrrdNew cmtk_nrrdNew #define nrrdNix cmtk_nrrdNix #define nrrdNuke cmtk_nrrdNuke #define nrrdPGM cmtk_nrrdPGM #define nrrdPPM cmtk_nrrdPPM #define nrrdType cmtk_nrrdType #define nrrdTypeSize cmtk_nrrdTypeSize #define nrrdWrap_nva cmtk_nrrdWrap_nva #define nrrdWrap_va cmtk_nrrdWrap_va #define _nrrdAxisInfoCopy cmtk__nrrdAxisInfoCopy #define _nrrdAxisInfoInit cmtk__nrrdAxisInfoInit #define _nrrdCopy cmtk__nrrdCopy #define _nrrdKindAltered cmtk__nrrdKindAltered #define nrrdAxesInsert cmtk_nrrdAxesInsert #define nrrdAxesPermute cmtk_nrrdAxesPermute #define nrrdAxisInfoCopy cmtk_nrrdAxisInfoCopy #define nrrdAxisInfoGet_nva cmtk_nrrdAxisInfoGet_nva #define nrrdBasicInfoCopy cmtk_nrrdBasicInfoCopy #define nrrdBiffKey cmtk_nrrdBiffKey #define nrrdContentSet_va cmtk_nrrdContentSet_va #define nrrdCopy cmtk_nrrdCopy #define nrrdElementNumber cmtk_nrrdElementNumber #define nrrdElementSize cmtk_nrrdElementSize #define nrrdInvertPerm cmtk_nrrdInvertPerm #define nrrdKindSize cmtk_nrrdKindSize #define nrrdMaybeAlloc_nva cmtk_nrrdMaybeAlloc_nva #define nrrdShuffle cmtk_nrrdShuffle #define nrrdStateKeyValuePairsPropagate cmtk_nrrdStateKeyValuePairsPropagate #define nrrdStateKindNoop cmtk_nrrdStateKindNoop #define _nrrdAxisInfoCopy cmtk__nrrdAxisInfoCopy #define _nrrdAxisInfoInit cmtk__nrrdAxisInfoInit #define _nrrdAxisInfoNewInit cmtk__nrrdAxisInfoNewInit #define _nrrdCenter cmtk__nrrdCenter #define _nrrdCenter2 cmtk__nrrdCenter2 #define _nrrdCheck cmtk__nrrdCheck #define _nrrdKindAltered cmtk__nrrdKindAltered #define _nrrdSpaceVecExists cmtk__nrrdSpaceVecExists #define nrrdAxisInfoCopy cmtk_nrrdAxisInfoCopy #define nrrdAxisInfoGet_nva cmtk_nrrdAxisInfoGet_nva #define nrrdAxisInfoGet_va cmtk_nrrdAxisInfoGet_va #define nrrdAxisInfoIdx cmtk_nrrdAxisInfoIdx #define nrrdAxisInfoIdxRange cmtk_nrrdAxisInfoIdxRange #define nrrdAxisInfoMinMaxSet cmtk_nrrdAxisInfoMinMaxSet #define nrrdAxisInfoPos cmtk_nrrdAxisInfoPos #define nrrdAxisInfoPosRange cmtk_nrrdAxisInfoPosRange #define nrrdAxisInfoSet_nva cmtk_nrrdAxisInfoSet_nva #define nrrdAxisInfoSet_va cmtk_nrrdAxisInfoSet_va #define nrrdAxisInfoSpacingSet cmtk_nrrdAxisInfoSpacingSet #define nrrdBiffKey cmtk_nrrdBiffKey #define nrrdCopy cmtk_nrrdCopy #define nrrdDefaultCenter cmtk_nrrdDefaultCenter #define nrrdDefaultSpacing cmtk_nrrdDefaultSpacing #define nrrdDomainAxesGet cmtk_nrrdDomainAxesGet #define nrrdKindIsDomain cmtk_nrrdKindIsDomain #define nrrdKindSize cmtk_nrrdKindSize #define nrrdNonSpatialAxesGet cmtk_nrrdNonSpatialAxesGet #define nrrdOrientationReduce cmtk_nrrdOrientationReduce #define nrrdRangeAxesGet cmtk_nrrdRangeAxesGet #define nrrdSpaceSet cmtk_nrrdSpaceSet #define nrrdSpaceVecNorm cmtk_nrrdSpaceVecNorm #define nrrdSpaceVecScale cmtk_nrrdSpaceVecScale #define nrrdSpaceVecSetNaN cmtk_nrrdSpaceVecSetNaN #define nrrdSpacingCalculate cmtk_nrrdSpacingCalculate #define nrrdSpatialAxesGet cmtk_nrrdSpatialAxesGet #define nrrdStateKindNoop cmtk_nrrdStateKindNoop #define _nrrdCheck cmtk__nrrdCheck #define _nrrdCheckEnums cmtk__nrrdCheckEnums #define _nrrdContentGet cmtk__nrrdContentGet #define _nrrdContentSet_nva cmtk__nrrdContentSet_nva #define _nrrdContentSet_va cmtk__nrrdContentSet_va #define _nrrdFieldCheck cmtk__nrrdFieldCheck #define _nrrdFieldCheckSpaceInfo cmtk__nrrdFieldCheckSpaceInfo #define _nrrdFieldCheck_axis_maxs cmtk__nrrdFieldCheck_axis_maxs #define _nrrdFieldCheck_axis_mins cmtk__nrrdFieldCheck_axis_mins #define _nrrdFieldCheck_block_size cmtk__nrrdFieldCheck_block_size #define _nrrdFieldCheck_centers cmtk__nrrdFieldCheck_centers #define _nrrdFieldCheck_dimension cmtk__nrrdFieldCheck_dimension #define _nrrdFieldCheck_keyvalue cmtk__nrrdFieldCheck_keyvalue #define _nrrdFieldCheck_kinds cmtk__nrrdFieldCheck_kinds #define _nrrdFieldCheck_labels cmtk__nrrdFieldCheck_labels #define _nrrdFieldCheck_measurement_frame cmtk__nrrdFieldCheck_measurement_frame #define _nrrdFieldCheck_noop cmtk__nrrdFieldCheck_noop #define _nrrdFieldCheck_old_max cmtk__nrrdFieldCheck_old_max #define _nrrdFieldCheck_old_min cmtk__nrrdFieldCheck_old_min #define _nrrdFieldCheck_sizes cmtk__nrrdFieldCheck_sizes #define _nrrdFieldCheck_space cmtk__nrrdFieldCheck_space #define _nrrdFieldCheck_space_dimension cmtk__nrrdFieldCheck_space_dimension #define _nrrdFieldCheck_space_directions cmtk__nrrdFieldCheck_space_directions #define _nrrdFieldCheck_space_origin cmtk__nrrdFieldCheck_space_origin #define _nrrdFieldCheck_space_units cmtk__nrrdFieldCheck_space_units #define _nrrdFieldCheck_spacings cmtk__nrrdFieldCheck_spacings #define _nrrdFieldCheck_thicknesses cmtk__nrrdFieldCheck_thicknesses #define _nrrdFieldCheck_type cmtk__nrrdFieldCheck_type #define _nrrdFieldCheck_units cmtk__nrrdFieldCheck_units #define _nrrdSizeCheck cmtk__nrrdSizeCheck #define _nrrdSplitSizes cmtk__nrrdSplitSizes #define nrrdAxisInfoGet_nva cmtk_nrrdAxisInfoGet_nva #define nrrdBiffKey cmtk_nrrdBiffKey #define nrrdCenter cmtk_nrrdCenter #define nrrdCheck cmtk_nrrdCheck #define nrrdContentSet_va cmtk_nrrdContentSet_va #define nrrdDefaultCenter cmtk_nrrdDefaultCenter #define nrrdDefaultWriteEncodingType cmtk_nrrdDefaultWriteEncodingType #define nrrdDescribe cmtk_nrrdDescribe #define nrrdElementNumber cmtk_nrrdElementNumber #define nrrdElementSize cmtk_nrrdElementSize #define nrrdEncodingType cmtk_nrrdEncodingType #define nrrdField cmtk_nrrdField #define nrrdKind cmtk_nrrdKind #define nrrdKindSize cmtk_nrrdKindSize #define nrrdOriginCalculate cmtk_nrrdOriginCalculate #define nrrdSameSize cmtk_nrrdSameSize #define nrrdSanity cmtk_nrrdSanity #define nrrdSpace cmtk_nrrdSpace #define nrrdSpaceDimension cmtk_nrrdSpaceDimension #define nrrdSpaceDimensionSet cmtk_nrrdSpaceDimensionSet #define nrrdSpaceOriginGet cmtk_nrrdSpaceOriginGet #define nrrdSpaceOriginSet cmtk_nrrdSpaceOriginSet #define nrrdSpaceSet cmtk_nrrdSpaceSet #define nrrdSpaceVecCopy cmtk_nrrdSpaceVecCopy #define nrrdSpaceVecNorm cmtk_nrrdSpaceVecNorm #define nrrdSpaceVecScale cmtk_nrrdSpaceVecScale #define nrrdSpaceVecScaleAdd2 cmtk_nrrdSpaceVecScaleAdd2 #define nrrdSpaceVecSetNaN cmtk_nrrdSpaceVecSetNaN #define nrrdStateAlwaysSetContent cmtk_nrrdStateAlwaysSetContent #define nrrdStateDisableContent cmtk_nrrdStateDisableContent #define nrrdStateUnknownContent cmtk_nrrdStateUnknownContent #define nrrdType cmtk_nrrdType #define nrrdTypeIsIntegral cmtk_nrrdTypeIsIntegral #define nrrdTypeSize cmtk_nrrdTypeSize #define _nrrdFormatURLLine0 cmtk__nrrdFormatURLLine0 #define _nrrdFormatURLLine1 cmtk__nrrdFormatURLLine1 #define nrrdCommentAdd cmtk_nrrdCommentAdd #define nrrdCommentClear cmtk_nrrdCommentClear #define nrrdCommentCopy cmtk_nrrdCommentCopy #define _nrrdKeyValueIdxFind cmtk__nrrdKeyValueIdxFind #define _nrrdKeyValueWrite cmtk__nrrdKeyValueWrite #define _nrrdWriteEscaped cmtk__nrrdWriteEscaped #define nrrdKeyValueAdd cmtk_nrrdKeyValueAdd #define nrrdKeyValueClear cmtk_nrrdKeyValueClear #define nrrdKeyValueCopy cmtk_nrrdKeyValueCopy #define nrrdKeyValueErase cmtk_nrrdKeyValueErase #define nrrdKeyValueGet cmtk_nrrdKeyValueGet #define nrrdKeyValueIndex cmtk_nrrdKeyValueIndex #define nrrdKeyValueSize cmtk_nrrdKeyValueSize #define nrrdStateKeyValueReturnInternalPointers cmtk_nrrdStateKeyValueReturnInternalPointers #define _nrrdBlockEndian cmtk__nrrdBlockEndian #define _nrrdNoopEndian cmtk__nrrdNoopEndian #define _nrrdSwap16Endian cmtk__nrrdSwap16Endian #define _nrrdSwap32Endian cmtk__nrrdSwap32Endian #define _nrrdSwap64Endian cmtk__nrrdSwap64Endian #define _nrrdSwapEndian cmtk__nrrdSwapEndian #define nrrdElementNumber cmtk_nrrdElementNumber #define nrrdSwapEndian cmtk_nrrdSwapEndian #define nrrdType cmtk_nrrdType #define _nrrdContainsPercentThisAndMore cmtk__nrrdContainsPercentThisAndMore #define _nrrdDataFNCheck cmtk__nrrdDataFNCheck #define _nrrdDataFNNumber cmtk__nrrdDataFNNumber #define _nrrdFieldCheck cmtk__nrrdFieldCheck #define _nrrdFieldSep cmtk__nrrdFieldSep #define _nrrdGetQuotedString cmtk__nrrdGetQuotedString #define _nrrdHeaderCheck cmtk__nrrdHeaderCheck #define _nrrdNoSpaceVector cmtk__nrrdNoSpaceVector #define _nrrdOneLine cmtk__nrrdOneLine #define _nrrdReadNrrdParseField cmtk__nrrdReadNrrdParseField #define _nrrdReadNrrdParse_axis_maxs cmtk__nrrdReadNrrdParse_axis_maxs #define _nrrdReadNrrdParse_axis_mins cmtk__nrrdReadNrrdParse_axis_mins #define _nrrdReadNrrdParse_block_size cmtk__nrrdReadNrrdParse_block_size #define _nrrdReadNrrdParse_byte_skip cmtk__nrrdReadNrrdParse_byte_skip #define _nrrdReadNrrdParse_centers cmtk__nrrdReadNrrdParse_centers #define _nrrdReadNrrdParse_comment cmtk__nrrdReadNrrdParse_comment #define _nrrdReadNrrdParse_content cmtk__nrrdReadNrrdParse_content #define _nrrdReadNrrdParse_data_file cmtk__nrrdReadNrrdParse_data_file #define _nrrdReadNrrdParse_dimension cmtk__nrrdReadNrrdParse_dimension #define _nrrdReadNrrdParse_encoding cmtk__nrrdReadNrrdParse_encoding #define _nrrdReadNrrdParse_endian cmtk__nrrdReadNrrdParse_endian #define _nrrdReadNrrdParse_keyvalue cmtk__nrrdReadNrrdParse_keyvalue #define _nrrdReadNrrdParse_kinds cmtk__nrrdReadNrrdParse_kinds #define _nrrdReadNrrdParse_labels cmtk__nrrdReadNrrdParse_labels #define _nrrdReadNrrdParse_line_skip cmtk__nrrdReadNrrdParse_line_skip #define _nrrdReadNrrdParse_max cmtk__nrrdReadNrrdParse_max #define _nrrdReadNrrdParse_measurement_frame cmtk__nrrdReadNrrdParse_measurement_frame #define _nrrdReadNrrdParse_min cmtk__nrrdReadNrrdParse_min #define _nrrdReadNrrdParse_nonfield cmtk__nrrdReadNrrdParse_nonfield #define _nrrdReadNrrdParse_number cmtk__nrrdReadNrrdParse_number #define _nrrdReadNrrdParse_old_max cmtk__nrrdReadNrrdParse_old_max #define _nrrdReadNrrdParse_old_min cmtk__nrrdReadNrrdParse_old_min #define _nrrdReadNrrdParse_sample_units cmtk__nrrdReadNrrdParse_sample_units #define _nrrdReadNrrdParse_sizes cmtk__nrrdReadNrrdParse_sizes #define _nrrdReadNrrdParse_space cmtk__nrrdReadNrrdParse_space #define _nrrdReadNrrdParse_space_dimension cmtk__nrrdReadNrrdParse_space_dimension #define _nrrdReadNrrdParse_space_directions cmtk__nrrdReadNrrdParse_space_directions #define _nrrdReadNrrdParse_space_origin cmtk__nrrdReadNrrdParse_space_origin #define _nrrdReadNrrdParse_space_units cmtk__nrrdReadNrrdParse_space_units #define _nrrdReadNrrdParse_spacings cmtk__nrrdReadNrrdParse_spacings #define _nrrdReadNrrdParse_thicknesses cmtk__nrrdReadNrrdParse_thicknesses #define _nrrdReadNrrdParse_type cmtk__nrrdReadNrrdParse_type #define _nrrdReadNrrdParse_units cmtk__nrrdReadNrrdParse_units #define _nrrdSpaceVectorParse cmtk__nrrdSpaceVectorParse #define _nrrdSplitSizes cmtk__nrrdSplitSizes #define nrrdAxisInfoSet_nva cmtk_nrrdAxisInfoSet_nva #define nrrdBiffKey cmtk_nrrdBiffKey #define nrrdCenter cmtk_nrrdCenter #define nrrdCommentAdd cmtk_nrrdCommentAdd #define nrrdEncodingArray cmtk_nrrdEncodingArray #define nrrdEncodingType cmtk_nrrdEncodingType #define nrrdField cmtk_nrrdField #define nrrdFieldInfoParse cmtk_nrrdFieldInfoParse #define nrrdKeyValueAdd cmtk_nrrdKeyValueAdd #define nrrdKind cmtk_nrrdKind #define nrrdSpace cmtk_nrrdSpace #define nrrdSpaceSet cmtk_nrrdSpaceSet #define nrrdType cmtk_nrrdType #define _nrrdGzCheckHeader cmtk__nrrdGzCheckHeader #define _nrrdGzClose cmtk__nrrdGzClose #define _nrrdGzDestroy cmtk__nrrdGzDestroy #define _nrrdGzDummySymbol cmtk__nrrdGzDummySymbol #define _nrrdGzErrMsg cmtk__nrrdGzErrMsg #define _nrrdGzGetByte cmtk__nrrdGzGetByte #define _nrrdGzOpen cmtk__nrrdGzOpen #define _nrrdGzRead cmtk__nrrdGzRead #define _nrrdGzWrite cmtk__nrrdGzWrite #define nrrdBiffKey cmtk_nrrdBiffKey #define _nrrdCalloc cmtk__nrrdCalloc #define _nrrdCheck cmtk__nrrdCheck #define _nrrdContainsPercentThisAndMore cmtk__nrrdContainsPercentThisAndMore #define _nrrdDataFNNumber cmtk__nrrdDataFNNumber #define _nrrdFieldSep cmtk__nrrdFieldSep #define _nrrdHeaderStringOneLine cmtk__nrrdHeaderStringOneLine #define _nrrdHeaderStringOneLineStrlen cmtk__nrrdHeaderStringOneLineStrlen #define _nrrdLineSep cmtk__nrrdLineSep #define _nrrdNoSpaceVector cmtk__nrrdNoSpaceVector #define _nrrdOneLine cmtk__nrrdOneLine #define _nrrdRead cmtk__nrrdRead #define _nrrdRelativePathFlag cmtk__nrrdRelativePathFlag #define _nrrdSplitName cmtk__nrrdSplitName #define _nrrdTextSep cmtk__nrrdTextSep #define nrrdAxesInsert cmtk_nrrdAxesInsert #define nrrdBiffKey cmtk_nrrdBiffKey #define nrrdByteSkip cmtk_nrrdByteSkip #define nrrdElementNumber cmtk_nrrdElementNumber #define nrrdElementSize cmtk_nrrdElementSize #define nrrdEncodingRaw cmtk_nrrdEncodingRaw #define nrrdFormatArray cmtk_nrrdFormatArray #define nrrdFormatNRRD cmtk_nrrdFormatNRRD #define nrrdFormatUnknown cmtk_nrrdFormatUnknown #define nrrdInit cmtk_nrrdInit #define nrrdIoStateNew cmtk_nrrdIoStateNew #define nrrdIoStateNix cmtk_nrrdIoStateNix #define nrrdLineSkip cmtk_nrrdLineSkip #define nrrdLoad cmtk_nrrdLoad #define nrrdLoadMulti cmtk_nrrdLoadMulti #define nrrdRead cmtk_nrrdRead #define nrrdSanity cmtk_nrrdSanity #define nrrdStateGrayscaleImage3D cmtk_nrrdStateGrayscaleImage3D #define nrrdStateVerboseIO cmtk_nrrdStateVerboseIO #define nrrdStringRead cmtk_nrrdStringRead #define _nrrdContainsPercentThisAndMore cmtk__nrrdContainsPercentThisAndMore #define _nrrdEncodingMaybeSet cmtk__nrrdEncodingMaybeSet #define _nrrdFieldInteresting cmtk__nrrdFieldInteresting #define _nrrdFormatMaybeGuess cmtk__nrrdFormatMaybeGuess #define _nrrdFormatMaybeSet cmtk__nrrdFormatMaybeSet #define _nrrdFormatNRRD_whichVersion cmtk__nrrdFormatNRRD_whichVersion #define _nrrdFprintFieldInfo cmtk__nrrdFprintFieldInfo #define _nrrdNoSpaceVector cmtk__nrrdNoSpaceVector #define _nrrdSplitName cmtk__nrrdSplitName #define _nrrdSprintFieldInfo cmtk__nrrdSprintFieldInfo #define _nrrdStrcatSpaceVector cmtk__nrrdStrcatSpaceVector #define _nrrdWrite cmtk__nrrdWrite #define nrrdBiffKey cmtk_nrrdBiffKey #define nrrdCenter cmtk_nrrdCenter #define nrrdCheck cmtk_nrrdCheck #define nrrdDefaultWriteEncodingType cmtk_nrrdDefaultWriteEncodingType #define nrrdElementNumber cmtk_nrrdElementNumber #define nrrdElementSize cmtk_nrrdElementSize #define nrrdEncodingArray cmtk_nrrdEncodingArray #define nrrdEncodingUnknown cmtk_nrrdEncodingUnknown #define nrrdField cmtk_nrrdField #define nrrdFormatArray cmtk_nrrdFormatArray #define nrrdFormatNRRD cmtk_nrrdFormatNRRD #define nrrdFormatUnknown cmtk_nrrdFormatUnknown #define nrrdIoStateEncodingGet cmtk_nrrdIoStateEncodingGet #define nrrdIoStateEncodingSet cmtk_nrrdIoStateEncodingSet #define nrrdIoStateFormatGet cmtk_nrrdIoStateFormatGet #define nrrdIoStateFormatSet cmtk_nrrdIoStateFormatSet #define nrrdIoStateGet cmtk_nrrdIoStateGet #define nrrdIoStateNew cmtk_nrrdIoStateNew #define nrrdIoStateNix cmtk_nrrdIoStateNix #define nrrdIoStateSet cmtk_nrrdIoStateSet #define nrrdKind cmtk_nrrdKind #define nrrdSave cmtk_nrrdSave #define nrrdSaveMulti cmtk_nrrdSaveMulti #define nrrdSpace cmtk_nrrdSpace #define nrrdStateVerboseIO cmtk_nrrdStateVerboseIO #define nrrdStringWrite cmtk_nrrdStringWrite #define nrrdType cmtk_nrrdType #define nrrdWrite cmtk_nrrdWrite #define _nrrdFormatEPS cmtk__nrrdFormatEPS #define _nrrdFormatNRRD cmtk__nrrdFormatNRRD #define _nrrdFormatPNG cmtk__nrrdFormatPNG #define _nrrdFormatPNM cmtk__nrrdFormatPNM #define _nrrdFormatText cmtk__nrrdFormatText #define _nrrdFormatUnknown cmtk__nrrdFormatUnknown #define _nrrdFormatUnknown_available cmtk__nrrdFormatUnknown_available #define _nrrdFormatUnknown_contentStartsLike cmtk__nrrdFormatUnknown_contentStartsLike #define _nrrdFormatUnknown_fitsInto cmtk__nrrdFormatUnknown_fitsInto #define _nrrdFormatUnknown_nameLooksLike cmtk__nrrdFormatUnknown_nameLooksLike #define _nrrdFormatUnknown_read cmtk__nrrdFormatUnknown_read #define _nrrdFormatUnknown_write cmtk__nrrdFormatUnknown_write #define _nrrdFormatVTK cmtk__nrrdFormatVTK #define nrrdBiffKey cmtk_nrrdBiffKey #define nrrdFormatArray cmtk_nrrdFormatArray #define nrrdFormatUnknown cmtk_nrrdFormatUnknown #define _nrrdCalloc cmtk__nrrdCalloc #define _nrrdDataFNNumber cmtk__nrrdDataFNNumber #define _nrrdFieldInteresting cmtk__nrrdFieldInteresting #define _nrrdFieldRequired cmtk__nrrdFieldRequired #define _nrrdFormatNRRD cmtk__nrrdFormatNRRD #define _nrrdFormatNRRD_available cmtk__nrrdFormatNRRD_available #define _nrrdFormatNRRD_contentStartsLike cmtk__nrrdFormatNRRD_contentStartsLike #define _nrrdFormatNRRD_fitsInto cmtk__nrrdFormatNRRD_fitsInto #define _nrrdFormatNRRD_nameLooksLike cmtk__nrrdFormatNRRD_nameLooksLike #define _nrrdFormatNRRD_read cmtk__nrrdFormatNRRD_read #define _nrrdFormatNRRD_whichVersion cmtk__nrrdFormatNRRD_whichVersion #define _nrrdFormatNRRD_write cmtk__nrrdFormatNRRD_write #define _nrrdFormatURLLine0 cmtk__nrrdFormatURLLine0 #define _nrrdFormatURLLine1 cmtk__nrrdFormatURLLine1 #define _nrrdFprintFieldInfo cmtk__nrrdFprintFieldInfo #define _nrrdHeaderCheck cmtk__nrrdHeaderCheck #define _nrrdKeyValueWrite cmtk__nrrdKeyValueWrite #define _nrrdOneLine cmtk__nrrdOneLine #define _nrrdReadNrrdParseField cmtk__nrrdReadNrrdParseField #define _nrrdSprintFieldInfo cmtk__nrrdSprintFieldInfo #define nrrdBiffKey cmtk_nrrdBiffKey #define nrrdByteSkip cmtk_nrrdByteSkip #define nrrdElementNumber cmtk_nrrdElementNumber #define nrrdElementSize cmtk_nrrdElementSize #define nrrdEncodingAscii cmtk_nrrdEncodingAscii #define nrrdField cmtk_nrrdField #define nrrdFieldInfoParse cmtk_nrrdFieldInfoParse #define nrrdFormatNRRD cmtk_nrrdFormatNRRD #define nrrdIoStateDataFileIterBegin cmtk_nrrdIoStateDataFileIterBegin #define nrrdIoStateDataFileIterNext cmtk_nrrdIoStateDataFileIterNext #define nrrdKeyValueSize cmtk_nrrdKeyValueSize #define nrrdLineSkip cmtk_nrrdLineSkip #define nrrdStateVerboseIO cmtk_nrrdStateVerboseIO #define nrrdSwapEndian cmtk_nrrdSwapEndian #define nrrdType cmtk_nrrdType #define _nrrdEncodingAscii cmtk__nrrdEncodingAscii #define _nrrdEncodingBzip2 cmtk__nrrdEncodingBzip2 #define _nrrdEncodingGzip cmtk__nrrdEncodingGzip #define _nrrdEncodingHex cmtk__nrrdEncodingHex #define _nrrdEncodingRaw cmtk__nrrdEncodingRaw #define _nrrdEncodingUnknown cmtk__nrrdEncodingUnknown #define _nrrdEncodingUnknown_available cmtk__nrrdEncodingUnknown_available #define _nrrdEncodingUnknown_read cmtk__nrrdEncodingUnknown_read #define _nrrdEncodingUnknown_write cmtk__nrrdEncodingUnknown_write #define nrrdBiffKey cmtk_nrrdBiffKey #define nrrdEncodingArray cmtk_nrrdEncodingArray #define nrrdEncodingUnknown cmtk_nrrdEncodingUnknown #define _nrrdEncodingRaw cmtk__nrrdEncodingRaw #define _nrrdEncodingRaw_available cmtk__nrrdEncodingRaw_available #define _nrrdEncodingRaw_read cmtk__nrrdEncodingRaw_read #define _nrrdEncodingRaw_write cmtk__nrrdEncodingRaw_write #define nrrdBiffKey cmtk_nrrdBiffKey #define nrrdElementSize cmtk_nrrdElementSize #define nrrdEncodingRaw cmtk_nrrdEncodingRaw #define nrrdStateVerboseIO cmtk_nrrdStateVerboseIO #define _nrrdEncodingAscii cmtk__nrrdEncodingAscii #define _nrrdEncodingAscii_available cmtk__nrrdEncodingAscii_available #define _nrrdEncodingAscii_read cmtk__nrrdEncodingAscii_read #define _nrrdEncodingAscii_write cmtk__nrrdEncodingAscii_write #define nrrdBiffKey cmtk_nrrdBiffKey #define nrrdElementSize cmtk_nrrdElementSize #define nrrdEncodingAscii cmtk_nrrdEncodingAscii #define nrrdIInsert cmtk_nrrdIInsert #define nrrdSprint cmtk_nrrdSprint #define nrrdType cmtk_nrrdType #define nrrdTypePrintfStr cmtk_nrrdTypePrintfStr #define _nrrdEncodingHex cmtk__nrrdEncodingHex #define _nrrdEncodingHex_available cmtk__nrrdEncodingHex_available #define _nrrdEncodingHex_read cmtk__nrrdEncodingHex_read #define _nrrdEncodingHex_write cmtk__nrrdEncodingHex_write #define _nrrdReadHexTable cmtk__nrrdReadHexTable #define _nrrdWriteHexTable cmtk__nrrdWriteHexTable #define nrrdBiffKey cmtk_nrrdBiffKey #define nrrdElementSize cmtk_nrrdElementSize #define nrrdEncodingHex cmtk_nrrdEncodingHex #define _nrrdEncodingGzip cmtk__nrrdEncodingGzip #define _nrrdEncodingGzip_available cmtk__nrrdEncodingGzip_available #define _nrrdEncodingGzip_read cmtk__nrrdEncodingGzip_read #define _nrrdEncodingGzip_write cmtk__nrrdEncodingGzip_write #define _nrrdGzClose cmtk__nrrdGzClose #define _nrrdGzOpen cmtk__nrrdGzOpen #define _nrrdGzRead cmtk__nrrdGzRead #define _nrrdGzWrite cmtk__nrrdGzWrite #define nrrdBiffKey cmtk_nrrdBiffKey #define nrrdElementSize cmtk_nrrdElementSize #define nrrdEncodingGzip cmtk_nrrdEncodingGzip #define _nrrdKindAltered cmtk__nrrdKindAltered #define nrrdAxisInfoCopy cmtk_nrrdAxisInfoCopy #define nrrdAxisInfoGet_nva cmtk_nrrdAxisInfoGet_nva #define nrrdAxisInfoPosRange cmtk_nrrdAxisInfoPosRange #define nrrdBasicInfoCopy cmtk_nrrdBasicInfoCopy #define nrrdBiffKey cmtk_nrrdBiffKey #define nrrdContentSet_va cmtk_nrrdContentSet_va #define nrrdCrop cmtk_nrrdCrop #define nrrdElementSize cmtk_nrrdElementSize #define nrrdMaybeAlloc_nva cmtk_nrrdMaybeAlloc_nva #define nrrdSlice cmtk_nrrdSlice #define nrrdSpaceVecCopy cmtk_nrrdSpaceVecCopy #define nrrdSpaceVecScaleAdd2 cmtk_nrrdSpaceVecScaleAdd2 #define nrrdStateKeyValuePairsPropagate cmtk_nrrdStateKeyValuePairsPropagate #define nrrdStateKindNoop cmtk_nrrdStateKindNoop #define _nrrdEncodingBzip2 cmtk__nrrdEncodingBzip2 #define _nrrdEncodingBzip2_available cmtk__nrrdEncodingBzip2_available #define _nrrdEncodingBzip2_read cmtk__nrrdEncodingBzip2_read #define _nrrdEncodingBzip2_write cmtk__nrrdEncodingBzip2_write #define nrrdBiffKey cmtk_nrrdBiffKey #define nrrdEncodingBzip2 cmtk_nrrdEncodingBzip2 #define _nrrdFormatEPS cmtk__nrrdFormatEPS #define _nrrdFormatEPS_available cmtk__nrrdFormatEPS_available #define _nrrdFormatEPS_contentStartsLike cmtk__nrrdFormatEPS_contentStartsLike #define _nrrdFormatEPS_fitsInto cmtk__nrrdFormatEPS_fitsInto #define _nrrdFormatEPS_nameLooksLike cmtk__nrrdFormatEPS_nameLooksLike #define _nrrdFormatEPS_read cmtk__nrrdFormatEPS_read #define _nrrdFormatEPS_write cmtk__nrrdFormatEPS_write #define nrrdBiffKey cmtk_nrrdBiffKey #define nrrdFormatEPS cmtk_nrrdFormatEPS #define _nrrdFormatPNG cmtk__nrrdFormatPNG #define _nrrdFormatPNG_available cmtk__nrrdFormatPNG_available #define _nrrdFormatPNG_contentStartsLike cmtk__nrrdFormatPNG_contentStartsLike #define _nrrdFormatPNG_fitsInto cmtk__nrrdFormatPNG_fitsInto #define _nrrdFormatPNG_nameLooksLike cmtk__nrrdFormatPNG_nameLooksLike #define _nrrdFormatPNG_read cmtk__nrrdFormatPNG_read #define _nrrdFormatPNG_write cmtk__nrrdFormatPNG_write #define nrrdBiffKey cmtk_nrrdBiffKey #define nrrdFormatPNG cmtk_nrrdFormatPNG #define _nrrdFormatPNM cmtk__nrrdFormatPNM #define _nrrdFormatPNM_available cmtk__nrrdFormatPNM_available #define _nrrdFormatPNM_contentStartsLike cmtk__nrrdFormatPNM_contentStartsLike #define _nrrdFormatPNM_fitsInto cmtk__nrrdFormatPNM_fitsInto #define _nrrdFormatPNM_nameLooksLike cmtk__nrrdFormatPNM_nameLooksLike #define _nrrdFormatPNM_read cmtk__nrrdFormatPNM_read #define _nrrdFormatPNM_write cmtk__nrrdFormatPNM_write #define nrrdBiffKey cmtk_nrrdBiffKey #define nrrdFormatPNM cmtk_nrrdFormatPNM #define _nrrdFormatText cmtk__nrrdFormatText #define _nrrdFormatText_available cmtk__nrrdFormatText_available #define _nrrdFormatText_contentStartsLike cmtk__nrrdFormatText_contentStartsLike #define _nrrdFormatText_fitsInto cmtk__nrrdFormatText_fitsInto #define _nrrdFormatText_nameLooksLike cmtk__nrrdFormatText_nameLooksLike #define _nrrdFormatText_read cmtk__nrrdFormatText_read #define _nrrdFormatText_write cmtk__nrrdFormatText_write #define nrrdBiffKey cmtk_nrrdBiffKey #define nrrdFormatText cmtk_nrrdFormatText #define _nrrdFormatVTK cmtk__nrrdFormatVTK #define _nrrdFormatVTK_available cmtk__nrrdFormatVTK_available #define _nrrdFormatVTK_contentStartsLike cmtk__nrrdFormatVTK_contentStartsLike #define _nrrdFormatVTK_fitsInto cmtk__nrrdFormatVTK_fitsInto #define _nrrdFormatVTK_nameLooksLike cmtk__nrrdFormatVTK_nameLooksLike #define _nrrdFormatVTK_read cmtk__nrrdFormatVTK_read #define _nrrdFormatVTK_write cmtk__nrrdFormatVTK_write #define nrrdBiffKey cmtk_nrrdBiffKey #define nrrdFormatVTK cmtk_nrrdFormatVTK #endif /* __cmtk_NrrdIO_mangle_h */ cmtk-3.3.1/Utilities/NrrdIO/comment.c000066400000000000000000000065461276303427400174150ustar00rootroot00000000000000/* NrrdIO: stand-alone code for basic nrrd functionality Copyright (C) 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. */ #include "NrrdIO.h" #include "privateNrrd.h" /* ******** nrrdCommentAdd() ** ** Adds a given string to the list of comments ** Leading spaces (' ') and comment chars ('#') are not included. ** ** This function does NOT use biff. */ int nrrdCommentAdd(Nrrd *nrrd, const char *_str) { /* static const char me[]="nrrdCommentAdd";*/ char *str; unsigned int ii; if (!(nrrd && _str)) { /* sprintf(err, "%s: got NULL pointer", me); biffMaybeAdd(NRRD, err, useBiff); */ return 1; } _str += strspn(_str, " #"); if (!strlen(_str)) { /* we don't bother adding comments with no length */ return 0; } if (!strcmp(_str, _nrrdFormatURLLine0) || !strcmp(_str, _nrrdFormatURLLine1)) { /* sneaky hack: don't store the format URL comment lines */ return 0; } str = airStrdup(_str); if (!str) { /* sprintf(err, "%s: couldn't strdup given string", me); biffMaybeAdd(NRRD, err, useBiff); */ return 1; } /* clean out carraige returns that would screw up reader */ airOneLinify(str); ii = airArrayLenIncr(nrrd->cmtArr, 1); if (!nrrd->cmtArr->data) { /* sprintf(err, "%s: couldn't lengthen comment array", me); biffMaybeAdd(NRRD, err, useBiff); */ return 1; } nrrd->cmt[ii] = str; return 0; } /* ******** nrrdCommentClear() ** ** blows away comments, but does not blow away the comment airArray */ void nrrdCommentClear(Nrrd *nrrd) { if (nrrd) { airArrayLenSet(nrrd->cmtArr, 0); } } /* ******** nrrdCommentCopy() ** ** copies comments from one nrrd to another ** Existing comments in nout are blown away ** ** This does NOT use biff. */ int nrrdCommentCopy(Nrrd *nout, const Nrrd *nin) { /* static const char me[]="nrrdCommentCopy"; */ int E; unsigned int numc, ii; if (!(nout && nin)) { /* sprintf(err, "%s: got NULL pointer", me); biffMaybeAdd(NRRD, err, useBiff); */ return 1; } if (nout == nin) { /* can't satisfy semantics of copying with nout==nin */ return 2; } nrrdCommentClear(nout); numc = nin->cmtArr->len; E = 0; for (ii=0; iicmt[ii]); } if (E) { /* sprintf(err, "%s: couldn't add all comments", me); biffMaybeAdd(NRRD, err, useBiff); */ return 3; } return 0; } cmtk-3.3.1/Utilities/NrrdIO/defaultsNrrd.c000066400000000000000000000066051276303427400204040ustar00rootroot00000000000000/* NrrdIO: stand-alone code for basic nrrd functionality Copyright (C) 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. */ #include "NrrdIO.h" #include "privateNrrd.h" /* ** these aren't "const"s because the user should be able to change ** default behavior- until a more sophisticated mechanism for this ** kind of control is developed, it seems simple and usable enough to ** have this be global state which we agree to treat nicely, as in, ** threads shouldn't be changing these willy-nilly. ** ** What IS a "default"? A default is the assertion of a certain ** choice in situations where the user hasn't set it explicitly, but ** COULD. The pad value in resampling is a good example: it is set by ** a constructor to nrrdDefaultResamplePadValue, but the user can also set it ** explicitly. */ int nrrdDefaultWriteEncodingType = nrrdEncodingTypeRaw; int nrrdDefaultWriteBareText = AIR_TRUE; unsigned int nrrdDefaultWriteCharsPerLine = 75; unsigned int nrrdDefaultWriteValsPerLine = 8; int nrrdDefaultCenter = nrrdCenterCell; double nrrdDefaultSpacing = 1.0; /* these aren't really "defaults" because there's no other channel for specifying this information. It is just global state. Obviously, like defaults, they are not thread-safe if different threads ever set them differently. */ int nrrdStateVerboseIO = 0; int nrrdStateKeyValuePairsPropagate = AIR_FALSE; int nrrdStateAlwaysSetContent = AIR_TRUE; int nrrdStateDisableContent = AIR_FALSE; const char *nrrdStateUnknownContent = NRRD_UNKNOWN; int nrrdStateGrayscaleImage3D = AIR_FALSE; /* there is no sane reason to change this initialization */ int nrrdStateKeyValueReturnInternalPointers = AIR_FALSE; /* Making the default for this be AIR_TRUE means that nrrd is not only completely conservative about updating kind, but purposely stupid. Nrrd is only going to implement the most converative kind of logic anyway, based on existing sementics nailed down by the format spec. */ int nrrdStateKindNoop = AIR_FALSE; /* these are helper functions for min/max testing */ airLLong _nrrdLLongMaxHelp(airLLong val) { return val*2 + 1; } airLLong _nrrdLLongMinHelp(airLLong val) { return val*2; } airULLong _nrrdULLongMaxHelp(airULLong val) { return val + 1; } /* should the acceptance (or not) of malformed NRRD header fields embedded in PNM or text comments be controlled here? */ /* Are there other assumptions currently built into nrrd which could stand to be user-controllable? */ cmtk-3.3.1/Utilities/NrrdIO/dio.c000066400000000000000000000223631276303427400165210ustar00rootroot00000000000000/* NrrdIO: stand-alone code for basic nrrd functionality Copyright (C) 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. */ #include "NrrdIO.h" #include "teemDio.h" #if TEEM_DIO == 0 #else /* HEY: these may be SGI-specific */ #include #include #include #endif #if TEEM_DIO == 0 const int airMyDio = 0; #else const int airMyDio = 1; #endif int airDisableDio = AIR_FALSE; static const char _airNoDioErr[AIR_NODIO_MAX+2][AIR_STRLEN_SMALL] = { "(invalid noDio value)", "CAN TOO do direct I/O!", "direct I/O apparently not available on this architecture", "direct I/O apparently not suitable for given file format", "won't do direct I/O on std{in|out|err}", "got -1 as file descriptor", "fcntl(F_DIOINFO) to learn direct I/O specifics failed", "requested transfer size is too small", "requested transfer size not a multiple of d_miniosz", "data memory address not multiple of d_mem", "current file position not multiple of d_miniosz", "fcntl(F_SETFL, FDIRECT) to turn on direct I/O failed", "memalign() test (on a small chuck of memory) failed", "direct I/O (in air library) has been disabled with airDisableDio" }; const char * airNoDioErr(int noDio) { if (AIR_IN_CL(0, noDio, AIR_NODIO_MAX)) { return _airNoDioErr[noDio+1]; } else { return _airNoDioErr[0]; } } /* ******** airDioTest ** ** does everything necessary to assess whether direct IO can be used ** to read a data segment of a given size, from a given file ** descriptor, into a given pointer. The given pointer ptr can be ** NULL, and/or the size can be 0, in order to test the other aspects ** of direct IO. The return value of this is from the airNoDio_* enum. ** Note that airNoDio_okay means, "actually, direct IO *does* seem to ** be possible here". */ #if TEEM_DIO == 0 int airDioTest(int fd, const void *ptr, size_t size) { AIR_UNUSED(fd); AIR_UNUSED(ptr); AIR_UNUSED(size); /* Teem makefiles think no direct IO is possible on this architecture */ return airNoDio_arch; } #else int airDioTest(int fd, const void *ptr, size_t size) { struct dioattr dioinfo; void *tmp; int flags; if (airDisableDio) { /* user turned direct I/O off */ return airNoDio_disable; } if (0 == fd || 1 == fd || 2 == fd) { /* This was added because I was noticing a problem with piping between unrrdu programs- sometimes the fread() of the receiving data through a unix pipe ("|") failed to read all the data. If the body of this function was bypassed (with "return airNoDio_disable;", for instance), then the problem went away. The problematic call seemed to be the fflush() below (Tue Feb 1 06:47:33 EST 2005: which has since been removed with the change of this function's argument from a FILE * to an integral file descriptor). I don't think direct I/O is possible on stdin, stdout, or stdout, since the fcntl() call below fails on stdin and stdout. However, something about making that fcntl() call changes something which means that about half the time, the read() on a piped stdin fails (on an irix6.n32 O2, at least). So, seems to be safest to just explicitly say that direct I/O is unavailable, based solely on the file descriptor number (0, 1, 2). */ return airNoDio_std; } if (-1 == fd) { /* caller probably couldn't get the underlying file descriptor */ return airNoDio_fd; } if (0 != fcntl(fd, F_DIOINFO, &dioinfo)) { /* couldn't learn direct I/O specifics */ return airNoDio_dioinfo; } if (size) { /* ** direct I/O requirements: ** 1) xfer size between d_miniosz and d_maxiosz ** 2) xfer size a multiple of d_miniosz ** 3) memory buffer on d_mem-byte boundary ** 4) file position on d_miniosz-byte boundary ** ** As long as xfer size is >= d_miniosz and meets req. #2, then ** we can break the xfer into d_maxiosz-size pieces of need be. ** We can test #3 here if we're given non-NULL ptr ** We can always test #4 */ if (size < dioinfo.d_miniosz) { /* fails req. #1 above */ return airNoDio_small; } /* we don't actually check for being too large, since we can always do IO on d_maxiosz-sized pieces */ if (size % dioinfo.d_miniosz) { /* fails req. #2 above */ return airNoDio_size; } } if (ptr) { if ((unsigned long)(ptr) % dioinfo.d_mem) { /* fails req. #3 above */ return airNoDio_ptr; } } else { tmp = memalign(dioinfo.d_mem, dioinfo.d_miniosz); if (!tmp) { /* couldn't even alloc (via memalign) the minimum size */ return airNoDio_test; } free(tmp); } if (lseek(fd, 0, SEEK_CUR) % dioinfo.d_miniosz) { /* fails req. #4 above */ return airNoDio_fpos; } flags = fcntl(fd, F_GETFL); if (-1 == fcntl(fd, F_SETFL, flags | FDIRECT)) { /* couln't turn on direct I/O */ return airNoDio_setfl; } /* put things back the way they were */ fcntl(fd, F_SETFL, flags); /* as far as we know, direct I/O seems workable */ return airNoDio_okay; } #endif /* ******** airDioInfo ** ** does the fcntl stuff to learn the direct IO parameters: ** align: required alignment of memory (pointer must be multiple of this) ** min: minimum size of dio transfer ** max: maximum size of dio transfer ** ** NOTE: this does not try to do any error checking, because it assumes ** that you've already called airDioTest without incident. */ #if TEEM_DIO == 0 void airDioInfo(int *align, int *min, int *max, int fd) { AIR_UNUSED(align); AIR_UNUSED(min); AIR_UNUSED(max); AIR_UNUSED(fd); return; } #else void airDioInfo(int *align, int *min, int *max, int fd) { struct dioattr dioinfo; if (align && min && max && !fcntl(fd, F_DIOINFO, &dioinfo)) { *align = dioinfo.d_mem; *min = dioinfo.d_miniosz; *max = dioinfo.d_maxiosz; } return; } #endif /* ******** airDioMalloc ** ** does direct IO compatible memory allocation. ** ** NOTE: like airDioInfo, this assumes that you've called airDioTest ** without incident */ #if TEEM_DIO == 0 void * airDioMalloc(size_t size, int fd) { AIR_UNUSED(size); AIR_UNUSED(fd); return NULL; } #else void * airDioMalloc(size_t size, int fd) { int align, min, max; airDioInfo(&align, &min, &max, fd); return memalign(align, size); } #endif /* ******** airDioRead ** ** like read(), but for direct IO. The idea is that you call this on as ** big a chunk of memory as possible. ** ** NOTE: like airDioInfo, this assumes that you've called airDioTest ** without incident */ #if TEEM_DIO == 0 size_t airDioRead(int fd, void *_ptr, size_t size) { AIR_UNUSED(fd); AIR_UNUSED(_ptr); AIR_UNUSED(size); return 0; } #else size_t airDioRead(int fd, void *_ptr, size_t size) { size_t red, totalred; int align, min, max, flags; size_t remain, part; char *ptr; if (!( _ptr && airNoDio_okay == airDioTest(fd, _ptr, size) )) { return 0; } flags = fcntl(fd, F_GETFL); fcntl(fd, F_SETFL, flags | FDIRECT); airDioInfo(&align, &min, &max, fd); remain = size; totalred = 0; ptr = (char*)_ptr; do { part = AIR_MIN(remain, max); red = read(fd, ptr, part); totalred += red; if (red != part) { break; } ptr += red; remain -= red; } while (remain); fcntl(fd, F_SETFL, flags); return totalred; } #endif /* ******** airDioWrite ** ** like write(), but for direct IO. The idea is that you call this on as ** big a chunk of memory as possible. ** ** NOTE: like airDioInfo, this assumes that you've called airDioTest ** without incident */ #if TEEM_DIO == 0 size_t airDioWrite(int fd, const void *_ptr, size_t size) { AIR_UNUSED(fd); AIR_UNUSED(_ptr); AIR_UNUSED(size); return 0; } #else size_t airDioWrite(int fd, const void *_ptr, size_t size) { size_t rit, totalrit; int align, min, max, flags; size_t remain, part; char *ptr; if (!( _ptr && (airNoDio_okay == airDioTest(fd, _ptr, size)) )) { return 0; } flags = fcntl(fd, F_GETFL); fcntl(fd, F_SETFL, flags | FDIRECT); airDioInfo(&align, &min, &max, fd); remain = size; totalrit = 0; ptr = (char*)_ptr; do { part = AIR_MIN(remain, max); rit = write(fd, ptr, part); totalrit += rit; if (rit != part) { break; } ptr += rit; remain -= rit; } while (remain); fcntl(fd, F_SETFL, flags); return totalrit; } #endif cmtk-3.3.1/Utilities/NrrdIO/encoding.c000066400000000000000000000066251276303427400175370ustar00rootroot00000000000000/* NrrdIO: stand-alone code for basic nrrd functionality Copyright (C) 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. */ #include "NrrdIO.h" #include "privateNrrd.h" /* ** what a NrrdEncoding can assume: ** -- the given nrrd struct has been filled out for the sake of knowing ** nrrd->dim, nrrd->axis[0].size, nrrd->type, and nrrd->blockSize ** AND NOTHING ELSE. See nrrd.h for why those fields, of all things ** are needed for {en/de}coding ** ** what a NrrdEncoding has to do: ** -- read data from file into the "data" argument (BUT NOT nrrd->data!!), ** or vice versa. ** -- respect nrrdStateVerboseIO with messages to stderr, if possible ** -- in case of error, put text error messages into biff via ** biffAddf(NRRD, ...) ** ** The "unknown" encoding below is intended to serve as a template for ** any new encodings being developed. */ static int _nrrdEncodingUnknown_available(void) { /* insert code here */ return AIR_FALSE; } static int _nrrdEncodingUnknown_read(FILE *file, void *data, size_t elementNum, Nrrd *nrrd, struct NrrdIoState_t *nio) { static const char me[]="_nrrdEncodingUnknown_read"; /* insert code here, and remove error handling below */ AIR_UNUSED(file); AIR_UNUSED(data); AIR_UNUSED(elementNum); AIR_UNUSED(nrrd); AIR_UNUSED(nio); biffAddf(NRRD, "%s: ERROR!!! trying to read unknown encoding", me); return 1; } static int _nrrdEncodingUnknown_write(FILE *file, const void *data, size_t elementNum, const Nrrd *nrrd, struct NrrdIoState_t *nio) { static const char me[]="_nrrdEncodingUnknown_write"; /* insert code here, and remove error handling below */ AIR_UNUSED(file); AIR_UNUSED(data); AIR_UNUSED(elementNum); AIR_UNUSED(nrrd); AIR_UNUSED(nio); biffAddf(NRRD, "%s: ERROR!!! trying to write unknown encoding", me); return 1; } const NrrdEncoding _nrrdEncodingUnknown = { "unknown", /* name */ "unknown", /* suffix */ AIR_FALSE, /* endianMatters */ AIR_FALSE, /* isCompression */ _nrrdEncodingUnknown_available, _nrrdEncodingUnknown_read, _nrrdEncodingUnknown_write }; const NrrdEncoding *const nrrdEncodingUnknown = &_nrrdEncodingUnknown; const NrrdEncoding *const nrrdEncodingArray[NRRD_ENCODING_TYPE_MAX+1] = { &_nrrdEncodingUnknown, &_nrrdEncodingRaw, &_nrrdEncodingAscii, &_nrrdEncodingHex, &_nrrdEncodingGzip, &_nrrdEncodingBzip2 }; cmtk-3.3.1/Utilities/NrrdIO/encodingAscii.c000066400000000000000000000130301276303427400204740ustar00rootroot00000000000000/* NrrdIO: stand-alone code for basic nrrd functionality Copyright (C) 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. */ #include "NrrdIO.h" #include "privateNrrd.h" static FILE *_fileSave = NULL; static int _nrrdEncodingAscii_available(void) { return AIR_TRUE; } static int _nrrdEncodingAscii_read(FILE *file, void *_data, size_t elNum, Nrrd *nrrd, NrrdIoState *nio) { static const char me[]="_nrrdEncodingAscii_read"; char numbStr[AIR_STRLEN_HUGE]; /* HEY: fix this */ char *nstr; size_t I; char *data; int tmp; AIR_UNUSED(nio); _fileSave = file; if (nrrdTypeBlock == nrrd->type) { biffAddf(NRRD, "%s: can't read nrrd type %s from %s", me, airEnumStr(nrrdType, nrrdTypeBlock), nrrdEncodingAscii->name); return 1; } data = (char*)_data; I = 0; while (I < elNum) { char stmp1[AIR_STRLEN_SMALL], stmp2[AIR_STRLEN_SMALL]; /* HEY: we can easily suffer here from a standard buffer overflow problem; this was a source of a mysterious unu crash: echo "0 0 0 0 1 0 0 0 0" \ | unu reshape -s 9 1 1 \ | unu pad -min 0 0 0 -max 8 8 8 \ | unu make -s 9 9 9 -t float -e ascii -ls 9 \ -spc LPS -orig "(0,0,0)" -dirs "(1,0,0) (0,1,0) (0,0,1)" This particular case is resolved by changing AIR_STRLEN_HUGE to AIR_STRLEN_HUGE*100, but the general problem remains. This motivated adding the memory corruption test */ if (1 != fscanf(file, "%s", numbStr)) { biffAddf(NRRD, "%s: couldn't parse element %s of %s", me, airSprintSize_t(stmp1, I+1), airSprintSize_t(stmp2, elNum)); return 1; } if (file != _fileSave) { fprintf(stderr, "%s: PANIC memory corruption detected\n", me); /* this may crash, hence the fprintf above to help debug */ biffAddf(NRRD, "%s: PANIC memory corruption detected", me); return 1; } if (!strcmp(",", numbStr)) { /* its an isolated comma, not a value, pass over this */ continue; } /* get past any commas prefixing a number (without space) */ nstr = numbStr + strspn(numbStr, ","); if (nrrd->type >= nrrdTypeInt) { /* sscanf supports putting value directly into this type */ if (1 != airSingleSscanf(nstr, nrrdTypePrintfStr[nrrd->type], (void*)(data + I*nrrdElementSize(nrrd)))) { biffAddf(NRRD, "%s: couldn't parse %s %s of %s (\"%s\")", me, airEnumStr(nrrdType, nrrd->type), airSprintSize_t(stmp1, I+1), airSprintSize_t(stmp2, elNum), nstr); return 1; } } else { /* sscanf value into an int first */ if (1 != airSingleSscanf(nstr, "%d", &tmp)) { biffAddf(NRRD, "%s: couldn't parse element %s of %s (\"%s\")", me, airSprintSize_t(stmp1, I+1), airSprintSize_t(stmp2, elNum), nstr); return 1; } nrrdIInsert[nrrd->type](data, I, tmp); } I++; } return 0; } static int _nrrdEncodingAscii_write(FILE *file, const void *_data, size_t elNum, const Nrrd *nrrd, NrrdIoState *nio) { static const char me[]="_nrrdEncodingAscii_write"; char buff[AIR_STRLEN_MED]; size_t bufflen, linelen; const char *data; size_t I; if (nrrdTypeBlock == nrrd->type) { biffAddf(NRRD, "%s: can't write nrrd type %s to %s", me, airEnumStr(nrrdType, nrrdTypeBlock), nrrdEncodingAscii->name); return 1; } data = AIR_CAST(const char*, _data); linelen = 0; for (I=0; Itype](buff, data); if (1 == nrrd->dim) { fprintf(file, "%s\n", buff); } else if (nrrd->dim == 2 && nrrd->axis[0].size <= nio->valsPerLine) { fprintf(file, "%s%c", buff, (I+1)%(nrrd->axis[0].size) ? ' ' : '\n'); } else { bufflen = strlen(buff); if (linelen+bufflen+1 <= nio->charsPerLine) { fprintf(file, "%s%s", I ? " " : "", buff); linelen += (I ? 1 : 0) + bufflen; } else { fprintf(file, "\n%s", buff); linelen = bufflen; } } data += nrrdElementSize(nrrd); } /* just to be sure, we always end with a carraige return */ fprintf(file, "\n"); return 0; } const NrrdEncoding _nrrdEncodingAscii = { "ASCII", /* name */ "ascii", /* suffix */ AIR_FALSE, /* endianMatters */ AIR_FALSE, /* isCompression */ _nrrdEncodingAscii_available, _nrrdEncodingAscii_read, _nrrdEncodingAscii_write }; const NrrdEncoding *const nrrdEncodingAscii = &_nrrdEncodingAscii; cmtk-3.3.1/Utilities/NrrdIO/encodingBzip2.c000066400000000000000000000046151276303427400204430ustar00rootroot00000000000000/* NrrdIO: stand-alone code for basic nrrd functionality Copyright (C) 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. */ #include "NrrdIO.h" #include "privateNrrd.h" int _nrrdEncodingBzip2_available(void) { return AIR_FALSE; } int _nrrdEncodingBzip2_read(FILE *file, void *data, size_t elementNum, Nrrd *nrrd, struct NrrdIoState_t *nio) { char me[]="_nrrdEncodingBzip2_read", err[AIR_STRLEN_MED]; AIR_UNUSED(file); AIR_UNUSED(data); AIR_UNUSED(elementNum); AIR_UNUSED(nrrd); AIR_UNUSED(nio); sprintf(err, "%s: Sorry, %s encoding not available in NrrdIO", me, nrrdEncodingBzip2->name); biffAdd(NRRD, err); return 1; } int _nrrdEncodingBzip2_write(FILE *file, const void *data, size_t elementNum, const Nrrd *nrrd, struct NrrdIoState_t *nio) { char me[]="_nrrdEncodingBzip2_write", err[AIR_STRLEN_MED]; AIR_UNUSED(file); AIR_UNUSED(data); AIR_UNUSED(elementNum); AIR_UNUSED(nrrd); AIR_UNUSED(nio); sprintf(err, "%s: Sorry, %s encoding not available in NrrdIO", me, nrrdEncodingBzip2->name); biffAdd(NRRD, err); return 1; } const NrrdEncoding _nrrdEncodingBzip2 = { "bzip2", /* name */ "raw.bz2", /* suffix */ AIR_TRUE, /* endianMatters */ AIR_TRUE, /* isCompression */ _nrrdEncodingBzip2_available, _nrrdEncodingBzip2_read, _nrrdEncodingBzip2_write }; const NrrdEncoding *const nrrdEncodingBzip2 = &_nrrdEncodingBzip2; cmtk-3.3.1/Utilities/NrrdIO/encodingGzip.c000066400000000000000000000254231276303427400203660ustar00rootroot00000000000000/* NrrdIO: stand-alone code for basic nrrd functionality Copyright (C) 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. */ #include "NrrdIO.h" #include "privateNrrd.h" static int _nrrdEncodingGzip_available(void) { #if TEEM_ZLIB return AIR_TRUE; #else return AIR_FALSE; #endif } /* ** Maximum size that allow zlib to try to read or write at once. ** The real limit is UINT_MAX, but a smaller value here permits ** exercising the multi-chunk capability of the code below. */ static unsigned int _nrrdZlibMaxChunk = UINT_MAX; /* ** nio->byteSkip < 0 functionality contributed by Katharina Quintus */ static int _nrrdEncodingGzip_read(FILE *file, void *_data, size_t elNum, Nrrd *nrrd, NrrdIoState *nio) { static const char me[]="_nrrdEncodingGzip_read"; #if TEEM_ZLIB size_t sizeData, sizeRed; int error; long int bi; unsigned int didread, sizeChunk, maxChunk; char *data; gzFile gzfin; airPtrPtrUnion appu; sizeData = nrrdElementSize(nrrd)*elNum; /* Create the gzFile for reading in the gzipped data. */ if ((gzfin = _nrrdGzOpen(file, "rb")) == Z_NULL) { /* there was a problem */ biffAddf(NRRD, "%s: error opening gzFile", me); return 1; } /* keeps track of how many bytes have been successfully read in */ sizeRed = 0; /* zlib can only handle data sizes up to UINT_MAX ==> if there's more than UINT_MAX bytes to read in, we read in in chunks. However, we wrap a value _nrrdZlibMaxChunk around UINT_MAX for testing purposes. Given how sizeChunk is used below, we also cap chunk size at _nrrdZlibMaxChunk/2 to prevent overflow. */ maxChunk = _nrrdZlibMaxChunk/2; sizeChunk = AIR_CAST(unsigned int, AIR_MIN(sizeData, maxChunk)); if (nio->byteSkip < 0) { /* We don't know the size of the size to skip before the data, so decompress the data first into a temporary memory buffer. Then the byteskipping is then just memcpy-ing the appropriate region of memory from "buff" into the given "_data" pointer */ char *buff; airArray *buffArr; long backwards; /* setting the airArray increment to twice the chunk size means that for headers that are small compared to the data, the airArray never actually has to reallocate. The unit is 1 because we are managing the reading in terms of bytes (sizeof(char)==1 by definition) */ buff = NULL; appu.c = &buff; buffArr = airArrayNew(appu.v, NULL, 1, 2*sizeChunk); airArrayLenSet(buffArr, sizeChunk); if (!( buffArr && buffArr->data )) { biffAddf(NRRD, "%s: couldn't initialize airArray\n", me); return 1; } /* we keep reading in chunks as long as there hasn't been an error, and we haven't hit EOF (EOF signified by read == 0). Unlike the code below (for positive byteskip), we are obligated to read until the bitter end, and can't update sizeChunk to encompass only the required data. */ while (!(error = _nrrdGzRead(gzfin, buff + sizeRed, sizeChunk, &didread)) && didread > 0) { sizeRed += didread; if (didread >= sizeChunk) { /* we were able to read as much data as we requested, maybe there is more, so we need to make our temp buffer bigger */ unsigned int newlen = buffArr->len + sizeChunk; if (newlen < buffArr->len) { biffAddf(NRRD, "%s: array size will exceed uint capacity", me); return 1; } airArrayLenSet(buffArr, newlen); if (!buffArr->data) { biffAddf(NRRD, "%s: couldn't re-allocate data buffer", me); return 1; } } } if (error) { biffAddf(NRRD, "%s: error reading from gzFile", me); return 1; } /* backwards is (positive) number of bytes AFTER data that we ignore */ backwards = -nio->byteSkip - 1; if (sizeRed < sizeData + AIR_CAST(size_t, backwards)) { char stmp1[AIR_STRLEN_SMALL], stmp2[AIR_STRLEN_SMALL]; biffAddf(NRRD, "%s: expected %s bytes but received only %s", me, airSprintSize_t(stmp1, sizeData + AIR_CAST(size_t, backwards)), airSprintSize_t(stmp2, sizeRed)); return 1; } /* also handles nio->byteSkip == -N-1 signifying extra N bytes at end */ memcpy(_data, buff + sizeRed - sizeData - backwards, sizeData); airArrayNuke(buffArr); } else { /* no negative byteskip: after byteskipping, we can read directly into given data buffer */ if (nio->byteSkip > 0) { for (bi=0; bibyteSkip; bi++) { unsigned char b; /* Check to see if a single byte was able to be read. */ if (_nrrdGzRead(gzfin, &b, 1, &didread) != 0 || didread != 1) { biffAddf(NRRD, "%s: hit an error skipping byte %ld of %ld", me, bi, nio->byteSkip); return 1; } } } /* Pointer to chunks as we read them. */ data = AIR_CAST(char *, _data); while (!(error = _nrrdGzRead(gzfin, data, sizeChunk, &didread)) && didread > 0) { /* Increment the data pointer to the next available chunk. */ data += didread; sizeRed += didread; /* We only want to read as much data as we need, so we need to check to make sure that we don't request data that might be there but that we don't want. This will reduce sizeChunk when we get to the last block (which may be smaller than the original sizeChunk). */ if (sizeData >= sizeRed && sizeData - sizeRed < sizeChunk) { sizeChunk = AIR_CAST(unsigned int, sizeData - sizeRed); } } if (error) { biffAddf(NRRD, "%s: error reading from gzFile", me); return 1; } /* Check to see if we got out as much as we thought we should. */ if (sizeRed != sizeData) { char stmp1[AIR_STRLEN_SMALL], stmp2[AIR_STRLEN_SMALL]; biffAddf(NRRD, "%s: expected %s bytes but received %s", me, airSprintSize_t(stmp1, sizeData), airSprintSize_t(stmp2, sizeRed)); return 1; } } /* Close the gzFile. Since _nrrdGzClose does not close the FILE* we will not encounter problems when dataFile is closed later. */ if (_nrrdGzClose(gzfin)) { biffAddf(NRRD, "%s: error closing gzFile", me); return 1; } return 0; #else AIR_UNUSED(file); AIR_UNUSED(_data); AIR_UNUSED(elNum); AIR_UNUSED(nrrd); AIR_UNUSED(nio); biffAddf(NRRD, "%s: sorry, this nrrd not compiled with gzip enabled", me); return 1; #endif } static int _nrrdEncodingGzip_write(FILE *file, const void *_data, size_t elNum, const Nrrd *nrrd, NrrdIoState *nio) { static const char me[]="_nrrdEncodingGzip_write"; #if TEEM_ZLIB size_t sizeData, sizeWrit; int fmt_i=0, error; const char *data; char fmt[4]; gzFile gzfout; unsigned int wrote, sizeChunk; sizeData = nrrdElementSize(nrrd)*elNum; /* Set format string based on the NrrdIoState parameters. */ fmt[fmt_i++] = 'w'; if (0 <= nio->zlibLevel && nio->zlibLevel <= 9) fmt[fmt_i++] = AIR_CAST(char, '0' + nio->zlibLevel); switch (nio->zlibStrategy) { case nrrdZlibStrategyHuffman: fmt[fmt_i++] = 'h'; break; case nrrdZlibStrategyFiltered: fmt[fmt_i++] = 'f'; break; case nrrdZlibStrategyDefault: default: break; } fmt[fmt_i] = 0; /* Create the gzFile for writing in the gzipped data. */ if ((gzfout = _nrrdGzOpen(file, fmt)) == Z_NULL) { /* there was a problem */ biffAddf(NRRD, "%s: error opening gzFile", me); return 1; } /* zlib can only handle data sizes up to UINT_MAX ==> if there's more than UINT_MAX bytes to write out, we write out in chunks. As above, we wrap _nrrdZlibMaxChunk around UINT_MAX for testing purposes. */ sizeChunk = AIR_CAST(unsigned int, AIR_MIN(sizeData, _nrrdZlibMaxChunk)); /* keeps track of what how much has been successfully written */ sizeWrit = 0; /* Pointer to the chunks as we write them. */ data = AIR_CAST(const char *, _data); /* Ok, now we can begin writing. */ while ((error = _nrrdGzWrite(gzfout, AIR_CVOIDP(data), sizeChunk, &wrote)) == 0 && wrote > 0) { /* Increment the data pointer to the next available spot. */ data += wrote; sizeWrit += wrote; /* We only want to write as much data as we need, so we need to check to make sure that we don't write more data than is there. This will reduce sizeChunk when we get to the last block (which may be smaller than the original sizeChunk). */ if (sizeData >= sizeWrit && sizeData - sizeWrit < sizeChunk) sizeChunk = AIR_CAST(unsigned int, sizeData - sizeWrit); } if (error) { biffAddf(NRRD, "%s: error writing to gzFile", me); return 1; } /* Check to see if we wrote out as much as we thought we should. */ if (sizeWrit != sizeData) { char stmp1[AIR_STRLEN_SMALL], stmp2[AIR_STRLEN_SMALL]; biffAddf(NRRD, "%s: expected to write %s bytes, but only wrote %s", me, airSprintSize_t(stmp1, sizeData), airSprintSize_t(stmp2, sizeWrit)); return 1; } /* Close the gzFile. Since _nrrdGzClose does not close the FILE* we will not encounter problems when dataFile is closed later. */ if (_nrrdGzClose(gzfout)) { biffAddf(NRRD, "%s: error closing gzFile", me); return 1; } return 0; #else AIR_UNUSED(file); AIR_UNUSED(_data); AIR_UNUSED(elNum); AIR_UNUSED(nrrd); AIR_UNUSED(nio); biffAddf(NRRD, "%s: sorry, this nrrd not compiled with zlib " "(needed for gzip) enabled", me); return 1; #endif } const NrrdEncoding _nrrdEncodingGzip = { "gzip", /* name */ "raw.gz", /* suffix */ AIR_TRUE, /* endianMatters */ AIR_TRUE, /* isCompression */ _nrrdEncodingGzip_available, _nrrdEncodingGzip_read, _nrrdEncodingGzip_write }; const NrrdEncoding *const nrrdEncodingGzip = &_nrrdEncodingGzip; cmtk-3.3.1/Utilities/NrrdIO/encodingHex.c000066400000000000000000000115011276303427400201710ustar00rootroot00000000000000/* NrrdIO: stand-alone code for basic nrrd functionality Copyright (C) 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. */ #include "NrrdIO.h" #include "privateNrrd.h" static const int _nrrdWriteHexTable[16] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; /* ** -2: not allowed, error ** -1: whitespace ** [0,15]: values */ static const int _nrrdReadHexTable[128] = { /* 0 1 2 3 4 5 6 7 8 9 */ -2, -2, -2, -2, -2, -2, -2, -2, -2, -1, /* 0 */ -1, -1, -1, -1, -2, -2, -2, -2, -2, -2, /* 10 */ -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, /* 20 */ -2, -2, -1, -2, -2, -2, -2, -2, -2, -2, /* 30 */ -2, -2, -2, -2, -2, -2, -2, -2, 0, 1, /* 40 */ 2, 3, 4, 5, 6, 7, 8, 9, -2, -2, /* 50 */ -2, -2, -2, -2, -2, 10, 11, 12, 13, 14, /* 60 */ 15, -2, -2, -2, -2, -2, -2, -2, -2, -2, /* 70 */ -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, /* 80 */ -2, -2, -2, -2, -2, -2, -2, 10, 11, 12, /* 90 */ 13, 14, 15, -2, -2, -2, -2, -2, -2, -2, /* 100 */ -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, /* 110 */ -2, -2, -2, -2, -2, -2, -2, -2 /* 120 */ }; static int _nrrdEncodingHex_available(void) { return AIR_TRUE; } static int _nrrdEncodingHex_read(FILE *file, void *_data, size_t elNum, Nrrd *nrrd, NrrdIoState *nio) { static const char me[]="_nrrdEncodingHex_read"; size_t nibIdx, nibNum; unsigned char *data; int car=0, nib; AIR_UNUSED(nio); data = AIR_CAST(unsigned char *, _data); nibIdx = 0; nibNum = 2*elNum*nrrdElementSize(nrrd); if (nibNum/elNum != 2*nrrdElementSize(nrrd)) { biffAddf(NRRD, "%s: size_t can't hold 2*(#bytes in array)\n", me); return 1; } while (nibIdx < nibNum) { unsigned char nibshift; car = fgetc(file); if (EOF == car) break; nib = _nrrdReadHexTable[car & 127]; if (-2 == nib) { /* not a valid hex character */ break; } if (-1 == nib) { /* its white space */ continue; } /* else it is a valid character, representing a value from 0 to 15 */ nibshift = AIR_CAST(unsigned char, nib << (4*(1-(nibIdx & 1)))); /* HEY not sure why the cast is needed with gcc v4.8 -Wconversion */ *data = AIR_CAST(unsigned char, *data + nibshift); data += nibIdx & 1; nibIdx++; } if (nibIdx != nibNum) { char stmp1[AIR_STRLEN_SMALL], stmp2[AIR_STRLEN_SMALL]; if (EOF == car) { biffAddf(NRRD, "%s: hit EOF getting byte %s of %s", me, airSprintSize_t(stmp1, nibIdx/2), airSprintSize_t(stmp2, nibNum/2)); } else { biffAddf(NRRD, "%s: hit invalid character ('%c') getting " "byte %s of %s", me, car, airSprintSize_t(stmp1, nibIdx/2), airSprintSize_t(stmp2, nibNum/2)); } return 1; } return 0; } static int _nrrdEncodingHex_write(FILE *file, const void *_data, size_t elNum, const Nrrd *nrrd, NrrdIoState *nio) { /* static const char me[]="_nrrdEncodingHex_write"; */ const unsigned char *data; size_t byteIdx, byteNum; unsigned int bytesPerLine; bytesPerLine = AIR_MAX(1, nio->charsPerLine/2); data = AIR_CAST(const unsigned char*, _data); byteNum = elNum*nrrdElementSize(nrrd); for (byteIdx=0; byteIdx>4], _nrrdWriteHexTable[(*data)&15]); if (bytesPerLine-1 == byteIdx % bytesPerLine) { fprintf(file, "\n"); } data++; } /* just to be sure, we always end with a carraige return */ fprintf(file, "\n"); return 0; } const NrrdEncoding _nrrdEncodingHex = { "hex", /* name */ "hex", /* suffix */ AIR_TRUE, /* endianMatters */ AIR_FALSE, /* isCompression */ _nrrdEncodingHex_available, _nrrdEncodingHex_read, _nrrdEncodingHex_write }; const NrrdEncoding *const nrrdEncodingHex = &_nrrdEncodingHex; cmtk-3.3.1/Utilities/NrrdIO/encodingRaw.c000066400000000000000000000161621276303427400202060ustar00rootroot00000000000000/* NrrdIO: stand-alone code for basic nrrd functionality Copyright (C) 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. */ #include "NrrdIO.h" #include "privateNrrd.h" static int _nrrdEncodingRaw_available(void) { return AIR_TRUE; } static int _nrrdEncodingRaw_read(FILE *file, void *data, size_t elementNum, Nrrd *nrrd, NrrdIoState *nio) { static const char me[]="_nrrdEncodingRaw_read"; size_t ret, bsize; int fd, dio, car; long savePos; char *data_c; size_t elementSize, maxChunkSize, remainderValue, chunkSize; size_t retTmp; char stmp[3][AIR_STRLEN_SMALL]; bsize = nrrdElementSize(nrrd)*elementNum; if (nio->format->usesDIO) { fd = fileno(file); dio = airDioTest(fd, data, bsize); } else { fd = -1; dio = airNoDio_format; } if (airNoDio_okay == dio) { if (2 <= nrrdStateVerboseIO) { fprintf(stderr, "with direct I/O ... "); } ret = airDioRead(fd, data, bsize); if (ret != bsize) { biffAddf(NRRD, "%s: airDioRead got read only %s of %sbytes " "(%g%% of expected)", me, airSprintSize_t(stmp[0], ret), airSprintSize_t(stmp[1], bsize), 100.0*AIR_CAST(double, ret)/AIR_CAST(double, bsize)); return 1; } } else { if (2 <= nrrdStateVerboseIO) { if (AIR_DIO && nio->format->usesDIO) { fprintf(stderr, "with fread(), not DIO: %s ...", airNoDioErr(dio)); } } /* HEY: There's a bug in fread/fwrite in gcc 4.2.1 (with SnowLeopard). When it reads/writes a >=2GB data array, it pretends to succeed (i.e. the return value is the right number) but it hasn't actually read/written the data. The work-around is to loop over the data, reading/writing 1GB (or smaller) chunks. */ ret = 0; data_c = (char *)data; elementSize = nrrdElementSize(nrrd); maxChunkSize = 1024 * 1024 * 1024 / elementSize; while(ret < elementNum) { remainderValue = elementNum-ret; if (remainderValue < maxChunkSize) { chunkSize = remainderValue; } else { chunkSize = maxChunkSize; } retTmp = fread(&(data_c[ret*elementSize]), elementSize, chunkSize, file); ret += retTmp; if (retTmp != chunkSize) { biffAddf(NRRD, "%s: fread got only %s %s-sized things, not %s " "(%g%% of expected)", me, airSprintSize_t(stmp[0], ret), airSprintSize_t(stmp[1], nrrdElementSize(nrrd)), airSprintSize_t(stmp[2], elementNum), 100.0*AIR_CAST(double, ret)/AIR_CAST(double, elementNum)); return 1; } } car = fgetc(file); if (EOF != car) { if (1 <= nrrdStateVerboseIO) { fprintf(stderr, "%s: WARNING: finished reading raw data, " "but file not at EOF\n", me); } ungetc(car, file); } if (2 <= nrrdStateVerboseIO && nio->byteSkip && stdin != file) { savePos = ftell(file); if (!fseek(file, 0, SEEK_END)) { double frac = (AIR_CAST(double, bsize) /AIR_CAST(double, ftell(file) + 1)); fprintf(stderr, "(%s: used %g%% of file for nrrd data)\n", me, 100.0*frac); fseek(file, savePos, SEEK_SET); } } } return 0; } static int _nrrdEncodingRaw_write(FILE *file, const void *data, size_t elementNum, const Nrrd *nrrd, NrrdIoState *nio) { static const char me[]="_nrrdEncodingRaw_write"; int fd, dio; size_t ret, bsize; const char *data_c; size_t elementSize, maxChunkSize, remainderValue, chunkSize; size_t retTmp; char stmp[3][AIR_STRLEN_SMALL]; bsize = nrrdElementSize(nrrd)*elementNum; if (nio->format->usesDIO) { fd = fileno(file); dio = airDioTest(fd, data, bsize); } else { fd = -1; dio = airNoDio_format; } if (airNoDio_okay == dio) { if (2 <= nrrdStateVerboseIO) { fprintf(stderr, "with direct I/O ... "); } ret = airDioWrite(fd, data, bsize); if (ret != bsize) { biffAddf(NRRD, "%s: airDioWrite wrote only %s of %s bytes " "(%g%% of expected)", me, airSprintSize_t(stmp[0], ret), airSprintSize_t(stmp[1], bsize), 100.0*AIR_CAST(double, ret)/AIR_CAST(double, bsize)); return 1; } } else { if (2 <= nrrdStateVerboseIO) { if (AIR_DIO && nio->format->usesDIO) { fprintf(stderr, "with fread(), not DIO: %s ...", airNoDioErr(dio)); } } /* HEY: There's a bug in fread/fwrite in gcc 4.2.1 (with SnowLeopard). When it reads/writes a >=2GB data array, it pretends to succeed (i.e. the return value is the right number) but it hasn't actually read/written the data. The work-around is to loop over the data, reading/writing 1GB (or smaller) chunks. */ ret = 0; data_c = AIR_CAST(const char *, data); elementSize = nrrdElementSize(nrrd); maxChunkSize = 1024 * 1024 * 1024 / elementSize; while(ret < elementNum) { remainderValue = elementNum-ret; if (remainderValue < maxChunkSize) { chunkSize = remainderValue; } else { chunkSize = maxChunkSize; } retTmp = fwrite(&(data_c[ret*elementSize]), elementSize, chunkSize, file); ret += retTmp; if (retTmp != chunkSize) { biffAddf(NRRD, "%s: fwrite wrote only %s %s-sized things, not %s " "(%g%% of expected)", me, airSprintSize_t(stmp[0], ret), airSprintSize_t(stmp[1], nrrdElementSize(nrrd)), airSprintSize_t(stmp[2], elementNum), 100.0*AIR_CAST(double, ret)/AIR_CAST(double, elementNum)); return 1; } } fflush(file); /* if (ferror(file)) { biffAddf(NRRD, "%s: ferror returned non-zero", me); return 1; } */ } return 0; } const NrrdEncoding _nrrdEncodingRaw = { "raw", /* name */ "raw", /* suffix */ AIR_TRUE, /* endianMatters */ AIR_FALSE, /* isCompression */ _nrrdEncodingRaw_available, _nrrdEncodingRaw_read, _nrrdEncodingRaw_write }; const NrrdEncoding *const nrrdEncodingRaw = &_nrrdEncodingRaw; cmtk-3.3.1/Utilities/NrrdIO/endianAir.c000066400000000000000000000044411276303427400176350ustar00rootroot00000000000000/* NrrdIO: stand-alone code for basic nrrd functionality Copyright (C) 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. */ #include "NrrdIO.h" /* ******** airMyEndian() ** ** determine at run-time if we are little (1234) or big (4321) endian */ int airMyEndian(void) { int tmpI, ret; char leastbyte; /* set int to 1: least signficant byte will be 1, most signficant byte will be 0 */ tmpI = 1; /* cast address of (4-byte) int to char*, and dereference, which retrieves the byte at the low-address-end of int (the "first" byte in memory ordering). On big endian, we're getting the most significant byte (0); on little endian, we're getting least significant byte (1) */ leastbyte = *(AIR_CAST(char*, &tmpI)); if (leastbyte) { ret = airEndianLittle; } else { ret = airEndianBig; } return ret; } static const char * _airEndianStr[] = { "(unknown endian)", "little", "big" }; static const char * _airEndianDesc[] = { "unknown endianness", "Intel and compatible", "Everyone besides Intel and compatible" }; static const int _airEndianVal[] = { airEndianUnknown, airEndianLittle, airEndianBig, }; static const airEnum _airEndian = { "endian", 2, _airEndianStr, _airEndianVal, _airEndianDesc, NULL, NULL, AIR_FALSE }; const airEnum *const airEndian = &_airEndian; cmtk-3.3.1/Utilities/NrrdIO/endianNrrd.c000066400000000000000000000077011276303427400200310ustar00rootroot00000000000000/* NrrdIO: stand-alone code for basic nrrd functionality Copyright (C) 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. */ #include "NrrdIO.h" static void _nrrdSwap16Endian(void *_data, size_t N) { unsigned short *data, dd, fix, mask; size_t I; if (!_data) { return; } data = AIR_CAST(unsigned short *, _data); mask = AIR_CAST(unsigned short, 0x00FFu); for (I=0; I>= 0x08; fix = (dd & mask) | AIR_CAST(unsigned short, fix << 0x08); data[I] = fix; } } static void _nrrdSwap32Endian(void *_data, size_t N) { unsigned int *data, dd, fix, mask; size_t I; if (!_data) { return; } data = AIR_CAST(unsigned int *, _data); mask = 0x000000FFu; for (I=0; I>= 0x08; fix = (dd & mask) | (fix << 0x08); dd >>= 0x08; fix = (dd & mask) | (fix << 0x08); dd >>= 0x08; fix = (dd & mask) | (fix << 0x08); data[I] = fix; } } static void _nrrdSwap64Endian(void *_data, size_t N) { airULLong *data, dd, fix, mask; size_t I; if (!_data) { return; } data = AIR_CAST(airULLong *, _data); mask = AIR_ULLONG(0x00000000000000FF); for (I=0; I>= 0x08; fix = (dd & mask) | (fix << 0x08); dd >>= 0x08; fix = (dd & mask) | (fix << 0x08); dd >>= 0x08; fix = (dd & mask) | (fix << 0x08); dd >>= 0x08; fix = (dd & mask) | (fix << 0x08); dd >>= 0x08; fix = (dd & mask) | (fix << 0x08); dd >>= 0x08; fix = (dd & mask) | (fix << 0x08); dd >>= 0x08; fix = (dd & mask) | (fix << 0x08); data[I] = fix; } } static void _nrrdNoopEndian(void *data, size_t N) { AIR_UNUSED(data); AIR_UNUSED(N); return; } static void _nrrdBlockEndian(void *data, size_t N) { char me[]="_nrrdBlockEndian"; AIR_UNUSED(data); AIR_UNUSED(N); fprintf(stderr, "%s: WARNING: can't fix endiannes of nrrd type %s\n", me, airEnumStr(nrrdType, nrrdTypeBlock)); } static void (*_nrrdSwapEndian[])(void *, size_t) = { _nrrdNoopEndian, /* 0: nobody knows! */ _nrrdNoopEndian, /* 1: signed 1-byte integer */ _nrrdNoopEndian, /* 2: unsigned 1-byte integer */ _nrrdSwap16Endian, /* 3: signed 2-byte integer */ _nrrdSwap16Endian, /* 4: unsigned 2-byte integer */ _nrrdSwap32Endian, /* 5: signed 4-byte integer */ _nrrdSwap32Endian, /* 6: unsigned 4-byte integer */ _nrrdSwap64Endian, /* 7: signed 8-byte integer */ _nrrdSwap64Endian, /* 8: unsigned 8-byte integer */ _nrrdSwap32Endian, /* 9: 4-byte floating point */ _nrrdSwap64Endian, /* 10: 8-byte floating point */ _nrrdBlockEndian /* 11: size user defined at run time */ }; void nrrdSwapEndian(Nrrd *nrrd) { if (nrrd && nrrd->data && !airEnumValCheck(nrrdType, nrrd->type)) { _nrrdSwapEndian[nrrd->type](nrrd->data, nrrdElementNumber(nrrd)); } return; } cmtk-3.3.1/Utilities/NrrdIO/enum.c000066400000000000000000000160721276303427400167120ustar00rootroot00000000000000/* NrrdIO: stand-alone code for basic nrrd functionality Copyright (C) 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. */ #include "NrrdIO.h" /* ** Until Teem has its own printf implementation, this will have to do; ** it is imperfect because these are not functionally identical. */ #if defined(WIN32) || defined(_WIN32) # define snprintf _snprintf #endif /* ******** airEnumUnknown ** ** return the value representing "unknown" in an enum */ int airEnumUnknown(const airEnum *enm) { if (enm && enm->val) { return enm->val[0]; } else { return 0; } } /* ** _airEnumIndex() ** ** given an enum "enm" and value "val", return the index into enm->str[] ** and enm->desc[] which correspond to that value. To be safe, when ** given an invalid enum value, we return zero. */ static unsigned int _airEnumIndex(const airEnum *enm, int val) { unsigned int ii, ret; ret = 0; if (enm->val) { for (ii=1; ii<=enm->M; ii++) { if (val == enm->val[ii]) { ret = ii; break; } } } else { unsigned int uval; uval = AIR_UINT(val); ret = (0 <= val && uval <= enm->M) ? uval : 0; } return ret; } /* ** returns non-zero if there is an error: given "val" is *not* ** a valid value of the airEnum "enm" */ int airEnumValCheck(const airEnum *enm, int val) { return (0 == _airEnumIndex(enm, val)); } const char * airEnumStr(const airEnum *enm, int val) { unsigned int idx; idx = _airEnumIndex(enm, val); return enm->str[idx]; } const char * airEnumDesc(const airEnum *enm, int val) { unsigned int idx; idx = _airEnumIndex(enm, val); return enm->desc[idx]; } int airEnumVal(const airEnum *enm, const char *str) { char *strCpy, test[AIR_STRLEN_SMALL]; unsigned int ii; if (!str) { return airEnumUnknown(enm); } strCpy = airStrdup(str); if (!enm->sense) { airToLower(strCpy); } if (enm->strEqv) { /* want strlen and not airStrlen here because the strEqv array should be terminated by a non-null empty string */ for (ii=0; strlen(enm->strEqv[ii]); ii++) { airStrcpy(test, AIR_STRLEN_SMALL, enm->strEqv[ii]); if (!enm->sense) { airToLower(test); } if (!strcmp(test, strCpy)) { free(strCpy); return enm->valEqv[ii]; } } } else { /* enm->strEqv NULL */ for (ii=1; ii<=enm->M; ii++) { airStrcpy(test, AIR_STRLEN_SMALL, enm->str[ii]); if (!enm->sense) { airToLower(test); } if (!strcmp(test, strCpy)) { free(strCpy); return enm->val ? enm->val[ii] : (int)ii; /* HEY scrutinize cast */ } } } /* else we never matched a string */ free(strCpy); return airEnumUnknown(enm); } /* ******** airEnumFmtDesc() ** ** Formats a description line for one element "val" of airEnum "enm", ** and puts the result in a NEWLY ALLOCATED string which is the return ** of this function. The formatting is done via sprintf(), as governed ** by "fmt", which should contain to "%s" conversion sequences, the ** first for the string version "val", and the second for the ** description If "canon", then the canonical string representation ** will be used (the one in enm->str[]), otherwise the shortest string ** representation will be used (which differs from the canonical one ** when there is a strEqv[]/valEqv[] pair defining a shorter string) */ char * airEnumFmtDesc(const airEnum *enm, int val, int canon, const char *fmt) { const char *desc; char *buff, ident[AIR_STRLEN_SMALL]; const char *_ident; int i; size_t len; if (!(enm && enm->desc && fmt)) { return airStrdup("(airEnumDesc: invalid args)"); } if (airEnumValCheck(enm, val)) { val = airEnumUnknown(enm); } _ident = airEnumStr(enm, val); if (!canon && enm->strEqv) { len = airStrlen(_ident); for (i=0; airStrlen(enm->strEqv[i]); i++) { if (val != enm->valEqv[i]) { /* this isn't a string representing the value we care about */ continue; } if (airStrlen(enm->strEqv[i]) < len) { /* this one is shorter */ len = airStrlen(enm->strEqv[i]); _ident = enm->strEqv[i]; } } } airStrcpy(ident, AIR_STRLEN_SMALL, _ident); if (!enm->sense) { airToLower(ident); } desc = enm->desc[_airEnumIndex(enm, val)]; buff = AIR_CALLOC(airStrlen(fmt) + airStrlen(ident) + airStrlen(desc) + 1, char); if (buff) { sprintf(buff, fmt, ident, desc); } return buff; } static void _enumPrintVal(FILE *file, const airEnum *enm, int ii) { if (enm->desc) { fprintf(file, "desc: %s\n", enm->desc[ii]); } if (enm->strEqv) { unsigned int jj; fprintf(file, "eqv:"); fflush(file); jj = 0; while (airStrlen(enm->strEqv[jj])) { if (enm->valEqv[jj] == (enm->val ? enm->val[ii] : ii)) { fprintf(file, " \"%s\"", enm->strEqv[jj]); } jj++; } fprintf(file, "\n"); } } void airEnumPrint(FILE *file, const airEnum *enm) { int ii; /* this should arguable be unsigned int, but airEnum values were kept as "int", even after the great unsigned conversion */ if (!(file && enm)) { return; } if (airStrlen(enm->name)) { fprintf(file, "airEnum \"%s\":\n", enm->name); } else { fprintf(file, "airEnum (NO NAME!):\n"); } fprintf(file, "(%s case sensitive)\n", (enm->sense ? "yes, is" : "is not")); if (enm->val) { fprintf(file, "Values (%u valid) given explicitly\n", enm->M); fprintf(file, "--- (0) %d: \"%s\"\n", enm->val[0], enm->str[0]); for (ii=1; ii<=AIR_CAST(int, enm->M); ii++) { fprintf(file, "--- (%d) %d: \"%s\" == \"%s\"\n", ii, enm->val[ii], enm->str[ii], airEnumStr(enm, enm->val[ii])); _enumPrintVal(file, enm, ii); } } else { /* enm->val NULL */ fprintf(file, "Values implicit; [1,%u] valid\n", enm->M); fprintf(file, "--- 0: \"%s\"\n", enm->str[0]); for (ii=1; ii<=AIR_CAST(int, enm->M); ii++) { fprintf(file, "--- %d: %s == %s\n", ii, enm->str[ii], airEnumStr(enm, ii)); _enumPrintVal(file, enm, ii); } } return; } /* this is the end */ cmtk-3.3.1/Utilities/NrrdIO/enumsNrrd.c000066400000000000000000000570611276303427400177260ustar00rootroot00000000000000/* NrrdIO: stand-alone code for basic nrrd functionality Copyright (C) 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. */ #include "NrrdIO.h" /* ** Rules of thumb for editing these things. The airEnum definitions are ** unfortunately EXTREMELY sensitive to small typo errors, and there is ** no good way to detect the errors. So: ** ** 1) Be awake and undistracted. Turn down the music. ** 2) When editing the char arrays, make sure that you put commas ** where you mean them to be. C's automatic string concatenation ** is not your friend here. In fact, EXPLOIT the fact that you can have ** a comma after the last element of a list (of strings)- it decreases ** the chances that adding a new element at the end will be thwarted by ** the lack of a comma at the end of the previous (and previously last) ** string. ** 3) When editing the *StrEqv and *ValEqv arrays, make absolutely ** sure that both are changed in parallel. Use only one enum value ** per line; putting all equivalents on that line, and make sure that ** there is one line in both *StrEqv and *ValEqv for all the possible ** enum values, and that there are as many elements in each line. ** 4) Make sure that additions here are reflected in nrrdEnums.h and ** vice versa. */ /* ------------------------ nrrdFormat ------------------------- */ static const char * _nrrdFormatTypeStr[NRRD_FORMAT_TYPE_MAX+1] = { "(unknown_format)", "nrrd", "pnm", "png", "vtk", "text", "eps", }; static const char * _nrrdFormatTypeDesc[NRRD_FORMAT_TYPE_MAX+1] = { "unknown_format", "native format for nearly raw raster data", "Portable aNy Map: includes PGM for grayscale and PPM for color", "Portable Network Graphics: lossless compression of 8- and 16-bit data", "Visualization ToolKit STRUCTURED_POINTS data", "white-space-delimited plain text encoding of 2-D float array", "Encapsulated PostScript images", }; static const char * _nrrdFormatTypeStrEqv[] = { "nrrd", "pnm", "png", "vtk", "table", "text", "txt", "eps", "" }; static const int _nrrdFormatTypeValEqv[] = { nrrdFormatTypeNRRD, nrrdFormatTypePNM, nrrdFormatTypePNG, nrrdFormatTypeVTK, nrrdFormatTypeText, nrrdFormatTypeText, nrrdFormatTypeText, nrrdFormatTypeEPS, }; airEnum _nrrdFormatType = { "format", NRRD_FORMAT_TYPE_MAX, _nrrdFormatTypeStr, NULL, _nrrdFormatTypeDesc, _nrrdFormatTypeStrEqv, _nrrdFormatTypeValEqv, AIR_FALSE }; const airEnum *const nrrdFormatType = &_nrrdFormatType; /* ------------------------ nrrdType ------------------------- */ static const char * _nrrdTypeStr[NRRD_TYPE_MAX+1] = { "(unknown_type)", "signed char", "unsigned char", "short", "unsigned short", "int", "unsigned int", "long long int", "unsigned long long int", "float", "double", "block", }; static const char * _nrrdTypeDesc[NRRD_TYPE_MAX+1] = { "unknown type", "signed 1-byte integer", "unsigned 1-byte integer", "signed 2-byte integer", "unsigned 2-byte integer", "signed 4-byte integer", "unsigned 4-byte integer", "signed 8-byte integer", "unsigned 8-byte integer", "4-byte floating point", "8-byte floating point", "size user-defined at run-time", }; #define ntCH nrrdTypeChar #define ntUC nrrdTypeUChar #define ntSH nrrdTypeShort #define ntUS nrrdTypeUShort #define ntIN nrrdTypeInt #define ntUI nrrdTypeUInt #define ntLL nrrdTypeLLong #define ntUL nrrdTypeULLong #define ntFL nrrdTypeFloat #define ntDB nrrdTypeDouble #define ntBL nrrdTypeBlock static const char * _nrrdTypeStrEqv[] = { "signed char", /* but NOT just "char" */ "int8", "int8_t", "uchar", "unsigned char", "uint8", "uint8_t", "short", "short int", "signed short", "signed short int", "int16", "int16_t", "ushort", "unsigned short", "unsigned short int", "uint16", "uint16_t", "int", "signed int", "int32", "int32_t", "uint", "unsigned int", "uint32", "uint32_t", "longlong", "long long", "long long int", "signed long long", "signed long long int", "int64", "int64_t", "ulonglong", "unsigned long long", "unsigned long long int", "uint64", "uint64_t", "float", "double", "block", "" }; static const int _nrrdTypeValEqv[] = { ntCH, ntCH, ntCH, ntUC, ntUC, ntUC, ntUC, ntSH, ntSH, ntSH, ntSH, ntSH, ntSH, ntUS, ntUS, ntUS, ntUS, ntUS, ntIN, ntIN, ntIN, ntIN, ntUI, ntUI, ntUI, ntUI, ntLL, ntLL, ntLL, ntLL, ntLL, ntLL, ntLL, ntUL, ntUL, ntUL, ntUL, ntUL, ntFL, ntDB, ntBL, }; airEnum _nrrdType = { "type", NRRD_TYPE_MAX, _nrrdTypeStr, NULL, _nrrdTypeDesc, _nrrdTypeStrEqv, _nrrdTypeValEqv, AIR_FALSE }; const airEnum *const nrrdType = &_nrrdType; /* ------------------------ nrrdEncodingType ------------------------- */ static const char * _nrrdEncodingTypeStr[NRRD_ENCODING_TYPE_MAX+1] = { "(unknown_encoding)", "raw", "ascii", "hex", "gz", "bz2", }; static const char * _nrrdEncodingTypeDesc[NRRD_ENCODING_TYPE_MAX+1] = { "unknown encoding", "file is byte-for-byte same as memory representation", "values written out in ASCII", "case-insenstive hexadecimal encoding (2 chars / byte)", "gzip compression of binary encoding", "bzip2 compression of binary encoding", }; static const char * _nrrdEncodingTypeStrEqv[] = { "raw", "txt", "text", "ascii", "hex", "gz", "gzip", "bz2", "bzip2", "" }; static const int _nrrdEncodingTypeValEqv[] = { nrrdEncodingTypeRaw, nrrdEncodingTypeAscii, nrrdEncodingTypeAscii, nrrdEncodingTypeAscii, nrrdEncodingTypeHex, nrrdEncodingTypeGzip, nrrdEncodingTypeGzip, nrrdEncodingTypeBzip2, nrrdEncodingTypeBzip2, }; airEnum _nrrdEncodingType = { "encoding", NRRD_ENCODING_TYPE_MAX, _nrrdEncodingTypeStr, NULL, _nrrdEncodingTypeDesc, _nrrdEncodingTypeStrEqv, _nrrdEncodingTypeValEqv, AIR_FALSE }; const airEnum *const nrrdEncodingType = &_nrrdEncodingType; /* ------------------------ nrrdCenter ------------------------- */ static const char * _nrrdCenterStr[NRRD_CENTER_MAX+1] = { "(unknown_center)", "node", "cell", }; static const char * _nrrdCenterDesc[NRRD_CENTER_MAX+1] = { "unknown centering", "samples are at boundaries between elements along axis", "samples are at centers of elements along axis", }; static const airEnum _nrrdCenter_enum = { "centering", NRRD_CENTER_MAX, _nrrdCenterStr, NULL, _nrrdCenterDesc, NULL, NULL, AIR_FALSE }; const airEnum *const nrrdCenter = &_nrrdCenter_enum; /* ------------------------ nrrdKind ------------------------- */ /* nrrdKindUnknown, nrrdKindDomain, * 1: any image domain * nrrdKindSpace, * 2: a spatial domain * nrrdKindTime, * 3: a temporal domain * * -------------------------- end domain kinds * * -------------------------- begin range kinds * nrrdKindList, * 4: any list of values, non-resample-able * nrrdKindPoint, * 5: coords of a point * nrrdKindVector, * 6: coeffs of (contravariant) vector * nrrdKindCovariantVector, * 7: coeffs of covariant vector (eg gradient) * nrrdKindNormal, * 8: coeffs of unit-length covariant vector * * -------------------------- end arbitrary size kinds * * -------------------------- begin size-specific kinds * nrrdKindStub, * 9: axis with one sample (a placeholder) * nrrdKindScalar, * 10: effectively, same as a stub * nrrdKindComplex, * 11: real and imaginary components * nrrdKind2Vector, * 12: 2 component vector * nrrdKind3Color, * 13: ANY 3-component color value * nrrdKindRGBColor, * 14: RGB, no colorimetry * nrrdKindHSVColor, * 15: HSV, no colorimetry * nrrdKindXYZColor, * 16: perceptual primary colors * nrrdKind4Color, * 17: ANY 4-component color value * nrrdKindRGBAColor, * 18: RGBA, no colorimetry * nrrdKind3Vector, * 19: 3-component vector * nrrdKind3Gradient, * 20: 3-component covariant vector * nrrdKind3Normal, * 21: 3-component covector, assumed normalized * nrrdKind4Vector, * 22: 4-component vector * nrrdKindQuaternion, * 23: (w,x,y,z), not necessarily normalized * nrrdKind2DSymMatrix, * 24: Mxx Mxy Myy * nrrdKind2DMaskedSymMatrix, * 25: mask Mxx Mxy Myy * nrrdKind2DMatrix, * 26: Mxx Mxy Myx Myy * nrrdKind2DMaskedMatrix, * 27: mask Mxx Mxy Myx Myy * nrrdKind3DSymMatrix, * 28: Mxx Mxy Mxz Myy Myz Mzz * nrrdKind3DMaskedSymMatrix, * 29: mask Mxx Mxy Mxz Myy Myz Mzz * nrrdKind3DMatrix, * 30: Mxx Mxy Mxz Myx Myy Myz Mzx Mzy Mzz * nrrdKind3DMaskedMatrix, * 31: mask Mxx Mxy Mxz Myx Myy Myz Mzx Mzy Mzz * */ static const char * _nrrdKindStr[NRRD_KIND_MAX+1] = { "(unknown_kind)", "domain", "space", "time", "list", "point", "vector", "covariant-vector", "normal", "stub", "scalar", "complex", "2-vector", "3-color", "RGB-color", "HSV-color", "XYZ-color", "4-color", "RGBA-color", "3-vector", "3-gradient", "3-normal", "4-vector", "quaternion", "2D-symmetric-matrix", "2D-masked-symmetric-matrix", "2D-matrix", "2D-masked-matrix", "3D-symmetric-matrix", "3D-masked-symmetric-matrix", "3D-matrix", "3D-masked-matrix", }; static const char * _nrrdKindDesc[NRRD_KIND_MAX+1] = { "unknown kind", "a domain variable of the function which the nrrd samples", "a spatial domain, like the axes of a measured volume image", "a temporal domain, as from time-varying measurements", "some list of attributes; it makes no sense to resample along these", "coordinates of a point", "coefficients of a (contravariant) vector", "coefficients of a covariant vector, such as a gradient", "coefficients of a normalized covariant vector", "a place-holder axis with a single sample", "axis used to indicate that the nrrd contains a scalar value", "real and imaginary parts of a value", "a 2-component vector", "any 3-component color value", "red-green-blue color", "hue-saturation-value single hexcone color", "perceptual primaries color", "any 4-component color value", "red-green-blue-alpha color", "a 3-element (contravariant) vector", "a 3-element gradient (covariant) vector", "a 3-element (covariant) vector which is assumed normalized", "a 4-element (contravariant) vector", "quaternion: x y z w", "3 elements of 2D symmetric matrix: Mxx Mxy Myy", "mask plus 3 elements of 2D symmetric matrix: mask Mxx Mxy Myy", "4 elements of general 2D matrix: Mxx Mxy Myx Myy", "mask plus 4 elements of general 2D matrix: mask Mxx Mxy Myx Myy", "6 elements of 3D symmetric matrix: Mxx Mxy Mxz Myy Myz Mzz", "mask plus 6 elements of 3D symmetric matrix: mask Mxx Mxy Mxz Myy Myz Mzz", "9 elements of general 3D matrix: Mxx Mxy Mxz Myx Myy Myz Mzx Mzy Mzz", "mask plus 9 elements of general 3D matrix: mask Mxx Mxy Mxz Myx Myy Myz Mzx Mzy Mzz", }; static const char * _nrrdKindStr_Eqv[] = { "domain", "space", "time", "list", "point", "vector", "contravariant-vector", "covariant-vector", "normal", "stub", "scalar", "complex", "2-vector", "3-color", "RGB-color", "RGBcolor", "RGB", "HSV-color", "HSVcolor", "HSV", "XYZ-color", "4-color", "RGBA-color", "RGBAcolor", "RGBA", "3-vector", "3-gradient", "3-normal", "4-vector", "quaternion", "2D-symmetric-matrix", "2D-sym-matrix", "2D-symmetric-tensor", "2D-sym-tensor", "2D-masked-symmetric-matrix", "2D-masked-sym-matrix", "2D-masked-symmetric-tensor", "2D-masked-sym-tensor", "2D-matrix", "2D-tensor", "2D-masked-matrix", "2D-masked-tensor", "3D-symmetric-matrix", "3D-sym-matrix", "3D-symmetric-tensor", "3D-sym-tensor", "3D-masked-symmetric-matrix", "3D-masked-sym-matrix", "3D-masked-symmetric-tensor", "3D-masked-sym-tensor", "3D-matrix", "3D-tensor", "3D-masked-matrix", "3D-masked-tensor", "" }; static int _nrrdKindVal_Eqv[] = { nrrdKindDomain, nrrdKindSpace, nrrdKindTime, nrrdKindList, nrrdKindPoint, nrrdKindVector, nrrdKindVector, nrrdKindCovariantVector, nrrdKindNormal, nrrdKindStub, nrrdKindScalar, nrrdKindComplex, nrrdKind2Vector, nrrdKind3Color, nrrdKindRGBColor, nrrdKindRGBColor, nrrdKindRGBColor, nrrdKindHSVColor, nrrdKindHSVColor, nrrdKindHSVColor, nrrdKindXYZColor, nrrdKind4Color, nrrdKindRGBAColor, nrrdKindRGBAColor, nrrdKindRGBAColor, nrrdKind3Vector, nrrdKind3Gradient, nrrdKind3Normal, nrrdKind4Vector, nrrdKindQuaternion, nrrdKind2DSymMatrix, nrrdKind2DSymMatrix, nrrdKind2DSymMatrix, nrrdKind2DSymMatrix, nrrdKind2DMaskedSymMatrix, nrrdKind2DMaskedSymMatrix, nrrdKind2DMaskedSymMatrix, nrrdKind2DMaskedSymMatrix, nrrdKind2DMatrix, nrrdKind2DMatrix, nrrdKind2DMaskedMatrix, nrrdKind2DMaskedMatrix, nrrdKind3DSymMatrix, nrrdKind3DSymMatrix, nrrdKind3DSymMatrix, nrrdKind3DSymMatrix, nrrdKind3DMaskedSymMatrix, nrrdKind3DMaskedSymMatrix, nrrdKind3DMaskedSymMatrix, nrrdKind3DMaskedSymMatrix, nrrdKind3DMatrix, nrrdKind3DMatrix, nrrdKind3DMaskedMatrix, nrrdKind3DMaskedMatrix, }; static const airEnum _nrrdKind_enum = { "kind", NRRD_KIND_MAX, _nrrdKindStr, NULL, _nrrdKindDesc, _nrrdKindStr_Eqv, _nrrdKindVal_Eqv, AIR_FALSE }; const airEnum *const nrrdKind = &_nrrdKind_enum; /* ------------------------ nrrdField ------------------------- */ static const char * _nrrdFieldStr[NRRD_FIELD_MAX+1] = { "Ernesto \"Che\" Guevara", "#", "content", "number", "type", "block size", "dimension", "space", "space dimension", "sizes", "spacings", "thicknesses", "axis mins", "axis maxs", "space directions", "centerings", "kinds", "labels", "units", "min", "max", "old min", "old max", "endian", "encoding", "line skip", "byte skip", "key/value", /* this is the one field for which the canonical string here is totally different from the field identifier in the NRRD file format (":="). We include nrrdField_keyvalue in the enum because it is very useful to have a consistent way of identifying lines in the format */ "sample units", "space units", "space origin", "measurement frame", "data file", }; static const char * _nrrdFieldDesc[NRRD_FIELD_MAX+1] = { "unknown field identifier", "comment", "short description of whole array and/or its provenance", "total number of samples in array", "type of sample value", "number of bytes in one block (for block-type)", "number of axes in array", "identifier for space in which array grid lies", "dimension of space in which array grid lies", "list of number of samples along each axis, aka \"dimensions\" of the array", "list of sample spacings along each axis", "list of sample thicknesses along each axis", "list of minimum positions associated with each axis", "list of maximum positions associated with each axis", "list of direction inter-sample vectors for each axis", "list of sample centerings for each axis", "list of kinds for each axis", "list of short descriptions for each axis", "list of units in which each axes' spacing and thickness is measured", "supposed minimum array value", "supposed maximum array value", "minimum array value prior to quantization", "maximum array value prior to quantization", "endiannes of data as written in file", "encoding of data written in file", "number of lines to skip prior to byte skip and reading data", "number of bytes to skip after line skip and prior to reading data", "string-based key/value pairs", "units of measurement of (scalar) values inside array itself", "list of units for measuring origin and direct vectors' coefficients", "location in space of center of first (lowest memory address) sample", "maps coords of (non-scalar) values to coords of surrounding space", "with detached headers, where is data to be found", }; static const char * _nrrdFieldStrEqv[] = { "#", "content", "number", "type", "block size", "blocksize", "dimension", "space", "space dimension", "spacedimension", "sizes", "spacings", "thicknesses", "axis mins", "axismins", "axis maxs", "axismaxs", "space directions", "spacedirections", "centers", "centerings", "kinds", "labels", "units", "min", "max", "old min", "oldmin", "old max", "oldmax", "endian", "encoding", "line skip", "lineskip", "byte skip", "byteskip", "key/value", /* bogus, here to keep the airEnum complete */ "sample units", "sampleunits", "space units", "spaceunits", "space origin", "spaceorigin", "measurement frame", "measurementframe", "data file", "datafile", "" }; static const int _nrrdFieldValEqv[] = { nrrdField_comment, nrrdField_content, nrrdField_number, nrrdField_type, nrrdField_block_size, nrrdField_block_size, nrrdField_dimension, nrrdField_space, nrrdField_space_dimension, nrrdField_space_dimension, nrrdField_sizes, nrrdField_spacings, nrrdField_thicknesses, nrrdField_axis_mins, nrrdField_axis_mins, nrrdField_axis_maxs, nrrdField_axis_maxs, nrrdField_space_directions, nrrdField_space_directions, nrrdField_centers, nrrdField_centers, nrrdField_kinds, nrrdField_labels, nrrdField_units, nrrdField_min, nrrdField_max, nrrdField_old_min, nrrdField_old_min, nrrdField_old_max, nrrdField_old_max, nrrdField_endian, nrrdField_encoding, nrrdField_line_skip, nrrdField_line_skip, nrrdField_byte_skip, nrrdField_byte_skip, nrrdField_keyvalue, nrrdField_sample_units, nrrdField_sample_units, nrrdField_space_units, nrrdField_space_units, nrrdField_space_origin, nrrdField_space_origin, nrrdField_measurement_frame, nrrdField_measurement_frame, nrrdField_data_file, nrrdField_data_file, }; static const airEnum _nrrdField = { "nrrd_field", NRRD_FIELD_MAX, _nrrdFieldStr, NULL, _nrrdFieldDesc, _nrrdFieldStrEqv, _nrrdFieldValEqv, AIR_FALSE /* field identifiers not case sensitive */ }; const airEnum *const nrrdField = &_nrrdField; /* ------------------------ nrrdSpace ------------------------- */ /* nrrdSpaceUnknown, nrrdSpaceRightAnteriorSuperior, * 1: NIFTI-1 (right-handed) * nrrdSpaceLeftAnteriorSuperior, * 2: standard Analyze (left-handed) * nrrdSpaceLeftPosteriorSuperior, * 3: DICOM 3.0 (right-handed) * nrrdSpaceRightAnteriorSuperiorTime, * 4: * nrrdSpaceLeftAnteriorSuperiorTime, * 5: * nrrdSpaceLeftPosteriorSuperiorTime, * 6: * nrrdSpaceScannerXYZ, * 7: ACR/NEMA 2.0 (pre-DICOM 3.0) * nrrdSpaceScannerXYZTime, * 8: * nrrdSpace3DRightHanded, * 9: * nrrdSpace3DLeftHanded, * 10: * nrrdSpace3DRightHandedTime, * 11: * nrrdSpace3DLeftHandedTime, * 12: * nrrdSpaceLast */ static const char * _nrrdSpaceStr[NRRD_SPACE_MAX+1] = { "(unknown_space)", "right-anterior-superior", "left-anterior-superior", "left-posterior-superior", "right-anterior-superior-time", "left-anterior-superior-time", "left-posterior-superior-time", "scanner-xyz", "scanner-xyz-time", "3D-right-handed", "3D-left-handed", "3D-right-handed-time", "3D-left-handed-time", }; static const char * _nrrdSpaceDesc[NRRD_SPACE_MAX+1] = { "unknown space", "right-anterior-superior (used in NIFTI-1 and SPL's 3D Slicer)", "left-anterior-superior (used in Analyze 7.5)", "left-posterior-superior (used in DICOM 3)", "right-anterior-superior-time", "left-anterior-superior-time", "left-posterior-superior-time", "scanner-xyz (used in ACR/NEMA 2.0)", "scanner-xyz-time", "3D-right-handed", "3D-left-handed", "3D-right-handed-time", "3D-left-handed-time", }; static const char * _nrrdSpaceStrEqv[] = { "right-anterior-superior", "right anterior superior", "rightanteriorsuperior", "RAS", "left-anterior-superior", "left anterior superior", "leftanteriorsuperior", "LAS", "left-posterior-superior", "left posterior superior", "leftposteriorsuperior", "LPS", "right-anterior-superior-time", "right anterior superior time", "rightanteriorsuperiortime", "RAST", "left-anterior-superior-time", "left anterior superior time", "leftanteriorsuperiortime", "LAST", "left-posterior-superior-time", "left posterior superior time", "leftposteriorsuperiortime", "LPST", "scanner-xyz", "scanner-xyz-time", "scanner-xyzt", "3D-right-handed", "3D right handed", "3Drighthanded", "3D-left-handed", "3D left handed", "3Dlefthanded", "3D-right-handed-time", "3D right handed time", "3Drighthandedtime", "3D-left-handed-time", "3D left handed time", "3Dlefthandedtime", "" }; static const int _nrrdSpaceValEqv[] = { nrrdSpaceRightAnteriorSuperior, nrrdSpaceRightAnteriorSuperior, nrrdSpaceRightAnteriorSuperior, nrrdSpaceRightAnteriorSuperior, nrrdSpaceLeftAnteriorSuperior, nrrdSpaceLeftAnteriorSuperior, nrrdSpaceLeftAnteriorSuperior, nrrdSpaceLeftAnteriorSuperior, nrrdSpaceLeftPosteriorSuperior, nrrdSpaceLeftPosteriorSuperior, nrrdSpaceLeftPosteriorSuperior, nrrdSpaceLeftPosteriorSuperior, nrrdSpaceRightAnteriorSuperiorTime, nrrdSpaceRightAnteriorSuperiorTime, nrrdSpaceRightAnteriorSuperiorTime, nrrdSpaceRightAnteriorSuperiorTime, nrrdSpaceLeftAnteriorSuperiorTime, nrrdSpaceLeftAnteriorSuperiorTime, nrrdSpaceLeftAnteriorSuperiorTime, nrrdSpaceLeftAnteriorSuperiorTime, nrrdSpaceLeftPosteriorSuperiorTime, nrrdSpaceLeftPosteriorSuperiorTime, nrrdSpaceLeftPosteriorSuperiorTime, nrrdSpaceLeftPosteriorSuperiorTime, nrrdSpaceScannerXYZ, nrrdSpaceScannerXYZTime, nrrdSpaceScannerXYZTime, nrrdSpace3DRightHanded, nrrdSpace3DRightHanded, nrrdSpace3DRightHanded, nrrdSpace3DLeftHanded, nrrdSpace3DLeftHanded, nrrdSpace3DLeftHanded, nrrdSpace3DRightHandedTime, nrrdSpace3DRightHandedTime, nrrdSpace3DRightHandedTime, nrrdSpace3DLeftHandedTime, nrrdSpace3DLeftHandedTime, nrrdSpace3DLeftHandedTime }; static const airEnum _nrrdSpace = { "space", NRRD_SPACE_MAX, _nrrdSpaceStr, NULL, _nrrdSpaceDesc, _nrrdSpaceStrEqv, _nrrdSpaceValEqv, AIR_FALSE }; const airEnum *const nrrdSpace = &_nrrdSpace; /* ------------------------ nrrdSpacingStatus ------------------------- */ static const char * _nrrdSpacingStatusStr[NRRD_SPACING_STATUS_MAX+1] = { "(unknown_status)", "none", "scalarNoSpace", "scalarWithSpace", "direction", }; static const char * _nrrdSpacingStatusDesc[NRRD_BOUNDARY_MAX+1] = { "unknown spacing status behavior", "neither axis->spacing nor axis->spaceDirection set", "axis->spacing set normally", "axis->spacing set, with surround space (?)", "axis->spaceDirection set normally", }; static const airEnum _nrrdSpacingStatus = { "spacing status", NRRD_SPACING_STATUS_MAX, _nrrdSpacingStatusStr, NULL, _nrrdSpacingStatusDesc, NULL, NULL, AIR_FALSE }; const airEnum *const nrrdSpacingStatus = &_nrrdSpacingStatus; cmtk-3.3.1/Utilities/NrrdIO/format.c000066400000000000000000000100741276303427400172320ustar00rootroot00000000000000/* NrrdIO: stand-alone code for basic nrrd functionality Copyright (C) 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. */ #include "NrrdIO.h" #include "privateNrrd.h" /* ** what a NrrdFormat can assume: ** -- that nio->format has been set to you already ** -- for read(): that nio->path has been set to the path of the file being ** read in, if the information was ever available ** -- for contentStartsLike() and read(): that nio->line contains the ** first line of of the file, in order to determine the file type ** ** what a NrrdFormat has to do: ** -- respect nio->skipData to whatever extent makes sense on top of how the ** NrrdEncoding respects it (by making read and write no-ops). ** nrrdFormatNRRD, for instance, won't create empty detached data files ** if nio->skipData. ** -- determine what NrrdEncoding to use, if there's a choice ** -- respect nrrdStateVerboseIO with messages to stderr, if possible ** ** The "unknown" format is intended as a template for writing new formats. */ static int _nrrdFormatUnknown_available(void) { /* insert code here */ return AIR_FALSE; } static int _nrrdFormatUnknown_nameLooksLike(const char *filename) { /* insert code here */ AIR_UNUSED(filename); return AIR_FALSE; } static int _nrrdFormatUnknown_fitsInto(const Nrrd *nrrd, const NrrdEncoding *encoding, int useBiff) { static const char me[]="_nrrdFormatUnknown_fitsInto"; if (!(nrrd && encoding)) { biffMaybeAddf(useBiff, NRRD, "%s: got NULL nrrd (%p) or encoding (%p)", me, AIR_CVOIDP(nrrd), AIR_CVOIDP(encoding)); return AIR_FALSE; } /* insert code here */ return AIR_FALSE; } static int _nrrdFormatUnknown_contentStartsLike(NrrdIoState *nio) { /* insert code here */ AIR_UNUSED(nio); return AIR_FALSE; } static int _nrrdFormatUnknown_read(FILE *file, Nrrd *nrrd, NrrdIoState *nio) { static const char me[]="_nrrdFormatUnknown_read"; /* insert code here, and remove error handling below */ AIR_UNUSED(file); AIR_UNUSED(nrrd); AIR_UNUSED(nio); biffAddf(NRRD, "%s: ERROR!!! trying to read unknown format", me); return 1; } static int _nrrdFormatUnknown_write(FILE *file, const Nrrd *nrrd, NrrdIoState *nio) { static const char me[]="_nrrdFormatUnknown_write"; /* insert code here, and remove error handling below */ AIR_UNUSED(file); AIR_UNUSED(nrrd); AIR_UNUSED(nio); biffAddf(NRRD, "%s: ERROR!!! trying to write unknown format", me); return 1; } static const NrrdFormat _nrrdFormatUnknown = { "unknown", AIR_FALSE, /* isImage */ AIR_TRUE, /* readable */ AIR_FALSE, /* usesDIO */ _nrrdFormatUnknown_available, _nrrdFormatUnknown_nameLooksLike, _nrrdFormatUnknown_fitsInto, _nrrdFormatUnknown_contentStartsLike, _nrrdFormatUnknown_read, _nrrdFormatUnknown_write }; const NrrdFormat *const nrrdFormatUnknown = &_nrrdFormatUnknown; const NrrdFormat *const nrrdFormatArray[NRRD_FORMAT_TYPE_MAX+1] = { &_nrrdFormatUnknown, &_nrrdFormatNRRD, &_nrrdFormatPNM, &_nrrdFormatPNG, &_nrrdFormatVTK, &_nrrdFormatText, &_nrrdFormatEPS }; cmtk-3.3.1/Utilities/NrrdIO/formatEPS.c000066400000000000000000000054251276303427400176060ustar00rootroot00000000000000/* NrrdIO: stand-alone code for basic nrrd functionality Copyright (C) 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. */ #include "NrrdIO.h" #include "privateNrrd.h" int _nrrdFormatEPS_available(void) { return AIR_FALSE; } int _nrrdFormatEPS_nameLooksLike(const char *filename) { return airEndsWith(filename, NRRD_EXT_EPS); } int _nrrdFormatEPS_fitsInto(const Nrrd *nrrd, const NrrdEncoding *encoding, int useBiff) { char me[]="_nrrdFormatEPS_fitsInto", err[AIR_STRLEN_MED]; AIR_UNUSED(nrrd); AIR_UNUSED(encoding); AIR_UNUSED(useBiff); sprintf(err, "%s: Sorry, %s format not available in NrrdIO", me, nrrdFormatEPS->name); biffMaybeAdd(NRRD, err, useBiff); return AIR_FALSE; } int _nrrdFormatEPS_contentStartsLike(NrrdIoState *nio) { AIR_UNUSED(nio); return AIR_FALSE; } int _nrrdFormatEPS_read(FILE *file, Nrrd *nrrd, NrrdIoState *nio) { char me[]="_nrrdReadEPS", err[AIR_STRLEN_MED]; AIR_UNUSED(file); AIR_UNUSED(nrrd); AIR_UNUSED(nio); sprintf(err, "%s: Sorry, %s format not available in NrrdIO", me, nrrdFormatEPS->name); biffAdd(NRRD, err); return 1; } int _nrrdFormatEPS_write(FILE *file, const Nrrd *nrrd, NrrdIoState *nio) { char me[]="_nrrdFormatEPS_write", err[AIR_STRLEN_MED]; AIR_UNUSED(file); AIR_UNUSED(nrrd); AIR_UNUSED(nio); sprintf(err, "%s: Sorry, %s format not available in NrrdIO", me, nrrdFormatEPS->name); biffAdd(NRRD, err); return 1; } const NrrdFormat _nrrdFormatEPS = { "EPS", AIR_FALSE, /* isImage */ AIR_FALSE, /* readable */ AIR_FALSE, /* usesDIO */ _nrrdFormatEPS_available, _nrrdFormatEPS_nameLooksLike, _nrrdFormatEPS_fitsInto, _nrrdFormatEPS_contentStartsLike, _nrrdFormatEPS_read, _nrrdFormatEPS_write }; const NrrdFormat *const nrrdFormatEPS = &_nrrdFormatEPS; cmtk-3.3.1/Utilities/NrrdIO/formatNRRD.c000066400000000000000000000713061276303427400177250ustar00rootroot00000000000000/* NrrdIO: stand-alone code for basic nrrd functionality Copyright (C) 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. */ #include "NrrdIO.h" #include "privateNrrd.h" /*** **** Info about string handling for fields The Nrrd format can include strings in different fields, with different rules for each one; see http://teem.sourceforge.net/nrrd/format.html Below is some documentation of how the strings are handled, which is mostly a documentation of old (long-established) code, the gathering of which uncovered some problems and led to some new code (as of Thu Aug 23 09:32:11 CDT 2012). Conceptual inconsistencies in the different handlings of strings merit further review, including updating the file format spec (e.g. what non-ASCII characters can be allowed where? Unicode? what encoding? etc). This all also highlights the need for having a completely uniform way of setting these fields at one-time via the nrrd library (e.g. can use nrrdAxisInfoSet for "units" but there is no API for setting "space units"). Should that API flag as error if you try to include characters that can't be losslessly saved, or should it silently transform things? ** Comments: On disk, delimited by the NRRD_COMMENT_CHAR ('#') and the end of the line, but the format spec doesn't address any escaping. Input comments processed via: nrrd/formatNrrd.c/_nrrdFormatNRRD_read() --> nrrd/parseNrrd.c/_nrrdReadNrrdParse_comment() --> nrrd/comment.c/nrrdCommentAdd() --> air/string.c/airOneLinify() On write, output comments processed via: nrrd/formatNrrd.c/_nrrdFormatNRRD_write() --> air/string.c/airOneLinify() ==> There is no escaping of anything: white-space is compressed into a single ' '. Probably justified justified given format spec. ** Content: On disk, finished with the end of line. No mention of escaping in the format spec. Input content processed via: nrrd/formatNrrd.c/_nrrdFormatNRRD_read() --> nrrd/parseNrrd.c/_nrrdReadNrrdParse_content() which does NO processing, just airStrdup On write, output content processed via: nrrd/formatNrrd.c/_nrrdFormatNRRD_write() --> nrrd/write.c/_nrrdSprintFieldInfo() (maybe via _nrrdFprintFieldInfo()) --> air/string.c/airOneLinify() ==> not only is there no escaping, but there's some assymmetry in the use of airOneLinify. If information is being encoded in the number of contiguous spaces in the content, its preserved on input but not on output. Still, there's no chance of writing a broken file. ** key/value pairs: The keys and values are separated by ":=", and the format spec says (string) "\n" means (character) '\n' and "\\" means '\\'. On input, both keys and values processed via: nrrd/formatNrrd.c/_nrrdFormatNRRD_read() --> nrrd/parseNrrd.c/_nrrdReadNrrdParse_keyvalue() --> air/string.c/airUnescape(), which deals with "\n" and "\\" ONLY (not quotes, not other whitespace), and then --> nrrd/keyvalue.c/nrrdKeyValueAdd(), which only does an airStrdup On output, keys and values processed via nrrd/formatNrrd.c/_nrrdFormatNRRD_write() --> nrrd/keyvalue.c/_nrrdKeyValueWrite() --> nrrd/keyvalue.c/_nrrdWriteEscaped() which is invoked to escape \n and \, and (NOTE!) to convert all other whitespace to ' ' Aside from the file format spec, the nrrd *library* does not really have any strictures about the characters that are allowed at run-time in key/values (and indeed nrrdKeyValueAdd just does an airStrdup). But without converting or escaping, say, '\r', you'll generate a broken NRRD file, hence the new handling of converting other whitespace to ' '. ** labels and units: A "-delimited string per axis. Format spec is very specific for labels, and implies units are the same: "Within each label, double quotes may be included by escaping them (\"), but no other form of escaping is supported". On input: nrrd/formatNrrd.c/_nrrdFormatNRRD_read() --> nrrd/parseNrrd.c/_nrrdReadNrrdParse_labels() or _nrrdReadNrrdParse_units() --> nrrd/parseNrrd.c/_nrrdGetQuotedString() which does the work of unescaping \" On output: nrrd/formatNrrd.c/_nrrdFormatNRRD_write() --> nrrd/write.c/_nrrdSprintFieldInfo() (maybe via _nrrdFprintFieldInfo()) --> nrrd/keyvalue.c/_nrrdWriteEscaped() which is invoked to escape ", and (NOTE!) to convert all other whitespace to ' ' Same concern above about characters that when written would generate a bad NRRD file, but which are not documented as escape-able in label or unit ** space units: A "-delimited string per axis of *world-space* (NOT the same a per-axis field, like units). Format is sadly silent on issue of escaping for these; so we might as well treat them like labels & units units. On input: nrrd/formatNrrd.c/_nrrdFormatNRRD_read() --> nrrd/parseNrrd.c/_nrrdReadNrrdParse_space_units --> nrrd/parseNrrd.c/_nrrdGetQuotedString() which does the work of unescaping \" On output: nrrd/formatNrrd.c/_nrrdFormatNRRD_write() --> nrrd/write.c/_nrrdSprintFieldInfo() (maybe via _nrrdFprintFieldInfo()) --> nrrd/keyvalue.c/_nrrdWriteEscaped() which is invoked to escape ", and (NOTE!) to convert all other whitespace to ' ' ** sample units: like content and comments, not a quoted string. On input: nrrd/formatNrrd.c/_nrrdFormatNRRD_read() --> nrrd/parseNrrd.c/_nrrdReadNrrdParse_sample_units() which does nothing except a strdup On output: nrrd/formatNrrd.c/_nrrdFormatNRRD_write() --> nrrd/write.c/_nrrdSprintFieldInfo() (maybe via _nrrdFprintFieldInfo()) --> air/string.c/airOneLinify() **** ***/ #define MAGIC "NRRD" #define MAGIC0 "NRRD00.01" #define MAGIC1 "NRRD0001" #define MAGIC2 "NRRD0002" #define MAGIC3 "NRRD0003" #define MAGIC4 "NRRD0004" #define MAGIC5 "NRRD0005" const char * _nrrdFormatURLLine0 = "Complete NRRD file format specification at:"; const char * _nrrdFormatURLLine1 = "http://teem.sourceforge.net/nrrd/format.html"; void nrrdIoStateDataFileIterBegin(NrrdIoState *nio) { nio->dataFNIndex = 0; return; } /* this macro suggested by Bryan Worthen */ /* if str = '-', strcmp() is 0, && short circuits, return false ** else str != '-' ** if str[1] = ':', its probably a windows full path, != is 0, return false ** else str[1] != ':' ** if str[0] = '/', its a normal full path, return false */ #define _NEED_PATH(str) (strcmp("-", (str)) \ && ':' != (str)[1] \ && '/' != (str)[0]) /* ** this is responsible for the header-relative path processing ** ** NOTE: if the filename is "-", then because it does not start with '/', ** it would normally be prefixed by nio->path, so it needs special handling ** ** NOTE: this should work okay with nio->headerStringRead, I think ... */ int nrrdIoStateDataFileIterNext(FILE **fileP, NrrdIoState *nio, int reading) { static const char me[]="nrrdIoStateDataFileIterNext"; char *fname=NULL; int ii, needPath; unsigned int num, fi; size_t maxl; airArray *mop; mop = airMopNew(); airMopAdd(mop, (void*)fileP, (airMopper)airSetNull, airMopOnError); if (!fileP) { biffAddf(NRRD, "%s: got NULL pointer", me); airMopError(mop); return 1; } if (!_nrrdDataFNNumber(nio)) { biffAddf(NRRD, "%s: there appear to be zero datafiles!", me); airMopError(mop); return 1; } if (nio->dataFNIndex >= _nrrdDataFNNumber(nio)) { /* there is no next data file, but we don't make that an error (though as of Tue Oct 2 22:53:14 CDT 2012, GLK can't remember why this condition would ever occur) */ nio->dataFNIndex = _nrrdDataFNNumber(nio); airMopOkay(mop); *fileP = NULL; return 0; } /* HEY: some of this error checking is done far more often than needed */ if (nio->dataFNFormat || nio->dataFNArr->len) { needPath = AIR_FALSE; maxl = 0; if (nio->dataFNFormat) { needPath = _NEED_PATH(nio->dataFNFormat); /* assuming 10-digit integers is plenty big */ maxl = 10 + strlen(nio->dataFNFormat); } else { for (fi=0; fidataFNArr->len; fi++) { needPath |= _NEED_PATH(nio->dataFN[fi]); maxl = AIR_MAX(maxl, strlen(nio->dataFN[fi])); } } if (needPath && !airStrlen(nio->path)) { biffAddf(NRRD, "%s: need nio->path for header-relative datafiles", me); airMopError(mop); return 1; } fname = (char*)malloc(airStrlen(nio->path) + strlen("/") + maxl + 1); if (!fname) { biffAddf(NRRD, "%s: couldn't allocate filename buffer", me); airMopError(mop); return 1; } airMopAdd(mop, fname, airFree, airMopAlways); } if (nio->dataFNFormat) { /* ---------------------------------------------------------- */ /* --------- base.%d [] ------------- */ /* ---------------------------------------------------------- */ num = 0; for (ii = nio->dataFNMin; ((nio->dataFNStep > 0 && ii <= nio->dataFNMax) || (nio->dataFNStep < 0 && ii >= nio->dataFNMax)); ii += nio->dataFNStep) { if (num == nio->dataFNIndex) { break; } num += 1; } if (_NEED_PATH(nio->dataFNFormat)) { strcpy(fname, nio->path); strcat(fname, "/"); sprintf(fname + strlen(nio->path) + strlen("/"), nio->dataFNFormat, ii); } else { sprintf(fname, nio->dataFNFormat, ii); } } else if (nio->dataFNArr->len) { /* ---------------------------------------------------------- */ /* ------------------- LIST or single ----------------------- */ /* ---------------------------------------------------------- */ if (_NEED_PATH(nio->dataFN[nio->dataFNIndex])) { sprintf(fname, "%s/%s", nio->path, nio->dataFN[nio->dataFNIndex]); } else { strcpy(fname, nio->dataFN[nio->dataFNIndex]); } } /* else data file is attached */ if (nio->dataFNFormat || nio->dataFNArr->len) { *fileP = airFopen(fname, reading ? stdin : stdout, reading ? "rb" : "wb"); if (!(*fileP)) { biffAddf(NRRD, "%s: couldn't open \"%s\" (data file %u of %u) for %s", me, fname, nio->dataFNIndex+1, _nrrdDataFNNumber(nio), reading ? "reading" : "writing"); airMopError(mop); return 1; } } else { /* data file is attached */ if (nio->headerStringRead) { /* except we were never reading from a file to begin with, but this isn't an error */ *fileP = NULL; } else { *fileP = nio->headerFile; } } nio->dataFNIndex++; airMopOkay(mop); return 0; } /* ** we try to use the oldest format that will hold the nrrd */ int _nrrdFormatNRRD_whichVersion(const Nrrd *nrrd, NrrdIoState *nio) { int ret; if (_nrrdFieldInteresting(nrrd, nio, nrrdField_measurement_frame)) { ret = 5; } else if (_nrrdFieldInteresting(nrrd, nio, nrrdField_thicknesses) || _nrrdFieldInteresting(nrrd, nio, nrrdField_space) || _nrrdFieldInteresting(nrrd, nio, nrrdField_space_dimension) || _nrrdFieldInteresting(nrrd, nio, nrrdField_sample_units) || airStrlen(nio->dataFNFormat) || nio->dataFNArr->len > 1) { ret = 4; } else if (_nrrdFieldInteresting(nrrd, nio, nrrdField_kinds)) { ret = 3; } else if (nrrdKeyValueSize(nrrd)) { ret = 2; } else { ret = 1; } return ret; } static int _nrrdFormatNRRD_available(void) { return AIR_TRUE; } static int _nrrdFormatNRRD_nameLooksLike(const char *filename) { return (airEndsWith(filename, NRRD_EXT_NRRD) || airEndsWith(filename, NRRD_EXT_NHDR)); } static int _nrrdFormatNRRD_fitsInto(const Nrrd *nrrd, const NrrdEncoding *encoding, int useBiff) { static const char me[]="_nrrdFormatNRRD_fitsInto"; if (!( nrrd && encoding )) { biffMaybeAddf(useBiff, NRRD, "%s: got NULL nrrd (%p) or encoding (%p)", me, AIR_CVOIDP(nrrd), AIR_CVOIDP(encoding)); return AIR_FALSE; } /* everything fits in a nrrd */ return AIR_TRUE; } static int _nrrdFormatNRRD_contentStartsLike(NrrdIoState *nio) { return (!strcmp(MAGIC0, nio->line) || !strcmp(MAGIC1, nio->line) || !strcmp(MAGIC2, nio->line) || !strcmp(MAGIC3, nio->line) || !strcmp(MAGIC4, nio->line) || !strcmp(MAGIC5, nio->line) ); } /* ** _nrrdHeaderCheck() ** ** minimal consistency checks on relationship between fields of nrrd, ** only to be used after the headers is parsed, and before the data is ** read, to make sure that information required for reading data is in ** fact known. ** ** NOTE: this is not the place to do the sort of checking done by ** nrrdCheck(), because it includes I/O-specific stuff ** */ int _nrrdHeaderCheck(Nrrd *nrrd, NrrdIoState *nio, int checkSeen) { static const char me[]="_nrrdHeaderCheck"; int i; if (checkSeen) { for (i=1; i<=NRRD_FIELD_MAX; i++) { if (_nrrdFieldRequired[i] && !nio->seen[i]) { biffAddf(NRRD, "%s: didn't see required field: %s", me, airEnumStr(nrrdField, i)); return 1; } } } if (nrrdTypeBlock == nrrd->type && !nrrd->blockSize) { biffAddf(NRRD, "%s: type is %s, but missing field: %s", me, airEnumStr(nrrdType, nrrdTypeBlock), airEnumStr(nrrdField, nrrdField_block_size)); return 1; } if (!nrrdElementSize(nrrd)) { biffAddf(NRRD, "%s: nrrd reports zero element size!", me); return 1; } /* _nrrdReadNrrdParse_sizes() checks axis[i].size, which completely determines the return of nrrdElementNumber() */ if (airEndianUnknown == nio->endian && nio->encoding->endianMatters && 1 != nrrdElementSize(nrrd)) { biffAddf(NRRD, "%s: type (%s) and encoding (%s) require %s info", me, airEnumStr(nrrdType, nrrd->type), nio->encoding->name, airEnumStr(nrrdField, nrrdField_endian)); return 1; } /* we don't really try to enforce consistency with the min/max/center/size information on each axis, other than the value checking done by the _nrrdReadNrrdParse_* functions, because we only really care that we know each axis size. Past that, if the user messes it up, its not really our problem ... */ return 0; } /* ** NOTE: currently, this will read, without complaints or errors, ** newer NRRD format features from older NRRD files (as indicated by ** magic), such as key/value pairs from a NRRD0001 file, even though ** strictly speaking these are violations of the format. ** ** NOTE: by giving a NULL "file", you can make this function basically ** do the work of reading in datafiles, without any header parsing */ static int _nrrdFormatNRRD_read(FILE *file, Nrrd *nrrd, NrrdIoState *nio) { static const char me[]="_nrrdFormatNRRD_read"; /* Dynamically allocated for space reasons. */ /* MWC: These strlen usages look really unsafe. */ int ret; unsigned int llen; size_t valsPerPiece; char *data; FILE *dataFile=NULL; /* record where the header is being read from for the sake of nrrdIoStateDataFileIterNext() */ nio->headerFile = file; /* GLK forgets the context in which file might be reasonably NULL but on Fri Sep 23 09:48:41 EDT 2005 this was "if (file) { ..." */ /* nio->headerStringRead is NULL whenever IO from string is not being done */ if (file || nio->headerStringRead) { if (!_nrrdFormatNRRD_contentStartsLike(nio)) { biffAddf(NRRD, "%s: this doesn't look like a %s file", me, nrrdFormatNRRD->name); return 1; } /* parse all the header lines */ do { nio->pos = 0; if (_nrrdOneLine(&llen, nio, file)) { biffAddf(NRRD, "%s: trouble getting line of header", me); return 1; } if (llen > 1) { ret = _nrrdReadNrrdParseField(nio, AIR_TRUE); if (!ret) { biffAddf(NRRD, "%s: trouble parsing NRRD field identifier from " "in \"%s\"", me, nio->line); return 1; } /* comments and key/values are allowed multiple times */ if (nio->seen[ret] && !(ret == nrrdField_comment || ret == nrrdField_keyvalue)) { biffAddf(NRRD, "%s: already set field %s", me, airEnumStr(nrrdField, ret)); return 1; } if (nrrdFieldInfoParse[ret](file, nrrd, nio, AIR_TRUE)) { biffAddf(NRRD, "%s: trouble parsing %s info |%s|", me, airEnumStr(nrrdField, ret), nio->line + nio->pos); return 1; } nio->seen[ret] = AIR_TRUE; } } while (llen > 1); /* either 0 == llen: we're at EOF (or end of nio->headerStringRead), or 1 == llen: we just read the empty line separating header from data */ if (0 == llen && !nio->headerStringRead && !nio->dataFNFormat && 0 == nio->dataFNArr->len) { /* we're at EOF, we're not reading from a string, but there's apparently no separate data file */ biffAddf(NRRD, "%s: hit end of header, but no \"%s\" given", me, airEnumStr(nrrdField, nrrdField_data_file)); return 1; } } if (_nrrdHeaderCheck(nrrd, nio, !!file)) { biffAddf(NRRD, "%s: %s", me, (llen ? "finished reading header, but there were problems" : "hit EOF before seeing a complete valid header")); return 1; } /* we seemed to have read in a valid header; now allocate the memory. For directIO-compatible allocation we need to get the first datafile */ nrrdIoStateDataFileIterBegin(nio); /* NOTE: if nio->headerStringRead, this may set dataFile to NULL */ if (nrrdIoStateDataFileIterNext(&dataFile, nio, AIR_TRUE)) { biffAddf(NRRD, "%s: couldn't open the first datafile", me); return 1; } if (nio->skipData) { nrrd->data = NULL; data = NULL; } else { if (_nrrdCalloc(nrrd, nio, dataFile)) { biffAddf(NRRD, "%s: couldn't allocate memory for data", me); return 1; } data = (char*)nrrd->data; } /* iterate through datafiles and read them in */ /* NOTE: you have to open dataFile even in the case of skipData, because caller might have set keepNrrdDataFileOpen, in which case you need to do any line or byte skipping if it is specified */ valsPerPiece = nrrdElementNumber(nrrd)/_nrrdDataFNNumber(nio); while (dataFile) { /* ---------------- skip, if need be */ if (nrrdLineSkip(dataFile, nio)) { biffAddf(NRRD, "%s: couldn't skip lines", me); return 1; } if (!nio->encoding->isCompression) { /* bytes are skipped here for non-compression encodings, but are skipped within the decompressed stream for compression encodings */ if (nrrdByteSkip(dataFile, nrrd, nio)) { biffAddf(NRRD, "%s: couldn't skip bytes", me); return 1; } } /* ---------------- read the data itself */ if (2 <= nrrdStateVerboseIO) { fprintf(stderr, "(%s: reading %s data ... ", me, nio->encoding->name); fflush(stderr); } if (!nio->skipData) { if (nio->encoding->read(dataFile, data, valsPerPiece, nrrd, nio)) { if (2 <= nrrdStateVerboseIO) { fprintf(stderr, "error!\n"); } biffAddf(NRRD, "%s:", me); return 1; } } if (2 <= nrrdStateVerboseIO) { fprintf(stderr, "done)\n"); } /* ---------------- go to next data file */ if (nio->keepNrrdDataFileOpen && _nrrdDataFNNumber(nio) == 1) { nio->dataFile = dataFile; } else { if (dataFile != nio->headerFile) { dataFile = airFclose(dataFile); } } data += valsPerPiece*nrrdElementSize(nrrd); if (nrrdIoStateDataFileIterNext(&dataFile, nio, AIR_TRUE)) { biffAddf(NRRD, "%s: couldn't get the next datafile", me); return 1; } } if (airEndianUnknown != nio->endian && nrrd->data) { /* we positively know the endianness of data just read */ if (1 < nrrdElementSize(nrrd) && nio->encoding->endianMatters && nio->endian != airMyEndian()) { /* endianness exposed in encoding, and its wrong */ if (2 <= nrrdStateVerboseIO) { fprintf(stderr, "(%s: fixing endianness ... ", me); fflush(stderr); } nrrdSwapEndian(nrrd); if (2 <= nrrdStateVerboseIO) { fprintf(stderr, "done)\n"); fflush(stderr); } } } return 0; } static int _nrrdFormatNRRD_write(FILE *file, const Nrrd *nrrd, NrrdIoState *nio) { static const char me[]="_nrrdFormatNRRD_write"; char strbuf[AIR_STRLEN_MED], *strptr, *tmp; int ii; unsigned int jj; airArray *mop; FILE *dataFile=NULL; size_t valsPerPiece; char *data; mop = airMopNew(); if (!(file || nio->headerStringWrite || nio->learningHeaderStrlen)) { biffAddf(NRRD, "%s: have no file or string to write to, nor are " "learning header string length", me); airMopError(mop); return 1; } if (nrrdTypeBlock == nrrd->type && nrrdEncodingAscii == nio->encoding) { biffAddf(NRRD, "%s: can't write nrrd type %s with %s encoding", me, airEnumStr(nrrdType, nrrdTypeBlock), nrrdEncodingAscii->name); airMopError(mop); return 1; } /* record where the header is being written to for the sake of nrrdIoStateDataFileIterNext(). This may be NULL if nio->headerStringWrite is non-NULL */ nio->headerFile = file; /* we have to make sure that the data filename information is set (if needed), so that it can be printed by _nrrdFprintFieldInfo */ if (nio->detachedHeader && !nio->dataFNFormat && 0 == nio->dataFNArr->len) { /* NOTE: this means someone requested a detached header, but we don't already have implicit (via dataFNFormat) or explicit (via dataFN[]) information about the data file */ /* NOTE: whether or not nio->skipData, we have to contrive a filename to say in the "data file" field, which is stored in nio->dataFN[0], because the data filename will be "interesting", according to _nrrdFieldInteresting() */ /* NOTE: Fri Feb 4 01:42:20 EST 2005 the way this is now set up, having a name in dataFN[0] will trump the name implied by nio->{path,base}, which is a useful way for the user to explicitly set the output data filename (as with unu make -od) */ if (!( !!airStrlen(nio->path) && !!airStrlen(nio->base) )) { biffAddf(NRRD, "%s: can't create data file name: nio's " "path and base empty", me); airMopError(mop); return 1; } tmp = (char*)malloc(strlen(nio->base) + strlen(".") + strlen(nio->encoding->suffix) + 1); if (!tmp) { biffAddf(NRRD, "%s: couldn't allocate data filename", me); airMopError(mop); return 1; } airMopAdd(mop, tmp, airFree, airMopOnError); sprintf(tmp, "%s.%s", nio->base, nio->encoding->suffix); jj = airArrayLenIncr(nio->dataFNArr, 1); if (!nio->dataFNArr->data) { biffAddf(NRRD, "%s: can't increase dataFNArr storage", me); airMopError(mop); return 1; } nio->dataFN[jj] = tmp; } /* the magic is in fact the first thing to be written */ if (file) { fprintf(file, "%s%04d\n", MAGIC, _nrrdFormatNRRD_whichVersion(nrrd, nio)); } else if (nio->headerStringWrite) { sprintf(nio->headerStringWrite, "%s%04d\n", MAGIC, _nrrdFormatNRRD_whichVersion(nrrd, nio)); } else { nio->headerStrlen = AIR_CAST(unsigned int, strlen(MAGIC) + strlen("0000")) + 1; } /* write the advertisement about where to get the file format */ if (!nio->skipFormatURL) { if (file) { fprintf(file, "# %s\n", _nrrdFormatURLLine0); fprintf(file, "# %s\n", _nrrdFormatURLLine1); } else if (nio->headerStringWrite) { sprintf(strbuf, "# %s\n", _nrrdFormatURLLine0); strcat(nio->headerStringWrite, strbuf); sprintf(strbuf, "# %s\n", _nrrdFormatURLLine1); strcat(nio->headerStringWrite, strbuf); } else { nio->headerStrlen += sprintf(strbuf, "# %s\n", _nrrdFormatURLLine0); nio->headerStrlen += sprintf(strbuf, "# %s\n", _nrrdFormatURLLine1); } } /* this is where the majority of the header printing happens */ for (ii=1; ii<=NRRD_FIELD_MAX; ii++) { if (_nrrdFieldInteresting(nrrd, nio, ii)) { if (file) { _nrrdFprintFieldInfo (file, "", nrrd, nio, ii); } else if (nio->headerStringWrite) { _nrrdSprintFieldInfo(&strptr, "", nrrd, nio, ii); if (strptr) { strcat(nio->headerStringWrite, strptr); strcat(nio->headerStringWrite, "\n"); free(strptr); strptr = NULL; } } else { _nrrdSprintFieldInfo(&strptr, "", nrrd, nio, ii); if (strptr) { nio->headerStrlen += AIR_CAST(unsigned int, strlen(strptr)); nio->headerStrlen += AIR_CAST(unsigned int, strlen("\n")); free(strptr); strptr = NULL; } } } } /* comments and key/value pairs handled differently */ for (jj=0; jjcmtArr->len; jj++) { char *strtmp; strtmp = airOneLinify(airStrdup(nrrd->cmt[jj])); if (file) { fprintf(file, "%c %s\n", NRRD_COMMENT_CHAR, strtmp); } else if (nio->headerStringWrite) { strptr = (char*)malloc(1 + strlen(" ") + strlen(strtmp) + strlen("\n") + 1); sprintf(strptr, "%c %s\n", NRRD_COMMENT_CHAR, strtmp); strcat(nio->headerStringWrite, strptr); free(strptr); strptr = NULL; } else { nio->headerStrlen += (1 + AIR_CAST(unsigned int, strlen(" ") + strlen(strtmp) + strlen("\n")) + 1); } airFree(strtmp); } for (jj=0; jjkvpArr->len; jj++) { if (file) { _nrrdKeyValueWrite(file, NULL, NULL, nrrd->kvp[0 + 2*jj], nrrd->kvp[1 + 2*jj]); } else if (nio->headerStringWrite) { _nrrdKeyValueWrite(NULL, &strptr, NULL, nrrd->kvp[0 + 2*jj], nrrd->kvp[1 + 2*jj]); if (strptr) { strcat(nio->headerStringWrite, strptr); free(strptr); strptr = NULL; } } else { _nrrdKeyValueWrite(NULL, &strptr, NULL, nrrd->kvp[0 + 2*jj], nrrd->kvp[1 + 2*jj]); if (strptr) { nio->headerStrlen += AIR_CAST(unsigned int, strlen(strptr)); free(strptr); strptr = NULL; } } } if (file) { if (!( nio->detachedHeader || _nrrdDataFNNumber(nio) > 1 )) { fprintf(file, "\n"); } } if (file && !nio->skipData) { nrrdIoStateDataFileIterBegin(nio); if (nrrdIoStateDataFileIterNext(&dataFile, nio, AIR_FALSE)) { biffAddf(NRRD, "%s: couldn't write the first datafile", me); airMopError(mop); return 1; } valsPerPiece = nrrdElementNumber(nrrd)/_nrrdDataFNNumber(nio); data = (char*)nrrd->data; do { /* ---------------- write data */ if (2 <= nrrdStateVerboseIO) { fprintf(stderr, "(%s: writing %s data ", me, nio->encoding->name); fflush(stderr); } if (nio->encoding->write(dataFile, data, valsPerPiece, nrrd, nio)) { if (2 <= nrrdStateVerboseIO) { fprintf(stderr, "error!\n"); } biffAddf(NRRD, "%s: couldn't write %s data", me, nio->encoding->name); airMopError(mop); return 1; } if (2 <= nrrdStateVerboseIO) { fprintf(stderr, "done)\n"); } /* ---------------- go to next data file */ if (dataFile != nio->headerFile) { dataFile = airFclose(dataFile); } data += valsPerPiece*nrrdElementSize(nrrd); if (nrrdIoStateDataFileIterNext(&dataFile, nio, AIR_TRUE)) { biffAddf(NRRD, "%s: couldn't get the next datafile", me); airMopError(mop); return 1; } } while (dataFile); } airMopOkay(mop); return 0; } const NrrdFormat _nrrdFormatNRRD = { "NRRD", AIR_FALSE, /* isImage */ AIR_TRUE, /* readable */ AIR_TRUE, /* usesDIO */ _nrrdFormatNRRD_available, _nrrdFormatNRRD_nameLooksLike, _nrrdFormatNRRD_fitsInto, _nrrdFormatNRRD_contentStartsLike, _nrrdFormatNRRD_read, _nrrdFormatNRRD_write }; const NrrdFormat *const nrrdFormatNRRD = &_nrrdFormatNRRD; cmtk-3.3.1/Utilities/NrrdIO/formatPNG.c000066400000000000000000000054251276303427400176030ustar00rootroot00000000000000/* NrrdIO: stand-alone code for basic nrrd functionality Copyright (C) 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. */ #include "NrrdIO.h" #include "privateNrrd.h" int _nrrdFormatPNG_available(void) { return AIR_FALSE; } int _nrrdFormatPNG_nameLooksLike(const char *filename) { return airEndsWith(filename, NRRD_EXT_PNG); } int _nrrdFormatPNG_fitsInto(const Nrrd *nrrd, const NrrdEncoding *encoding, int useBiff) { char me[]="_nrrdFormatPNG_fitsInto", err[AIR_STRLEN_MED]; AIR_UNUSED(nrrd); AIR_UNUSED(encoding); AIR_UNUSED(useBiff); sprintf(err, "%s: Sorry, %s format not available in NrrdIO", me, nrrdFormatPNG->name); biffMaybeAdd(NRRD, err, useBiff); return AIR_FALSE; } int _nrrdFormatPNG_contentStartsLike(NrrdIoState *nio) { AIR_UNUSED(nio); return AIR_FALSE; } int _nrrdFormatPNG_read(FILE *file, Nrrd *nrrd, NrrdIoState *nio) { char me[]="_nrrdReadPNG", err[AIR_STRLEN_MED]; AIR_UNUSED(file); AIR_UNUSED(nrrd); AIR_UNUSED(nio); sprintf(err, "%s: Sorry, %s format not available in NrrdIO", me, nrrdFormatPNG->name); biffAdd(NRRD, err); return 1; } int _nrrdFormatPNG_write(FILE *file, const Nrrd *nrrd, NrrdIoState *nio) { char me[]="_nrrdFormatPNG_write", err[AIR_STRLEN_MED]; AIR_UNUSED(file); AIR_UNUSED(nrrd); AIR_UNUSED(nio); sprintf(err, "%s: Sorry, %s format not available in NrrdIO", me, nrrdFormatPNG->name); biffAdd(NRRD, err); return 1; } const NrrdFormat _nrrdFormatPNG = { "PNG", AIR_FALSE, /* isImage */ AIR_FALSE, /* readable */ AIR_FALSE, /* usesDIO */ _nrrdFormatPNG_available, _nrrdFormatPNG_nameLooksLike, _nrrdFormatPNG_fitsInto, _nrrdFormatPNG_contentStartsLike, _nrrdFormatPNG_read, _nrrdFormatPNG_write }; const NrrdFormat *const nrrdFormatPNG = &_nrrdFormatPNG; cmtk-3.3.1/Utilities/NrrdIO/formatPNM.c000066400000000000000000000055101276303427400176040ustar00rootroot00000000000000/* NrrdIO: stand-alone code for basic nrrd functionality Copyright (C) 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. */ #include "NrrdIO.h" #include "privateNrrd.h" int _nrrdFormatPNM_available(void) { return AIR_FALSE; } int _nrrdFormatPNM_nameLooksLike(const char *filename) { return (airEndsWith(filename, NRRD_EXT_PGM) || airEndsWith(filename, NRRD_EXT_PPM)); } int _nrrdFormatPNM_fitsInto(const Nrrd *nrrd, const NrrdEncoding *encoding, int useBiff) { char me[]="_nrrdFormatPNM_fitsInto", err[AIR_STRLEN_MED]; AIR_UNUSED(nrrd); AIR_UNUSED(encoding); AIR_UNUSED(useBiff); sprintf(err, "%s: Sorry, %s format not available in NrrdIO", me, nrrdFormatPNM->name); biffMaybeAdd(NRRD, err, useBiff); return AIR_FALSE; } int _nrrdFormatPNM_contentStartsLike(NrrdIoState *nio) { AIR_UNUSED(nio); return AIR_FALSE; } int _nrrdFormatPNM_read(FILE *file, Nrrd *nrrd, NrrdIoState *nio) { char me[]="_nrrdReadPNM", err[AIR_STRLEN_MED]; AIR_UNUSED(file); AIR_UNUSED(nrrd); AIR_UNUSED(nio); sprintf(err, "%s: Sorry, %s format not available in NrrdIO", me, nrrdFormatPNM->name); biffAdd(NRRD, err); return 1; } int _nrrdFormatPNM_write(FILE *file, const Nrrd *nrrd, NrrdIoState *nio) { char me[]="_nrrdFormatPNM_write", err[AIR_STRLEN_MED]; AIR_UNUSED(file); AIR_UNUSED(nrrd); AIR_UNUSED(nio); sprintf(err, "%s: Sorry, %s format not available in NrrdIO", me, nrrdFormatPNM->name); biffAdd(NRRD, err); return 1; } const NrrdFormat _nrrdFormatPNM = { "PNM", AIR_FALSE, /* isImage */ AIR_FALSE, /* readable */ AIR_FALSE, /* usesDIO */ _nrrdFormatPNM_available, _nrrdFormatPNM_nameLooksLike, _nrrdFormatPNM_fitsInto, _nrrdFormatPNM_contentStartsLike, _nrrdFormatPNM_read, _nrrdFormatPNM_write }; const NrrdFormat *const nrrdFormatPNM = &_nrrdFormatPNM; cmtk-3.3.1/Utilities/NrrdIO/formatText.c000066400000000000000000000055731276303427400201070ustar00rootroot00000000000000/* NrrdIO: stand-alone code for basic nrrd functionality Copyright (C) 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. */ #include "NrrdIO.h" #include "privateNrrd.h" int _nrrdFormatText_available(void) { return AIR_FALSE; } int _nrrdFormatText_nameLooksLike(const char *fname) { return (airEndsWith(fname, NRRD_EXT_TEXT) || airEndsWith(fname, ".text") || airEndsWith(fname, ".ascii")); } int _nrrdFormatText_fitsInto(const Nrrd *nrrd, const NrrdEncoding *encoding, int useBiff) { char me[]="_nrrdFormatText_fitsInto", err[AIR_STRLEN_MED]; AIR_UNUSED(nrrd); AIR_UNUSED(encoding); AIR_UNUSED(useBiff); sprintf(err, "%s: Sorry, %s format not available in NrrdIO", me, nrrdFormatText->name); biffMaybeAdd(NRRD, err, useBiff); return AIR_FALSE; } int _nrrdFormatText_contentStartsLike(NrrdIoState *nio) { AIR_UNUSED(nio); return AIR_FALSE; } int _nrrdFormatText_read(FILE *file, Nrrd *nrrd, NrrdIoState *nio) { char me[]="_nrrdReadText", err[AIR_STRLEN_MED]; AIR_UNUSED(file); AIR_UNUSED(nrrd); AIR_UNUSED(nio); sprintf(err, "%s: Sorry, %s format not available in NrrdIO", me, nrrdFormatText->name); biffAdd(NRRD, err); return 1; } int _nrrdFormatText_write(FILE *file, const Nrrd *nrrd, NrrdIoState *nio) { char me[]="_nrrdFormatText_write", err[AIR_STRLEN_MED]; AIR_UNUSED(file); AIR_UNUSED(nrrd); AIR_UNUSED(nio); sprintf(err, "%s: Sorry, %s format not available in NrrdIO", me, nrrdFormatText->name); biffAdd(NRRD, err); return 1; } const NrrdFormat _nrrdFormatText = { "text", AIR_FALSE, /* isImage */ AIR_FALSE, /* readable */ AIR_FALSE, /* usesDIO */ _nrrdFormatText_available, _nrrdFormatText_nameLooksLike, _nrrdFormatText_fitsInto, _nrrdFormatText_contentStartsLike, _nrrdFormatText_read, _nrrdFormatText_write }; const NrrdFormat *const nrrdFormatText = &_nrrdFormatText; cmtk-3.3.1/Utilities/NrrdIO/formatVTK.c000066400000000000000000000055121276303427400176200ustar00rootroot00000000000000/* NrrdIO: stand-alone code for basic nrrd functionality Copyright (C) 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. */ #include "NrrdIO.h" #include "privateNrrd.h" int _nrrdFormatVTK_available(void) { return AIR_FALSE; } int _nrrdFormatVTK_nameLooksLike(const char *fname) { return airEndsWith(fname, NRRD_EXT_VTK); } int _nrrdFormatVTK_fitsInto(const Nrrd *nrrd, const NrrdEncoding *encoding, int useBiff) { char me[]="_nrrdFormatVTK_fitsInto", err[AIR_STRLEN_MED]; AIR_UNUSED(nrrd); AIR_UNUSED(encoding); AIR_UNUSED(useBiff); sprintf(err, "%s: Sorry, %s format not available in NrrdIO", me, nrrdFormatVTK->name); biffMaybeAdd(NRRD, err, useBiff); return AIR_FALSE; } int _nrrdFormatVTK_contentStartsLike(NrrdIoState *nio) { AIR_UNUSED(nio); return AIR_FALSE; } int _nrrdFormatVTK_read(FILE *file, Nrrd *nrrd, NrrdIoState *nio) { char me[]="_nrrdReadVTK", err[AIR_STRLEN_MED]; AIR_UNUSED(file); AIR_UNUSED(nrrd); AIR_UNUSED(nio); sprintf(err, "%s: Sorry, %s format not available in NrrdIO", me, nrrdFormatVTK->name); biffAdd(NRRD, err); return 1; } /* this strongly assumes that nrrdFitsInFormat() was true */ int _nrrdFormatVTK_write(FILE *file, const Nrrd *nrrd, NrrdIoState *nio) { char me[]="_nrrdFormatVTK_write", err[AIR_STRLEN_MED]; AIR_UNUSED(file); AIR_UNUSED(nrrd); AIR_UNUSED(nio); sprintf(err, "%s: Sorry, %s format not available in NrrdIO", me, nrrdFormatVTK->name); biffAdd(NRRD, err); return 1; } const NrrdFormat _nrrdFormatVTK = { "VTK", AIR_FALSE, /* isImage */ AIR_FALSE, /* readable */ AIR_FALSE, /* usesDIO */ _nrrdFormatVTK_available, _nrrdFormatVTK_nameLooksLike, _nrrdFormatVTK_fitsInto, _nrrdFormatVTK_contentStartsLike, _nrrdFormatVTK_read, _nrrdFormatVTK_write }; const NrrdFormat *const nrrdFormatVTK = &_nrrdFormatVTK; cmtk-3.3.1/Utilities/NrrdIO/gzio.c000066400000000000000000000516741276303427400167250ustar00rootroot00000000000000/* NrrdIO: stand-alone code for basic nrrd functionality Copyright (C) 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. */ /* This file is a modified version of the 'gzio.c' and 'zutil.h' source files from the zlib 1.1.4 distribution. zlib.h -- interface of the 'zlib' general purpose compression library version 1.1.4, March 11th, 2002 Copyright (C) 1995-2002 Jean-loup Gailly and Mark Adler This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. Jean-loup Gailly Mark Adler jloup@gzip.org madler@alumni.caltech.edu The data format used by the zlib library is described by RFCs (Request for Comments) 1950 to 1952 in the files ftp://ds.internic.net/rfc/rfc1950.txt (zlib format), rfc1951.txt (deflate format) and rfc1952.txt (gzip format). */ #if TEEM_ZLIB #include "NrrdIO.h" #include "privateNrrd.h" #ifdef _WIN32 /* Window 95 & Windows NT */ # define _NRRD_OS_CODE 0x0b #endif #if defined(MACOS) || defined(TARGET_OS_MAC) || defined(__APPLE_CC__) # define _NRRD_OS_CODE 0x07 #endif #ifndef _NRRD_OS_CODE # define _NRRD_OS_CODE 0x03 /* assume Unix */ #endif /* default memLevel */ #if MAX_MEM_LEVEL >= 8 # define _NRRD_DEF_MEM_LEVEL 8 #else # define _NRRD_DEF_MEM_LEVEL MAX_MEM_LEVEL #endif /* stream buffer size */ #define _NRRD_Z_BUFSIZE 16 * 1024 /* gzip flag byte */ #define _NRRD_ASCII_FLAG 0x01 /* bit 0 set: file probably ascii text */ #define _NRRD_HEAD_CRC 0x02 /* bit 1 set: header CRC present */ #define _NRRD_EXTRA_FIELD 0x04 /* bit 2 set: extra field present */ #define _NRRD_ORIG_NAME 0x08 /* bit 3 set: original file name present */ #define _NRRD_COMMENT 0x10 /* bit 4 set: file comment present */ #define _NRRD_RESERVED 0xE0 /* bits 5..7: reserved */ typedef struct _NrrdGzStream { z_stream stream; int z_err; /* error code for last stream operation */ int z_eof; /* set if end of input file */ FILE *file; /* .gz file */ Byte *inbuf; /* input buffer */ Byte *outbuf; /* output buffer */ uLong crc; /* crc32 of uncompressed data */ char *msg; /* error message */ int transparent; /* 1 if input file is not a .gz file */ char mode; /* 'w' or 'r' */ long startpos; /* start of compressed data in file (header skipped) */ } _NrrdGzStream; static int _nrrdGzMagic[2] = {0x1f, 0x8b}; /* gzip magic header */ /* zlib error messages */ static const char *_nrrdGzErrMsg[10] = { "need dictionary", /* Z_NEED_DICT 2 */ "stream end", /* Z_STREAM_END 1 */ "", /* Z_OK 0 */ "file error", /* Z_ERRNO (-1) */ "stream error", /* Z_STREAM_ERROR (-2) */ "data error", /* Z_DATA_ERROR (-3) */ "insufficient memory", /* Z_MEM_ERROR (-4) */ "buffer error", /* Z_BUF_ERROR (-5) */ "incompatible version",/* Z_VERSION_ERROR (-6) */ ""}; #define _NRRD_GZ_ERR_MSG(err) _nrrdGzErrMsg[Z_NEED_DICT-(err)] /* some forward declarations for things in this file */ static void _nrrdGzCheckHeader(_NrrdGzStream *s); static int _nrrdGzDestroy(_NrrdGzStream *s); static int _nrrdGzDoFlush(gzFile file, int flush); static void _nrrdGzPutLong(FILE *file, uLong x); static uLong _nrrdGzGetLong(_NrrdGzStream *s); /* ** _nrrdGzOpen() ** ** Opens a gzip (.gz) file for reading or writing. The mode parameter ** is like in fopen ("rb" or "wb"). The file represented by the FILE* pointer ** should be open already with the same mode. The mode parameter can also be ** used to specify the compression level "[0-9]" and strategy "[f|h]". ** ** The compression level must be between 0 and 9: 1 gives best speed, ** 9 gives best compression, 0 gives no compression at all (the input data ** is simply copied a block at a time). The default level is 6. ** ** The strategy parameter is used to tune the compression algorithm. Use ** "f" for data produced by a filter (or predictor), or "h" to force Huffman ** encoding only (no string match). Filtered data consists mostly of small ** values with a somewhat random distribution. In this case, the compression ** algorithm is tuned to compress them better. The effect of "f" is to force ** more Huffman coding and less string matching; it is somewhat intermediate ** between the default and Huffman. The strategy parameter only affects the ** compression ratio but not the correctness of the compressed output even ** if it is not set appropriately. ** ** The complete syntax for the mode parameter is: "(r|w[a])[0-9][f|h]". ** ** Returns Z_NULL if the file could not be opened or if there was ** insufficient memory to allocate the (de)compression state; errno ** can be checked to distinguish the two cases (if errno is zero, the ** zlib error is Z_MEM_ERROR). */ gzFile _nrrdGzOpen(FILE* fd, const char* mode) { static const char me[]="_nrrdGzOpen"; int error; int level = Z_DEFAULT_COMPRESSION; /* compression level */ int strategy = Z_DEFAULT_STRATEGY; /* compression strategy */ const char *p = mode; _NrrdGzStream *s; char fmode[AIR_STRLEN_MED]; /* copy of mode, without the compression level */ char *m = fmode; if (!mode) { biffAddf(NRRD, "%s: no file mode specified", me); return Z_NULL; } /* allocate stream struct */ s = (_NrrdGzStream *)calloc(1, sizeof(_NrrdGzStream)); if (!s) { biffAddf(NRRD, "%s: failed to allocate stream buffer", me); return Z_NULL; } /* initialize stream struct */ s->stream.zalloc = (alloc_func)0; s->stream.zfree = (free_func)0; s->stream.opaque = (voidpf)0; s->stream.next_in = s->inbuf = Z_NULL; s->stream.next_out = s->outbuf = Z_NULL; s->stream.avail_in = s->stream.avail_out = 0; s->file = NULL; s->z_err = Z_OK; s->z_eof = 0; s->crc = crc32(0L, Z_NULL, 0); s->msg = NULL; s->transparent = 0; /* parse mode flag */ s->mode = '\0'; do { if (*p == 'r') s->mode = 'r'; if (*p == 'w' || *p == 'a') s->mode = 'w'; if (*p >= '0' && *p <= '9') { level = *p - '0'; } else if (*p == 'f') { strategy = Z_FILTERED; } else if (*p == 'h') { strategy = Z_HUFFMAN_ONLY; } else { *m++ = *p; /* copy the mode */ } } while (*p++ && m != fmode + sizeof(fmode)); if (s->mode == '\0') { biffAddf(NRRD, "%s: invalid file mode", me); return _nrrdGzDestroy(s), (gzFile)Z_NULL; } if (s->mode == 'w') { error = deflateInit2(&(s->stream), level, Z_DEFLATED, -MAX_WBITS, _NRRD_DEF_MEM_LEVEL, strategy); /* windowBits is passed < 0 to suppress zlib header */ s->stream.next_out = s->outbuf = (Byte*)calloc(1, _NRRD_Z_BUFSIZE); if (error != Z_OK || s->outbuf == Z_NULL) { biffAddf(NRRD, "%s: stream init failed", me); return _nrrdGzDestroy(s), (gzFile)Z_NULL; } } else { s->stream.next_in = s->inbuf = (Byte*)calloc(1, _NRRD_Z_BUFSIZE); error = inflateInit2(&(s->stream), -MAX_WBITS); /* windowBits is passed < 0 to tell that there is no zlib header. * Note that in this case inflate *requires* an extra "dummy" byte * after the compressed stream in order to complete decompression and * return Z_STREAM_END. Here the gzip CRC32 ensures that 4 bytes are * present after the compressed stream. */ if (error != Z_OK || s->inbuf == Z_NULL) { biffAddf(NRRD, "%s: stream init failed", me); return _nrrdGzDestroy(s), (gzFile)Z_NULL; } } s->stream.avail_out = _NRRD_Z_BUFSIZE; errno = 0; s->file = fd; if (s->file == NULL) { biffAddf(NRRD, "%s: null file pointer", me); return _nrrdGzDestroy(s), (gzFile)Z_NULL; } if (s->mode == 'w') { /* Write a very simple .gz header: */ fprintf(s->file, "%c%c%c%c%c%c%c%c%c%c", _nrrdGzMagic[0], _nrrdGzMagic[1], Z_DEFLATED, 0 /*flags*/, 0,0,0,0 /*time*/, 0 /*xflags*/, _NRRD_OS_CODE); s->startpos = 10L; /* We use 10L instead of ftell(s->file) to because ftell causes an * fflush on some systems. This version of the library doesn't use * startpos anyway in write mode, so this initialization is not * necessary. */ } else { _nrrdGzCheckHeader(s); /* skip the .gz header */ s->startpos = (ftell(s->file) - s->stream.avail_in); } return (gzFile)s; } /* ** _nrrdGzClose() ** ** Flushes all pending output if necessary, closes the compressed file ** and deallocates the (de)compression state. */ int _nrrdGzClose (gzFile file) { static const char me[]="_nrrdGzClose"; int error; _NrrdGzStream *s = (_NrrdGzStream*)file; if (s == NULL) { biffAddf(NRRD, "%s: invalid stream", me); return 1; } if (s->mode == 'w') { error = _nrrdGzDoFlush(file, Z_FINISH); if (error != Z_OK) { biffAddf(NRRD, "%s: failed to flush pending data", me); return _nrrdGzDestroy((_NrrdGzStream*)file); } _nrrdGzPutLong(s->file, s->crc); _nrrdGzPutLong(s->file, s->stream.total_in); } return _nrrdGzDestroy((_NrrdGzStream*)file); } /* ** _nrrdGzRead() ** ** Reads the given number of uncompressed bytes from the compressed file. ** Returns the number of bytes actually read (0 for end of file). */ int _nrrdGzRead(gzFile file, void* buf, unsigned int len, unsigned int* didread) { static const char me[]="_nrrdGzRead"; _NrrdGzStream *s = (_NrrdGzStream*)file; Bytef *start = (Bytef*)buf; /* starting point for crc computation */ Byte *next_out; /* == stream.next_out but not forced far (for MSDOS) */ if (s == NULL || s->mode != 'r') { biffAddf(NRRD, "%s: invalid stream or file mode", me); *didread = 0; return 1; } if (s->z_err == Z_DATA_ERROR || s->z_err == Z_ERRNO) { biffAddf(NRRD, "%s: data read error", me); *didread = 0; return 1; } if (s->z_err == Z_STREAM_END) { *didread = 0; return 0; /* EOF */ } next_out = (Byte*)buf; s->stream.next_out = (Bytef*)buf; s->stream.avail_out = len; while (s->stream.avail_out != 0) { if (s->transparent) { /* Copy first the lookahead bytes: */ uInt n = s->stream.avail_in; if (n > s->stream.avail_out) n = s->stream.avail_out; if (n > 0) { memcpy(s->stream.next_out, s->stream.next_in, n); next_out += n; s->stream.next_out = next_out; s->stream.next_in += n; s->stream.avail_out -= n; s->stream.avail_in -= n; } if (s->stream.avail_out > 0) { s->stream.avail_out -= (uInt)fread(next_out, 1, s->stream.avail_out, s->file); } len -= s->stream.avail_out; s->stream.total_in += len; s->stream.total_out += len; if (len == 0) s->z_eof = 1; *didread = len; return 0; } if (s->stream.avail_in == 0 && !s->z_eof) { errno = 0; s->stream.avail_in = (uInt)fread(s->inbuf, 1, _NRRD_Z_BUFSIZE, s->file); if (s->stream.avail_in == 0) { s->z_eof = 1; if (ferror(s->file)) { s->z_err = Z_ERRNO; break; } } s->stream.next_in = s->inbuf; } s->z_err = inflate(&(s->stream), Z_NO_FLUSH); if (s->z_err == Z_STREAM_END) { /* Check CRC and original size */ s->crc = crc32(s->crc, start, (uInt)(s->stream.next_out - start)); start = s->stream.next_out; if (_nrrdGzGetLong(s) != s->crc) { s->z_err = Z_DATA_ERROR; } else { (void)_nrrdGzGetLong(s); /* The uncompressed length returned by above getlong() may * be different from s->stream.total_out) in case of * concatenated .gz files. Check for such files: */ _nrrdGzCheckHeader(s); if (s->z_err == Z_OK) { uLong total_in = s->stream.total_in; uLong total_out = s->stream.total_out; inflateReset(&(s->stream)); s->stream.total_in = total_in; s->stream.total_out = total_out; s->crc = crc32(0L, Z_NULL, 0); } } } if (s->z_err != Z_OK || s->z_eof) break; } s->crc = crc32(s->crc, start, (uInt)(s->stream.next_out - start)); *didread = len - s->stream.avail_out; return 0; } /* ** _nrrdGzWrite() ** ** Writes the given number of uncompressed bytes into the compressed file. ** Returns the number of bytes actually written (0 in case of error). */ int _nrrdGzWrite(gzFile file, const void* buf, unsigned int len, unsigned int* written) { static const char me[]="_nrrdGzWrite"; _NrrdGzStream *s = (_NrrdGzStream*)file; void *nonconstbuf; if (s == NULL || s->mode != 'w') { biffAddf(NRRD, "%s: invalid stream or file mode", me); *written = 0; return 1; } /* If you google for "const correct zlib" or "zlib.h is not const-correct" you'll find zlib mailing list discussions of how zlib doesn't have all the consts that it should, and various code examples of using multiple casts to hide the problem. Here's a slow way that doesn't use mere casting to make the const go away */ memcpy(&nonconstbuf, &buf, sizeof(void*)); s->stream.next_in = (Bytef*)nonconstbuf; s->stream.avail_in = len; while (s->stream.avail_in != 0) { if (s->stream.avail_out == 0) { s->stream.next_out = s->outbuf; if (fwrite(s->outbuf, 1, _NRRD_Z_BUFSIZE, s->file) != _NRRD_Z_BUFSIZE) { s->z_err = Z_ERRNO; biffAddf(NRRD, "%s: failed to write to file", me); break; } s->stream.avail_out = _NRRD_Z_BUFSIZE; } s->z_err = deflate(&(s->stream), Z_NO_FLUSH); if (s->z_err != Z_OK) break; } s->crc = crc32(s->crc, (const Bytef *)buf, len); *written = len - s->stream.avail_in; return 0; } /* ** _nrrdGzGetByte() ** ** Reads a byte from a _NrrdGzStream. Updates next_in and avail_in. ** Returns EOF for end of file. ** IN assertion: the stream s has been sucessfully opened for reading. */ static int _nrrdGzGetByte(_NrrdGzStream *s) { static const char me[]="_nrrdGzGetByte"; if (s->z_eof) return EOF; if (s->stream.avail_in == 0) { errno = 0; s->stream.avail_in = (uInt)fread(s->inbuf, 1, _NRRD_Z_BUFSIZE, s->file); if (s->stream.avail_in == 0) { s->z_eof = 1; if (ferror(s->file)) { biffAddf(NRRD, "%s: failed to read from file", me); s->z_err = Z_ERRNO; } return EOF; } s->stream.next_in = s->inbuf; } s->stream.avail_in--; return *(s->stream.next_in)++; } /* ******** _nrrdGzCheckHeader() ** ** Checks the gzip header of a _NrrdGzStream opened for reading. Sets ** the stream mode to transparent if the gzip magic header is not ** present; sets s->err to Z_DATA_ERROR if the magic header is present ** but the rest of the header is incorrect. ** IN assertion: the stream s has already been created sucessfully; ** s->stream.avail_in is zero for the first time, but may be non-zero ** for concatenated .gz files. */ static void _nrrdGzCheckHeader(_NrrdGzStream *s) { static const char me[]="_nrrdGzCheckHeader"; int method; /* method byte */ int flags; /* flags byte */ uInt len; int c; /* Check the gzip magic header */ for (len = 0; len < 2; len++) { c = _nrrdGzGetByte(s); if (c != _nrrdGzMagic[len]) { if (len != 0) s->stream.avail_in++, s->stream.next_in--; if (c != EOF) { s->stream.avail_in++, s->stream.next_in--; s->transparent = 1; } s->z_err = s->stream.avail_in != 0 ? Z_OK : Z_STREAM_END; return; } } method = _nrrdGzGetByte(s); flags = _nrrdGzGetByte(s); if (method != Z_DEFLATED || (flags & _NRRD_RESERVED) != 0) { biffAddf(NRRD, "%s: gzip compression method is not deflate", me); s->z_err = Z_DATA_ERROR; return; } /* Discard time, xflags and OS code: */ for (len = 0; len < 6; len++) (void)_nrrdGzGetByte(s); if ((flags & _NRRD_EXTRA_FIELD) != 0) { /* skip the extra field */ len = (uInt)_nrrdGzGetByte(s); len += ((uInt)_nrrdGzGetByte(s))<<8; /* len is garbage if EOF but the loop below will quit anyway */ while (len-- != 0 && _nrrdGzGetByte(s) != EOF) ; } if ((flags & _NRRD_ORIG_NAME) != 0) { /* skip the original file name */ while ((c = _nrrdGzGetByte(s)) != 0 && c != EOF) ; } if ((flags & _NRRD_COMMENT) != 0) { /* skip the .gz file comment */ while ((c = _nrrdGzGetByte(s)) != 0 && c != EOF) ; } if ((flags & _NRRD_HEAD_CRC) != 0) { /* skip the header crc */ for (len = 0; len < 2; len++) (void)_nrrdGzGetByte(s); } s->z_err = s->z_eof ? Z_DATA_ERROR : Z_OK; } /* ** _nrrdGzDestroy() ** ** Cleans up then free the given _NrrdGzStream. Returns a zlib error code. ** Try freeing in the reverse order of allocations. FILE* s->file is not ** closed. Because we didn't allocate it, we shouldn't delete it. */ static int _nrrdGzDestroy(_NrrdGzStream *s) { static const char me[]="_nrrdGzDestroy"; int error = Z_OK; if (s == NULL) { biffAddf(NRRD, "%s: invalid stream", me); return 1; } s->msg = (char *)airFree(s->msg); if (s->stream.state != NULL) { if (s->mode == 'w') { error = deflateEnd(&(s->stream)); } else if (s->mode == 'r') { error = inflateEnd(&(s->stream)); } } if (error != Z_OK) { biffAddf(NRRD, "%s: %s", me, _NRRD_GZ_ERR_MSG(error)); } if (s->z_err < 0) error = s->z_err; if (error != Z_OK) { biffAddf(NRRD, "%s: %s", me, _NRRD_GZ_ERR_MSG(error)); } s->inbuf = (Byte *)airFree(s->inbuf); s->outbuf = (Byte *)airFree(s->outbuf); airFree(s); /* avoiding unused value warnings, no NULL set */ return error != Z_OK; } /* ** _nrrdGzDoFlush() ** ** Flushes all pending output into the compressed file. The parameter ** flush is the same as in the deflate() function. */ static int _nrrdGzDoFlush(gzFile file, int flush) { static const char me[]="_nrrdGzDoFlush"; uInt len; int done = 0; _NrrdGzStream *s = (_NrrdGzStream*)file; if (s == NULL || s->mode != 'w') { biffAddf(NRRD, "%s: invalid stream or file mode", me); return Z_STREAM_ERROR; } s->stream.avail_in = 0; /* should be zero already anyway */ for (;;) { len = _NRRD_Z_BUFSIZE - s->stream.avail_out; if (len != 0) { if ((uInt)fwrite(s->outbuf, 1, len, s->file) != len) { s->z_err = Z_ERRNO; return Z_ERRNO; } s->stream.next_out = s->outbuf; s->stream.avail_out = _NRRD_Z_BUFSIZE; } if (done) break; s->z_err = deflate(&(s->stream), flush); /* Ignore the second of two consecutive flushes: */ if (len == 0 && s->z_err == Z_BUF_ERROR) s->z_err = Z_OK; /* deflate has finished flushing only when it hasn't used up * all the available space in the output buffer: */ done = (s->stream.avail_out != 0 || s->z_err == Z_STREAM_END); if (s->z_err != Z_OK && s->z_err != Z_STREAM_END) break; } return s->z_err == Z_STREAM_END ? Z_OK : s->z_err; } /* ** _nrrdGzPutLong() ** ** Outputs a long in LSB order to the given file. */ static void _nrrdGzPutLong(FILE* file, uLong x) { int n; for (n = 0; n < 4; n++) { fputc((int)(x & 0xff), file); x >>= 8; } } /* ** _nrrdGzGetLong() ** ** Reads a long in LSB order from the given _NrrdGzStream. ** Sets z_err in case of error. */ static uLong _nrrdGzGetLong(_NrrdGzStream *s) { uLong x = (uLong)_nrrdGzGetByte(s); int c; x += ((uLong)_nrrdGzGetByte(s))<<8; x += ((uLong)_nrrdGzGetByte(s))<<16; c = _nrrdGzGetByte(s); if (c == EOF) s->z_err = Z_DATA_ERROR; x += ((uLong)c)<<24; return x; } #endif /* TEEM_ZLIB */ /* ** random symbol to have in object file, even when Zlib not enabled */ int _nrrdGzDummySymbol(void) { return 42; } cmtk-3.3.1/Utilities/NrrdIO/keyvalue.c000066400000000000000000000211321276303427400175640ustar00rootroot00000000000000/* NrrdIO: stand-alone code for basic nrrd functionality Copyright (C) 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. */ #include "NrrdIO.h" #include "privateNrrd.h" /*** **** NONE of the nrrdKeyValue functions use biff. **** They don't use them now, and they never should. **** Unless I change my mind. ***/ /* ******** nrrdKeyValueSize ** ** returns the number of key/value pairs in a nrrd */ unsigned int nrrdKeyValueSize(const Nrrd *nrrd) { if (!nrrd) { return 0; } return nrrd->kvpArr->len; } /* ******** nrrdKeyValueIndex ** ** given an int in [0 .. #key/value pairs - 1], sets *keyP and *valueP ** to put to the corresponding key and value. ** ** NOTE: whether or not *keyP and *valueP are set to pointers to memory ** "inside" the nrrd struct (pointers which you had better not free()!) ** is controlled by nrrdStateKeyValueReturnInternalPointers, which defaults ** to AIR_FALSE */ void nrrdKeyValueIndex(const Nrrd *nrrd, char **keyP, char **valueP, unsigned int ki) { if (!( nrrd && keyP && valueP && ki < nrrd->kvpArr->len )) { if (keyP) { *keyP = NULL; } if (valueP) { *valueP = NULL; } return; } if (nrrdStateKeyValueReturnInternalPointers) { *keyP = nrrd->kvp[0 + 2*ki]; *valueP = nrrd->kvp[1 + 2*ki]; } else { *keyP = airStrdup(nrrd->kvp[0 + 2*ki]); *valueP = airStrdup(nrrd->kvp[1 + 2*ki]); } return; } static unsigned int _kvpIdxFind(const Nrrd *nrrd, const char *key, int *found) { unsigned int nk, ki, ret; nk = nrrd->kvpArr->len; for (ki=0; kikvp[0 + 2*ki], key)) { break; } } if (kikvpArr->len; for (ki=0; kikvp[0 + 2*ki] = (char *)airFree(nrrd->kvp[0 + 2*ki]); nrrd->kvp[1 + 2*ki] = (char *)airFree(nrrd->kvp[1 + 2*ki]); } airArrayLenSet(nrrd->kvpArr, 0); return; } int nrrdKeyValueErase(Nrrd *nrrd, const char *key) { unsigned int nk, ki; int found; if (!( nrrd && key )) { /* got NULL pointer */ return 1; } ki = _kvpIdxFind(nrrd, key, &found); if (!found) { return 0; } nrrd->kvp[0 + 2*ki] = (char *)airFree(nrrd->kvp[0 + 2*ki]); nrrd->kvp[1 + 2*ki] = (char *)airFree(nrrd->kvp[1 + 2*ki]); nk = nrrd->kvpArr->len; for (; kikvp[0 + 2*ki] = nrrd->kvp[0 + 2*(ki+1)]; nrrd->kvp[1 + 2*ki] = nrrd->kvp[1 + 2*(ki+1)]; } airArrayLenIncr(nrrd->kvpArr, -1); return 0; } /* ******** nrrdKeyValueAdd ** ** This will COPY the given strings, and so does not depend on ** them existing past the return of this function ** ** NOTE: Despite what might be most logical, there is no effort made ** here to cleanup key or value, including any escaping or filtering ** that might be warranted for white space other than \n ** ** does NOT use BIFF */ int nrrdKeyValueAdd(Nrrd *nrrd, const char *key, const char *value) { unsigned int ki; int found; if (!( nrrd && key && value )) { /* got NULL pointer */ return 1; } if (!strlen(key)) { /* reject empty keys */ return 1; } ki = _kvpIdxFind(nrrd, key, &found); if (found) { /* over-writing value for an existing key, so have to free old value */ airFree(nrrd->kvp[1 + 2*ki]); nrrd->kvp[1 + 2*ki] = airStrdup(value); } else { /* adding value for a new key */ ki = airArrayLenIncr(nrrd->kvpArr, 1); nrrd->kvp[0 + 2*ki] = airStrdup(key); nrrd->kvp[1 + 2*ki] = airStrdup(value); } return 0; } /* ******** nrrdKeyValueGet ** ** NOTE: whether or not *keyP and *valueP are set to pointers to memory ** "inside" the nrrd struct (pointers which you had better not free()!) ** is controlled by nrrdStateKeyValueReturnInternalPointers, which defaults ** to AIR_FALSE ** ** does NOT use BIFF */ char * nrrdKeyValueGet(const Nrrd *nrrd, const char *key) { char *ret; unsigned int ki; int found; if (!( nrrd && key )) { /* got NULL pointer */ return NULL; } ki = _kvpIdxFind(nrrd, key, &found); if (found) { if (nrrdStateKeyValueReturnInternalPointers) { ret = nrrd->kvp[1 + 2*ki]; } else { ret = airStrdup(nrrd->kvp[1 + 2*ki]); } } else { ret = NULL; } return ret; } /* ** Does the escaping of special characters in a string that ** is being written either to "FILE *file" or "char *dst" ** (WHICH IS ASSUMED to be allocated to be big enough!) ** Which characters to escape should be put in string "toescape" ** currently supported: \n \ " ** Also, converts characters in "tospace" to a space. Being in ** toescape trumps being in tospace, so tospace can be harmlessly ** set to, say, AIR_WHITESPACE. ** ** accident of history that this function is in this file */ void _nrrdWriteEscaped(FILE *file, char *dst, const char *str, const char *toescape, const char *tospace) { /* static const char me[]="_nrrdWriteEscaped"; */ size_t ci, gslen; /* given strlen */ gslen = strlen(str); for (ci=0; cikvpArr->len; ki++) { key = nin->kvp[0 + 2*ki]; value = nin->kvp[1 + 2*ki]; if (nrrdKeyValueAdd(nout, key, value)) { return 3; } } return 0; } cmtk-3.3.1/Utilities/NrrdIO/methodsNrrd.c000066400000000000000000000571461276303427400202460ustar00rootroot00000000000000/* NrrdIO: stand-alone code for basic nrrd functionality Copyright (C) 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. */ #include "NrrdIO.h" #include "privateNrrd.h" /* Wed Sep 14 05:55:40 EDT 2005: these are no longer used void nrrdPeripheralInit(Nrrd *nrrd) { nrrdBasicInfoInit(nrrd, NRRD_BASIC_INFO_DATA_BIT | NRRD_BASIC_INFO_TYPE_BIT | NRRD_BASIC_INFO_BLOCKSIZE_BIT | NRRD_BASIC_INFO_DIMENSION_BIT | NRRD_BASIC_INFO_CONTENT_BIT | NRRD_BASIC_INFO_COMMENTS_BIT | NRRD_BASIC_INFO_KEYVALUEPAIRS_BIT); return; } int nrrdPeripheralCopy(Nrrd *nout, const Nrrd *nin) { nrrdBasicInfoCopy(nout, nin, NRRD_BASIC_INFO_DATA_BIT | NRRD_BASIC_INFO_TYPE_BIT | NRRD_BASIC_INFO_BLOCKSIZE_BIT | NRRD_BASIC_INFO_DIMENSION_BIT | NRRD_BASIC_INFO_CONTENT_BIT | NRRD_BASIC_INFO_COMMENTS_BIT | NRRD_BASIC_INFO_KEYVALUEPAIRS_BIT); return 0; } */ /* ------------------------------------------------------------ */ void nrrdIoStateInit(NrrdIoState *nio) { if (nio) { nio->path = (char *)airFree(nio->path); nio->base = (char *)airFree(nio->base); nio->line = (char *)airFree(nio->line); nio->dataFNFormat = (char *)airFree(nio->dataFNFormat); /* the way IO to/from strings works, I don't think this should be freed */ nio->headerStringRead = NULL; nio->headerStringWrite = NULL; airArrayLenSet(nio->dataFNArr, 0); /* closing this is always someone else's responsibility */ nio->headerFile = NULL; nio->dataFile = NULL; nio->dataFileDim = 0; nio->dataFNMin = 0; nio->dataFNMax = 0; nio->dataFNStep = 0; nio->dataFNIndex = 0; nio->lineLen = 0; nio->pos = 0; nio->endian = airEndianUnknown; nio->lineSkip = 0; nio->headerStrlen = 0; nio->headerStrpos = 0; nio->byteSkip = 0; memset(nio->seen, 0, (NRRD_FIELD_MAX+1)*sizeof(int)); nio->detachedHeader = AIR_FALSE; nio->bareText = nrrdDefaultWriteBareText; nio->charsPerLine = nrrdDefaultWriteCharsPerLine; nio->valsPerLine = nrrdDefaultWriteValsPerLine; nio->skipData = AIR_FALSE; nio->skipFormatURL = AIR_FALSE; nio->keepNrrdDataFileOpen = AIR_FALSE; nio->zlibLevel = -1; nio->zlibStrategy = nrrdZlibStrategyDefault; nio->bzip2BlockSize = -1; nio->learningHeaderStrlen = AIR_FALSE; nio->oldData = NULL; nio->oldDataSize = 0; nio->format = nrrdFormatUnknown; nio->encoding = nrrdEncodingUnknown; } return; } NrrdIoState * nrrdIoStateNew(void) { NrrdIoState *nio; nio = (NrrdIoState *)calloc(1, sizeof(NrrdIoState)); if (nio) { airPtrPtrUnion appu; nio->path = NULL; nio->base = NULL; nio->line = NULL; nio->dataFNFormat = NULL; nio->dataFN = NULL; nio->headerStringRead = NULL; nio->headerStringWrite = NULL; appu.cp = &(nio->dataFN); nio->dataFNArr = airArrayNew(appu.v, NULL, sizeof(char *), NRRD_FILENAME_INCR); airArrayPointerCB(nio->dataFNArr, airNull, airFree); nio->format = nrrdFormatUnknown; nio->encoding = nrrdEncodingUnknown; nrrdIoStateInit(nio); } return nio; } NrrdIoState * nrrdIoStateNix(NrrdIoState *nio) { nio->path = (char *)airFree(nio->path); nio->base = (char *)airFree(nio->base); nio->line = (char *)airFree(nio->line); nio->dataFNFormat = (char *)airFree(nio->dataFNFormat); nio->dataFNArr = airArrayNuke(nio->dataFNArr); /* the NrrdIoState never owned nio->oldData; we don't free it */ airFree(nio); /* no NULL assignment, else compile warnings */ return NULL; } /* ------------------------------------------------------------ */ /* see axis.c for axis-specific "methods" */ /* ------------------------------------------------------------ */ /* ******** nrrdBasicInfoInit ** ** resets "basic" (per-array) information ** formerly nrrdPeripheralInit ** ** the bitflag communicates which fields should *not* be initialized */ void nrrdBasicInfoInit(Nrrd *nrrd, int bitflag) { int dd, ee; if (!nrrd) { return; } if (!(NRRD_BASIC_INFO_DATA_BIT & bitflag)) { nrrd->data = airFree(nrrd->data); } if (!(NRRD_BASIC_INFO_TYPE_BIT & bitflag)) { nrrd->type = nrrdTypeUnknown; } if (!(NRRD_BASIC_INFO_BLOCKSIZE_BIT & bitflag)) { nrrd->blockSize = 0; } if (!(NRRD_BASIC_INFO_DIMENSION_BIT & bitflag)) { nrrd->dim = 0; } if (!(NRRD_BASIC_INFO_CONTENT_BIT & bitflag)) { nrrd->content = (char *)airFree(nrrd->content); } if (!(NRRD_BASIC_INFO_SAMPLEUNITS_BIT & bitflag)) { nrrd->sampleUnits = (char *)airFree(nrrd->sampleUnits); } if (!(NRRD_BASIC_INFO_SPACE_BIT & bitflag)) { nrrd->space = nrrdSpaceUnknown; nrrd->spaceDim = 0; } if (!(NRRD_BASIC_INFO_SPACEDIMENSION_BIT & bitflag)) { nrrd->space = nrrdSpaceUnknown; nrrd->spaceDim = 0; } if (!(NRRD_BASIC_INFO_SPACEUNITS_BIT & bitflag)) { for (dd=0; ddspaceUnits[dd] = (char *)airFree(nrrd->spaceUnits[dd]); } } if (!(NRRD_BASIC_INFO_SPACEORIGIN_BIT & bitflag)) { for (dd=0; ddspaceOrigin[dd] = AIR_NAN; } } if (!(NRRD_BASIC_INFO_MEASUREMENTFRAME_BIT & bitflag)) { for (dd=0; ddmeasurementFrame[dd][ee] = AIR_NAN; } } } if (!(NRRD_BASIC_INFO_OLDMIN_BIT & bitflag)) { nrrd->oldMin = AIR_NAN; } if (!(NRRD_BASIC_INFO_OLDMAX_BIT & bitflag)) { nrrd->oldMax = AIR_NAN; } if (!(NRRD_BASIC_INFO_COMMENTS_BIT & bitflag)) { nrrdCommentClear(nrrd); } if (!(NRRD_BASIC_INFO_KEYVALUEPAIRS_BIT & bitflag)) { nrrdKeyValueClear(nrrd); } return; } /* ******** nrrdBasicInfoCopy ** ** copies "basic" (per-array) information ** formerly known as nrrdPeripheralCopy, which was not used consistently ** ** the bitflag communicates which fields should *not* be copied */ int nrrdBasicInfoCopy(Nrrd *dest, const Nrrd *src, int bitflag) { static const char me[]="nrrdBasicInfoCopy"; unsigned int dd, ee; if (!( dest && src )) return 0; if (dest == src) { /* nothing to do */ return 0; } if (!(NRRD_BASIC_INFO_DATA_BIT & bitflag)) { dest->data = src->data; } if (!(NRRD_BASIC_INFO_TYPE_BIT & bitflag)) { dest->type = src->type; } if (!(NRRD_BASIC_INFO_BLOCKSIZE_BIT & bitflag)) { dest->blockSize = src->blockSize; } if (!(NRRD_BASIC_INFO_DIMENSION_BIT & bitflag)) { dest->dim = src->dim; } if (!(NRRD_BASIC_INFO_CONTENT_BIT & bitflag)) { dest->content = (char *)airFree(dest->content); dest->content = airStrdup(src->content); if (src->content && !dest->content) { biffAddf(NRRD, "%s: couldn't copy content", me); return 1; } } if (!(NRRD_BASIC_INFO_SAMPLEUNITS_BIT & bitflag)) { dest->sampleUnits = (char *)airFree(dest->sampleUnits); dest->sampleUnits = airStrdup(src->sampleUnits); if (src->sampleUnits && !dest->sampleUnits) { biffAddf(NRRD, "%s: couldn't copy sampleUnits", me); return 1; } } if (!(NRRD_BASIC_INFO_SPACE_BIT & bitflag)) { dest->space = src->space; } if (!(NRRD_BASIC_INFO_SPACEDIMENSION_BIT & bitflag)) { dest->spaceDim = src->spaceDim; } if (!(NRRD_BASIC_INFO_SPACEUNITS_BIT & bitflag)) { for (dd=0; ddspaceDim; dd++) { dest->spaceUnits[dd] = (char *)airFree(dest->spaceUnits[dd]); dest->spaceUnits[dd] = airStrdup(src->spaceUnits[dd]); if (src->spaceUnits[dd] && !dest->spaceUnits[dd]) { biffAddf(NRRD, "%s: couldn't copy spaceUnits[%d]", me, dd); return 1; } } for (dd=src->spaceDim; ddspaceUnits[dd] = (char *)airFree(dest->spaceUnits[dd]); } } if (!(NRRD_BASIC_INFO_SPACEORIGIN_BIT & bitflag)) { for (dd=0; ddspaceDim-1) { dest->spaceOrigin[dd] = src->spaceOrigin[dd]; } else { dest->spaceOrigin[dd] = AIR_NAN; } } } if (!(NRRD_BASIC_INFO_MEASUREMENTFRAME_BIT & bitflag)) { for (dd=0; ddspaceDim-1 && ee <= src->spaceDim-1) { dest->measurementFrame[dd][ee] = src->measurementFrame[dd][ee]; } else { dest->measurementFrame[dd][ee] = AIR_NAN; } } } for (dd=src->spaceDim; ddspaceOrigin[dd] = AIR_NAN; } } if (!(NRRD_BASIC_INFO_OLDMIN_BIT & bitflag)) { dest->oldMin = src->oldMin; } if (!(NRRD_BASIC_INFO_OLDMAX_BIT & bitflag)) { dest->oldMax = src->oldMax; } if (!(NRRD_BASIC_INFO_COMMENTS_BIT & bitflag)) { if (nrrdCommentCopy(dest, src)) { biffAddf(NRRD, "%s: trouble copying comments", me); return 1; } } if (!(NRRD_BASIC_INFO_KEYVALUEPAIRS_BIT & bitflag)) { if (nrrdKeyValueCopy(dest, src)) { biffAddf(NRRD, "%s: trouble copying key/value pairs", me); return 1; } } return 0; } /* ******* nrrdInit ** ** initializes a nrrd to default state. All nrrd functions in the ** business of initializing a nrrd struct use this function. Mostly ** just sets values to 0, NaN, "", NULL, or Unknown */ void nrrdInit(Nrrd *nrrd) { int ii; if (nrrd) { nrrdBasicInfoInit(nrrd, NRRD_BASIC_INFO_NONE); for (ii=0; iiaxis + ii); } } return; } /* ******** nrrdNew() ** ** creates and initializes a Nrrd ** ** this does NOT use biff */ Nrrd * nrrdNew(void) { int ii; Nrrd *nrrd; airPtrPtrUnion appu; nrrd = (Nrrd*)(calloc(1, sizeof(Nrrd))); if (!nrrd) { return NULL; } /* explicitly set pointers to NULL, since calloc isn't officially guaranteed to do that. */ nrrd->data = NULL; for (ii=0; iiaxis + ii); } for (ii=0; iispaceUnits[ii] = NULL; } nrrd->content = NULL; nrrd->sampleUnits = NULL; /* create comment airArray (even though it starts empty) */ nrrd->cmt = NULL; appu.cp = &(nrrd->cmt); nrrd->cmtArr = airArrayNew(appu.v, NULL, sizeof(char *), NRRD_COMMENT_INCR); if (!nrrd->cmtArr) { return NULL; } airArrayPointerCB(nrrd->cmtArr, airNull, airFree); /* create key/value airArray (even thought it starts empty) */ nrrd->kvp = NULL; appu.cp = &(nrrd->kvp); nrrd->kvpArr = airArrayNew(appu.v, NULL, 2*sizeof(char *), NRRD_KEYVALUE_INCR); if (!nrrd->kvpArr) { return NULL; } /* key/value airArray uses no callbacks for now */ /* finish initializations */ nrrdInit(nrrd); return nrrd; } /* ******** nrrdNix() ** ** does nothing with the array data inside, just does whatever is needed ** to free the nrrd itself ** ** returns NULL ** ** this does NOT use biff */ Nrrd * nrrdNix(Nrrd *nrrd) { int ii; if (nrrd) { for (ii=0; iiaxis[ii])); } for (ii=0; iispaceUnits[ii] = (char *)airFree(nrrd->spaceUnits[ii]); } nrrd->content = (char *)airFree(nrrd->content); nrrd->sampleUnits = (char *)airFree(nrrd->sampleUnits); nrrdCommentClear(nrrd); nrrd->cmtArr = airArrayNix(nrrd->cmtArr); nrrdKeyValueClear(nrrd); nrrd->kvpArr = airArrayNix(nrrd->kvpArr); airFree(nrrd); } return NULL; } /* ******** nrrdEmpty() ** ** frees data inside nrrd AND resets all its state, so its the ** same as what comes from nrrdNew(). This includes free()ing ** any comments. */ Nrrd * nrrdEmpty(Nrrd *nrrd) { if (nrrd) { nrrd->data = airFree(nrrd->data); nrrdInit(nrrd); } return nrrd; } /* ******** nrrdNuke() ** ** blows away the nrrd and everything inside ** ** always returns NULL */ Nrrd * nrrdNuke(Nrrd *nrrd) { if (nrrd) { nrrdEmpty(nrrd); nrrdNix(nrrd); } return NULL; } /* ------------------------------------------------------------ */ int _nrrdSizeCheck(const size_t *size, unsigned int dim, int useBiff) { static const char me[]="_nrrdSizeCheck"; size_t num, pre; unsigned int ai; pre = num = 1; for (ai=0; aiblockSize at some other time. */ int nrrdWrap_nva(Nrrd *nrrd, void *data, int type, unsigned int dim, const size_t *size) { static const char me[]="nrrdWrap_nva"; if (!(nrrd && size)) { biffAddf(NRRD, "%s: got NULL pointer", me); return 1; } nrrd->data = data; nrrd->type = type; nrrd->dim = dim; if (_nrrdSizeCheck(size, dim, AIR_TRUE)) { biffAddf(NRRD, "%s:", me); return 1; } nrrdAxisInfoSet_nva(nrrd, nrrdAxisInfoSize, size); return 0; } /* ******** nrrdWrap_va() ** ** Minimal var args wrapper around nrrdWrap_nva, with the advantage of ** taking all the axes sizes as the var args. ** ** This is THE BEST WAY to wrap a nrrd around existing raster data, ** assuming that the dimension is known at compile time. ** ** If successful, returns 0, otherwise, 1. ** This does use biff. */ int nrrdWrap_va(Nrrd *nrrd, void *data, int type, unsigned int dim, ...) { static const char me[]="nrrdWrap_va"; va_list ap; size_t size[NRRD_DIM_MAX]; unsigned int ai; if (!(nrrd && data)) { biffAddf(NRRD, "%s: got NULL pointer", me); return 1; } va_start(ap, dim); for (ai=0; aidata; for (I=0; Idata) { if (nrrdMaybeAlloc_nva(nout, nin->type, nin->dim, size)) { biffAddf(NRRD, "%s: couldn't allocate data", me); return 1; } memcpy(nout->data, nin->data, nrrdElementNumber(nin)*nrrdElementSize(nin)); } else { /* someone is trying to copy structs without data, fine fine fine */ if (nrrdWrap_nva(nout, NULL, nin->type, nin->dim, size)) { biffAddf(NRRD, "%s: couldn't allocate data", me); return 1; } } nrrdAxisInfoCopy(nout, nin, NULL, NRRD_AXIS_INFO_SIZE_BIT); /* if nin->data non-NULL (second branch above), this will harmlessly unset and set type and dim */ nrrdBasicInfoInit(nout, NRRD_BASIC_INFO_DATA_BIT | bitflag); if (nrrdBasicInfoCopy(nout, nin, NRRD_BASIC_INFO_DATA_BIT | bitflag)) { biffAddf(NRRD, "%s: trouble copying basic info", me); return 1; } return 0; } /* ******** nrrdCopy ** ** copy method for nrrds. nout will end up as an "exact" copy of nin. ** New space for data is allocated here, and output nrrd points to it. ** Comments from old are added to comments for new, so these are also ** newly allocated. nout->ptr is not set, nin->ptr is not read. */ int nrrdCopy(Nrrd *nout, const Nrrd *nin) { static const char me[]="nrrdCopy"; if (_nrrdCopy(nout, nin, NRRD_BASIC_INFO_NONE)) { biffAddf(NRRD, "%s:", me); return 1; } return 0; } /* ******** nrrdAlloc_nva() ** ** allocates data array and sets information. If this is a block type ** nrrd, it is necessary to set nrrd->blockSize PRIOR to calling ** this function. ** ** This function will always allocate more memory (via calloc), but ** it will free() nrrd->data if it is non-NULL when passed in. ** ** This function takes the same "don't mess with peripheral information" ** attitude as nrrdWrap(). ** ** Note to Gordon: don't get clever and change ANY axis-specific ** information here. It may be very convenient to set that before ** nrrdAlloc or nrrdMaybeAlloc ** ** Note: This function DOES use biff */ int nrrdAlloc_nva(Nrrd *nrrd, int type, unsigned int dim, const size_t *size) { static const char me[]="nrrdAlloc_nva"; size_t num, esize; char stmp[2][AIR_STRLEN_SMALL]; if (!(nrrd && size)) { biffAddf(NRRD, "%s: got NULL pointer", me); return 1; } if (airEnumValCheck(nrrdType, type)) { biffAddf(NRRD, "%s: type (%d) is invalid", me, type); return 1; } if (nrrdTypeBlock == type) { if (!(0 < nrrd->blockSize)) { biffAddf(NRRD, "%s: given nrrd->blockSize %s invalid", me, airSprintSize_t(stmp[0], nrrd->blockSize)); return 1; } } if (!AIR_IN_CL(1, dim, NRRD_DIM_MAX)) { biffAddf(NRRD, "%s: dim (%d) not in valid range [1,%d]", me, dim, NRRD_DIM_MAX); return 1; } nrrd->data = airFree(nrrd->data); if (nrrdWrap_nva(nrrd, NULL, type, dim, size)) { biffAddf(NRRD, "%s:", me); return 1 ; } num = nrrdElementNumber(nrrd); esize = nrrdElementSize(nrrd); nrrd->data = calloc(num, esize); if (!(nrrd->data)) { biffAddf(NRRD, "%s: calloc(%s,%s) failed", me, airSprintSize_t(stmp[0], num), airSprintSize_t(stmp[1], esize)); return 1 ; } return 0; } /* ******** nrrdAlloc_va() ** ** Handy wrapper around nrrdAlloc_nva, which takes, as its vararg list, ** all the axes sizes. */ int nrrdAlloc_va(Nrrd *nrrd, int type, unsigned int dim, ...) { static const char me[]="nrrdAlloc_va"; size_t size[NRRD_DIM_MAX]; unsigned int ai; va_list ap; if (!nrrd) { biffAddf(NRRD, "%s: got NULL pointer", me); return 1; } va_start(ap, dim); for (ai=0; aitype) { biffAddf(NRRD, "%s: can't change from one block nrrd to another", me); return 1; } if (!(0 < nrrd->blockSize)) { char stmp[AIR_STRLEN_SMALL]; biffAddf(NRRD, "%s: given nrrd->blockSize %s invalid", me, airSprintSize_t(stmp, nrrd->blockSize)); return 1; } elementSizeWant = nrrd->blockSize; } else { elementSizeWant = nrrdTypeSize[type]; } if (_nrrdSizeCheck(size, dim, AIR_TRUE)) { biffAddf(NRRD, "%s:", me); return 1; } if (!(nrrd->data)) { need = 1; } else { numWant = 1; for (ai=0; aidata, type, dim, size)) { biffAddf(NRRD, "%s:", me); return 1; } /* but we may have to initialize memory */ if (zeroWhenNoAlloc) { memset(nrrd->data, 0, nrrdElementNumber(nrrd)*nrrdElementSize(nrrd)); } } return 0; } /* ******** nrrdMaybeAlloc_nva ** ** NOTE: this is now just a wrapper around _nrrdMaybeAllocMaybeZero_nva; ** below info referred to original implementation. ** ** calls nrrdAlloc_nva if the requested space is different than ** what is currently held ** ** also subscribes to the "don't mess with peripheral information" philosophy */ int nrrdMaybeAlloc_nva(Nrrd *nrrd, int type, unsigned int dim, const size_t *size) { static const char me[]="nrrdMaybeAlloc_nva"; int ret; ret = _nrrdMaybeAllocMaybeZero_nva(nrrd, type, dim, size, AIR_TRUE); if (ret) { biffAddf(NRRD, "%s: trouble", me); } return ret; } /* ******** nrrdMaybeAlloc_va() ** ** Handy wrapper around nrrdAlloc, which takes, as its vararg list ** all the axes sizes, thereby calculating the total number. */ int nrrdMaybeAlloc_va(Nrrd *nrrd, int type, unsigned int dim, ...) { static const char me[]="nrrdMaybeAlloc_va"; size_t size[NRRD_DIM_MAX]; unsigned int ai; va_list ap; if (!nrrd) { biffAddf(NRRD, "%s: got NULL pointer", me); return 1; } va_start(ap, dim); for (ai=0; ai #include #include #else #include #endif /* ******** airTeemVersion ******** airTeemReleaseDate ** ** updated with each release to contain a string representation of ** the Teem version number and release date. Originated in version 1.5; ** use of TEEM_VERSION #defines started in 1.9 */ const char * airTeemVersion = TEEM_VERSION_STRING; const char * airTeemReleaseDate = "19 Dec 2012"; double _airSanityHelper(double val) { return val*val*val; } /* ******** airNull() ** ** returns NULL */ void * airNull(void) { return NULL; } /* ******** airSetNull ** ** dereferences and sets to NULL, returns NULL */ void * airSetNull(void **ptrP) { if (ptrP) { *ptrP = NULL; } return NULL; } /* ******** airFree() ** ** to facilitate setting a newly free()'d pointer; always returns NULL. ** also makes sure that NULL is not passed to free(). */ void * airFree(void *ptr) { if (ptr) { free(ptr); } return NULL; } /* ******** airFopen() ** ** encapsulates that idea that "-" is either standard in or stardard ** out, and does McRosopht stuff required to make piping work ** ** Does not error checking. If fopen fails, then C' errno and strerror are ** left untouched for the caller to access. */ FILE * airFopen(const char *name, FILE *std, const char *mode) { FILE *ret; if (!strcmp(name, "-")) { ret = std; #ifdef _WIN32 if (strchr(mode, 'b')) { _setmode(_fileno(ret), _O_BINARY); } #endif } else { ret = fopen(name, mode); } return ret; } /* ******** airFclose() ** ** just to facilitate setting a newly fclose()'d file pointer to NULL ** also makes sure that NULL is not passed to fclose(), and won't close ** stdin, stdout, or stderr (its up to the user to open these correctly) */ FILE * airFclose(FILE *file) { if (file) { if (!( stdin == file || stdout == file || stderr == file )) { fclose(file); } } return NULL; } /* ******** airSinglePrintf ** ** a complete stand-in for {f|s}printf(), as long as the given format ** string contains exactly one conversion sequence. The utility of ** this is to standardize the printing of IEEE 754 special values: ** QNAN, SNAN -> "NaN" ** POS_INF -> "+inf" ** NEG_INF -> "-inf" ** The format string can contain other things besides just the ** conversion sequence: airSingleFprintf(f, " (%f)\n", AIR_NAN) ** will be the same as fprintf(f, " (%s)\n", "NaN"); ** ** To get fprintf behavior, pass "str" as NULL ** to get sprintf bahavior, pass "file" as NULL ** ** Finding a complete {f|s|}printf replacement is a priority for Teem 2.0 */ int airSinglePrintf(FILE *file, char *str, const char *_fmt, ...) { char *fmt, buff[AIR_STRLEN_LARGE]; double val=0, gVal, fVal; int ret, isF, isD, cls; char *conv=NULL, *p0, *p1, *p2, *p3, *p4, *p5; va_list ap; va_start(ap, _fmt); fmt = airStrdup(_fmt); /* this is needlessly complicated; the "l" modifier is a no-op */ p0 = strstr(fmt, "%e"); p1 = strstr(fmt, "%f"); p2 = strstr(fmt, "%g"); p3 = strstr(fmt, "%le"); p4 = strstr(fmt, "%lf"); p5 = strstr(fmt, "%lg"); isF = p0 || p1 || p2; isD = p3 || p4 || p5; /* the code here says "isF" and "isD" as if it means "is float" or "is double". It really should be "is2" or "is3", as in, "is 2-character conv. seq., or "is 3-character conv. seq." */ if (isF) { conv = p0 ? p0 : (p1 ? p1 : p2); } if (isD) { conv = p3 ? p3 : (p4 ? p4 : p5); } if (isF || isD) { /* use "double" instead of "float" because var args are _always_ subject to old-style C type promotions: float promotes to double */ val = va_arg(ap, double); cls = airFPClass_d(val); switch (cls) { case airFP_SNAN: case airFP_QNAN: case airFP_POS_INF: case airFP_NEG_INF: if (isF) { memcpy(conv, "%s", 2); } else { /* this sneakiness allows us to replace a 3-character conversion sequence for a double (such as %lg) with a 3-character conversion for a string, which we know has at most 4 characters */ memcpy(conv, "%4s", 3); } break; } #define PRINT(F, S, C, V) ((F) ? fprintf((F),(C),(V)) : sprintf((S),(C),(V))) switch (cls) { case airFP_SNAN: case airFP_QNAN: ret = PRINT(file, str, fmt, "NaN"); break; case airFP_POS_INF: ret = PRINT(file, str, fmt, "+inf"); break; case airFP_NEG_INF: ret = PRINT(file, str, fmt, "-inf"); break; default: if (p2 || p5) { /* got "%g" or "%lg", see if it would be better to use "%f" */ sprintf(buff, "%f", val); sscanf(buff, "%lf", &fVal); sprintf(buff, "%g", val); sscanf(buff, "%lf", &gVal); if (fVal != gVal) { /* using %g (or %lg) lost precision!! Use %f (or %lf) instead */ if (p2) { memcpy(conv, "%f", 2); } else { memcpy(conv, "%lf", 3); } } } ret = PRINT(file, str, fmt, val); break; } } else { /* conversion sequence is neither for float nor double */ ret = file ? vfprintf(file, fmt, ap) : vsprintf(str, fmt, ap); } va_end(ap); free(fmt); return ret; } /* ******** airSprintSize_t ** ** sprints a single size_t to a given string, side-stepping ** non-standardized format specifier confusion with printf */ char * airSprintSize_t(char _str[AIR_STRLEN_SMALL], size_t val) { char str[AIR_STRLEN_SMALL]; unsigned int si; if (!_str) { return NULL; } si = AIR_STRLEN_SMALL-1; str[si] = '\0'; do { str[--si] = AIR_CAST(char, (val % 10) + '0'); val /= 10; } while (val); strcpy(_str, str + si); return _str; } /* ******** airSprintPtrdiff_t ** ** sprints a single ptrdiff_t to a given string, side-stepping ** non-standardized format specifier confusion with printf */ char * airSprintPtrdiff_t(char _str[AIR_STRLEN_SMALL], ptrdiff_t val) { char str[AIR_STRLEN_SMALL]; unsigned int si; int sign; if (!_str) { return NULL; } si = AIR_STRLEN_SMALL-1; str[si] = '\0'; sign = (val < 0 ? -1 : 1); do { ptrdiff_t dig; dig = val % 10; str[--si] = AIR_CAST(char, dig > 0 ? dig + '0' : -dig + '0'); val /= 10; } while (val); if (-1 == sign) { str[--si] = '-'; } strcpy(_str, str + si); return _str; } cmtk-3.3.1/Utilities/NrrdIO/mop.c000066400000000000000000000172051276303427400165400ustar00rootroot00000000000000/* NrrdIO: stand-alone code for basic nrrd functionality Copyright (C) 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. */ #include "NrrdIO.h" /* learned: using these functions correctly to manage even simple memory usage can be very tricky. problem 0: even trying to write airMopPrint, I foolishly thought: "print the string, then free it". But the print callback clobbered the free callback, because of the semantics of airMopAdd(). So, I had to add _airMopAdd(). problem 1: debugging hest with purify, on case of hitting error after parsing multiple variable parameter option of strings: so, I allocated an array of strings (arrays), and registered all the strings with airMopMem(), and registered the array itself also with airMopMem(). Again, got clobbered. airSetNull(&((*vP)[0])) clobbered airFree(*vP). So, I gave up on using airMopMem() for the individual elements, and am using simply airMopAdd(airFree). The alternative was to change the airMopAdd()s in airMopMem() to _airMopAdd()s, but I didn't feel confident that this would be safe. ----------- SO: as a result of all that: airMopAdd() will no longer over-write a callback based on the pointer It will only over-write the "when" of a (pointer,callback) pair, so that you can't register multiple copies of a (pointer,callback) pair (regardless of differences, if any, in "when"). Therefore, there will be AT MOST ONE instance of a (pointer,callback) pair in a mop. _airMopAdd() was nixed. airMopSub() and airMopUnMem were created */ #define AIR_MOP_INCR 10 airArray * airMopNew() { return airArrayNew(NULL, NULL, sizeof(airMop), AIR_MOP_INCR); } /* ** except for allocation error, this always returns 0, ** to facilitate this weird idiom: ** ** if (!(nmeasr = nrrdNew()) ** || airMopAdd(mop, nmeasr, (airMopper)nrrdNuke, airMopAlways) ** || !(nsize = nrrdNew()) ** || airMopAdd(mop, nsize, (airMopper)nrrdNuke, airMopAlways) ** || !(pair = AIR_CAST(ccpair *, calloc(pctx->CCNum, sizeof(ccpair)))) ** || airMopAdd(mop, pair, airFree, airMopAlways)) { ** ** GLK may regret this. */ int airMopAdd(airArray *arr, void *ptr, airMopper mop, int when) { static const char me[]="airMopAdd"; airMop *mops; unsigned int ii; if (!arr) { return 0; } mops = (airMop *)arr->data; /* first see if this is something we already set a callback for */ for (ii=0; iilen; ii++) { if (mops[ii].ptr == ptr && mops[ii].mop == mop) { mops[ii].when = when; /* we're done */ return 0; } } /* this is a new ptr */ ii = airArrayLenIncr(arr, 1); if (!arr->data) { fprintf(stderr, "%s: PANIC: can't re-allocate mop array\n", me); return 1; } mops = (airMop *)arr->data; mops[ii].ptr = ptr; mops[ii].mop = mop; mops[ii].when = when; return 0; } void airMopSub(airArray *arr, void *ptr, airMopper mop) { airMop *mops; unsigned int ii; if (!arr) { return; } mops = (airMop *)arr->data; /* first see if this is something we already set a callback for */ for (ii=0; iilen; ii++) { if (mops[ii].ptr == ptr && mops[ii].mop == mop) { mops[ii].ptr = NULL; mops[ii].mop = NULL; mops[ii].when = airMopNever; return; } } /* else we've never seen this before, user is a moron */ return; } void airMopMem(airArray *arr, void *_ptrP, int when) { void **ptrP; if (!(arr && _ptrP)) { return; } ptrP = (void **)_ptrP; airMopAdd(arr, ptrP, (airMopper)airSetNull, when); airMopAdd(arr, *ptrP, airFree, when); /* printf("airMopMem(0x%p): will free() 0x%p\n", (void*)arr, (void*)(*ptrP)); printf("airMopMem(0x%p): will set 0x%p to NULL\n", (void*)arr, (void*)ptrP); */ return; } void airMopUnMem(airArray *arr, void *_ptrP) { void **ptrP; if (!(arr && _ptrP)) { return; } ptrP = (void **)_ptrP; airMopSub(arr, ptrP, (airMopper)airSetNull); airMopSub(arr, *ptrP, airFree); return; } static void * _airMopPrint(void *_str) { char *str; str = (char *)_str; if (str) { printf("%s\n", str); } return NULL; } void airMopPrint(airArray *arr, const void *_str, int when) { char *copy; if (!(arr && _str)) return; copy = airStrdup(AIR_CAST(const char*, _str)); airMopAdd(arr, copy, airFree, airMopAlways); airMopAdd(arr, copy, _airMopPrint, when); return; } static const char _airMopWhenStr[4][128] = { " never", " error", " okay", "always", }; /* ** This is to overcome the warning about ** "ISO C forbids conversion of function pointer to object pointer type"; ** the result here is thus implementation-dependent */ typedef union { airMopper m; void *v; } mvunion; void airMopDebug(airArray *arr) { airMop *mops; unsigned int ii; mvunion mvu; if (!arr) return; mops = (airMop *)arr->data; printf("airMopDebug: _________________________ mop stack for 0x%p:\n", AIR_VOIDP(arr)); if (arr->len) { ii = arr->len; do { ii--; printf("%4u: ", ii); if (NULL == mops[ii].mop && NULL == mops[ii].ptr && airMopNever == mops[ii].when) { printf("no-op\n"); continue; } /* else */ printf("%s: ", _airMopWhenStr[mops[ii].when]); if (airFree == mops[ii].mop) { printf("airFree(0x%p)\n", AIR_VOIDP(mops[ii].ptr)); continue; } if ((airMopper)airSetNull == mops[ii].mop) { printf("airSetNull(0x%p)\n", AIR_VOIDP(mops[ii].ptr)); continue; } if (_airMopPrint == mops[ii].mop) { printf("_airMopPrint(\"%s\" == 0x%p)\n", AIR_CAST(char*, mops[ii].ptr), AIR_VOIDP(mops[ii].ptr)); continue; } if ((airMopper)airFclose == mops[ii].mop) { printf("airFclose(0x%p)\n", AIR_VOIDP(mops[ii].ptr)); continue; } /* else */ mvu.m = mops[ii].mop; printf("0x%p(0x%p)\n", AIR_VOIDP(mvu.v), AIR_VOIDP(mops[ii].ptr)); } while (ii); } printf("airMopDebug: ^^^^^^^^^^^^^^^^^^^^^^^^^\n"); } void airMopDone(airArray *arr, int error) { airMop *mops; unsigned int ii; /* printf("airMopDone(%p): hello, %s\n", (void*)arr, error ? "error" : "okay"); */ if (arr) { mops = (airMop *)arr->data; if (arr->len) { ii = arr->len; do { ii--; if (mops[ii].ptr && (airMopAlways == mops[ii].when || (airMopOnError == mops[ii].when && error) || (airMopOnOkay == mops[ii].when && !error))) { mops[ii].mop(mops[ii].ptr); } } while (ii); } airArrayNuke(arr); /* printf("airMopDone(%p): done!\n", (void*)arr); */ } return; } void airMopError(airArray *arr) { airMopDone(arr, AIR_TRUE); } void airMopOkay(airArray *arr) { airMopDone(arr, AIR_FALSE); } cmtk-3.3.1/Utilities/NrrdIO/parseAir.c000066400000000000000000000245541276303427400175200ustar00rootroot00000000000000/* NrrdIO: stand-alone code for basic nrrd functionality Copyright (C) 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. */ #include "NrrdIO.h" static const char * _airBoolStr[] = { "(unknown bool)", "false", "true" }; static const char * _airBoolDesc[] = { "unknown boolean", "false", "true" }; static const int _airBoolVal[] = { -1, AIR_FALSE, AIR_TRUE }; static const char * _airBoolStrEqv[] = { "0", "no", "n", "false", "f", "off", "nope", "1", "yes", "y", "true", "t", "on", "yea", "" }; static const int _airBoolValEqv[] = { AIR_FALSE, AIR_FALSE, AIR_FALSE, AIR_FALSE, AIR_FALSE, AIR_FALSE, AIR_FALSE, AIR_TRUE, AIR_TRUE, AIR_TRUE, AIR_TRUE, AIR_TRUE, AIR_TRUE, AIR_TRUE }; static const airEnum _airBool = { "boolean", 2, _airBoolStr, _airBoolVal, _airBoolDesc, _airBoolStrEqv, _airBoolValEqv, AIR_FALSE }; const airEnum *const airBool = &_airBool; double airAtod(const char *str) { double val = 0.0; airSingleSscanf(str, "%lf", &val); return val; } int airSingleSscanf(const char *str, const char *fmt, void *ptr) { char *tmp; double val; int ret; if (!strcmp(fmt, "%e") || !strcmp(fmt, "%f") || !strcmp(fmt, "%g") || !strcmp(fmt, "%le") || !strcmp(fmt, "%lf") || !strcmp(fmt, "%lg")) { tmp = airStrdup(str); if (!tmp) { return 0; } airToLower(tmp); if (strstr(tmp, "nan")) { val = AIR_NAN; } else if (strstr(tmp, "-inf")) { val = AIR_NEG_INF; } else if (strstr(tmp, "inf")) { val = AIR_POS_INF; } else { /* nothing special matched; pass it off to sscanf() */ ret = sscanf(str, fmt, ptr); free(tmp); return ret; } /* else we matched "nan", "-inf", or "inf", and set val accordingly */ if (!strncmp(fmt, "%l", 2)) { /* we were given a double pointer */ *((double *)(ptr)) = val; } else { /* we were given a float pointer */ *((float *)(ptr)) = AIR_CAST(float, val); } free(tmp); return 1; } else if (!strcmp(fmt, "%z")) { /* its a size_t */ size_t tsz = 0; /* tmp size_t */ const char *chh = str; /* char here */ while (chh) { int dig; dig = AIR_CAST(int, *chh - '0'); if (AIR_IN_CL(0, dig, 9)) { tsz = 10*tsz + AIR_CAST(size_t, dig); } else { break; } chh++; } *((size_t *)(ptr)) = tsz; return 1; } else { /* not a float, double, or size_t, let sscanf handle it */ return sscanf(str, fmt, ptr); } } #define _PARSE_STR_ARGS(type) type *out, const char *_s, \ const char *ct, unsigned int n, ... #define _PARSE_STR_BODY(format) \ unsigned int i; \ char *tmp, *s, *last; \ \ /* if we got NULL, there's nothing to do */ \ if (!(out && _s && ct)) \ return 0; \ \ /* copy the input so that we don't change it */ \ s = airStrdup(_s); \ \ /* keep calling airStrtok() until we have everything */ \ for (i=0; i 1 || !greedy) { tmp = airStrtok(i ? NULL : s, ct, &last); } else { tmp = s; } if (!tmp) { airMopError(mop); return i; } out[i] = airStrdup(tmp); if (!out[i]) { airMopError(mop); return i; } airMopMem(mop, out+i, airMopOnError); } airMopOkay(mop); return n; } unsigned int airParseStrE(int *out, const char *_s, const char *ct, unsigned int n, ...) { unsigned int i; char *tmp, *s, *last; airArray *mop; va_list ap; airEnum *enm; /* grab the enum every time, prior to error checking */ va_start(ap, n); enm = va_arg(ap, airEnum *); va_end(ap); /* if we got NULL, there's nothing to do */ if (!(out && _s && ct)) { return 0; } mop = airMopNew(); /* copy the input so that we don't change it */ s = airStrdup(_s); airMopMem(mop, &s, airMopAlways); if (1 == n) { /* Because it should be permissible to have spaces in what is intended to be only a single string from an airEnum, we treat 1==n differently, and do NOT use airStrtok to tokenize the input string s into spaces. We check the whole s string */ out[0] = airEnumVal(enm, s); if (airEnumUnknown(enm) == out[0]) { airMopError(mop); return 0; } } else { /* keep calling airStrtok() until we have everything */ for (i=0; istr[0])) { airMopError(mop); return i; } } } airMopOkay(mop); return n; } unsigned int (*airParseStr[AIR_TYPE_MAX+1])(void *, const char *, const char *, unsigned int, ...) = { NULL, (unsigned int (*)(void *, const char *, const char *, unsigned int, ...))airParseStrB, (unsigned int (*)(void *, const char *, const char *, unsigned int, ...))airParseStrI, (unsigned int (*)(void *, const char *, const char *, unsigned int, ...))airParseStrUI, (unsigned int (*)(void *, const char *, const char *, unsigned int, ...))airParseStrLI, (unsigned int (*)(void *, const char *, const char *, unsigned int, ...))airParseStrULI, (unsigned int (*)(void *, const char *, const char *, unsigned int, ...))airParseStrZ, (unsigned int (*)(void *, const char *, const char *, unsigned int, ...))airParseStrF, (unsigned int (*)(void *, const char *, const char *, unsigned int, ...))airParseStrD, (unsigned int (*)(void *, const char *, const char *, unsigned int, ...))airParseStrC, (unsigned int (*)(void *, const char *, const char *, unsigned int, ...))airParseStrS, (unsigned int (*)(void *, const char *, const char *, unsigned int, ...))airParseStrE, NULL /* no standard way of parsing type "other" */ }; cmtk-3.3.1/Utilities/NrrdIO/parseNrrd.c000066400000000000000000001311221276303427400177000ustar00rootroot00000000000000/* NrrdIO: stand-alone code for basic nrrd functionality Copyright (C) 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. */ #include "NrrdIO.h" #include "privateNrrd.h" /* ** _nrrdReadNrrdParseField() ** ** This is for parsing the stuff BEFORE the colon */ int _nrrdReadNrrdParseField(NrrdIoState *nio, int useBiff) { static const char me[]="_nrrdReadNrrdParseField"; char *next, *buff, *colon, *keysep; int ret, fld=nrrdField_unknown, noField, badField=AIR_FALSE; next = nio->line + nio->pos; /* determining if the line is a comment is simple */ if (NRRD_COMMENT_CHAR == next[0]) { return nrrdField_comment; } if (!( buff = airStrdup(next) )) { biffMaybeAddf(useBiff, NRRD, "%s: couldn't allocate buffer!", me); return nrrdField_unknown; } /* #1: "...if you see a colon, then look for an equal sign..." */ /* Look for colon: if no colon, or failed to parse as a field, look for * equal sign, if that failed then error */ /* Let the separator be := */ /* Escape \n */ colon = strstr(buff, ": "); noField = !colon; if (colon) { *colon = '\0'; badField = ( nrrdField_unknown == (fld = airEnumVal(nrrdField, buff)) ); } if (noField || badField) { keysep = strstr(buff, ":="); if (!keysep) { if (noField) { biffMaybeAddf(useBiff, NRRD, "%s: didn't see \": \" or \":=\" in line", me); } else { biffMaybeAddf(useBiff, NRRD, "%s: failed to parse \"%s\" as field identifier", me, buff); } free(buff); return nrrdField_unknown; } free(buff); ret = nrrdField_keyvalue; } else { /* *colon = '\0'; */ /* else we successfully parsed a field identifier */ next += strlen(buff) + 2; free(buff); /* skip whitespace prior to start of first field descriptor */ next += strspn(next, _nrrdFieldSep); nio->pos = AIR_CAST(int, next - nio->line); ret = fld; } return ret; } /* ** NOTE: it is a common but unfortunate property of these parsers that ** they set values in the nrrd first, and then check their validity ** later. The reason for this is mostly the desire to centralize ** validity checking in one place, and right now that's in the ** _nrrdFieldCheck[] array of checkers */ static int _nrrdReadNrrdParse_nonfield(FILE *file, Nrrd *nrrd, NrrdIoState *nio, int useBiff) { AIR_UNUSED(file); AIR_UNUSED(nrrd); AIR_UNUSED(nio); AIR_UNUSED(useBiff); /* char c; c= 10; write(2,&c,1); c= 69; write(2,&c,1); c=108; write(2,&c,1); c= 32; write(2,&c,1); c= 67; write(2,&c,1); c=104; write(2,&c,1); c=101; write(2,&c,1); c= 32; write(2,&c,1); c= 86; write(2,&c,1); c=105; write(2,&c,1); c=118; write(2,&c,1); c=101; write(2,&c,1); c= 33; write(2,&c,1); c= 10; write(2,&c,1); c= 10; write(2,&c,1); */ return 0; } static int _nrrdReadNrrdParse_comment(FILE *file, Nrrd *nrrd, NrrdIoState *nio, int useBiff) { static const char me[]="_nrrdReadNrrdParse_comment"; char *info; AIR_UNUSED(file); info = nio->line + nio->pos; /* this skips the '#' at nio->line[nio->pos] and any other ' ' and '#' */ if (nrrdCommentAdd(nrrd, info)) { biffMaybeAddf(useBiff, NRRD, "%s: trouble adding comment", me); return 1; } return 0; } static int _nrrdReadNrrdParse_content(FILE *file, Nrrd *nrrd, NrrdIoState *nio, int useBiff) { static const char me[]="_nrrdReadNrrdParse_content"; char *info; AIR_UNUSED(file); info = nio->line + nio->pos; if (strlen(info) && !(nrrd->content = airStrdup(info))) { biffMaybeAddf(useBiff, NRRD, "%s: couldn't strdup() content", me); return 1; } return 0; } static int _nrrdReadNrrdParse_number(FILE *file, Nrrd *nrrd, NrrdIoState *nio, int useBiff) { /* static const char me[]="_nrrdReadNrrdParse_number"; char *info; info = nio->line + nio->pos; if (1 != sscanf(info, NRRD_BIG_INT_PRINTF, &(nrrd->num))) { biffMaybeAddf(useBiff, NRRD, "%s: couldn't parse number \"%s\"", me, info); return 1; } */ AIR_UNUSED(file); AIR_UNUSED(nrrd); AIR_UNUSED(nio); AIR_UNUSED(useBiff); /* It was decided to just completely ignore this field. "number" is ** entirely redundant with the (required) sizes field, and there is no ** need to save it to, or learn it from, the header. In fact the "num" ** field was eliminated from the Nrrd struct some time ago, in favor of ** the nrrdElementNumber() function. It may seem odd or unfortunate that ** ** number: Hank Hill sells propane and propane accessories ** ** is a valid field specification, but at least Peggy is proud ... */ return 0; } static int _nrrdReadNrrdParse_type(FILE *file, Nrrd *nrrd, NrrdIoState *nio, int useBiff) { static const char me[]="_nrrdReadNrrdParse_type"; char *info; AIR_UNUSED(file); info = nio->line + nio->pos; if (!(nrrd->type = airEnumVal(nrrdType, info))) { biffMaybeAddf(useBiff, NRRD, "%s: couldn't parse type \"%s\"", me, info); return 1; } if (_nrrdFieldCheck[nrrdField_type](nrrd, useBiff)) { biffMaybeAddf(useBiff, NRRD, "%s: trouble", me); return 1; } return 0; } #define _PARSE_ONE_VAL(FIELD, CONV, TYPE) \ if (1 != sscanf(info, CONV, &(FIELD))) { \ biffMaybeAddf(useBiff, NRRD, "%s: couldn't parse " TYPE \ " from \"%s\"", me, info); \ return 1; \ } static int _nrrdReadNrrdParse_block_size(FILE *file, Nrrd *nrrd, NrrdIoState *nio, int useBiff) { static const char me[]="_nrrdReadNrrdParse_block_size"; char *info; AIR_UNUSED(file); info = nio->line + nio->pos; if (1 != airSingleSscanf(info, "%z", &(nrrd->blockSize))) { biffMaybeAddf(useBiff, NRRD, "%s: couldn't parse size_t" " from \"%s\"", me, info); } /* because blockSize and type fields may appear in any order, we can't use _nrrdFieldCheck[] */ return 0; } static int _nrrdReadNrrdParse_dimension(FILE *file, Nrrd *nrrd, NrrdIoState *nio, int useBiff) { static const char me[]="_nrrdReadNrrdParse_dimension"; char *info; AIR_UNUSED(file); info = nio->line + nio->pos; _PARSE_ONE_VAL(nrrd->dim, "%u", "unsigned int"); if (_nrrdFieldCheck[nrrdField_dimension](nrrd, useBiff)) { biffMaybeAddf(useBiff, NRRD, "%s: trouble", me); return 1; } return 0; } /* ** checking nrrd->dim against zero is valid because it is initialized ** to zero, and, _nrrdReadNrrdParse_dimension() won't allow it to be ** set to anything outside the range [1, NRRD_DIM_MAX] */ #define _CHECK_HAVE_DIM \ if (0 == nrrd->dim) { \ biffMaybeAddf(useBiff, NRRD, \ "%s: don't yet have a valid dimension", me); \ return 1; \ } #define _CHECK_HAVE_SPACE_DIM \ if (0 == nrrd->spaceDim) { \ biffMaybeAddf(useBiff, NRRD, \ "%s: don't yet have a valid space dimension", me); \ return 1; \ } #define _CHECK_GOT_ALL_VALUES \ if (nrrd->dim != ret) { \ biffMaybeAddf(useBiff, NRRD, \ "%s: parsed %d values, but dimension is %d", \ me, ret, nrrd->dim); \ return 1; \ } static int _nrrdReadNrrdParse_sizes(FILE *file, Nrrd *nrrd, NrrdIoState *nio, int useBiff) { static const char me[]="_nrrdReadNrrdParse_sizes"; unsigned int ret; size_t val[NRRD_DIM_MAX]; char *info; AIR_UNUSED(file); info = nio->line + nio->pos; _CHECK_HAVE_DIM; ret = airParseStrZ(val, info, _nrrdFieldSep, nrrd->dim); _CHECK_GOT_ALL_VALUES; nrrdAxisInfoSet_nva(nrrd, nrrdAxisInfoSize, val); /* HEY: this is a very imperfect check of excess info */ if (nrrd->dim+1 == airParseStrZ(val, info, _nrrdFieldSep, nrrd->dim+1)) { biffMaybeAddf(useBiff, NRRD, "%s: seem to have more than expected %d sizes", me, nrrd->dim); return 1; } if (_nrrdFieldCheck[nrrdField_sizes](nrrd, useBiff)) { biffMaybeAddf(useBiff, NRRD, "%s: trouble", me); return 1; } return 0; } static int _nrrdReadNrrdParse_spacings(FILE *file, Nrrd *nrrd, NrrdIoState *nio, int useBiff) { static const char me[]="_nrrdReadNrrdParse_spacings"; unsigned int ret; double val[NRRD_DIM_MAX]; char *info; AIR_UNUSED(file); info = nio->line + nio->pos; _CHECK_HAVE_DIM; ret = airParseStrD(val, info, _nrrdFieldSep, nrrd->dim); _CHECK_GOT_ALL_VALUES; nrrdAxisInfoSet_nva(nrrd, nrrdAxisInfoSpacing, val); /* HEY: this is a very imperfect check of excess info */ if (nrrd->dim+1 == airParseStrD(val, info, _nrrdFieldSep, nrrd->dim+1)) { biffMaybeAddf(useBiff, NRRD, "%s: seem to have more than expected %d spacings", me, nrrd->dim); return 1; } if (_nrrdFieldCheck[nrrdField_spacings](nrrd, useBiff)) { biffMaybeAddf(useBiff, NRRD, "%s: trouble", me); return 1; } return 0; } static int _nrrdReadNrrdParse_thicknesses(FILE *file, Nrrd *nrrd, NrrdIoState *nio, int useBiff) { static const char me[]="_nrrdReadNrrdParse_thicknesses"; unsigned int ret; double val[NRRD_DIM_MAX]; char *info; AIR_UNUSED(file); info = nio->line + nio->pos; _CHECK_HAVE_DIM; ret = airParseStrD(val, info, _nrrdFieldSep, nrrd->dim); _CHECK_GOT_ALL_VALUES; nrrdAxisInfoSet_nva(nrrd, nrrdAxisInfoThickness, val); /* HEY: this is a very imperfect check of excess info */ if (nrrd->dim+1 == airParseStrD(val, info, _nrrdFieldSep, nrrd->dim+1)) { biffMaybeAddf(useBiff, NRRD, "%s: seem to have more than expected %d thicknesses", me, nrrd->dim); return 1; } if (_nrrdFieldCheck[nrrdField_thicknesses](nrrd, useBiff)) { biffMaybeAddf(useBiff, NRRD, "%s: trouble", me); return 1; } return 0; } static int _nrrdReadNrrdParse_axis_mins(FILE *file, Nrrd *nrrd, NrrdIoState *nio, int useBiff) { static const char me[]="_nrrdReadNrrdParse_axis_mins"; unsigned int ret; double val[NRRD_DIM_MAX]; char *info; AIR_UNUSED(file); info = nio->line + nio->pos; _CHECK_HAVE_DIM; ret = airParseStrD(val, info, _nrrdFieldSep, nrrd->dim); _CHECK_GOT_ALL_VALUES; nrrdAxisInfoSet_nva(nrrd, nrrdAxisInfoMin, val); /* HEY: this is a very imperfect check of excess info */ if (nrrd->dim+1 == airParseStrD(val, info, _nrrdFieldSep, nrrd->dim+1)) { biffMaybeAddf(useBiff, NRRD, "%s: seem to have more than expected %d axis mins", me, nrrd->dim); return 1; } if (_nrrdFieldCheck[nrrdField_axis_mins](nrrd, useBiff)) { biffMaybeAddf(useBiff, NRRD, "%s: trouble", me); return 1; } return 0; } static int _nrrdReadNrrdParse_axis_maxs(FILE *file, Nrrd *nrrd, NrrdIoState *nio, int useBiff) { static const char me[]="_nrrdReadNrrdParse_axis_maxs"; unsigned int ret; double val[NRRD_DIM_MAX]; char *info; AIR_UNUSED(file); info = nio->line + nio->pos; _CHECK_HAVE_DIM; ret = airParseStrD(val, info, _nrrdFieldSep, nrrd->dim); _CHECK_GOT_ALL_VALUES; nrrdAxisInfoSet_nva(nrrd, nrrdAxisInfoMax, val); /* HEY: this is a very imperfect check of excess info */ if (nrrd->dim+1 == airParseStrD(val, info, _nrrdFieldSep, nrrd->dim+1)) { biffMaybeAddf(useBiff, NRRD, "%s: seem to have more than expected %d axis maxs", me, nrrd->dim); return 1; } if (_nrrdFieldCheck[nrrdField_axis_maxs](nrrd, useBiff)) { biffMaybeAddf(useBiff, NRRD, "%s: trouble", me); return 1; } return 0; } int _nrrdSpaceVectorParse(double val[NRRD_SPACE_DIM_MAX], char **hhP, unsigned int spaceDim, int useBiff) { static const char me[]="_nrrdSpaceVectorParse"; char *hh, *buff, sep[]=",)"; airArray *mop; unsigned int ret, dd; size_t length; mop = airMopNew(); hh = *hhP; /* skip past space */ length = strspn(hh, _nrrdFieldSep); hh += length; /* make sure we have something */ if (!*hh) { biffMaybeAddf(useBiff, NRRD, "%s: hit end of string before seeing (", me); airMopError(mop); return 1; } /* first, see if we're getting the non-vector */ if ( (strstr(hh, _nrrdNoSpaceVector) == hh) ) { if (!hh[strlen(_nrrdNoSpaceVector)] || strchr(_nrrdFieldSep, hh[strlen(_nrrdNoSpaceVector)])) { /* yes, we got the non-vector */ for (dd=0; dd spaceDim) { biffMaybeAddf(useBiff, NRRD, "%s: space dimension is %d, but seem to have %d " "coefficients", me, spaceDim, ret); airMopError(mop); return 1; } /* try to parse the values */ ret = airParseStrD(val, buff+1, ",", spaceDim); if (spaceDim != ret) { biffMaybeAddf(useBiff, NRRD, "%s: parsed %d values, but space dimension is %d", me, ret, spaceDim); airMopError(mop); return 1; } } /* probably not useful */ for (dd=spaceDim; ddline + nio->pos; _CHECK_HAVE_DIM; _CHECK_HAVE_SPACE_DIM; for (dd=0; dddim; dd++) { if (_nrrdSpaceVectorParse(nrrd->axis[dd].spaceDirection, &info, nrrd->spaceDim, useBiff)) { biffMaybeAddf(useBiff, NRRD, "%s: trouble getting space vector %d of %d", me, dd+1, nrrd->dim); return 1; } } if (strlen(info) != strspn(info, _nrrdFieldSep)) { biffMaybeAddf(useBiff, NRRD, "%s: seem to have more than expected %d directions", me, nrrd->dim); return 1; } if (_nrrdFieldCheck[nrrdField_space_directions](nrrd, useBiff)) { biffMaybeAddf(useBiff, NRRD, "%s: trouble", me); return 1; } return 0; } static int _nrrdReadNrrdParse_centers(FILE *file, Nrrd *nrrd, NrrdIoState *nio, int useBiff) { static const char me[]="_nrrdReadNrrdParse_centers"; unsigned int ai; char *tok, *info, *last; airArray *mop; AIR_UNUSED(file); mop = airMopNew(); info = airStrdup(nio->line + nio->pos); airMopAdd(mop, info, airFree, airMopAlways); _CHECK_HAVE_DIM; for (ai=0; aidim; ai++) { tok = airStrtok(!ai ? info : NULL, _nrrdFieldSep, &last); if (!tok) { biffMaybeAddf(useBiff, NRRD, "%s: couldn't extract string for center %d of %d", me, ai+1, nrrd->dim); airMopError(mop); return 1; } if (!strcmp(tok, NRRD_UNKNOWN)) { nrrd->axis[ai].center = nrrdCenterUnknown; continue; } if (!strcmp(tok, NRRD_NONE)) { nrrd->axis[ai].center = nrrdCenterUnknown; continue; } if (!(nrrd->axis[ai].center = airEnumVal(nrrdCenter, tok))) { biffMaybeAddf(useBiff, NRRD, "%s: couldn't parse center \"%s\" for axis %d", me, tok, ai); airMopError(mop); return 1; } } if (airStrtok(!ai ? info : NULL, _nrrdFieldSep, &last)) { biffMaybeAddf(useBiff, NRRD, "%s: seem to have more than expected %d centers", me, nrrd->dim); airMopError(mop); return 1; } if (_nrrdFieldCheck[nrrdField_centers](nrrd, useBiff)) { biffMaybeAddf(useBiff, NRRD, "%s: trouble", me); airMopError(mop); return 1; } airMopOkay(mop); return 0; } static int _nrrdReadNrrdParse_kinds(FILE *file, Nrrd *nrrd, NrrdIoState *nio, int useBiff) { static const char me[]="_nrrdReadNrrdParse_kinds"; unsigned int ai; char *info, *tok, *last; airArray *mop; AIR_UNUSED(file); mop = airMopNew(); info = airStrdup(nio->line + nio->pos); airMopAdd(mop, info, airFree, airMopAlways); _CHECK_HAVE_DIM; for (ai=0; aidim; ai++) { tok = airStrtok(!ai ? info : NULL, _nrrdFieldSep, &last); if (!tok) { biffMaybeAddf(useBiff, NRRD, "%s: couldn't extract string for kind %d of %d", me, ai+1, nrrd->dim); airMopError(mop); return 1; } if (!strcmp(tok, NRRD_UNKNOWN)) { nrrd->axis[ai].kind = nrrdKindUnknown; continue; } if (!strcmp(tok, NRRD_NONE)) { nrrd->axis[ai].center = nrrdKindUnknown; continue; } if (!(nrrd->axis[ai].kind = airEnumVal(nrrdKind, tok))) { biffMaybeAddf(useBiff, NRRD, "%s: couldn't parse \"%s\" kind %d of %d", me, tok, ai+1, nrrd->dim); airMopError(mop); return 1; } } if (airStrtok(!ai ? info : NULL, _nrrdFieldSep, &last)) { biffMaybeAddf(useBiff, NRRD, "%s: seem to have more than expected %d kinds", me, nrrd->dim); airMopError(mop); return 1; } /* can't run this now because kinds can come before sizes, in which case the kind/size check in _nrrdFieldCheck_kinds will incorrectly flag an error ... if (_nrrdFieldCheck[nrrdField_kinds](nrrd, useBiff)) { biffMaybeAddf(useBiff, NRRD, "%s: trouble", me); airMopError(mop); return 1; } */ airMopOkay(mop); return 0; } static char * _nrrdGetQuotedString(char **hP, int useBiff) { static const char me[]="_nrrdGetQuotedString"; char *h, *buff, *ret; airArray *buffArr; unsigned int pos; airPtrPtrUnion appu; h = *hP; /* skip past space */ /* printf("!%s: h |%s|\n", me, h);*/ h += strspn(h, _nrrdFieldSep); /* printf("!%s: h |%s|\n", me, h);*/ /* make sure we have something */ if (!*h) { biffMaybeAddf(useBiff, NRRD, "%s: hit end of string before seeing opening \"", me); return NULL; } /* make sure we have a starting quote */ if ('"' != *h) { biffMaybeAddf(useBiff, NRRD, "%s: didn't start with \"", me); return NULL; } h++; /* parse string until end quote */ buff = NULL; appu.c = &buff; buffArr = airArrayNew(appu.v, NULL, sizeof(char), 2); if (!buffArr) { biffMaybeAddf(useBiff, NRRD, "%s: couldn't create airArray", me); return NULL; } pos = airArrayLenIncr(buffArr, 1); /* pos should get 0 */ while (h[pos]) { /* printf("!%s: h+%d |%s|\n", me, pos, h+pos); */ if ('\"' == h[pos]) { break; } if ('\\' == h[pos] && '\"' == h[pos+1]) { h += 1; } buff[pos] = h[pos]; pos = airArrayLenIncr(buffArr, 1); } if ('\"' != h[pos]) { biffMaybeAddf(useBiff, NRRD, "%s: didn't see ending \" soon enough", me); return NULL; } h += pos + 1; buff[pos] = 0; ret = airStrdup(buff); airArrayNuke(buffArr); *hP = h; return ret; } static int _nrrdReadNrrdParse_labels(FILE *file, Nrrd *nrrd, NrrdIoState *nio, int useBiff) { static const char me[]="_nrrdReadNrrdParse_labels"; char *h; /* this is the "here" pointer which gradually progresses through all the labels (for all axes) */ unsigned int ai; char *info; AIR_UNUSED(file); /* because we have to correctly interpret quote marks, we can't simply rely on airParseStrS */ info = nio->line + nio->pos; /* printf("!%s: info |%s|\n", me, info); */ _CHECK_HAVE_DIM; h = info; for (ai=0; aidim; ai++) { if (!( nrrd->axis[ai].label = _nrrdGetQuotedString(&h, useBiff) )) { biffMaybeAddf(useBiff, NRRD, "%s: couldn't get get label %d of %d\n", me, ai+1, nrrd->dim); return 1; } } if (strlen(h) != strspn(h, _nrrdFieldSep)) { biffMaybeAddf(useBiff, NRRD, "%s: seem to have more than expected %d labels", me, nrrd->dim); return 1; } if (_nrrdFieldCheck[nrrdField_labels](nrrd, useBiff)) { biffMaybeAddf(useBiff, NRRD, "%s: trouble", me); return 1; } return 0; } static int _nrrdReadNrrdParse_units(FILE *file, Nrrd *nrrd, NrrdIoState *nio, int useBiff) { static const char me[]="_nrrdReadNrrdParse_units"; char *h; /* this is the "here" pointer which gradually progresses through all the units (for all axes) */ unsigned int ai; char *info; AIR_UNUSED(file); /* because we have to correctly interpret quote marks, we can't simply rely on airParseStrS */ info = nio->line + nio->pos; /* printf("!%s: info |%s|\n", me, info); */ _CHECK_HAVE_DIM; h = info; for (ai=0; aidim; ai++) { if (!( nrrd->axis[ai].units = _nrrdGetQuotedString(&h, useBiff) )) { biffMaybeAddf(useBiff, NRRD, "%s: couldn't get get unit %d of %d\n", me, ai+1, nrrd->dim); return 1; } } if (strlen(h) != strspn(h, _nrrdFieldSep)) { biffMaybeAddf(useBiff, NRRD, "%s: seem to have more than expected %d units", me, nrrd->dim); return 1; } if (_nrrdFieldCheck[nrrdField_units](nrrd, useBiff)) { biffMaybeAddf(useBiff, NRRD, "%s: trouble", me); return 1; } return 0; } static int _nrrdReadNrrdParse_min(FILE *file, Nrrd *nrrd, NrrdIoState *nio, int useBiff) { AIR_UNUSED(file); AIR_UNUSED(nrrd); AIR_UNUSED(nio); AIR_UNUSED(useBiff); /* This field is no longer assumed to be anything meaningful, because nrrd->min no longer exists with the advent of NrrdRange. But, having the field is not an error, to not trip on older NRRD00.01 and NRRD0001 files which (legitimately) used it */ return 0; } static int _nrrdReadNrrdParse_max(FILE *file, Nrrd *nrrd, NrrdIoState *nio, int useBiff) { AIR_UNUSED(file); AIR_UNUSED(nrrd); AIR_UNUSED(nio); AIR_UNUSED(useBiff); /* nrrd->max no longer exists, see above */ return 0; } static int _nrrdReadNrrdParse_old_min(FILE *file, Nrrd *nrrd, NrrdIoState *nio, int useBiff) { static const char me[]="_nrrdReadNrrdParse_old_min"; char *info; AIR_UNUSED(file); info = nio->line + nio->pos; _PARSE_ONE_VAL(nrrd->oldMin, "%lg", "double"); if (_nrrdFieldCheck[nrrdField_old_min](nrrd, useBiff)) { biffMaybeAddf(useBiff, NRRD, "%s: trouble", me); return 1; } return 0; } static int _nrrdReadNrrdParse_old_max(FILE *file, Nrrd *nrrd, NrrdIoState *nio, int useBiff) { static const char me[]="_nrrdReadNrrdParse_old_max"; char *info; AIR_UNUSED(file); info = nio->line + nio->pos; _PARSE_ONE_VAL(nrrd->oldMax, "%lg", "double"); if (_nrrdFieldCheck[nrrdField_old_max](nrrd, useBiff)) { biffMaybeAddf(useBiff, NRRD, "%s: trouble", me); return 1; } return 0; } static int _nrrdReadNrrdParse_endian(FILE *file, Nrrd *nrrd, NrrdIoState *nio, int useBiff) { static const char me[]="_nrrdReadNrrdParse_endian"; char *info; AIR_UNUSED(file); AIR_UNUSED(nrrd); info = nio->line + nio->pos; if (!(nio->endian = airEnumVal(airEndian, info))) { biffMaybeAddf(useBiff, NRRD, "%s: couldn't parse endian \"%s\"", me, info); return 1; } return 0; } static int _nrrdReadNrrdParse_encoding(FILE *file, Nrrd *nrrd, NrrdIoState *nio, int useBiff) { static const char me[]="_nrrdReadNrrdParse_encoding"; char *info; int etype; AIR_UNUSED(file); AIR_UNUSED(nrrd); info = nio->line + nio->pos; if (!(etype = airEnumVal(nrrdEncodingType, info))) { biffMaybeAddf(useBiff, NRRD, "%s: couldn't parse encoding \"%s\"", me, info); return 1; } nio->encoding = nrrdEncodingArray[etype]; return 0; } static int _nrrdReadNrrdParse_line_skip(FILE *file, Nrrd *nrrd, NrrdIoState *nio, int useBiff) { static const char me[]="_nrrdReadNrrdParse_line_skip"; char *info; AIR_UNUSED(file); AIR_UNUSED(nrrd); info = nio->line + nio->pos; _PARSE_ONE_VAL(nio->lineSkip, "%u", "unsigned int"); /* now that its unsigned, what error checking can I do? if (!(0 <= nio->lineSkip)) { biffMaybeAddf(useBiff, NRRD, "%s: lineSkip value %d invalid", me, nio->lineSkip); return 1; } */ return 0; } static int _nrrdReadNrrdParse_byte_skip(FILE *file, Nrrd *nrrd, NrrdIoState *nio, int useBiff) { static const char me[]="_nrrdReadNrrdParse_byte_skip"; char *info; AIR_UNUSED(file); AIR_UNUSED(nrrd); info = nio->line + nio->pos; _PARSE_ONE_VAL(nio->byteSkip, "%ld", "long int"); /* this check is being removed to enable the undocumented (in the file format spec) ability to say "byte skip: -N-1" in order to skip backwards from EOF by N bytes ** if (!(-1 <= nio->byteSkip)) { ** biffMaybeAddf(useBiff, NRRD, ** "%s: byteSkip value %ld invalid", me, nio->byteSkip); ** return 1; ** } */ return 0; } static int _nrrdReadNrrdParse_keyvalue(FILE *file, Nrrd *nrrd, NrrdIoState *nio, int useBiff) { static const char me[]="_nrrdReadNrrdParse_keyvalue"; char *keysep, *line, *key, *value; AIR_UNUSED(file); /* we know this will find something */ line = airStrdup(nio->line); if (!line) { biffMaybeAddf(useBiff, NRRD, "%s: can't allocate parse line", me); return 1; } keysep = strstr(line, ":="); if (!keysep) { biffMaybeAddf(useBiff, NRRD, "%s: didn't see \":=\" key/value delimiter in \"%s\"", me, line); free(line); return 1; } keysep[0] = 0; keysep[1] = 0; key = line; value = keysep+2; /* convert escape sequences */ airUnescape(key); airUnescape(value); nrrdKeyValueAdd(nrrd, key, value); free(line); return 0; } static int _nrrdReadNrrdParse_sample_units(FILE *file, Nrrd *nrrd, NrrdIoState *nio, int useBiff) { static const char me[]="_nrrdReadNrrdParse_sample_units"; char *info; AIR_UNUSED(file); info = nio->line + nio->pos; if (strlen(info) && !(nrrd->sampleUnits = airStrdup(info))) { biffMaybeAddf(useBiff, NRRD, "%s: couldn't strdup() sampleUnits", me); return 1; } if (_nrrdFieldCheck[nrrdField_sample_units](nrrd, useBiff)) { biffMaybeAddf(useBiff, NRRD, "%s: trouble", me); return 1; } return 0; } static int _nrrdReadNrrdParse_space(FILE *file, Nrrd *nrrd, NrrdIoState *nio, int useBiff) { static const char me[]="_nrrdReadNrrdParse_space"; char *info; int space; AIR_UNUSED(file); info = nio->line + nio->pos; if (nio->seen[nrrdField_space_dimension]) { biffMaybeAddf(useBiff, NRRD, "%s: can't specify space after specifying " "space dimension (%d)", me, nrrd->spaceDim); return 1; } if (!(space = airEnumVal(nrrdSpace, info))) { biffMaybeAddf(useBiff, NRRD, "%s: couldn't parse space \"%s\"", me, info); return 1; } if (nrrdSpaceSet(nrrd, space)) { biffMaybeAddf(useBiff, NRRD, "%s: trouble", me); return 1; } if (_nrrdFieldCheck[nrrdField_space](nrrd, useBiff)) { biffMaybeAddf(useBiff, NRRD, "%s: trouble", me); return 1; } return 0; } static int _nrrdReadNrrdParse_space_dimension(FILE *file, Nrrd *nrrd, NrrdIoState *nio, int useBiff) { static const char me[]="_nrrdReadNrrdParse_space_dimension"; char *info; AIR_UNUSED(file); info = nio->line + nio->pos; if (nio->seen[nrrdField_space]) { biffMaybeAddf(useBiff, NRRD, "%s: can't specify space dimension after specifying " "space (%s)", me, airEnumStr(nrrdSpace, nrrd->space)); return 1; } _PARSE_ONE_VAL(nrrd->spaceDim, "%u", "unsigned int"); if (_nrrdFieldCheck[nrrdField_space_dimension](nrrd, useBiff)) { biffMaybeAddf(useBiff, NRRD, "%s: trouble", me); return 1; } return 0; } static int _nrrdReadNrrdParse_space_units(FILE *file, Nrrd *nrrd, NrrdIoState *nio, int useBiff) { static const char me[]="_nrrdReadNrrdParse_space_units"; char *h; /* this is the "here" pointer which gradually progresses through all the units (for all axes) */ unsigned int ai; char *info; AIR_UNUSED(file); /* because we have to correctly interpret quote marks, we can't simply rely on airParseStrS */ info = nio->line + nio->pos; /* printf("!%s: info |%s|\n", me, info); */ _CHECK_HAVE_SPACE_DIM; h = info; for (ai=0; aispaceDim; ai++) { if (!( nrrd->spaceUnits[ai] = _nrrdGetQuotedString(&h, useBiff) )) { biffMaybeAddf(useBiff, NRRD, "%s: couldn't get get space unit %d of %d", me, ai+1, nrrd->spaceDim); return 1; } } if (_nrrdGetQuotedString(&h, AIR_FALSE)) { biffMaybeAddf(useBiff, NRRD, "%s: seemed to have more than expected %d space units", me, nrrd->spaceDim); return 1; } if (_nrrdFieldCheck[nrrdField_space_units](nrrd, useBiff)) { biffMaybeAddf(useBiff, NRRD, "%s: trouble", me); return 1; } return 0; } static int _nrrdReadNrrdParse_space_origin(FILE *file, Nrrd *nrrd, NrrdIoState *nio, int useBiff) { static const char me[]="_nrrdReadNrrdParse_space_origin"; char *info; AIR_UNUSED(file); info = nio->line + nio->pos; _CHECK_HAVE_SPACE_DIM; if (_nrrdSpaceVectorParse(nrrd->spaceOrigin, &info, nrrd->spaceDim, useBiff)) { biffMaybeAddf(useBiff, NRRD, "%s: couldn't parse origin \"%s\"", me, info); return 1; } if (_nrrdFieldCheck[nrrdField_space_origin](nrrd, useBiff)) { biffMaybeAddf(useBiff, NRRD, "%s: trouble", me); return 1; } return 0; } static int _nrrdReadNrrdParse_measurement_frame(FILE *file, Nrrd *nrrd, NrrdIoState *nio, int useBiff) { static const char me[]="_nrrdReadNrrdParse_measurement_frame"; double colvec[NRRD_SPACE_DIM_MAX]; unsigned int dd, ii; char *info; AIR_UNUSED(file); info = nio->line + nio->pos; _CHECK_HAVE_SPACE_DIM; for (dd=0; ddspaceDim; dd++) { /* we are going through the *columns* of the mf matrix */ if (_nrrdSpaceVectorParse(colvec, &info, nrrd->spaceDim, useBiff)) { biffMaybeAddf(useBiff, NRRD, "%s: trouble getting space vector %d of %d", me, dd+1, nrrd->spaceDim); return 1; } for (ii=0; iimeasurementFrame[dd][ii] = (ii < nrrd->spaceDim ? colvec[ii] : AIR_NAN); } } if (strlen(info) != strspn(info, _nrrdFieldSep)) { biffMaybeAddf(useBiff, NRRD, "%s: seem to have more than expected %d directions", me, nrrd->spaceDim); return 1; } for (dd=nrrd->spaceDim; ddmeasurementFrame[dd][ii] = AIR_NAN; } } if (_nrrdFieldCheck[nrrdField_measurement_frame](nrrd, useBiff)) { biffMaybeAddf(useBiff, NRRD, "%s: trouble", me); return 1; } return 0; } int _nrrdContainsPercentThisAndMore(const char *str, char thss) { const char *hh, *tmp; tmp = str; do { hh = strchr(tmp, '%'); if (!( hh && hh[1] )) { return 0; } if ('%' == hh[1]) { /* its an escaped % */ tmp = hh + 2; } else { break; } } while (tmp[0]); hh++; hh += strspn(hh, "0123456789"); if (!( hh[0] == thss )) { return 0; } hh += strcspn(hh, _nrrdFieldSep); return !!hh; } unsigned int _nrrdDataFNNumber(NrrdIoState *nio) { unsigned int ret; int ii; if (nio->dataFNFormat) { /* datafiles given in iterator form; count number of values */ ret = 0; for (ii = nio->dataFNMin; ((nio->dataFNStep > 0 && ii <= nio->dataFNMax) || (nio->dataFNStep < 0 && ii >= nio->dataFNMax)); ii += nio->dataFNStep) { ret += 1; } } else if (nio->dataFNArr->len) { /* datafiles given as an explicit list, or as a single file name, and in either case, nrrdDataFNAdd() is used to add them to the dataFNArr */ ret = nio->dataFNArr->len; } else { /* datafile is same as (attached) header file */ ret = 1; } return ret; } /* ** this always requires that the per-axis size fields have been set */ int _nrrdDataFNCheck(NrrdIoState *nio, Nrrd *nrrd, int useBiff) { static const char me[]="_nrrdDataFNCheck"; size_t pieceSize, pieceNum; char stmp[AIR_STRLEN_SMALL]; if (!nio->seen[nrrdField_sizes]) { biffMaybeAddf(useBiff, NRRD, "%s: sorry, currently can't handle " "multiple detached data files without first knowing " "the \"%s\" field", me, airEnumStr(nrrdField, nrrdField_sizes)); return 1; } if (nio->dataFileDim < nrrd->dim) { /* this requires that the per-axis size fields have been set */ _nrrdSplitSizes(&pieceSize, &pieceNum, nrrd, nio->dataFileDim); if (pieceNum != _nrrdDataFNNumber(nio)) { biffMaybeAddf(useBiff, NRRD, "%s: expected %s filenames (of %u-D pieces) " "but got %u", me, airSprintSize_t(stmp, pieceNum), nio->dataFileDim, _nrrdDataFNNumber(nio)); return 1; } } else { /* we're getting data in "slabs" with the same dimension as the nrrd, so for simplicity we assume that they're all equal size */ if (_nrrdDataFNNumber(nio) > nrrd->axis[nrrd->dim-1].size) { biffMaybeAddf(useBiff, NRRD, "%s: can't have more pieces (%u) than axis %u " "slices (%s) when nrrd dimension and " "datafile dimension are both %u", me, _nrrdDataFNNumber(nio), nrrd->dim-1, airSprintSize_t(stmp, nrrd->axis[nrrd->dim-1].size), nrrd->dim); return 1; } if ((double)nrrd->axis[nrrd->dim-1].size/_nrrdDataFNNumber(nio) != nrrd->axis[nrrd->dim-1].size/_nrrdDataFNNumber(nio)) { biffMaybeAddf(useBiff, NRRD, "%s: number of datafiles (%d) doesn't divide into " "number of axis %u slices (%s)", me, (int)_nrrdDataFNNumber(nio), nrrd->dim-1, airSprintSize_t(stmp, nrrd->axis[nrrd->dim-1].size)); return 1; } } return 0; } /* ** Sat Jan 29 16:44:50 EST 2005: this used to "open the separate ** datafile, and set the FILE* in nio->dataFile, which otherwise will ** stay NULL", but now we support multiple detached data files. So. ** ** The job of this function is to map the "data file" specification to ** one or more filenames that can be passed direction to fopen for ** reading in the data. This involves parsing the various formats for ** identifying multiple data files, and possibly prefixing them with ** nio->path. */ static int _nrrdReadNrrdParse_data_file(FILE *ffile, Nrrd *nrrd, NrrdIoState *nio, int useBiff) { static const char me[]="_nrrdReadNrrdParse_data_file"; char *info, *nums; unsigned int linelen, tmp; airArray *mop; mop = airMopNew(); info = airStrdup(nio->line + nio->pos); if (!info) { biffMaybeAddf(useBiff, NRRD, "%s: couldn't copy line!", me); return 1; } airMopAdd(mop, info, airFree, airMopAlways); /* HEY: this change should be made someday if (_nrrdContainsPercentThisAndMore(info, 'd') || _nrrdContainsPercentThisAndMore(info, 'u')) { */ if (_nrrdContainsPercentThisAndMore(info, 'd')) { /* ---------------------------------------------------------- */ /* --------- format.%d [] ----------- */ /* ---------------------------------------------------------- */ size_t sspn; _CHECK_HAVE_DIM; nums = info + strcspn(info, _nrrdFieldSep); sspn = strspn(nums, _nrrdFieldSep); nums[0] = 0; /* terminate so that format is now in info */ nums += sspn; if (!( 3 == sscanf(nums, "%d %d %d",&(nio->dataFNMin), &(nio->dataFNMax), &(nio->dataFNStep)) )) { biffMaybeAddf(useBiff, NRRD, "%s: couldn't parse three ints (min, max, step) after " "data filename template", me); airMopError(mop); return 1; } if ( 4 == sscanf(nums, "%d %d %d %u", &(nio->dataFNMin), &(nio->dataFNMax), &(nio->dataFNStep), &(nio->dataFileDim)) ) { if (!AIR_IN_CL(1, nio->dataFileDim, nrrd->dim)) { biffMaybeAddf(useBiff, NRRD, "%s: datafile dimension %u outside valid range [1,%u]", me, nio->dataFileDim, nrrd->dim); airMopError(mop); return 1; } } else { nio->dataFileDim = nrrd->dim-1; } if (0 == nio->dataFNStep) { biffMaybeAddf(useBiff, NRRD, "%s: file number step must be non-zero", me); airMopError(mop); return 1; } if ((nio->dataFNMax - nio->dataFNMin)*(nio->dataFNStep) < 0) { biffMaybeAddf(useBiff, NRRD, "%s: file number max %d not approached from min %d " "by step %d", me, nio->dataFNMax, nio->dataFNMin, nio->dataFNStep); airMopError(mop); return 1; } if (!( nio->dataFNFormat = airStrdup(info) )) { biffMaybeAddf(useBiff, NRRD, "%s: couldn't copy data filename format", me); airMopError(mop); return 1; } if (_nrrdDataFNCheck(nio, nrrd, useBiff)) { biffMaybeAddf(useBiff, NRRD, "%s: trouble with number of datafiles", me); airMopError(mop); return 1; } } else if (!strncmp(info, NRRD_LIST_FLAG, strlen(NRRD_LIST_FLAG))) { /* ---------------------------------------------------------- */ /* ------------------------- LIST --------------------------- */ /* ---------------------------------------------------------- */ _CHECK_HAVE_DIM; if (_nrrdHeaderCheck(nrrd, nio, AIR_TRUE)) { biffMaybeAddf(useBiff, NRRD, "%s: NRRD header is incomplete. \"" NRRD_LIST_FLAG "\" data file specification must be " "contiguous with end of header!", me); airMopError(mop); return 1; } info += strlen(NRRD_LIST_FLAG); if (info[0]) { if (1 == sscanf(info, "%u", &(nio->dataFileDim))) { if (!AIR_IN_CL(1, nio->dataFileDim, nrrd->dim)) { biffMaybeAddf(useBiff, NRRD, "%s: datafile dimension %u outside " "valid range [1,%u]", me, nio->dataFileDim, nrrd->dim); airMopError(mop); return 1; } } else { biffMaybeAddf(useBiff, NRRD, "%s: couldn't parse info after \"" NRRD_LIST_FLAG "\" as an int", me); airMopError(mop); return 1; } } else { /* nothing after NRRD_LIST_FLAG, so dataFileDim is implicit */ nio->dataFileDim = nrrd->dim-1; } /* read in all the datafile names */ do { /* yes, nio->line is re-used/over-written here, but I don't think that's a problem */ if (_nrrdOneLine(&linelen, nio, ffile)) { biffMaybeAddf(useBiff, NRRD, "%s: trouble getting file name line", me); airMopError(mop); return 1; } if (linelen > 0) { tmp = airArrayLenIncr(nio->dataFNArr, 1); nio->dataFN[tmp] = airStrdup(nio->line); } } while (linelen > 0); if (_nrrdDataFNCheck(nio, nrrd, useBiff)) { biffMaybeAddf(useBiff, NRRD, "%s: trouble with number of datafiles", me); airMopError(mop); return 1; } } else { /* ---------------------------------------------------------- */ /* -------------------- (single filename) ------------------- */ /* ---------------------------------------------------------- */ /* there is apparently only a single detached data file; for this its okay to not yet know nrrd->dim */ tmp = airArrayLenIncr(nio->dataFNArr, 1); nio->dataFN[tmp] = airStrdup(info); nio->dataFileDim = 0; } airMopOkay(mop); return 0; } /* ******** nrrdFieldInfoParse[NRRD_FIELD_MAX+1]() ** ** These are all for parsing the stuff AFTER the colon */ int (*nrrdFieldInfoParse[NRRD_FIELD_MAX+1])(FILE *, Nrrd *, NrrdIoState *, int) = { _nrrdReadNrrdParse_nonfield, _nrrdReadNrrdParse_comment, _nrrdReadNrrdParse_content, _nrrdReadNrrdParse_number, _nrrdReadNrrdParse_type, _nrrdReadNrrdParse_block_size, _nrrdReadNrrdParse_dimension, _nrrdReadNrrdParse_space, _nrrdReadNrrdParse_space_dimension, _nrrdReadNrrdParse_sizes, _nrrdReadNrrdParse_spacings, _nrrdReadNrrdParse_thicknesses, _nrrdReadNrrdParse_axis_mins, _nrrdReadNrrdParse_axis_maxs, _nrrdReadNrrdParse_space_directions, _nrrdReadNrrdParse_centers, _nrrdReadNrrdParse_kinds, _nrrdReadNrrdParse_labels, _nrrdReadNrrdParse_units, _nrrdReadNrrdParse_min, _nrrdReadNrrdParse_max, _nrrdReadNrrdParse_old_min, _nrrdReadNrrdParse_old_max, _nrrdReadNrrdParse_endian, _nrrdReadNrrdParse_encoding, _nrrdReadNrrdParse_line_skip, _nrrdReadNrrdParse_byte_skip, _nrrdReadNrrdParse_keyvalue, _nrrdReadNrrdParse_sample_units, _nrrdReadNrrdParse_space_units, _nrrdReadNrrdParse_space_origin, _nrrdReadNrrdParse_measurement_frame, _nrrdReadNrrdParse_data_file }; /* kernel parsing is all in kernel.c */ cmtk-3.3.1/Utilities/NrrdIO/preamble.c000066400000000000000000000021221276303427400175240ustar00rootroot00000000000000/* NrrdIO: stand-alone code for basic nrrd functionality Copyright (C) 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. */ cmtk-3.3.1/Utilities/NrrdIO/privateAir.h000066400000000000000000000022201276303427400200470ustar00rootroot00000000000000/* NrrdIO: stand-alone code for basic nrrd functionality Copyright (C) 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. */ /* miscAir.c */ extern double _airSanityHelper(double val); cmtk-3.3.1/Utilities/NrrdIO/privateBiff.h000066400000000000000000000033551276303427400202140ustar00rootroot00000000000000/* NrrdIO: stand-alone code for basic nrrd functionality Copyright (C) 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. */ #ifdef __cplusplus extern "C" { #endif /* ** This private header exists because these functions are used in ** the biff sources, but no where else. Also, they take a va_list, ** which is unusual, and (currently) used for no other public functions ** in Teem. Use of va_list args complicates python wrapping (at least ** with the current ctypeslib mechanism), so these functions are being ** taken out of the public API. */ /* biffmsg.c */ extern void _biffMsgAddVL(biffMsg *msg, const char *errfmt, va_list args); extern void _biffMsgMoveVL(biffMsg *dest, biffMsg *src, const char *errfmt, va_list args); #ifdef __cplusplus } #endif cmtk-3.3.1/Utilities/NrrdIO/privateNrrd.h000066400000000000000000000146501276303427400202530ustar00rootroot00000000000000/* NrrdIO: stand-alone code for basic nrrd functionality Copyright (C) 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. */ /* * Modified 23-Oct-2012 by Torsten Rohlfing for CMTK, * (C) 2012 SRI International */ #ifdef _WIN32 #include #include #endif #ifdef __cplusplus extern "C" { #endif #define _NRRD_TEXT_INCR 1024 #define _NRRD_LLONG_MAX_HELP AIR_LLONG(2305843009213693951) #define _NRRD_LLONG_MIN_HELP AIR_LLONG(-2305843009213693952) #define _NRRD_WHITESPACE_NOTAB " \n\r\v\f" /* K+R pg. 157 */ /* ** _NRRD_SPACING ** ** returns nrrdDefSpacing if the argument doesn't exist, otherwise ** returns the argument */ #define _NRRD_SPACING(spc) (AIR_EXISTS(spc) ? spc: nrrdDefSpacing) typedef union { char **CP; int *I; unsigned int *UI; size_t *ST; double *D; const void *P; double (*V)[NRRD_SPACE_DIM_MAX]; } _nrrdAxisInfoSetPtrs; typedef union { char **CP; int *I; unsigned int *UI; size_t *ST; double *D; void *P; double (*V)[NRRD_SPACE_DIM_MAX]; } _nrrdAxisInfoGetPtrs; /* defaultsNrrd.c */ extern airLLong _nrrdLLongMaxHelp(airLLong val); extern airLLong _nrrdLLongMinHelp(airLLong val); extern airULLong _nrrdULLongMaxHelp(airULLong val); /* keyvalue.c */ extern void _nrrdWriteEscaped(FILE *file, char *dst, const char *str, const char *toescape, const char *tospace); extern int _nrrdKeyValueWrite(FILE *file, char **stringP, const char *prefix, const char *key, const char *value); /* formatXXX.c */ extern const char *_nrrdFormatURLLine0; extern const char *_nrrdFormatURLLine1; extern const NrrdFormat _nrrdFormatNRRD; extern const NrrdFormat _nrrdFormatPNM; extern const NrrdFormat _nrrdFormatPNG; extern const NrrdFormat _nrrdFormatVTK; extern const NrrdFormat _nrrdFormatText; extern const NrrdFormat _nrrdFormatEPS; extern int _nrrdHeaderCheck(Nrrd *nrrd, NrrdIoState *nio, int checkSeen); extern int _nrrdFormatNRRD_whichVersion(const Nrrd *nrrd, NrrdIoState *nio); /* encodingXXX.c */ extern const NrrdEncoding _nrrdEncodingRaw; extern const NrrdEncoding _nrrdEncodingAscii; extern const NrrdEncoding _nrrdEncodingHex; extern const NrrdEncoding _nrrdEncodingGzip; extern const NrrdEncoding _nrrdEncodingBzip2; /* read.c */ extern int _nrrdCalloc(Nrrd *nrrd, NrrdIoState *nio, FILE *file); extern char _nrrdFieldSep[]; /* arrays.c */ extern const int _nrrdFieldValidInImage[NRRD_FIELD_MAX+1]; extern const int _nrrdFieldValidInText[NRRD_FIELD_MAX+1]; extern const int _nrrdFieldOnePerAxis[NRRD_FIELD_MAX+1]; extern const char _nrrdEnumFieldStr[NRRD_FIELD_MAX+1][AIR_STRLEN_SMALL]; extern const int _nrrdFieldRequired[NRRD_FIELD_MAX+1]; /* simple.c */ extern char *_nrrdContentGet(const Nrrd *nin); extern int _nrrdContentSet_nva(Nrrd *nout, const char *func, char *content, const char *format, va_list arg); extern int _nrrdContentSet_va(Nrrd *nout, const char *func, char *content, const char *format, ...); extern int (*_nrrdFieldCheck[NRRD_FIELD_MAX+1])(const Nrrd *nrrd, int useBiff); extern void _nrrdSplitSizes(size_t *pieceSize, size_t *pieceNum, Nrrd *nrrd, unsigned int listDim); /* axis.c */ extern int _nrrdKindAltered(int kindIn, int resampling); extern void _nrrdAxisInfoCopy(NrrdAxisInfo *dest, const NrrdAxisInfo *src, int bitflag); extern void _nrrdAxisInfoInit(NrrdAxisInfo *axis); extern void _nrrdAxisInfoNewInit(NrrdAxisInfo *axis); extern int _nrrdCenter(int center); extern int _nrrdCenter2(int center, int def); /* convert.c */ extern void (*_nrrdConv[][NRRD_TYPE_MAX+1])(void *, const void *, size_t); extern void (*_nrrdClampConv[][NRRD_TYPE_MAX+1])(void *, const void *, size_t); /* read.c */ extern char _nrrdFieldStr[NRRD_FIELD_MAX+1][AIR_STRLEN_SMALL]; extern char _nrrdRelativePathFlag[]; extern char _nrrdFieldSep[]; extern char _nrrdNoSpaceVector[]; extern char _nrrdTextSep[]; extern void _nrrdSplitName(char **dirP, char **baseP, const char *name); /* write.c */ extern int _nrrdFieldInteresting(const Nrrd *nrrd, NrrdIoState *nio, int field); extern void _nrrdSprintFieldInfo(char **strP, const char *prefix, const Nrrd *nrrd, NrrdIoState *nio, int field); extern void _nrrdFprintFieldInfo(FILE *file, const char *prefix, const Nrrd *nrrd, NrrdIoState *nio, int field); /* parseNrrd.c */ extern int _nrrdReadNrrdParseField(NrrdIoState *nio, int useBiff); /* methodsNrrd.c */ extern void nrrdPeripheralInit(Nrrd *nrrd); extern int nrrdPeripheralCopy(Nrrd *nout, const Nrrd *nin); extern int _nrrdCopy(Nrrd *nout, const Nrrd *nin, int bitflag); extern int _nrrdSizeCheck(const size_t *size, unsigned int dim, int useBiff); extern void _nrrdTraverse(Nrrd *nrrd); extern int _nrrdMaybeAllocMaybeZero_nva(Nrrd *nrrd, int type, unsigned int dim, const size_t *size, int zeroWhenNoAlloc); #if TEEM_ZLIB #include "zlib.h" /* gzio.c */ extern gzFile _nrrdGzOpen(FILE* fd, const char *mode); extern int _nrrdGzClose(gzFile file); extern int _nrrdGzRead(gzFile file, void* buf, unsigned int len, unsigned int* read); extern int _nrrdGzWrite(gzFile file, const void* buf, unsigned int len, unsigned int* written); #endif #ifdef __cplusplus } #endif cmtk-3.3.1/Utilities/NrrdIO/qnanhibit.c000066400000000000000000000031021276303427400177110ustar00rootroot00000000000000/* NrrdIO: stand-alone code for basic nrrd functionality Copyright (C) 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. */ #include #include int main(int argc, char *argv[]) { char *me; float pinf, qnan; int i; me = argv[0]; if (sizeof(float) != sizeof(int)) { fprintf(stderr, "%s: MADNESS: sizeof(float)=%d != sizeof(int)=%d\n", me, (int)sizeof(float), (int)sizeof(int)); return -1; } pinf = FLT_MAX; pinf = pinf*pinf; qnan = pinf/pinf; i = *(int*)(&qnan); printf("-DTEEM_QNANHIBIT=%d\n", (i >> 22) & 1); return (int)((i >> 22) & 1); } cmtk-3.3.1/Utilities/NrrdIO/read.c000066400000000000000000000531111276303427400166540ustar00rootroot00000000000000/* NrrdIO: stand-alone code for basic nrrd functionality Copyright (C) 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. */ #include "NrrdIO.h" #include "privateNrrd.h" #if TEEM_BZIP2 #include #endif /* The "/ *Teem:" (without space) comments in here are an experiment */ char _nrrdRelativePathFlag[] = "./"; char _nrrdFieldSep[] = " \t"; char _nrrdLineSep[] = "\r\n"; char _nrrdNoSpaceVector[] = "none"; char _nrrdTextSep[] = " ,\t"; /* ** return length of next "line" in nio->headerStringRead */ unsigned int _nrrdHeaderStringOneLineStrlen(NrrdIoState *nio) { return AIR_CAST(unsigned int, strcspn(nio->headerStringRead + nio->headerStrpos, _nrrdLineSep)); } /* ** read next "line" in nio->headerStringRead */ unsigned int _nrrdHeaderStringOneLine(NrrdIoState *nio) { unsigned int len1, len2; len1 = _nrrdHeaderStringOneLineStrlen(nio); strncpy(nio->line, nio->headerStringRead + nio->headerStrpos, len1); nio->line[len1] = '\0'; nio->headerStrpos += len1; len2 = AIR_CAST(unsigned int, strspn(nio->headerStringRead + nio->headerStrpos, _nrrdLineSep)); nio->headerStrpos += len2; return len1; } /* ** _nrrdOneLine ** ** wrapper around airOneLine; does re-allocation of line buffer ** ("line") in the NrrdIoState if needed. The return value semantics ** are similar, except that what airOneLine would return, we put ** in *lenP. If there is an error (airOneLine returned 0, ** something couldn't be allocated), *lenP is set to 0, and ** we return 1. HITTING EOF IS NOT ACTUALLY AN ERROR, see code ** below. Otherwise we return 0. ** ** Does use biff */ int _nrrdOneLine(unsigned int *lenP, NrrdIoState *nio, FILE *file) { static const char me[]="_nrrdOneLine"; char **line; airArray *mop, *lineArr; airPtrPtrUnion appu; unsigned int lineIdx, len, needLen; if (!( lenP && nio && (file || nio->headerStringRead))) { biffAddf(NRRD, "%s: got NULL pointer (%p, %p, %p/%p)", me, AIR_CAST(void*, lenP), AIR_CAST(void*, nio), AIR_CAST(void*, file), nio->headerStringRead); return 1; } if (0 == nio->lineLen) { /* nio->line hasn't been allocated for anything */ nio->lineLen = 3; nio->line = (char*)malloc(nio->lineLen); if (!nio->line) { biffAddf(NRRD, "%s: couldn't alloc %d-char line\n", me, nio->lineLen); *lenP = 0; return 1; } } if (file) { len = airOneLine(file, nio->line, nio->lineLen); } else { /* NOTE: NULL-ity error check above makes this safe */ needLen = _nrrdHeaderStringOneLineStrlen(nio); if (needLen+1 > nio->lineLen) { nio->lineLen = needLen+1; airFree(nio->line); /* lose previous allocated line */ nio->line = (char*)malloc(nio->lineLen); if (!nio->line) { biffAddf(NRRD, "%s: couldn't alloc %d-char line\n", me, nio->lineLen); *lenP = 0; return 1; } } len = _nrrdHeaderStringOneLine(nio); } if (len <= nio->lineLen) { /* otherwise we hit EOF (or end of nio->headerStringRead) before a newline, or the line (possibly empty) fit within the nio->line, neither of which is an error here */ *lenP = len; } else { /* line didn't fit in buffer, so we have to increase line buffer size and put the line together in pieces */ /* NOTE: this will never happen when reading from nio->headerStringRead */ appu.cp = &line; lineArr = airArrayNew(appu.v, NULL, sizeof(char *), 1); if (!lineArr) { biffAddf(NRRD, "%s: couldn't allocate airArray", me); *lenP = 0; return 1; } airArrayPointerCB(lineArr, airNull, airFree); mop = airMopNew(); airMopAdd(mop, lineArr, (airMopper)airArrayNuke, airMopAlways); while (len == nio->lineLen+1) { lineIdx = airArrayLenIncr(lineArr, 1); if (!lineArr->data) { biffAddf(NRRD, "%s: couldn't increment line buffer array", me); *lenP = 0; airMopError(mop); return 1; } line[lineIdx] = nio->line; nio->lineLen *= 2; nio->line = (char*)malloc(nio->lineLen); if (!nio->line) { biffAddf(NRRD, "%s: couldn't alloc %d-char line\n", me, nio->lineLen); *lenP = 0; airMopError(mop); return 1; } len = airOneLine(file, nio->line, nio->lineLen); } /* last part did fit in nio->line buffer, also save this into line[] */ lineIdx = airArrayLenIncr(lineArr, 1); if (!lineArr->data) { biffAddf(NRRD, "%s: couldn't increment line buffer array", me); *lenP = 0; airMopError(mop); return 1; } line[lineIdx] = nio->line; nio->lineLen *= 3; /* for good measure */ nio->line = (char*)malloc(nio->lineLen); if (!nio->line) { biffAddf(NRRD, "%s: couldn't alloc %d-char line\n", me, nio->lineLen); *lenP = 0; airMopError(mop); return 1; } /* now concatenate everything into a new nio->line */ strcpy(nio->line, ""); for (lineIdx=0; lineIdxlen; lineIdx++) { strcat(nio->line, line[lineIdx]); } /* HEY: API is bad: *lenP should be a size_t pointer! */ *lenP = AIR_UINT(strlen(nio->line)) + 1; airMopError(mop); } return 0; } /* ** _nrrdCalloc() ** ** allocates the data for the array, but only if necessary (as informed by ** nio->oldData and nio->oldDataSize). ** ** as a recent feature, this will handle the extra work of allocating ** memory in the special way required for direct IO, if possible. For ** this to work, though, the FILE *file has to be passed. Since file ** is not otherwise needed, it can be passed as NULL for non-direct-IO ** situations. In any case, if the directIO-compatible allocation fails ** its not error, and we revert to regular allocation. ** ** NOTE: this assumes the checking that is done by _nrrdHeaderCheck */ int _nrrdCalloc(Nrrd *nrrd, NrrdIoState *nio, FILE *file) { static const char me[]="_nrrdCalloc"; size_t needDataSize; int fd; needDataSize = nrrdElementNumber(nrrd)*nrrdElementSize(nrrd); if (nio->oldData && needDataSize == nio->oldDataSize) { /* re-use old data */ nrrd->data = nio->oldData; /* its not an error to have a directIO-incompatible pointer, so there's no other error checking to do here */ } else { nrrd->data = airFree(nrrd->data); fd = file ? fileno(file) : -1; if (nrrdEncodingRaw == nio->encoding && -1 != fd && airNoDio_okay == airDioTest(fd, NULL, needDataSize)) { nrrd->data = airDioMalloc(needDataSize, fd); } if (!nrrd->data) { /* directIO-compatible allocation wasn't tried, or it failed */ nrrd->data = malloc(needDataSize); } if (!nrrd->data) { char stmp1[AIR_STRLEN_SMALL], stmp2[AIR_STRLEN_SMALL]; biffAddf(NRRD, "%s: couldn't allocate %s things of size %s", me, airSprintSize_t(stmp1, nrrdElementNumber(nrrd)), airSprintSize_t(stmp2, nrrdElementSize(nrrd))); return 1; } } /* make it look like it came from calloc(), as used by nrrdNew() */ memset(nrrd->data, 0, needDataSize); return 0; } /* ******** nrrdLineSkip ** ** public for the sake of things like "unu make" ** uses the NrrdIoState for its line buffer (used by _nrrdOneLine) */ int nrrdLineSkip(FILE *dataFile, NrrdIoState *nio) { static const char me[]="nrrdLineSkip"; unsigned int lsi, skipRet; /* For compressed data: If you don't actually have ascii headers on top of your gzipped data then you will potentially huge lines while _nrrdOneLine looks for line terminations. Quoting Gordon: "Garbage in, Garbage out." */ if (!( dataFile && nio )) { biffAddf(NRRD, "%s: got NULL pointer", me); return 1; } for (lsi=0; lsilineSkip; lsi++) { if (_nrrdOneLine(&skipRet, nio, dataFile)) { biffAddf(NRRD, "%s: error skipping line %u of %u", me, lsi+1, nio->lineSkip); return 1; } if (!skipRet) { biffAddf(NRRD, "%s: hit EOF skipping line %u of %u", me, lsi+1, nio->lineSkip); return 1; } } return 0; } /* ******** nrrdByteSkip ** ** public for the sake of things like "unu make" ** uses nio for information about how much data should actually be skipped ** with negative byteSkip */ int nrrdByteSkip(FILE *dataFile, Nrrd *nrrd, NrrdIoState *nio) { static const char me[]="nrrdByteSkip"; int skipRet; size_t bsize; if (!( dataFile && nrrd && nio )) { biffAddf(NRRD, "%s: got NULL pointer", me); return 1; } if (nio->encoding->isCompression) { biffAddf(NRRD, "%s: this function can't work with compressed " "encoding %s", me, nio->encoding->name); return 1; } if (nio->byteSkip < 0) { long backwards; if (nrrdEncodingRaw != nio->encoding) { biffAddf(NRRD, "%s: this function can do backwards byte skip only " "in %s encoding, not %s", me, nrrdEncodingRaw->name, nio->encoding->name); return 1; } if (stdin == dataFile) { biffAddf(NRRD, "%s: can't fseek on stdin", me); return 1; } bsize = nrrdElementNumber(nrrd)/_nrrdDataFNNumber(nio); bsize *= nrrdElementSize(nrrd); /* backwards is (positive) number of bytes AFTER data that we ignore */ backwards = -nio->byteSkip - 1; /* HEY what if bsize fits in size_t but not in (signed) long? */ if (fseek(dataFile, -AIR_CAST(long, bsize) - backwards, SEEK_END)) { char stmp[AIR_STRLEN_SMALL]; biffAddf(NRRD, "%s: failed to fseek(dataFile, %s, SEEK_END)", me, airSprintSize_t(stmp, bsize)); return 1; } if (nrrdStateVerboseIO >= 2) { fprintf(stderr, "(%s: actually skipped %d bytes)\n", me, (int)ftell(dataFile)); } } else { if ((stdin == dataFile) || (-1==fseek(dataFile, nio->byteSkip, SEEK_CUR))) { long skipi; /* fseek failed, perhaps because we're reading stdin, so we revert to consuming the input one byte at a time */ for (skipi=0; skipibyteSkip; skipi++) { skipRet = fgetc(dataFile); if (EOF == skipRet) { biffAddf(NRRD, "%s: hit EOF skipping byte %ld of %ld", me, skipi, nio->byteSkip); return 1; } } } } return 0; } /* ** _nrrdRead() ** ** read in nrrd from a given file *OR* given string. The main job of ** this function is to start reading the file/string, to determine the ** format, and then call the appropriate format's reader. This means ** that the various encoding (data) readers can assume that ** nio->format is usefully set. ** ** If (file), the only input information that nio is used for is ** nio->path, so that detached header-relative data files can be ** found. If (string), the headerStr-related fields in the _nio will ** be set/used */ int _nrrdRead(Nrrd *nrrd, FILE *file, const char *string, NrrdIoState *_nio) { static const char me[]="_nrrdRead"; unsigned int llen; NrrdIoState *nio; int nfi; airArray *mop; /* sanity check, for good measure */ if (!nrrdSanity()) { biffAddf(NRRD, "%s: sanity check FAILED: have to fix and re-compile", me); return 1; } if (!((file || string) && nrrd)) { biffAddf(NRRD, "%s: got NULL pointer", me); return 1; } if (file && string) { biffAddf(NRRD, "%s: can't read from both file and string", me); return 1; } mop = airMopNew(); if (_nio) { nio = _nio; } else { nio = nrrdIoStateNew(); if (!nio) { biffAddf(NRRD, "%s: couldn't alloc I/O struct", me); return 1; } airMopAdd(mop, nio, (airMopper)nrrdIoStateNix, airMopAlways); } /* remember old data pointer and allocated size. Whether or not to free() this memory will be decided later */ nio->oldData = nrrd->data; nio->oldDataSize = (nio->oldData ? nrrdElementNumber(nrrd)*nrrdElementSize(nrrd) : 0); /* fprintf(stderr, "!%s: nio->oldData = %p, oldDataSize = %d\n", me, nio->oldData, (int)(nio->oldDataSize)); */ nrrd->data = NULL; /* initialize given nrrd (but we have thwarted freeing existing memory) */ nrrdInit(nrrd); /* tell the nio where to find the string to read from */ nio->headerStringRead = string; if (_nrrdOneLine(&llen, nio, file)) { biffAddf(NRRD, "%s: error getting first line (containing \"magic\")", me); airMopError(mop); return 1; } if (!llen) { biffAddf(NRRD, "%s: immediately hit EOF", me); airMopError(mop); return 1; } nio->format = nrrdFormatUnknown; for (nfi = nrrdFormatTypeUnknown+1; nfi < nrrdFormatTypeLast; nfi++) { if (nrrdFormatArray[nfi]->contentStartsLike(nio)) { nio->format = nrrdFormatArray[nfi]; break; } } if (nrrdFormatUnknown == nio->format) { char linestart[AIR_STRLEN_SMALL], stmp[AIR_STRLEN_SMALL]; airStrcpy(linestart, AIR_STRLEN_SMALL, nio->line); if (strlen(linestart) != strlen(nio->line)) { biffAddf(NRRD, "%s: couldn't parse (length %s) line starting " "with \"%s\" as magic or beginning of any recognized format", me, airSprintSize_t(stmp, strlen(nio->line)), linestart); } else { biffAddf(NRRD, "%s: couldn't parse \"%s\" as magic or beginning " "of any recognized format", me, nio->line); } airMopError(mop); return 1; } if (string && nrrdFormatNRRD != nio->format) { biffAddf(NRRD, "%s: sorry, can only read %s files from strings (not %s)", me, nrrdFormatNRRD->name, nio->format->name); airMopError(mop); return 1; } /* try to read the file */ if (nio->format->read(file, nrrd, nio)) { biffAddf(NRRD, "%s: trouble reading %s file", me, nio->format->name); airMopError(mop); return 1; } /* reshape up grayscale images, if desired */ if (nio->format->isImage && 2 == nrrd->dim && nrrdStateGrayscaleImage3D) { if (nrrdAxesInsert(nrrd, nrrd, 0)) { biffAddf(NRRD, "%s:", me); return 1; } } /* free prior memory if we didn't end up using it */ /* HEY: could actually do a check on the nio to refine this */ if (nio->oldData != nrrd->data) { nio->oldData = airFree(nio->oldData); nio->oldDataSize = 0; } /* finally, make sure that what we're returning isn't malformed somehow, except that we (probably stupidly) allow nrrd->data to be NULL, given the possibility of using nio->skipData */ if (_nrrdCheck(nrrd, AIR_FALSE, AIR_TRUE)) { biffAddf(NRRD, "%s: problem with nrrd after reading", me); return 1; } airMopOkay(mop); return 0; } /* ******** nrrdRead() ** ** now just a wrapper around _nrrdRead(); reads a NRRD from a FILE * */ int nrrdRead(Nrrd *nrrd, FILE *file, NrrdIoState *_nio) { static const char me[]="nrrdRead"; if (_nrrdRead(nrrd, file, NULL, _nio)) { biffAddf(NRRD, "%s: trouble", me); return 1; } return 0; } /* ******** nrrdStringRead() ** ** also a wrapper around _nrrdRead(); reads a NRRD from a char *. ** ** Because the same underlying _nrrdRead() is used, the same semantics ** about using existing nrrd->data when possible applies, as does the ** action of nrrdStateGrayscaleImage3D */ int nrrdStringRead(Nrrd *nrrd, const char *string, NrrdIoState *_nio) { static const char me[]="nrrdRead"; if (_nrrdRead(nrrd, NULL, string, _nio)) { biffAddf(NRRD, "%s: trouble", me); return 1; } return 0; } /* ** _nrrdSplitName() ** ** splits a file name into a path and a base filename. The path ** separator is '/', but there is a hack (thanks Torsten Rohlfing) ** which allows '\' to work on Windows. The division between the path ** and the base is the last path separator in the file name. The path ** is everything prior to this, and base is everything after (so the ** base does NOT start with the path separator). If there is not a ** '/' in the name, or if a path separator appears as the last ** character, then the path is set to ".", and the name is copied into ** base. */ void _nrrdSplitName(char **dirP, char **baseP, const char *name) { char *where; if (dirP) { *dirP = (char *)airFree(*dirP); } if (baseP) { *baseP = (char *)airFree(*baseP); } where = strrchr(name, '/'); #ifdef _WIN32 /* Deal with Windows "\" path separators; thanks to Torsten Rohlfing */ if ( !where || (strrchr(name, '\\') > where) ) { where = strrchr(name, '\\'); } #endif /* we found a valid break if the last directory character is somewhere in the string except the last character */ if (where && airStrlen(where) > 1) { if (dirP) { *dirP = airStrdup(name); (*dirP)[where - name] = 0; } if (baseP) { *baseP = airStrdup(where + 1); } } else { /* if the name had no slash, its in the current directory, which means that we need to explicitly store "." as the header directory in case we have header-relative data. */ if (dirP) { *dirP = airStrdup("."); } if (baseP) { *baseP = airStrdup(name); } } return; } /* ******** nrrdLoad() ** ** ** ** call tree for this, to help figure out what's going on ** ** read.c/nrrdLoad ** | read.c/_nrrdSplitName ** | read.c/nrrdRead ** | nio->format->read ** = formatNRRD.c/_nrrdFormatNRRD_read: ** | read.c/_nrrdOneLine ** | parseNrrd.c/_nrrdReadNrrdParseField ** | parseNrrd.c/nrrdFieldInfoParse[] ** = parseNrrd.c/_nrrdReadNrrdParse_data_file ** | fopen(dataName) ** | formatNRRD.c/_nrrdHeaderCheck ** | read.c/nrrdLineSkip ** | read.c/nrrdByteSkip ** | nio->encoding->read ** = encodingRaw.c/_nrrdEncodingRaw_read ** | read.c/_nrrdCalloc ** | formatNRRD.c/nrrdSwapEndian ** | miscAir.c/airFclose ** ** (more documentation here) ** ** sneakiness: returns 2 if the reason for problem was a failed fopen(). ** */ int /*Teem: biff if (ret) */ nrrdLoad(Nrrd *nrrd, const char *filename, NrrdIoState *nio) { static const char me[]="nrrdLoad"; FILE *file; airArray *mop; if (!(nrrd && filename)) { biffAddf(NRRD, "%s: got NULL pointer", me); return 1; } mop = airMopNew(); if (!nio) { nio = nrrdIoStateNew(); if (!nio) { biffAddf(NRRD, "%s: couldn't alloc I/O struct", me); return 1; } airMopAdd(mop, nio, (airMopper)nrrdIoStateNix, airMopAlways); } /* we save the directory of the filename given to us so that if it turns out that this is a detached header with a header-relative data file, then we will know how to find the data file */ _nrrdSplitName(&(nio->path), NULL, filename); /* printf("!%s: |%s|%s|\n", me, nio->dir, nio->base); */ if (!( file = airFopen(filename, stdin, "rb") )) { biffAddf(NRRD, "%s: fopen(\"%s\",\"rb\") failed: %s", me, filename, strerror(errno)); airMopError(mop); return 2; } airMopAdd(mop, file, (airMopper)airFclose, airMopOnError); /* non-error exiting is handled below */ if (nrrdRead(nrrd, file, nio)) { biffAddf(NRRD, "%s: trouble reading \"%s\"", me, filename); airMopError(mop); return 1; } if (nrrdFormatNRRD == nio->format && nio->keepNrrdDataFileOpen && file == nio->dataFile ) { /* we have to keep the datafile open. If was attached, we can't close file, because that is the datafile. If was detached, file != nio->dataFile, so we can close file. */ } else { /* always close non-NRRD files */ airFclose(file); } airMopOkay(mop); return 0; } int nrrdLoadMulti(Nrrd *const *nin, unsigned int ninLen, const char *fnameFormat, unsigned int numStart, NrrdIoState *nio) { static const char me[]="nrrdLoadMulti"; char *fname; airArray *mop; unsigned int nii; if (!(nin && fnameFormat)) { biffAddf(NRRD, "%s: got NULL pointer", me); return 1; } if (!( _nrrdContainsPercentThisAndMore(fnameFormat, 'u') )) { biffAddf(NRRD, "%s: given format \"%s\" doesn't seem to " "have the \"%%u\" conversion specification to sprintf " "an unsigned int\n", me, fnameFormat); return 1; } mop = airMopNew(); /* should be big enough for the number replacing the format sequence */ fname = AIR_CAST(char *, malloc(strlen(fnameFormat) + 128)); if (!(fname)) { biffAddf(NRRD, "%s: couldn't allocate local fname buffer", me); airMopError(mop); return 1; } airMopAdd(mop, fname, airFree, airMopAlways); for (nii=0; nii 0)) { biffAddf(NRRD, "%s: got NULL pointer or non-positive nn (%d)", me, nn); return 1; } /* use the given array "invp" as a temp buffer for validity checking */ memset(invp, 0, nn*sizeof(unsigned int)); for (ii=0; iidim )) { biffAddf(NRRD, "%s: given axis (%d) outside valid range [0, %d]", me, axis, nin->dim); return 1; } if (NRRD_DIM_MAX == nin->dim) { biffAddf(NRRD, "%s: given nrrd already at NRRD_DIM_MAX (%d)", me, NRRD_DIM_MAX); return 1; } if (nout != nin) { if (_nrrdCopy(nout, nin, (NRRD_BASIC_INFO_COMMENTS_BIT | (nrrdStateKeyValuePairsPropagate ? 0 : NRRD_BASIC_INFO_KEYVALUEPAIRS_BIT)))) { biffAddf(NRRD, "%s:", me); return 1; } } nout->dim = 1 + nin->dim; for (ai=nin->dim; ai>axis; ai--) { _nrrdAxisInfoCopy(&(nout->axis[ai]), &(nin->axis[ai-1]), NRRD_AXIS_INFO_NONE); } /* the ONLY thing we can say about the new axis is its size */ _nrrdAxisInfoInit(&(nout->axis[axis])); if (!nrrdStateKindNoop) { /* except maybe the kind */ nout->axis[axis].kind = nrrdKindStub; } nout->axis[axis].size = 1; if (nrrdContentSet_va(nout, func, nin, "%d", axis)) { biffAddf(NRRD, "%s:", me); return 1; } /* all basic info has already been copied by nrrdCopy() above */ return 0; } /* ******** nrrdAxesPermute ** ** changes the scanline ordering of the data in a nrrd ** ** The basic means by which data is moved around is with memcpy(). ** The goal is to call memcpy() as few times as possible, on memory ** segments as large as possible. Currently, this is done by ** detecting how many of the low-index axes are left untouched by ** the permutation- this constitutes a "scanline" which can be ** copied around as a unit. For permuting the y and z axes of a ** matrix-x-y-z order matrix volume, this optimization produced a ** factor of 5 speed up (exhaustive multi-platform tests, of course). ** ** The axes[] array determines the permutation of the axes. ** axis[i] = j means: axis i in the output will be the input's axis j ** (axis[i] answers: "what do I put here", from the standpoint of the output, ** not "where do I put this", from the standpoint of the input) */ int nrrdAxesPermute(Nrrd *nout, const Nrrd *nin, const unsigned int *axes) { static const char me[]="nrrdAxesPermute", func[]="permute"; char buff1[NRRD_DIM_MAX*30], buff2[AIR_STRLEN_SMALL]; size_t idxOut, idxIn, /* indices for input and output scanlines */ lineSize, /* size of block of memory which can be moved contiguously from input to output, thought of as a "scanline" */ numLines, /* how many "scanlines" there are to permute */ szIn[NRRD_DIM_MAX], *lszIn, szOut[NRRD_DIM_MAX], *lszOut, cIn[NRRD_DIM_MAX], cOut[NRRD_DIM_MAX]; char *dataIn, *dataOut; int axmap[NRRD_DIM_MAX]; unsigned int ai, /* running index along dimensions */ lowPax, /* lowest axis which is "p"ermutated */ ldim, /* nin->dim - lowPax */ ip[NRRD_DIM_MAX+1], /* inverse of permutation in "axes" */ laxes[NRRD_DIM_MAX+1]; /* copy of axes[], but shifted down by lowPax elements, to remove i such that i == axes[i] */ airArray *mop; mop = airMopNew(); if (!(nin && nout && axes)) { biffAddf(NRRD, "%s: got NULL pointer", me); airMopError(mop); return 1; } /* we don't actually need ip[], computing it is for error checking */ if (nrrdInvertPerm(ip, axes, nin->dim)) { biffAddf(NRRD, "%s: couldn't compute axis permutation inverse", me); airMopError(mop); return 1; } /* this shouldn't actually be necessary .. */ if (!nrrdElementSize(nin)) { biffAddf(NRRD, "%s: nrrd reports zero element size!", me); airMopError(mop); return 1; } for (ai=0; aidim && axes[ai] == ai; ai++) ; lowPax = ai; /* allocate output by initial copy */ if (nout != nin) { if (nrrdCopy(nout, nin)) { biffAddf(NRRD, "%s: trouble copying input", me); airMopError(mop); return 1; } dataIn = (char*)nin->data; } else { dataIn = (char*)calloc(nrrdElementNumber(nin), nrrdElementSize(nin)); if (!dataIn) { biffAddf(NRRD, "%s: couldn't create local copy of data", me); airMopError(mop); return 1; } airMopAdd(mop, dataIn, airFree, airMopAlways); memcpy(dataIn, nin->data, nrrdElementNumber(nin)*nrrdElementSize(nin)); } if (lowPax < nin->dim) { /* if lowPax == nin->dim, then we were given the identity permutation, so there's nothing to do other than the copy already done. Otherwise, here we are (actually, lowPax < nin->dim-1) */ for (ai=0; aidim; ai++) { axmap[ai] = AIR_INT(axes[ai]); } nrrdAxisInfoGet_nva(nin, nrrdAxisInfoSize, szIn); if (nrrdAxisInfoCopy(nout, nin, axmap, NRRD_AXIS_INFO_NONE)) { biffAddf(NRRD, "%s:", me); airMopError(mop); return 1; } nrrdAxisInfoGet_nva(nout, nrrdAxisInfoSize, szOut); /* the skinny */ lineSize = 1; for (ai=0; aidim - lowPax; memset(laxes, 0, sizeof(laxes)); for (ai=0; aidata); memset(cIn, 0, sizeof(cIn)); memset(cOut, 0, sizeof(cOut)); for (idxOut=0; idxOutdim; ai++) { sprintf(buff2, "%s%d", (ai ? "," : ""), axes[ai]); strcat(buff1, buff2); } if (nrrdContentSet_va(nout, func, nin, "%s", buff1)) { biffAddf(NRRD, "%s:", me); airMopError(mop); return 1; } if (nout != nin) { if (nrrdBasicInfoCopy(nout, nin, NRRD_BASIC_INFO_DATA_BIT | NRRD_BASIC_INFO_TYPE_BIT | NRRD_BASIC_INFO_BLOCKSIZE_BIT | NRRD_BASIC_INFO_DIMENSION_BIT | NRRD_BASIC_INFO_CONTENT_BIT | NRRD_BASIC_INFO_COMMENTS_BIT | (nrrdStateKeyValuePairsPropagate ? 0 : NRRD_BASIC_INFO_KEYVALUEPAIRS_BIT))) { biffAddf(NRRD, "%s:", me); airMopError(mop); return 1; } } } airMopOkay(mop); return 0; } /* ******** nrrdShuffle ** ** rearranges hyperslices of a nrrd along a given axis according to ** given permutation. This could be used to on a 4D array, ** representing a 3D volume of vectors, to re-order the vector ** components. ** ** the given permutation array must allocated for at least as long as ** the input nrrd along the chosen axis. perm[j] = i means that the ** value at position j in the _new_ array should come from position i ** in the _old_array. The standpoint is from the new, looking at ** where to find the values amid the old array (perm answers "what do ** I put here", not "where do I put this"). This allows multiple ** positions in the new array to copy from the same old position, and ** insures that there is an source for all positions along the new ** array. */ int nrrdShuffle(Nrrd *nout, const Nrrd *nin, unsigned int axis, const size_t *perm) { static const char me[]="nrrdShuffle", func[]="shuffle"; char buff2[AIR_STRLEN_SMALL]; /* Sun Feb 8 13:13:58 CST 2009: There was a memory bug here caused by using the same buff1[NRRD_DIM_MAX*30] declaration that had worked fine for nrrdAxesPermute and nrrdReshape, but does NOT work here because now samples along an axes are re-ordered, not axes, so its often not allocated for long enough to hold the string that's printed to it. Ideally there'd be another argument that says whether to document the shuffle in the content string, which would mean an API change. Or, we can use a secret heuristic (or maybe later a nrrdState variable) for determining when an axis is short enough to make documenting the shuffle interesting. This is useful since functions like nrrdFlip() probably do *not* need the shuffle (the sample reversal) to be documented for long axes */ #define LONGEST_INTERESTING_AXIS 42 char buff1[LONGEST_INTERESTING_AXIS*30]; unsigned int ai, ldim, len; size_t idxIn, idxOut, lineSize, numLines, size[NRRD_DIM_MAX], *lsize, cIn[NRRD_DIM_MAX+1], cOut[NRRD_DIM_MAX+1]; char *dataIn, *dataOut; if (!(nin && nout && perm)) { biffAddf(NRRD, "%s: got NULL pointer", me); return 1; } if (nout == nin) { biffAddf(NRRD, "%s: nout==nin disallowed", me); return 1; } if (!( axis < nin->dim )) { biffAddf(NRRD, "%s: axis %d outside valid range [0,%d]", me, axis, nin->dim-1); return 1; } len = AIR_CAST(unsigned int, nin->axis[axis].size); for (ai=0; aiblockSize = nin->blockSize; nrrdAxisInfoGet_nva(nin, nrrdAxisInfoSize, size); if (nrrdMaybeAlloc_nva(nout, nin->type, nin->dim, size)) { biffAddf(NRRD, "%s: failed to allocate output", me); return 1; } if (nrrdAxisInfoCopy(nout, nin, NULL, NRRD_AXIS_INFO_NONE)) { biffAddf(NRRD, "%s:", me); return 1; } /* the min and max along the shuffled axis are now meaningless */ nout->axis[axis].min = nout->axis[axis].max = AIR_NAN; /* do the safe thing first */ nout->axis[axis].kind = _nrrdKindAltered(nin->axis[axis].kind, AIR_FALSE); /* try cleverness */ if (!nrrdStateKindNoop) { if (0 == nrrdKindSize(nin->axis[axis].kind) || nrrdKindStub == nin->axis[axis].kind || nrrdKindScalar == nin->axis[axis].kind || nrrdKind2Vector == nin->axis[axis].kind || nrrdKind3Color == nin->axis[axis].kind || nrrdKind4Color == nin->axis[axis].kind || nrrdKind3Vector == nin->axis[axis].kind || nrrdKind3Gradient == nin->axis[axis].kind || nrrdKind3Normal == nin->axis[axis].kind || nrrdKind4Vector == nin->axis[axis].kind) { /* these kinds have no intrinsic ordering */ nout->axis[axis].kind = nin->axis[axis].kind; } } /* the skinny */ lineSize = 1; for (ai=0; aiaxis[ai].size; } numLines = nrrdElementNumber(nin)/lineSize; lineSize *= nrrdElementSize(nin); lsize = size + axis; ldim = nin->dim - axis; dataIn = AIR_CAST(char *, nin->data); dataOut = AIR_CAST(char *, nout->data); memset(cIn, 0, sizeof(cIn)); memset(cOut, 0, sizeof(cOut)); for (idxOut=0; idxOut>= 22; if (AIR_QNANHIBIT != (int)mant) { return airInsane_QNaNHiBit; } if (!( airFP_QNAN == airFPClass_f(AIR_NAN) && airFP_QNAN == airFPClass_f(AIR_QNAN) /* As of July 4 2012 GLK decides that the signalling NaN tests are more trouble than they're worth: the signal-ness of the NaN is not preserved in double-float conversion for some platforms (so airFP_SNAN == airFPClass_d(AIR_SNAN) has never been enforced), and there are more platforms for which (apparently) passing AIR_SNAN to airFPClass_d changes it to a quiet NaN, which defeats the purpose of the test. To summarize, given that: ** AIR_NAN and AIR_QNAN are checked here to be quiet NaN, after casting to both float and double, ** quiet NaN "hi bit" is tested above, and that ** quiet and signalling NaN are mutually exclusive, skipping the signalling NaN tests is unlikely to undermine knowing the correctness of the compile-time representation of NaNs. So the following line is now commented out for all platforms. */ /* && airFP_SNAN == airFPClass_f(AIR_SNAN) */ && airFP_QNAN == airFPClass_d(AIR_NAN) && airFP_QNAN == airFPClass_d(AIR_QNAN) )) { return airInsane_AIR_NAN; } if (!(airFP_QNAN == airFPClass_f(nanF) && airFP_POS_INF == airFPClass_f(pinfF) && airFP_NEG_INF == airFPClass_f(ninfF))) { /* really, this is verifying that assigning from a double to a float maintains the FPClass for non-existent values */ return airInsane_FltDblFPClass; } /* just make sure AIR_DIO is reasonably set (actually, this should be done by include/teemDio.h) */ switch (AIR_DIO) { case 0: break; case 1: break; default: return airInsane_dio; } _airSanity = 1; return airInsane_not; } static const char _airInsaneErr[AIR_INSANE_MAX+1][AIR_STRLEN_MED] = { "sanity checked PASSED!", "airMyEndian() is wrong", "AIR_EXISTS(+inf) was true", "AIR_EXISTS(-inf) was true", "AIR_EXISTS(NaN) was true", "air_FPClass_f() wrong after double->float assignment", "TEEM_QNANHIBIT is wrong", "airFPClass(AIR_QNAN) wrong", "TEEM_DIO has invalid value", "unsigned char isn't 8 bits", "sizeof(float), sizeof(int) not both == 4", "sizeof(double), sizeof(airLLong) not both == 8", }; static const char _airBadInsane[] = "(invalid insane value)"; const char * airInsaneErr(int insane) { if (AIR_IN_CL(0, insane, AIR_INSANE_MAX)) { return _airInsaneErr[insane]; } else { return _airBadInsane; } } cmtk-3.3.1/Utilities/NrrdIO/simple.c000066400000000000000000001320551276303427400172370ustar00rootroot00000000000000/* NrrdIO: stand-alone code for basic nrrd functionality Copyright (C) 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. */ #include "NrrdIO.h" #include "privateNrrd.h" #include /* The "/ *Teem:" (without space) comments in here are an experiment */ const char * nrrdBiffKey = "nrrd"; /* ******** nrrdSpaceDimension ** ** returns expected dimension of given space (from nrrdSpace* enum), or, ** 0 if there is no expected dimension. ** ** The expected behavior here is to return 0 for nrrdSpaceUnknown, because ** that is the right answer, not because its an error of any kind. */ unsigned int nrrdSpaceDimension(int space) { static const char me[]="nrrdSpaceDimension"; unsigned int ret; if (!( AIR_IN_OP(nrrdSpaceUnknown, space, nrrdSpaceLast) )) { /* they gave us invalid or unknown space */ return 0; } switch (space) { case nrrdSpaceRightAnteriorSuperior: case nrrdSpaceLeftAnteriorSuperior: case nrrdSpaceLeftPosteriorSuperior: case nrrdSpaceScannerXYZ: case nrrdSpace3DRightHanded: case nrrdSpace3DLeftHanded: ret = 3; break; case nrrdSpaceRightAnteriorSuperiorTime: case nrrdSpaceLeftAnteriorSuperiorTime: case nrrdSpaceLeftPosteriorSuperiorTime: case nrrdSpaceScannerXYZTime: case nrrdSpace3DRightHandedTime: case nrrdSpace3DLeftHandedTime: ret = 4; break; default: fprintf(stderr, "%s: PANIC: nrrdSpace %d not implemented!\n", me, space); ret = UINT_MAX; /* exit(1); */ break; } return ret; } /* ******** nrrdSpaceSet ** ** What to use to set space, when a value from nrrdSpace enum is known, ** or, to nullify all space-related information when passed nrrdSpaceUnknown */ int nrrdSpaceSet(Nrrd *nrrd, int space) { static const char me[]="nrrdSpaceSet"; unsigned axi, saxi; if (!nrrd) { biffAddf(NRRD, "%s: got NULL pointer", me); return 1; } if (nrrdSpaceUnknown == space) { nrrd->space = nrrdSpaceUnknown; nrrd->spaceDim = 0; for (axi=0; axiaxis[axi].spaceDirection); } for (saxi=0; saxispaceUnits[saxi]); nrrd->spaceUnits[saxi] = NULL; } nrrdSpaceVecSetNaN(nrrd->spaceOrigin); } else { if (airEnumValCheck(nrrdSpace, space)) { biffAddf(NRRD, "%s: given space (%d) not valid", me, space); return 1; } nrrd->space = space; nrrd->spaceDim = nrrdSpaceDimension(space); } return 0; } /* ******** nrrdSpaceDimensionSet ** ** What to use to set space, based on spaceDim alone (nrrd->space set to ** nrrdSpaceUnknown) */ int nrrdSpaceDimensionSet(Nrrd *nrrd, unsigned int spaceDim) { static const char me[]="nrrdSpaceDimensionSet"; if (!nrrd) { biffAddf(NRRD, "%s: got NULL pointer", me); return 1; } if (!( spaceDim <= NRRD_SPACE_DIM_MAX )) { biffAddf(NRRD, "%s: given spaceDim (%u) not valid", me, spaceDim); return 1; } nrrd->space = nrrdSpaceUnknown; nrrd->spaceDim = spaceDim; return 0; } /* ******** nrrdSpaceOriginGet ** ** retrieves the spaceOrigin from given nrrd, and returns spaceDim ** Indices 0 through spaceDim-1 are set in given vector[] to coords ** of space origin, and all further indices are set to NaN */ unsigned int nrrdSpaceOriginGet(const Nrrd *nrrd, double vector[NRRD_SPACE_DIM_MAX]) { unsigned int sdi, ret; if (nrrd && vector) { for (sdi=0; sdispaceDim; sdi++) { vector[sdi] = nrrd->spaceOrigin[sdi]; } for (sdi=nrrd->spaceDim; sdispaceDim; } else { ret = 0; } return ret; } /* ******** nrrdSpaceOriginSet ** ** convenience function for setting spaceOrigin. ** Note: space (or spaceDim) must be already set ** ** returns 1 if there were problems, 0 otherwise */ int nrrdSpaceOriginSet(Nrrd *nrrd, double vector[NRRD_SPACE_DIM_MAX]) { static const char me[]="nrrdSpaceOriginSet"; unsigned int sdi; if (!( nrrd && vector )) { biffAddf(NRRD, "%s: got NULL pointer", me); return 1; } if (!( 0 < nrrd->spaceDim && nrrd->spaceDim <= NRRD_SPACE_DIM_MAX )) { biffAddf(NRRD, "%s: set spaceDim %d not valid", me, nrrd->spaceDim); return 1; } for (sdi=0; sdispaceDim; sdi++) { nrrd->spaceOrigin[sdi] = vector[sdi]; } for (sdi=nrrd->spaceDim; sdispaceOrigin[sdi] = AIR_NAN; } return 0; } /* ******** nrrdOriginCalculate ** ** makes an effort to calculate something like an "origin" (as in ** nrrd->spaceOrigin) from the per-axis min, max, or spacing, when ** there is no real space information. Like the spaceOrigin, this ** location is supposed to be THE CENTER of the first sample. To ** avoid making assumptions about the nrrd or the caller, a default ** sample centering (defaultCenter) has to be provided (use either ** nrrdCenterNode or nrrdCenterCell). The axes that are used ** for the origin calculation have to be given explicitly- but they ** are likely the return of nrrdDomainAxesGet ** ** The computed origin is put into the given vector (origin). The return ** value takes on values from the nrrdOriginStatus* enum: ** ** nrrdOriginStatusUnknown: invalid arguments (e.g. NULL pointer, or ** axis values out of range) ** ** nrrdOriginStatusDirection: the chosen axes have spaceDirection set, ** which means caller should instead be using ** nrrdSpaceOriginGet ** ** nrrdOriginStatusNoMin: can't compute "origin" without axis->min ** ** nrrdOriginStatusNoMaxOrSpacing: can't compute origin without (axis->min ** and) either axis->max or axis->spacing ** ** nrrdOriginStatusOkay: all is well */ int nrrdOriginCalculate(const Nrrd *nrrd, unsigned int *axisIdx, unsigned int axisIdxNum, int defaultCenter, double *origin) { const NrrdAxisInfo *axis[NRRD_SPACE_DIM_MAX]; int center, okay, gotSpace, gotMin, gotMaxOrSpacing; unsigned int ai; double min, spacing; #define ERROR \ if (origin) { \ for (ai=0; aidim; } if (!okay) { ERROR; return nrrdOriginStatusUnknown; } /* learn axisInfo pointers */ for (ai=0; aiaxis + axisIdx[ai]; } gotSpace = AIR_FALSE; for (ai=0; aispaceDirection[0]); } if (nrrd->spaceDim > 0 && gotSpace) { ERROR; return nrrdOriginStatusDirection; } gotMin = AIR_TRUE; for (ai=0; aimin); } if (!gotMin) { ERROR; return nrrdOriginStatusNoMin; } gotMaxOrSpacing = AIR_TRUE; for (ai=0; aimax) || AIR_EXISTS(axis[ai]->spacing)); } if (!gotMaxOrSpacing) { ERROR; return nrrdOriginStatusNoMaxOrSpacing; } for (ai=0; aisize; min = axis[ai]->min; center = (nrrdCenterUnknown != axis[ai]->center ? axis[ai]->center : defaultCenter); denom = AIR_CAST(double, nrrdCenterCell == center ? size : size-1); spacing = (AIR_EXISTS(axis[ai]->spacing) ? axis[ai]->spacing : (axis[ai]->max - min)/denom); origin[ai] = min + (nrrdCenterCell == center ? spacing/2 : 0); } return nrrdOriginStatusOkay; } void nrrdSpaceVecCopy(double dst[NRRD_SPACE_DIM_MAX], const double src[NRRD_SPACE_DIM_MAX]) { unsigned int ii; for (ii=0; iispaceOrigin, nin->spaceOrigin); ** for (ai=0; aidim; ai++) { ** _nrrdSpaceVecScaleAdd2(nout->spaceOrigin, ** 1.0, nout->spaceOrigin, ** min[ai], nin->axis[ai].spaceDirection); ** } ** ** but the problem with this is that non-spatial axes end up clobbering ** the existance of otherwise existing spaceOrigin and spaceDirections. ** It was decided, however, that this logic should be outside the ** arithmetic functions below, not inside. NOTE: the old functionality ** is stuck in ITK 2.2, via NrrdIO. */ void nrrdSpaceVecScaleAdd2(double sum[NRRD_SPACE_DIM_MAX], double sclA, const double vecA[NRRD_SPACE_DIM_MAX], double sclB, const double vecB[NRRD_SPACE_DIM_MAX]) { unsigned int ii; for (ii=0; iicontent) ? airStrdup(nin->content) : airStrdup(nrrdStateUnknownContent)); if (!ret) { fprintf(stderr, "%s: PANIC: content strdup failed!\n", me); return NULL; } return ret; } int _nrrdContentSet_nva(Nrrd *nout, const char *func, char *content, const char *format, va_list arg) { static const char me[]="_nrrdContentSet_nva"; char *buff; if (nrrdStateDisableContent) { /* we kill content always */ nout->content = (char *)airFree(nout->content); return 0; } buff = (char *)malloc(128*AIR_STRLEN_HUGE); if (!buff) { biffAddf(NRRD, "%s: couln't alloc buffer!", me); return 1; } nout->content = (char *)airFree(nout->content); /* we are currently praying that this won't overflow the "buff" array */ /* HEY: replace with vsnprintf or whatever when its available */ vsprintf(buff, format, arg); nout->content = (char *)calloc(strlen("(,)") + airStrlen(func) + 1 /* '(' */ + airStrlen(content) + 1 /* ',' */ + airStrlen(buff) + 1 /* ')' */ + 1, sizeof(char)); /* '\0' */ if (!nout->content) { biffAddf(NRRD, "%s: couln't alloc output content!", me); airFree(buff); return 1; } sprintf(nout->content, "%s(%s%s%s)", func, content, airStrlen(buff) ? "," : "", buff); airFree(buff); /* no NULL assignment, else compile warnings */ return 0; } int _nrrdContentSet_va(Nrrd *nout, const char *func, char *content, const char *format, ...) { static const char me[]="_nrrdContentSet_va"; va_list ap; va_start(ap, format); if (_nrrdContentSet_nva(nout, func, content, format, ap)) { biffAddf(NRRD, "%s:", me); free(content); return 1; } va_end(ap); /* free(content); */ return 0; } /* ******** nrrdContentSet ** ** Kind of like sprintf, but for the content string of the nrrd. ** ** Whether or not we write a new content for an old nrrd ("nin") with ** NULL content is decided here, according to ** nrrdStateAlwaysSetContent. ** ** Does the string allocation and some attempts at error detection. ** Does allow nout==nin, which requires some care. */ int nrrdContentSet_va(Nrrd *nout, const char *func, const Nrrd *nin, const char *format, ...) { static const char me[]="nrrdContentSet_va"; va_list ap; char *content; if (!(nout && func && nin && format)) { biffAddf(NRRD, "%s: got NULL pointer", me); return 1; } if (nrrdStateDisableContent) { /* we kill content always */ nout->content = (char *)airFree(nout->content); return 0; } if (!nin->content && !nrrdStateAlwaysSetContent) { /* there's no input content, and we're not supposed to invent any content, so after freeing nout's content we're done */ nout->content = (char *)airFree(nout->content); return 0; } /* we copy the input nrrd content first, before blowing away the output content, in case nout == nin */ content = _nrrdContentGet(nin); va_start(ap, format); if (_nrrdContentSet_nva(nout, func, content, format, ap)) { biffAddf(NRRD, "%s:", me); va_end(ap); free(content); return 1; } va_end(ap); free(content); return 0; } /* ******** nrrdDescribe ** ** writes verbose description of nrrd to given file */ void nrrdDescribe(FILE *file, const Nrrd *nrrd) { unsigned int ai; char stmp[AIR_STRLEN_SMALL]; if (file && nrrd) { fprintf(file, "Nrrd at 0x%p:\n", AIR_CVOIDP(nrrd)); fprintf(file, "Data at 0x%p is %s elements of type %s.\n", nrrd->data, airSprintSize_t(stmp, nrrdElementNumber(nrrd)), airEnumStr(nrrdType, nrrd->type)); if (nrrdTypeBlock == nrrd->type) { fprintf(file, "The blocks have size %s\n", airSprintSize_t(stmp, nrrd->blockSize)); } if (airStrlen(nrrd->content)) { fprintf(file, "Content = \"%s\"\n", nrrd->content); } fprintf(file, "%d-dimensional array, with axes:\n", nrrd->dim); for (ai=0; aidim; ai++) { if (airStrlen(nrrd->axis[ai].label)) { fprintf(file, "%d: (\"%s\") ", ai, nrrd->axis[ai].label); } else { fprintf(file, "%d: ", ai); } fprintf(file, "%s-centered, size=%s, ", airEnumStr(nrrdCenter, nrrd->axis[ai].center), airSprintSize_t(stmp, nrrd->axis[ai].size)); airSinglePrintf(file, NULL, "spacing=%lg, \n", nrrd->axis[ai].spacing); airSinglePrintf(file, NULL, "thickness=%lg, \n", nrrd->axis[ai].thickness); airSinglePrintf(file, NULL, " axis(Min,Max) = (%lg,", nrrd->axis[ai].min); airSinglePrintf(file, NULL, "%lg)\n", nrrd->axis[ai].max); if (airStrlen(nrrd->axis[ai].units)) { fprintf(file, "units=%s, \n", nrrd->axis[ai].units); } } /* airSinglePrintf(file, NULL, "The min, max values are %lg", nrrd->min); airSinglePrintf(file, NULL, ", %lg\n", nrrd->max); */ airSinglePrintf(file, NULL, "The old min, old max values are %lg", nrrd->oldMin); airSinglePrintf(file, NULL, ", %lg\n", nrrd->oldMax); /* fprintf(file, "hasNonExist = %d\n", nrrd->hasNonExist); */ if (nrrd->cmtArr->len) { fprintf(file, "Comments:\n"); for (ai=0; aicmtArr->len; ai++) { fprintf(file, "%s\n", nrrd->cmt[ai]); } } fprintf(file, "\n"); } } int nrrdSpaceVecExists(unsigned int sdim, double vec[NRRD_SPACE_DIM_MAX]) { int exists; unsigned int ii; exists = AIR_EXISTS(vec[0]); for (ii=1; iispace || !airEnumValCheck(nrrdSpace, nrrd->space) )) { biffMaybeAddf(useBiff, NRRD, "%s: space %d invalid", me, nrrd->space); return 1; } if (!( nrrd->spaceDim <= NRRD_SPACE_DIM_MAX )) { biffMaybeAddf(useBiff, NRRD, "%s: space dimension %d is outside " "valid range [0,NRRD_SPACE_DIM_MAX] = [0,%d]", me, nrrd->dim, NRRD_SPACE_DIM_MAX); return 1; } if (nrrd->spaceDim) { if (nrrd->space) { if (nrrdSpaceDimension(nrrd->space) != nrrd->spaceDim) { biffMaybeAddf(useBiff, NRRD, "%s: space %s has dimension %d but spaceDim is %d", me, airEnumStr(nrrdSpace, nrrd->space), nrrdSpaceDimension(nrrd->space), nrrd->spaceDim); return 1; } } /* check that all coeffs of spaceOrigin have consistent existance */ exists = AIR_EXISTS(nrrd->spaceOrigin[0]); for (ii=0; iispaceDim; ii++) { if (exists ^ AIR_EXISTS(nrrd->spaceOrigin[ii])) { biffMaybeAddf(useBiff, NRRD, "%s: existance of space origin coefficients must " "be consistent (val[0] not like val[%d])", me, ii); return 1; } } /* check that all coeffs of measurementFrame have consistent existance */ exists = AIR_EXISTS(nrrd->measurementFrame[0][0]); for (dd=0; ddspaceDim; dd++) { for (ii=0; iispaceDim; ii++) { if (exists ^ AIR_EXISTS(nrrd->measurementFrame[dd][ii])) { biffMaybeAddf(useBiff, NRRD, "%s: existance of measurement frame coefficients " "must be consistent: [col][row] [%d][%d] not " "like [0][0])", me, dd, ii); return 1; } } } /* check on space directions */ for (dd=0; dddim; dd++) { exists = AIR_EXISTS(nrrd->axis[dd].spaceDirection[0]); for (ii=1; iispaceDim; ii++) { if (exists ^ AIR_EXISTS(nrrd->axis[dd].spaceDirection[ii])) { biffMaybeAddf(useBiff, NRRD, "%s: existance of space direction %d coefficients " "must be consistent (val[0] not like val[%d])", me, dd, ii); return 1; } } if (exists) { if (AIR_EXISTS(nrrd->axis[dd].min) || AIR_EXISTS(nrrd->axis[dd].max) || AIR_EXISTS(nrrd->axis[dd].spacing) || !!airStrlen(nrrd->axis[dd].units)) { biffMaybeAddf(useBiff, NRRD, "%s: axis[%d] has a direction vector, and so can't " "have min, max, spacing, or units set", me, dd); return 1; } } } } else { /* else there's not supposed to be anything in "space" */ if (nrrd->space) { biffMaybeAddf(useBiff, NRRD, "%s: space %s can't be set with spaceDim %d", me, airEnumStr(nrrdSpace, nrrd->space), nrrd->spaceDim); return 1; } /* -------- */ exists = AIR_FALSE; for (dd=0; ddspaceUnits[dd]); } if (exists) { biffMaybeAddf(useBiff, NRRD, "%s: spaceDim is 0, but space units is set", me); return 1; } /* -------- */ exists = AIR_FALSE; for (dd=0; ddspaceOrigin[dd]); } if (exists) { biffMaybeAddf(useBiff, NRRD, "%s: spaceDim is 0, but space origin is set", me); return 1; } /* -------- */ exists = AIR_FALSE; for (dd=0; ddaxis[ii].spaceDirection[dd]); } } if (exists) { biffMaybeAddf(useBiff, NRRD, "%s: spaceDim is 0, but space directions are set", me); return 1; } } return 0; } /* --------------------- per-field checks ---------------- ** ** Strictly speacking, these checks only apply to the nrrd itself, not ** to a potentially incomplete nrrd in the process of being read, so ** the NrrdIoState stuff is not an issue. This limits the utility of ** these to the field parsers for handling the more complex state ** involved in parsing some of the NRRD fields (like units). ** ** return 0 if it is valid, and 1 if there is an error */ static int _nrrdFieldCheck_noop(const Nrrd *nrrd, int useBiff) { AIR_UNUSED(nrrd); AIR_UNUSED(useBiff); return 0; } static int _nrrdFieldCheck_type(const Nrrd *nrrd, int useBiff) { static const char me[]="_nrrdFieldCheck_type"; if (airEnumValCheck(nrrdType, nrrd->type)) { biffMaybeAddf(useBiff, NRRD, "%s: type (%d) is not valid", me, nrrd->type); return 1; } return 0; } static int _nrrdFieldCheck_block_size(const Nrrd *nrrd, int useBiff) { static const char me[]="_nrrdFieldCheck_block_size"; char stmp[AIR_STRLEN_SMALL]; if (nrrdTypeBlock == nrrd->type && (!(0 < nrrd->blockSize)) ) { biffMaybeAddf(useBiff, NRRD, "%s: type is %s but nrrd->blockSize (%s) invalid", me, airEnumStr(nrrdType, nrrdTypeBlock), airSprintSize_t(stmp, nrrd->blockSize)); return 1; } if (nrrdTypeBlock != nrrd->type && (0 < nrrd->blockSize)) { biffMaybeAddf(useBiff, NRRD, "%s: type is %s (not block) but blockSize is %s", me, airEnumStr(nrrdType, nrrd->type), airSprintSize_t(stmp, nrrd->blockSize)); return 1; } return 0; } static int _nrrdFieldCheck_dimension(const Nrrd *nrrd, int useBiff) { static const char me[]="_nrrdFieldCheck_dimension"; if (!AIR_IN_CL(1, nrrd->dim, NRRD_DIM_MAX)) { biffMaybeAddf(useBiff, NRRD, "%s: dimension %u is outside valid range [1,%d]", me, nrrd->dim, NRRD_DIM_MAX); return 1; } return 0; } static int _nrrdFieldCheck_space(const Nrrd *nrrd, int useBiff) { static const char me[]="_nrrdFieldCheck_space"; if (_nrrdFieldCheckSpaceInfo(nrrd, useBiff)) { biffMaybeAddf(useBiff, NRRD, "%s: trouble", me); return 1; } return 0; } static int _nrrdFieldCheck_space_dimension(const Nrrd *nrrd, int useBiff) { static const char me[]="_nrrdFieldCheck_space_dimension"; if (_nrrdFieldCheckSpaceInfo(nrrd, useBiff)) { biffMaybeAddf(useBiff, NRRD, "%s: trouble", me); return 1; } return 0; } static int _nrrdFieldCheck_sizes(const Nrrd *nrrd, int useBiff) { static const char me[]="_nrrdFieldCheck_sizes"; size_t size[NRRD_DIM_MAX]; nrrdAxisInfoGet_nva(nrrd, nrrdAxisInfoSize, size); if (_nrrdSizeCheck(size, nrrd->dim, useBiff)) { biffMaybeAddf(useBiff, NRRD, "%s: trouble with array sizes", me); return 1; } return 0; } static int _nrrdFieldCheck_spacings(const Nrrd *nrrd, int useBiff) { static const char me[]="_nrrdFieldCheck_spacings"; double val[NRRD_DIM_MAX]; unsigned int ai; nrrdAxisInfoGet_nva(nrrd, nrrdAxisInfoSpacing, val); for (ai=0; aidim; ai++) { if (!( !airIsInf_d(val[ai]) && (airIsNaN(val[ai]) || (0 != val[ai])) )) { biffMaybeAddf(useBiff, NRRD, "%s: axis %d spacing (%g) invalid", me, ai, val[ai]); return 1; } } if (_nrrdFieldCheckSpaceInfo(nrrd, useBiff)) { biffMaybeAddf(useBiff, NRRD, "%s: trouble", me); return 1; } return 0; } static int _nrrdFieldCheck_thicknesses(const Nrrd *nrrd, int useBiff) { static const char me[]="_nrrdFieldCheck_thicknesses"; double val[NRRD_DIM_MAX]; unsigned int ai; nrrdAxisInfoGet_nva(nrrd, nrrdAxisInfoThickness, val); for (ai=0; aidim; ai++) { /* note that unlike spacing, we allow zero thickness, but it makes no sense to be negative */ if (!( !airIsInf_d(val[ai]) && (airIsNaN(val[ai]) || (0 <= val[ai])) )) { biffMaybeAddf(useBiff, NRRD, "%s: axis %d thickness (%g) invalid", me, ai, val[ai]); return 1; } } return 0; } static int _nrrdFieldCheck_axis_mins(const Nrrd *nrrd, int useBiff) { static const char me[]="_nrrdFieldCheck_axis_mins"; double val[NRRD_DIM_MAX]; unsigned int ai; int ret; nrrdAxisInfoGet_nva(nrrd, nrrdAxisInfoMin, val); for (ai=0; aidim; ai++) { if ((ret=airIsInf_d(val[ai]))) { biffMaybeAddf(useBiff, NRRD, "%s: axis %d min %sinf invalid", me, ai, 1==ret ? "+" : "-"); return 1; } } if (_nrrdFieldCheckSpaceInfo(nrrd, useBiff)) { biffMaybeAddf(useBiff, NRRD, "%s: trouble", me); return 1; } /* HEY: contemplate checking min != max, but what about stub axes ... */ return 0; } static int _nrrdFieldCheck_axis_maxs(const Nrrd *nrrd, int useBiff) { static const char me[]="_nrrdFieldCheck_axis_maxs"; double val[NRRD_DIM_MAX]; unsigned int ai; int ret; nrrdAxisInfoGet_nva(nrrd, nrrdAxisInfoMax, val); for (ai=0; aidim; ai++) { if ((ret=airIsInf_d(val[ai]))) { biffMaybeAddf(useBiff, NRRD, "%s: axis %d max %sinf invalid", me, ai, 1==ret ? "+" : "-"); return 1; } } if (_nrrdFieldCheckSpaceInfo(nrrd, useBiff)) { biffMaybeAddf(useBiff, NRRD, "%s: trouble", me); return 1; } /* HEY: contemplate checking min != max, but what about stub axes ... */ return 0; } static int _nrrdFieldCheck_space_directions(const Nrrd *nrrd, int useBiff) { static const char me[]="_nrrdFieldCheck_space_directions"; if (_nrrdFieldCheckSpaceInfo(nrrd, useBiff)) { biffMaybeAddf(useBiff, NRRD, "%s: space info problem", me); return 1; } return 0; } static int _nrrdFieldCheck_centers(const Nrrd *nrrd, int useBiff) { static const char me[]="_nrrdFieldCheck_centers"; unsigned int ai; int val[NRRD_DIM_MAX]; nrrdAxisInfoGet_nva(nrrd, nrrdAxisInfoCenter, val); for (ai=0; aidim; ai++) { if (!( nrrdCenterUnknown == val[ai] || !airEnumValCheck(nrrdCenter, val[ai]) )) { biffMaybeAddf(useBiff, NRRD, "%s: axis %d center %d invalid", me, ai, val[ai]); return 1; } } return 0; } static int _nrrdFieldCheck_kinds(const Nrrd *nrrd, int useBiff) { static const char me[]="_nrrdFieldCheck_kinds"; int val[NRRD_DIM_MAX]; unsigned int wantLen, ai; nrrdAxisInfoGet_nva(nrrd, nrrdAxisInfoKind, val); for (ai=0; aidim; ai++) { if (!( nrrdKindUnknown == val[ai] || !airEnumValCheck(nrrdKind, val[ai]) )) { biffMaybeAddf(useBiff, NRRD, "%s: axis %d kind %d invalid", me, ai, val[ai]); return 1; } wantLen = nrrdKindSize(val[ai]); if (wantLen && wantLen != nrrd->axis[ai].size) { char stmp[AIR_STRLEN_SMALL]; biffMaybeAddf(useBiff, NRRD, "%s: axis %d kind %s requires size %u, but have %s", me, ai, airEnumStr(nrrdKind, val[ai]), wantLen, airSprintSize_t(stmp, nrrd->axis[ai].size)); return 1; } } return 0; } static int _nrrdFieldCheck_labels(const Nrrd *nrrd, int useBiff) { /* char me[]="_nrrdFieldCheck_labels"; */ AIR_UNUSED(nrrd); AIR_UNUSED(useBiff); /* don't think there's anything to do here: the label strings are either NULL (which is okay) or non-NULL, but we have no restrictions on the validity of the strings */ return 0; } static int _nrrdFieldCheck_units(const Nrrd *nrrd, int useBiff) { static const char me[]="_nrrdFieldCheck_units"; /* as with labels- the strings themselves don't need checking themselves */ /* but per-axis units cannot be set for axes with space directions ... */ if (_nrrdFieldCheckSpaceInfo(nrrd, useBiff)) { biffMaybeAddf(useBiff, NRRD, "%s: space info problem", me); return 1; } return 0; } static int _nrrdFieldCheck_old_min(const Nrrd *nrrd, int useBiff) { static const char me[]="_nrrdFieldCheck_old_min"; int ret; if ((ret=airIsInf_d(nrrd->oldMin))) { biffMaybeAddf(useBiff, NRRD, "%s: old min %sinf invalid", me, 1==ret ? "+" : "-"); return 1; } /* oldMin == oldMax is perfectly valid */ return 0; } static int _nrrdFieldCheck_old_max(const Nrrd *nrrd, int useBiff) { static const char me[]="_nrrdFieldCheck_old_max"; int ret; if ((ret=airIsInf_d(nrrd->oldMax))) { biffMaybeAddf(useBiff, NRRD, "%s: old max %sinf invalid", me, 1==ret ? "+" : "-"); return 1; } /* oldMin == oldMax is perfectly valid */ return 0; } static int _nrrdFieldCheck_keyvalue(const Nrrd *nrrd, int useBiff) { /* char me[]="_nrrdFieldCheck_keyvalue"; */ AIR_UNUSED(nrrd); AIR_UNUSED(useBiff); /* nrrdKeyValueAdd() ensures that keys aren't repeated, not sure what other kind of checking can be done */ return 0; } static int _nrrdFieldCheck_space_units(const Nrrd *nrrd, int useBiff) { static const char me[]="_nrrdFieldCheck_space_units"; /* not sure if there's anything to specifically check for the space units themselves ... */ if (_nrrdFieldCheckSpaceInfo(nrrd, useBiff)) { biffMaybeAddf(useBiff, NRRD, "%s: space info problem", me); return 1; } return 0; } static int _nrrdFieldCheck_space_origin(const Nrrd *nrrd, int useBiff) { static const char me[]="_nrrdFieldCheck_space_origin"; /* pre-Fri Feb 11 04:25:36 EST 2005, I thought that the spaceOrigin must be known to describe the space/orientation stuff, but that's too restrictive, which is why below says AIR_FALSE instead of AIR_TRUE */ if (_nrrdFieldCheckSpaceInfo(nrrd, useBiff)) { biffMaybeAddf(useBiff, NRRD, "%s: space info problem", me); return 1; } return 0; } static int _nrrdFieldCheck_measurement_frame(const Nrrd *nrrd, int useBiff) { static const char me[]="_nrrdFieldCheck_measurement_frame"; if (_nrrdFieldCheckSpaceInfo(nrrd, useBiff)) { biffMaybeAddf(useBiff, NRRD, "%s: space info problem", me); return 1; } return 0; } int (*_nrrdFieldCheck[NRRD_FIELD_MAX+1])(const Nrrd *, int useBiff) = { _nrrdFieldCheck_noop, /* nonfield */ _nrrdFieldCheck_noop, /* comment */ _nrrdFieldCheck_noop, /* content */ _nrrdFieldCheck_noop, /* number */ _nrrdFieldCheck_type, _nrrdFieldCheck_block_size, _nrrdFieldCheck_dimension, _nrrdFieldCheck_space, _nrrdFieldCheck_space_dimension, _nrrdFieldCheck_sizes, _nrrdFieldCheck_spacings, _nrrdFieldCheck_thicknesses, _nrrdFieldCheck_axis_mins, _nrrdFieldCheck_axis_maxs, _nrrdFieldCheck_space_directions, _nrrdFieldCheck_centers, _nrrdFieldCheck_kinds, _nrrdFieldCheck_labels, _nrrdFieldCheck_units, _nrrdFieldCheck_noop, /* min */ _nrrdFieldCheck_noop, /* max */ _nrrdFieldCheck_old_min, _nrrdFieldCheck_old_max, _nrrdFieldCheck_noop, /* endian */ _nrrdFieldCheck_noop, /* encoding */ _nrrdFieldCheck_noop, /* line_skip */ _nrrdFieldCheck_noop, /* byte_skip */ _nrrdFieldCheck_keyvalue, _nrrdFieldCheck_noop, /* sample units */ _nrrdFieldCheck_space_units, _nrrdFieldCheck_space_origin, _nrrdFieldCheck_measurement_frame, _nrrdFieldCheck_noop, /* data_file */ }; int _nrrdCheck(const Nrrd *nrrd, int checkData, int useBiff) { static const char me[]="_nrrdCheck"; int fi; if (!nrrd) { biffMaybeAddf(useBiff, NRRD, "%s: got NULL pointer", me); return 1; } if (checkData) { if (!(nrrd->data)) { biffMaybeAddf(useBiff, NRRD, "%s: nrrd %p has NULL data pointer", me, AIR_CVOIDP(nrrd)); return 1; } } for (fi=nrrdField_unknown+1; fidim != n2->dim) { biffMaybeAddf(useBiff, NRRD, "%s: n1->dim (%u) != n2->dim (%u)", me, n1->dim, n2->dim); return 0; } for (ai=0; aidim; ai++) { if (n1->axis[ai].size != n2->axis[ai].size) { biffMaybeAddf(useBiff, NRRD, "%s: n1->axis[%d].size (%s) " "!= n2->axis[%d].size (%s)", me, ai, airSprintSize_t(stmp[0], n1->axis[ai].size), ai, airSprintSize_t(stmp[1], n2->axis[ai].size)); return 0; } } return 1; } /* ******** nrrdElementSize() ** ** So just how many bytes long is one element in this nrrd? This is ** needed (over the simple nrrdTypeSize[] array) because some nrrds ** may be of "block" type, and because it does bounds checking on ** nrrd->type. Returns 0 if given a bogus nrrd->type, or if the block ** size isn't greater than zero (in which case it sets nrrd->blockSize ** to 0, just out of spite). This function never returns a negative ** value; using (!nrrdElementSize(nrrd)) is a sufficient check for ** invalidity. ** ** Besides learning how many bytes long one element is, this function ** is useful as a way of detecting an invalid blocksize on a block nrrd. */ size_t nrrdElementSize (const Nrrd *nrrd) { if (!( nrrd && !airEnumValCheck(nrrdType, nrrd->type) )) { return 0; } if (nrrdTypeBlock != nrrd->type) { return nrrdTypeSize[nrrd->type]; } /* else its block type */ if (nrrd->blockSize > 0) { return nrrd->blockSize; } /* else we got an invalid block size */ /* nrrd->blockSize = 0; */ return 0; } /* ******** nrrdElementNumber() ** ** takes the place of old "nrrd->num": the number of elements in the ** nrrd, which is just the product of the axis sizes. A return of 0 ** means there's a problem. Negative numbers are never returned. ** ** does NOT use biff */ size_t nrrdElementNumber (const Nrrd *nrrd) { size_t num, size[NRRD_DIM_MAX]; unsigned int ai; if (!nrrd) { return 0; } /* else */ nrrdAxisInfoGet_nva(nrrd, nrrdAxisInfoSize, size); if (_nrrdSizeCheck(size, nrrd->dim, AIR_FALSE)) { /* the nrrd's size information is invalid, can't proceed */ return 0; } num = 1; for (ai=0; aidim; ai++) { /* negative numbers and overflow were caught by _nrrdSizeCheck() */ num *= size[ai]; } return num; } /* ** obviously, this requires that the per-axis size fields have been set */ void _nrrdSplitSizes(size_t *pieceSize, size_t *pieceNum, Nrrd *nrrd, unsigned int split) { unsigned int ai; size_t size[NRRD_DIM_MAX]; nrrdAxisInfoGet_nva(nrrd, nrrdAxisInfoSize, size); *pieceSize = 1; for (ai=0; aidim; ai++) { *pieceNum *= size[ai]; } return; } /* ******** nrrdHasNonExistSet() ** ** This function will always (assuming type is valid) set the value of ** nrrd->hasNonExist to either nrrdNonExistTrue or nrrdNonExistFalse, ** and it will return that value. For lack of a more sophisticated ** policy, blocks are currently always considered to be existent ** values (because nrrdTypeIsIntegral[nrrdTypeBlock] is currently true). ** This function will ALWAYS determine the correct answer and set the ** value of nrrd->hasNonExist: it ignores the value of ** nrrd->hasNonExist on the input nrrd. Exception: if nrrd is null or ** type is bogus, no action is taken and nrrdNonExistUnknown is ** returned. ** ** Because this will return either nrrdNonExistTrue or nrrdNonExistFalse, ** and because the C boolean value of these are true and false (respectively), ** it is possible (and encouraged) to use the return of this function ** as the expression of a conditional: ** ** if (nrrdHasNonExistSet(nrrd)) { ** ... handle existance of non-existent values ... ** } */ /* int nrrdHasNonExistSet(Nrrd *nrrd) { size_t I, N; float val; if (!( nrrd && !airEnumValCheck(nrrdType, nrrd->type) )) return nrrdNonExistUnknown; if (nrrdTypeIsIntegral[nrrd->type]) { nrrd->hasNonExist = nrrdNonExistFalse; } else { nrrd->hasNonExist = nrrdNonExistFalse; N = nrrdElementNumber(nrrd); for (I=0; Itype](nrrd->data, I); if (!AIR_EXISTS(val)) { nrrd->hasNonExist = nrrdNonExistTrue; break; } } } return nrrd->hasNonExist; } */ static int _nrrdCheckEnums(void) { static const char me[]="_nrrdCheckEnums"; char which[AIR_STRLEN_SMALL]; if (nrrdFormatTypeLast-1 != NRRD_FORMAT_TYPE_MAX) { strcpy(which, "nrrdFormat"); goto err; } if (nrrdTypeLast-1 != NRRD_TYPE_MAX) { strcpy(which, "nrrdType"); goto err; } if (nrrdEncodingTypeLast-1 != NRRD_ENCODING_TYPE_MAX) { strcpy(which, "nrrdEncodingType"); goto err; } if (nrrdCenterLast-1 != NRRD_CENTER_MAX) { strcpy(which, "nrrdCenter"); goto err; } if (nrrdAxisInfoLast-1 != NRRD_AXIS_INFO_MAX) { strcpy(which, "nrrdAxisInfo"); goto err; } /* can't really check on endian enum */ if (nrrdField_last-1 != NRRD_FIELD_MAX) { strcpy(which, "nrrdField"); goto err; } if (nrrdHasNonExistLast-1 != NRRD_HAS_NON_EXIST_MAX) { strcpy(which, "nrrdHasNonExist"); goto err; } /* no errors so far */ return 0; err: biffAddf(NRRD, "%s: Last vs. MAX incompatibility for %s enum", me, which); return 1; } /* ****** nrrdSanity ** ** makes sure that all the basic assumptions of nrrd hold for ** the architecture/etc which we're currently running on. ** ** returns 1 if all is okay, 0 if there is a problem */ int /*Teem: biff if (!ret) */ nrrdSanity(void) { static const char me[]="nrrdSanity"; int aret, type; size_t maxsize; airLLong tmpLLI; airULLong tmpULLI; static int _nrrdSanity = 0; if (_nrrdSanity) { /* we've been through this once before and things looked okay ... */ /* Is this thread-safe? I think so. If we assume that any two threads are going to compute the same value, isn't it the case that, at worse, both of them will go through all the tests and then set _nrrdSanity to the same thing? */ return 1; } aret = airSanity(); if (aret != airInsane_not) { biffAddf(NRRD, "%s: airSanity() failed: %s", me, airInsaneErr(aret)); return 0; } if (airEnumValCheck(nrrdEncodingType, nrrdDefaultWriteEncodingType)) { biffAddf(NRRD, "%s: nrrdDefaultWriteEncodingType (%d) not in valid " "range [%d,%d]", me, nrrdDefaultWriteEncodingType, nrrdEncodingTypeUnknown+1, nrrdEncodingTypeLast-1); return 0; } if (airEnumValCheck(nrrdCenter, nrrdDefaultCenter)) { biffAddf(NRRD, "%s: nrrdDefaultCenter (%d) not in valid range [%d,%d]", me, nrrdDefaultCenter, nrrdCenterUnknown+1, nrrdCenterLast-1); return 0; } if (!( nrrdTypeSize[nrrdTypeChar] == sizeof(char) && nrrdTypeSize[nrrdTypeUChar] == sizeof(unsigned char) && nrrdTypeSize[nrrdTypeShort] == sizeof(short) && nrrdTypeSize[nrrdTypeUShort] == sizeof(unsigned short) && nrrdTypeSize[nrrdTypeInt] == sizeof(int) && nrrdTypeSize[nrrdTypeUInt] == sizeof(unsigned int) && nrrdTypeSize[nrrdTypeLLong] == sizeof(airLLong) && nrrdTypeSize[nrrdTypeULLong] == sizeof(airULLong) && nrrdTypeSize[nrrdTypeFloat] == sizeof(float) && nrrdTypeSize[nrrdTypeDouble] == sizeof(double) )) { biffAddf(NRRD, "%s: sizeof() for nrrd types has problem: " "expected (%u,%u,%u,%u,%u,%u,%u,%u,%u,%u) " "but got (%u,%u,%u,%u,%u,%u,%u,%u,%u,%u)", me, AIR_CAST(unsigned int, nrrdTypeSize[nrrdTypeChar]), AIR_CAST(unsigned int, nrrdTypeSize[nrrdTypeUChar]), AIR_CAST(unsigned int, nrrdTypeSize[nrrdTypeShort]), AIR_CAST(unsigned int, nrrdTypeSize[nrrdTypeUShort]), AIR_CAST(unsigned int, nrrdTypeSize[nrrdTypeInt]), AIR_CAST(unsigned int, nrrdTypeSize[nrrdTypeUInt]), AIR_CAST(unsigned int, nrrdTypeSize[nrrdTypeLLong]), AIR_CAST(unsigned int, nrrdTypeSize[nrrdTypeULLong]), AIR_CAST(unsigned int, nrrdTypeSize[nrrdTypeFloat]), AIR_CAST(unsigned int, nrrdTypeSize[nrrdTypeDouble]), AIR_CAST(unsigned int, sizeof(char)), AIR_CAST(unsigned int, sizeof(unsigned char)), AIR_CAST(unsigned int, sizeof(short)), AIR_CAST(unsigned int, sizeof(unsigned short)), AIR_CAST(unsigned int, sizeof(int)), AIR_CAST(unsigned int, sizeof(unsigned int)), AIR_CAST(unsigned int, sizeof(airLLong)), AIR_CAST(unsigned int, sizeof(airULLong)), AIR_CAST(unsigned int, sizeof(float)), AIR_CAST(unsigned int, sizeof(double))); return 0; } /* check on NRRD_TYPE_SIZE_MAX */ maxsize = 0; for (type=nrrdTypeUnknown+1; type<=nrrdTypeLast-2; type++) { maxsize = AIR_MAX(maxsize, nrrdTypeSize[type]); } if (maxsize != NRRD_TYPE_SIZE_MAX) { biffAddf(NRRD, "%s: actual max type size is %u != %u == NRRD_TYPE_SIZE_MAX", me, AIR_CAST(unsigned int, maxsize), NRRD_TYPE_SIZE_MAX); return 0; } /* check on NRRD_TYPE_BIGGEST */ if (maxsize != sizeof(NRRD_TYPE_BIGGEST)) { biffAddf(NRRD, "%s: actual max type size is %u != " "%u == sizeof(NRRD_TYPE_BIGGEST)", me, AIR_CAST(unsigned int, maxsize), AIR_CAST(unsigned int, sizeof(NRRD_TYPE_BIGGEST))); return 0; } /* nrrd-defined min/max values for 64-bit integral types */ /* NOTE: because signed integral overflow is undefined in C, the tests for signed long long no longer use overflow (and an assumption of two's complement representation) to assess the correctness of NRRD_LLONG_MAX and NRRD_LLONG_MIN. We merely test that these values can be stored, which we do via indirect (perhaps needlessly so) means. (h/t Sean McBride for pointing this out) */ tmpLLI = _nrrdLLongMaxHelp(_nrrdLLongMaxHelp(_NRRD_LLONG_MAX_HELP)); if (!( tmpLLI > 0 && NRRD_LLONG_MAX == tmpLLI )) { biffAddf(NRRD, "%s: long long int can't hold NRRD_LLONG_MAX (" AIR_LLONG_FMT ")", me, NRRD_LLONG_MAX); return 0; } tmpLLI = _nrrdLLongMinHelp(_nrrdLLongMinHelp(_NRRD_LLONG_MIN_HELP)); if (!( tmpLLI < 0 && NRRD_LLONG_MIN == tmpLLI )) { biffAddf(NRRD, "%s: long long int can't hold NRRD_LLONG_MIN (" AIR_LLONG_FMT ")", me, NRRD_LLONG_MIN); return 0; } tmpULLI = _nrrdULLongMaxHelp(NRRD_ULLONG_MAX); if (tmpULLI != 0) { biffAddf(NRRD, "%s: unsigned long long int max (" AIR_ULLONG_FMT ") incorrect", me, NRRD_ULLONG_MAX); return 0; } if (_nrrdCheckEnums()) { biffAddf(NRRD, "%s: problem with enum definition", me); return 0; } if (!( NRRD_DIM_MAX >= 3 )) { biffAddf(NRRD, "%s: NRRD_DIM_MAX == %u seems awfully small, doesn't it?", me, NRRD_DIM_MAX); return 0; } if (!nrrdTypeIsIntegral[nrrdTypeBlock]) { biffAddf(NRRD, "%s: nrrdTypeInteger[nrrdTypeBlock] is not true, things " "could get wacky", me); return 0; } /* HEY: any other assumptions built into Teem? */ _nrrdSanity = 1; return 1; } cmtk-3.3.1/Utilities/NrrdIO/string.c000066400000000000000000000250101276303427400172440ustar00rootroot00000000000000/* NrrdIO: stand-alone code for basic nrrd functionality Copyright (C) 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. */ #include "NrrdIO.h" /* this has to default to false in order for airStrtok to be a functional substitute for strtok() */ int airStrtokQuoting = AIR_FALSE; /* ******** airStrdup() ** ** because they didn't put strdup() in ANSI. ** This will return NULL if given NULL. */ char * airStrdup(const char *s) { char *ret; if (!s) { ret = NULL; } else { ret = (char *)malloc(strlen(s)+1); if (ret) { strcpy(ret, s); } } return ret; } /* ******** airStrlen() ** ** just like strlen, but safe to call on NULL (for which return is 0) */ size_t airStrlen(const char *s) { size_t ret; if (!s) { ret = 0; } else { ret = strlen(s); } return ret; } /* ******** airStrtok() ** ** thread-safe strtok() replacement. Use just like strtok(), but on ** each call to parse a given string, pass as the last argument the ** address of a char*, to be used for saving state while the string is ** traversed. Like strtok(), this will alter the "s" array passed to ** it on the first call, and like strtok(), this returns pointers into ** this string (rather than allocating new strings for each token). */ char * airStrtok(char *s, const char *ct, char **last) { char *h, *e, *q; if (!(ct && last)) { /* can't do any work, bail */ return NULL; } h = s ? s : *last; if (!airStrlen(h)) return NULL; h += strspn(h, ct); if ('\"' == *h && airStrtokQuoting) { /* something is trying to be quoted, and, we'll respect that */ /* have to find the next un-escaped '\"' */ h++; q = h; while (*q && !('\"' == *q && '\\' != q[-1])) { q++; } if (*q) { /* we found an unescaped '\"' */ e = q; } else { /* give up; pretend we never tried to do this quoting stuff */ e = h + strcspn(h, ct); } } else { e = h + strcspn(h, ct); } if ('\0' == *e) { *last = e; } else { *e = '\0'; *last = e + 1; } return h; } /* ******** airStrntok() ** ** returns the number of tokens parsable by airStrtok(), but does ** NOT alter the given string */ unsigned int airStrntok(const char *_s, const char *ct) { char *s, *t, *l=NULL; unsigned int n = 0; if (_s && ct) { s = airStrdup(_s); t = airStrtok(s, ct, &l); while (t) { n++; t = airStrtok(NULL, ct, &l); } airFree(s); /* no NULL assignment to s, else compile warnings */ } return n; } char * airStrtrans(char *s, char from, char to) { size_t i, l; if (s) { l = strlen(s); for (i=0; i 0)) { return NULL; } srcLen = airStrlen(src); if (1 == dstSize || !srcLen) { dst[0] = '\0'; return dst; } /* else dstSize > 1 AND src is a non-empy string */ copyLen = AIR_MIN(dstSize-1, srcLen); for (ii=0; ii= strlen(suff))) return 0; if (!strncmp(s + strlen(s) - strlen(suff), suff, strlen(suff))) return 1; else return 0; } /* ******** airUnescape() ** ** unescapes \\ and \n in place in a given string. ** Always returns the same pointer as given */ char * airUnescape(char *s) { size_t i, j, len; int found=0; len = airStrlen(s); if (!len) return s; for (i=1, j=0; i= 3 /* need room for a character and a Windows newline */ && line && file)) { return 0; } /* c is always set at least once, but not so for any char in line[] */ for (ii=0; (ii <= size-2 /* room for line[ii] and \0 after that */ && EOF != (cc=getc(file)) /* didn't hit EOF trying to read char */ && cc != '\n' /* char isn't newline */ && cc != '\r'); /* char isn't carriage return */ ++ii) { line[ii] = AIR_CAST(char, cc); } if (EOF == cc) { /* for-loop terminated because we hit EOF */ line[0] = '\0'; return 0; } else if ('\r' == cc || '\n' == cc) { /* for-loop terminated because we hit '\n' or '\r' */ /* if it was '\r', see if next character is '\n' */ if ('\r' == cc) { cc = getc(file); if (EOF != cc && '\n' != cc) { /* oops, we got something, and it was not a '\n'; put it back */ ungetc(cc, file); } } line[ii] = '\0'; return ii+1; } else { /* for-loop terminated because we got to end of buffer (ii == size-1) */ cc = getc(file); /* but see if we were about to get '\r', "\r\n", or '\n' */ if ('\r' == cc) { int dd; dd = getc(file); if (EOF != dd && '\n' != dd) { /* oops, put it back */ ungetc(dd, file); } line[ii] = '\0'; return ii+1; } else if ('\n' == cc) { line[ii] = '\0'; return ii+1; } else { /* weren't about to get a line termination, we really did run out of buffer */ if (EOF != cc) { ungetc(cc, file); /* we're allowed one ungetc on ANY stream */ } line[size-1] = '\0'; return size+1; } } } cmtk-3.3.1/Utilities/NrrdIO/subset.c000066400000000000000000000327121276303427400172520ustar00rootroot00000000000000/* NrrdIO: stand-alone code for basic nrrd functionality Copyright (C) 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. */ #include "NrrdIO.h" #include "privateNrrd.h" /* ******** nrrdSlice() ** ** slices a nrrd along a given axis, at a given position. ** ** This is a newer version of the procedure, which is simpler, faster, ** and requires less memory overhead than the first one. It is based ** on the observation that any slice is a periodic square-wave pattern ** in the original data (viewed as a one- dimensional array). The ** characteristics of that periodic pattern are how far from the ** beginning it starts (offset), the length of the "on" part (length), ** the period (period), and the number of periods (numper). */ int nrrdSlice(Nrrd *nout, const Nrrd *cnin, unsigned int saxi, size_t pos) { static const char me[]="nrrdSlice", func[]="slice"; size_t I, rowLen, /* length of segment */ colStep, /* distance between start of each segment */ colLen, /* number of periods */ szOut[NRRD_DIM_MAX]; unsigned int ai, outdim; int map[NRRD_DIM_MAX]; const char *src; char *dest, stmp[2][AIR_STRLEN_SMALL]; airArray *mop; Nrrd *nin; if (!(cnin && nout)) { biffAddf(NRRD, "%s: got NULL pointer", me); return 1; } if (nout == cnin) { biffAddf(NRRD, "%s: nout==nin disallowed", me); return 1; } if (1 == cnin->dim) { if (0 != saxi) { biffAddf(NRRD, "%s: slice axis must be 0, not %u, for 1-D array", me, saxi); return 1; } } else { if (!( saxi < cnin->dim )) { biffAddf(NRRD, "%s: slice axis %d out of bounds (0 to %d)", me, saxi, cnin->dim-1); return 1; } } if (!( pos < cnin->axis[saxi].size )) { biffAddf(NRRD, "%s: position %s out of bounds (0 to %s)", me, airSprintSize_t(stmp[0], pos), airSprintSize_t(stmp[1], cnin->axis[saxi].size-1)); return 1; } /* this shouldn't actually be necessary .. */ if (!nrrdElementSize(cnin)) { biffAddf(NRRD, "%s: nrrd reports zero element size!", me); return 1; } /* HEY: copy and paste from measure.c/nrrdProject */ mop = airMopNew(); if (1 == cnin->dim) { /* There are more efficient ways of dealing with this case; this way is easy to implement because it leaves most of the established code below only superficially changed; uniformly replacing nin with (nin ? nin : cnin), even if pointlessly so; this expression that can't be assigned to a new variable because of the difference in const. */ nin = nrrdNew(); airMopAdd(mop, nin, (airMopper)nrrdNuke, airMopAlways); if (nrrdAxesInsert(nin, cnin, 1)) { biffAddf(NRRD, "%s: trouble inserting axis on 1-D array", me); airMopError(mop); return 1; } } else { nin = NULL; } /* set up control variables */ rowLen = colLen = 1; for (ai=0; ai<(nin ? nin : cnin)->dim; ai++) { if (ai < saxi) { rowLen *= (nin ? nin : cnin)->axis[ai].size; } else if (ai > saxi) { colLen *= (nin ? nin : cnin)->axis[ai].size; } } rowLen *= nrrdElementSize(nin ? nin : cnin); colStep = rowLen*(nin ? nin : cnin)->axis[saxi].size; outdim = (nin ? nin : cnin)->dim-1; for (ai=0; ai= saxi); szOut[ai] = (nin ? nin : cnin)->axis[map[ai]].size; } nout->blockSize = (nin ? nin : cnin)->blockSize; if (nrrdMaybeAlloc_nva(nout, (nin ? nin : cnin)->type, outdim, szOut)) { biffAddf(NRRD, "%s: failed to create slice", me); airMopError(mop); return 1; } /* the skinny */ src = AIR_CAST(const char *, (nin ? nin : cnin)->data); dest = AIR_CAST(char *, nout->data); src += rowLen*pos; for (I=0; Iaxis[saxi].spaceDirection[0])) { nrrdSpaceVecScaleAdd2(nout->spaceOrigin, 1.0, (nin ? nin : cnin)->spaceOrigin, AIR_CAST(double, pos), (nin ? nin : cnin)->axis[saxi].spaceDirection); } else { nrrdSpaceVecCopy(nout->spaceOrigin, (nin ? nin : cnin)->spaceOrigin); } airMopOkay(mop); return 0; } /* ******** nrrdCrop() ** ** select some sub-volume inside a given nrrd, producing an output ** nrrd with the same dimensions, but with equal or smaller sizes ** along each axis. */ int nrrdCrop(Nrrd *nout, const Nrrd *nin, size_t *min, size_t *max) { static const char me[]="nrrdCrop", func[] = "crop"; char buff1[NRRD_DIM_MAX*30], buff2[AIR_STRLEN_SMALL]; unsigned int ai; size_t I, lineSize, /* #bytes in one scanline to be copied */ typeSize, /* size of data type */ cIn[NRRD_DIM_MAX], /* coords for line start, in input */ cOut[NRRD_DIM_MAX], /* coords for line start, in output */ szIn[NRRD_DIM_MAX], szOut[NRRD_DIM_MAX], idxIn, idxOut, /* linear indices for input and output */ numLines; /* number of scanlines in output nrrd */ char *dataIn, *dataOut, stmp[3][AIR_STRLEN_SMALL]; /* errors */ if (!(nout && nin && min && max)) { biffAddf(NRRD, "%s: got NULL pointer", me); return 1; } if (nout == nin) { biffAddf(NRRD, "%s: nout==nin disallowed", me); return 1; } for (ai=0; aidim; ai++) { if (!(min[ai] <= max[ai])) { biffAddf(NRRD, "%s: axis %d min (%s) not <= max (%s)", me, ai, airSprintSize_t(stmp[0], min[ai]), airSprintSize_t(stmp[1], max[ai])); return 1; } if (!( min[ai] < nin->axis[ai].size && max[ai] < nin->axis[ai].size )) { biffAddf(NRRD, "%s: axis %d min (%s) or max (%s) out of bounds [0,%s]", me, ai, airSprintSize_t(stmp[0], min[ai]), airSprintSize_t(stmp[1], max[ai]), airSprintSize_t(stmp[2], nin->axis[ai].size-1)); return 1; } } /* this shouldn't actually be necessary .. */ if (!nrrdElementSize(nin)) { biffAddf(NRRD, "%s: nrrd reports zero element size!", me); return 1; } /* allocate */ nrrdAxisInfoGet_nva(nin, nrrdAxisInfoSize, szIn); numLines = 1; for (ai=0; aidim; ai++) { szOut[ai] = max[ai] - min[ai] + 1; if (ai) { numLines *= szOut[ai]; } } nout->blockSize = nin->blockSize; if (nrrdMaybeAlloc_nva(nout, nin->type, nin->dim, szOut)) { biffAddf(NRRD, "%s:", me); return 1; } lineSize = szOut[0]*nrrdElementSize(nin); /* the skinny */ typeSize = nrrdElementSize(nin); dataIn = (char *)nin->data; dataOut = (char *)nout->data; memset(cOut, 0, NRRD_DIM_MAX*sizeof(*cOut)); /* printf("!%s: nin->dim = %d\n", me, nin->dim); printf("!%s: min = %d %d %d\n", me, min[0], min[1], min[2]); printf("!%s: szIn = %d %d %d\n", me, szIn[0], szIn[1], szIn[2]); printf("!%s: szOut = %d %d %d\n", me, szOut[0], szOut[1], szOut[2]); printf("!%s: lineSize = %d\n", me, lineSize); printf("!%s: typeSize = %d\n", me, typeSize); printf("!%s: numLines = %d\n", me, (int)numLines); */ for (I=0; Idim; ai++) { cIn[ai] = cOut[ai] + min[ai]; } NRRD_INDEX_GEN(idxOut, cOut, szOut, nin->dim); NRRD_INDEX_GEN(idxIn, cIn, szIn, nin->dim); /* printf("!%s: %5d: cOut=(%3d,%3d,%3d) --> idxOut = %5d\n", me, (int)I, cOut[0], cOut[1], cOut[2], (int)idxOut); printf("!%s: %5d: cIn=(%3d,%3d,%3d) --> idxIn = %5d\n", me, (int)I, cIn[0], cIn[1], cIn[2], (int)idxIn); */ memcpy(dataOut + idxOut*typeSize, dataIn + idxIn*typeSize, lineSize); /* the lowest coordinate in cOut[] will stay zero, since we are copying one (1-D) scanline at a time */ NRRD_COORD_INCR(cOut, szOut, nin->dim, 1); } if (nrrdAxisInfoCopy(nout, nin, NULL, (NRRD_AXIS_INFO_SIZE_BIT | NRRD_AXIS_INFO_MIN_BIT | NRRD_AXIS_INFO_MAX_BIT ))) { biffAddf(NRRD, "%s:", me); return 1; } for (ai=0; aidim; ai++) { nrrdAxisInfoPosRange(&(nout->axis[ai].min), &(nout->axis[ai].max), nin, ai, AIR_CAST(double, min[ai]), AIR_CAST(double, max[ai])); /* do the safe thing first */ nout->axis[ai].kind = _nrrdKindAltered(nin->axis[ai].kind, AIR_FALSE); /* try cleverness */ if (!nrrdStateKindNoop) { if (nout->axis[ai].size == nin->axis[ai].size) { /* we can safely copy kind; the samples didn't change */ nout->axis[ai].kind = nin->axis[ai].kind; } else if (nrrdKind4Color == nin->axis[ai].kind && 3 == szOut[ai]) { nout->axis[ai].kind = nrrdKind3Color; } else if (nrrdKind4Vector == nin->axis[ai].kind && 3 == szOut[ai]) { nout->axis[ai].kind = nrrdKind3Vector; } else if ((nrrdKind4Vector == nin->axis[ai].kind || nrrdKind3Vector == nin->axis[ai].kind) && 2 == szOut[ai]) { nout->axis[ai].kind = nrrdKind2Vector; } else if (nrrdKindRGBAColor == nin->axis[ai].kind && 0 == min[ai] && 2 == max[ai]) { nout->axis[ai].kind = nrrdKindRGBColor; } else if (nrrdKind2DMaskedSymMatrix == nin->axis[ai].kind && 1 == min[ai] && max[ai] == szIn[ai]-1) { nout->axis[ai].kind = nrrdKind2DSymMatrix; } else if (nrrdKind2DMaskedMatrix == nin->axis[ai].kind && 1 == min[ai] && max[ai] == szIn[ai]-1) { nout->axis[ai].kind = nrrdKind2DMatrix; } else if (nrrdKind3DMaskedSymMatrix == nin->axis[ai].kind && 1 == min[ai] && max[ai] == szIn[ai]-1) { nout->axis[ai].kind = nrrdKind3DSymMatrix; } else if (nrrdKind3DMaskedMatrix == nin->axis[ai].kind && 1 == min[ai] && max[ai] == szIn[ai]-1) { nout->axis[ai].kind = nrrdKind3DMatrix; } } } strcpy(buff1, ""); for (ai=0; aidim; ai++) { sprintf(buff2, "%s[%s,%s]", (ai ? "x" : ""), airSprintSize_t(stmp[0], min[ai]), airSprintSize_t(stmp[1], max[ai])); strcat(buff1, buff2); } if (nrrdContentSet_va(nout, func, nin, "%s", buff1)) { biffAddf(NRRD, "%s:", me); return 1; } if (nrrdBasicInfoCopy(nout, nin, NRRD_BASIC_INFO_DATA_BIT | NRRD_BASIC_INFO_TYPE_BIT | NRRD_BASIC_INFO_BLOCKSIZE_BIT | NRRD_BASIC_INFO_DIMENSION_BIT | NRRD_BASIC_INFO_SPACEORIGIN_BIT | NRRD_BASIC_INFO_CONTENT_BIT | NRRD_BASIC_INFO_COMMENTS_BIT | (nrrdStateKeyValuePairsPropagate ? 0 : NRRD_BASIC_INFO_KEYVALUEPAIRS_BIT))) { biffAddf(NRRD, "%s:", me); return 1; } /* copy origin, then shift it along the spatial axes */ nrrdSpaceVecCopy(nout->spaceOrigin, nin->spaceOrigin); for (ai=0; aidim; ai++) { if (AIR_EXISTS(nin->axis[ai].spaceDirection[0])) { nrrdSpaceVecScaleAdd2(nout->spaceOrigin, 1.0, nout->spaceOrigin, AIR_CAST(double, min[ai]), nin->axis[ai].spaceDirection); } } return 0; } cmtk-3.3.1/Utilities/NrrdIO/teem32bit.h000066400000000000000000000030001276303427400175340ustar00rootroot00000000000000/* NrrdIO: stand-alone code for basic nrrd functionality Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. */ /* ** the end result of this is that the source file which includes ** this can be sure that TEEM_32BIT is set, and can be sure that ** it is set to either 0 or 1 */ #ifndef TEEM_32BIT # error TEEM_32BIT not defined, see architecture-specific .mk file or check compilation options #elif TEEM_32BIT == 1 # /* okay, its 1 */ #elif TEEM_32BIT == 0 # /* okay, its 0 */ #else # error TEEM_32BIT not set to 0 or 1, see architecture-specific .mk file or check compilation options #endif cmtk-3.3.1/Utilities/NrrdIO/teemDio.h000066400000000000000000000026531276303427400173410ustar00rootroot00000000000000/* NrrdIO: stand-alone code for basic nrrd functionality Copyright (C) 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. */ /* ** the end result of this is that the source file which includes ** this can be sure that TEEM_DIO is set, and can be sure that ** it is set to either 0 or 1 */ #ifndef TEEM_DIO # error TEEM_DIO not defined #elif TEEM_DIO == 1 # /* okay, its 1 */ #elif TEEM_DIO == 0 # /* okay, its 0 */ #else # error TEEM_DIO not set to 1 or 0 #endif cmtk-3.3.1/Utilities/NrrdIO/teemEndian.h000066400000000000000000000031121276303427400200130ustar00rootroot00000000000000/* NrrdIO: stand-alone code for basic nrrd functionality Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. */ /* ** the end result of this is that the source file which includes ** this can be sure that TEEM_ENDIAN is set, and can be sure that ** it is set to either 1234 or 4321 */ #ifndef TEEM_ENDIAN # error TEEM_ENDIAN not defined, see architecture-specific .mk file or check compilation options #elif TEEM_ENDIAN == 1234 # /* okay, its little endian */ #elif TEEM_ENDIAN == 4321 # /* okay, its big endian */ #else # error TEEM_ENDIAN not set to 1234 (little endian) or 4321 (big endian), see architecture-specific .mk file or check compilation options #endif cmtk-3.3.1/Utilities/NrrdIO/teemPng.h000066400000000000000000000027141276303427400173500ustar00rootroot00000000000000/* NrrdIO: stand-alone code for basic nrrd functionality Copyright (C) 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. */ /* ** the end result of this is that the source file which includes ** this can be sure that TEEM_ZLIB is set, so that the required ** compression library is always built in when PNG support is requested */ #ifdef TEEM_PNG # ifndef TEEM_ZLIB # error TEEM_PNG set, but TEEM_ZLIB not set # endif #endif #if TEEM_PNG && TEEM_VTK_MANGLE #include "vtk_png_mangle.h" #endif cmtk-3.3.1/Utilities/NrrdIO/teemQnanhibit.h000066400000000000000000000027171276303427400205440ustar00rootroot00000000000000/* NrrdIO: stand-alone code for basic nrrd functionality Copyright (C) 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. */ /* ** the end result of this is that the source file which includes ** this can be sure that TEEM_QNANHIBIT is set, and can be sure that ** it is set to either 0 or 1 */ #ifndef TEEM_QNANHIBIT # error TEEM_QNANHIBIT not defined #elif TEEM_QNANHIBIT == 1 # /* okay, its 1 */ #elif TEEM_QNANHIBIT == 0 # /* okay, its 0 */ #else # error TEEM_QNANHIBIT not set to 0 or 1 #endif cmtk-3.3.1/Utilities/NrrdIO/write.c000066400000000000000000001014211276303427400170710ustar00rootroot00000000000000/* NrrdIO: stand-alone code for basic nrrd functionality Copyright (C) 2012, 2011, 2010, 2009 University of Chicago Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. */ #include "NrrdIO.h" #include "privateNrrd.h" /* #include #include */ int nrrdIoStateSet(NrrdIoState *nio, int parm, int value) { static const char me[]="nrrdIoStateSet"; if (!nio) { biffAddf(NRRD, "%s: got NULL pointer", me); return 1; } if (!( AIR_IN_OP(nrrdIoStateUnknown, parm, nrrdIoStateLast) )) { biffAddf(NRRD, "%s: identifier %d not in valid range [%d,%d]", me, parm, nrrdIoStateUnknown+1, nrrdIoStateLast-1); return 1; } switch (parm) { case nrrdIoStateDetachedHeader: nio->detachedHeader = !!value; break; case nrrdIoStateBareText: nio->bareText = !!value; break; case nrrdIoStateCharsPerLine: if (value < 40) { biffAddf(NRRD, "%s: %d charsPerLine is awfully small", me, value); return 1; } /* cast won't lose info because "value" must be positive */ nio->charsPerLine = AIR_CAST(unsigned int, value); break; case nrrdIoStateValsPerLine: if (value < 4) { biffAddf(NRRD, "%s: %d valsPerLine is awfully small", me, value); return 1; } /* cast won't lose info because "value" must be positive */ nio->valsPerLine = AIR_CAST(unsigned int, value); break; case nrrdIoStateSkipData: nio->skipData = !!value; break; case nrrdIoStateKeepNrrdDataFileOpen: nio->keepNrrdDataFileOpen = !!value; break; case nrrdIoStateZlibLevel: if (!( AIR_IN_CL(-1, value, 9) )) { biffAddf(NRRD, "%s: zlibLevel %d invalid", me, value); return 1; } nio->zlibLevel = value; break; case nrrdIoStateZlibStrategy: if (!( AIR_IN_OP(nrrdZlibStrategyUnknown, value, nrrdZlibStrategyLast) )) { biffAddf(NRRD, "%s: zlibStrategy %d invalid", me, value); return 1; } nio->zlibStrategy = value; break; case nrrdIoStateBzip2BlockSize: if (!( AIR_IN_CL(-1, value, 9) )) { biffAddf(NRRD, "%s: bzip2BlockSize %d invalid", me, value); return 1; } nio->bzip2BlockSize = value; break; default: fprintf(stderr, "!%s: PANIC: didn't recognize parm %d\n", me, parm); return 1; } return 0; } int nrrdIoStateEncodingSet(NrrdIoState *nio, const NrrdEncoding *encoding) { static const char me[]="nrrdIoStateEncodingSet"; if (!( nio && encoding )) { if (nio) { nio->encoding = nrrdEncodingUnknown; } biffAddf(NRRD, "%s: got NULL pointer", me); return 1; } if (!encoding->available()) { nio->encoding = nrrdEncodingUnknown; biffAddf(NRRD, "%s: %s encoding isn't actually available", me, encoding->name); return 1; } nio->encoding = encoding; return 0; } int nrrdIoStateFormatSet(NrrdIoState *nio, const NrrdFormat *format) { static const char me[]="nrrdIoStateFormatSet"; if (!( nio && format )) { if (nio) { nio->format = nrrdFormatUnknown; } biffAddf(NRRD, "%s: got NULL pointer", me); return 1; } if (!format->available()) { nio->format = nrrdFormatUnknown; biffAddf(NRRD, "%s: %s format isn't actually available", me, format->name); return 1; } nio->format = format; return 0; } /* ** no biff */ int nrrdIoStateGet(NrrdIoState *nio, int parm) { static const char me[]="nrrdIoStateGet"; int value; if (!nio) { /* got NULL pointer */ return -1; } if (!( AIR_IN_OP(nrrdIoStateUnknown, parm, nrrdIoStateLast) )) { /* got bogus parameter identifier */ return -1; } switch (parm) { case nrrdIoStateDetachedHeader: value = !!nio->detachedHeader; break; case nrrdIoStateBareText: value = !!nio->bareText; break; case nrrdIoStateCharsPerLine: /* HEY: this cast is a bad because nio->charsPerLine is unsigned */ value = AIR_CAST(int, nio->charsPerLine); break; case nrrdIoStateValsPerLine: /* HEY: this cast is a bad because nio->valsPerLine is unsigned */ value = AIR_CAST(int, nio->valsPerLine); break; case nrrdIoStateSkipData: value = !!nio->skipData; break; case nrrdIoStateKeepNrrdDataFileOpen: value = !!nio->keepNrrdDataFileOpen; break; case nrrdIoStateZlibLevel: value = nio->zlibLevel; break; case nrrdIoStateZlibStrategy: value = nio->zlibStrategy; break; case nrrdIoStateBzip2BlockSize: value = nio->bzip2BlockSize; break; default: fprintf(stderr, "!%s: PANIC: didn't recognize parm %d\n", me, parm); return -1; } return value; } /* ** no biff */ const NrrdEncoding * nrrdIoStateEncodingGet(NrrdIoState *nio) { return nio ? nio->encoding : nrrdEncodingUnknown; } /* ** no biff */ const NrrdFormat * nrrdIoStateFormatGet(NrrdIoState *nio) { return nio ? nio->format : nrrdFormatUnknown; } void _nrrdStrcatSpaceVector(char *str, unsigned int spaceDim, const double val[NRRD_SPACE_DIM_MAX]) { char buff[AIR_STRLEN_MED]; /* bad Gordon */ unsigned int dd; if (AIR_EXISTS(val[0])) { strcat(str, "("); for (dd=0; dddim, NRRD_DIM_MAX) && nio && nio->encoding && AIR_IN_OP(nrrdField_unknown, field, nrrdField_last) )) { return 0; } ret = 0; switch (field) { case nrrdField_comment: /* comments and key/value pairs are always handled differently (by being printed explicity), so they are never "interesting" */ break; case nrrdField_content: ret = !!(airStrlen(nrrd->content)); break; case nrrdField_number: /* "number" is entirely redundant with "sizes", which is a required field. Absolutely nothing is lost in eliding "number" from the header, so "number" is NEVER interesting. Should this judgement later be found in error, this is the one place where the policy change can be implemented */ break; case nrrdField_type: /* this is vital */ ret = 1; break; case nrrdField_block_size: ret = (nrrdTypeBlock == nrrd->type); break; case nrrdField_dimension: /* this is vital */ ret = 1; break; case nrrdField_space: /* its interesting if its known */ ret = (nrrdSpaceUnknown != nrrd->space); break; case nrrdField_space_dimension: /* its interesting if its non-zero and if space is not known */ ret = (nrrd->spaceDim > 0 && nrrdSpaceUnknown == nrrd->space); break; case nrrdField_sizes: /* this is vital */ ret = 1; break; case nrrdField_spacings: for (ai=0; aidim; ai++) { ret |= AIR_EXISTS(nrrd->axis[ai].spacing); } break; case nrrdField_thicknesses: for (ai=0; aidim; ai++) { ret |= AIR_EXISTS(nrrd->axis[ai].thickness); } break; case nrrdField_axis_mins: for (ai=0; aidim; ai++) { ret |= AIR_EXISTS(nrrd->axis[ai].min); } break; case nrrdField_axis_maxs: for (ai=0; aidim; ai++) { ret |= AIR_EXISTS(nrrd->axis[ai].max); } break; case nrrdField_space_directions: ret = nrrd->spaceDim > 0; break; case nrrdField_centers: for (ai=0; aidim; ai++) { ret |= (nrrdCenterUnknown != nrrd->axis[ai].center); } break; case nrrdField_kinds: for (ai=0; aidim; ai++) { ret |= (nrrdKindUnknown != nrrd->axis[ai].kind); } break; case nrrdField_labels: for (ai=0; aidim; ai++) { ret |= !!(airStrlen(nrrd->axis[ai].label)); } break; case nrrdField_units: for (ai=0; aidim; ai++) { ret |= !!(airStrlen(nrrd->axis[ai].units)); } break; case nrrdField_min: case nrrdField_max: /* these no longer exist in the Nrrd struct; we never write them */ ret = AIR_FALSE; break; case nrrdField_old_min: ret = AIR_EXISTS(nrrd->oldMin); break; case nrrdField_old_max: ret = AIR_EXISTS(nrrd->oldMax); break; case nrrdField_endian: ret = nio->encoding->endianMatters && 1 < nrrdElementSize(nrrd); break; case nrrdField_encoding: /* this is vital */ ret = 1; break; case nrrdField_line_skip: ret = nio->lineSkip > 0; break; case nrrdField_byte_skip: ret = nio->byteSkip != 0; break; case nrrdField_keyvalue: /* comments and key/value pairs are always handled differently (by being printed explicity), so they are never "interesting" */ break; case nrrdField_sample_units: ret = !!airStrlen(nrrd->sampleUnits); break; case nrrdField_space_units: for (ai=0; aispaceDim; ai++) { ret |= !!(airStrlen(nrrd->spaceUnits[ai])); } break; case nrrdField_space_origin: /* we're trusting other validity checks to ensure that all the coeffs exist or not, together */ ret = (nrrd->spaceDim > 0 && AIR_EXISTS(nrrd->spaceOrigin[0])); break; case nrrdField_measurement_frame: /* we're trusting other validity checks to ensure that all the coeffs exist or not, together */ ret = (nrrd->spaceDim > 0 && AIR_EXISTS(nrrd->measurementFrame[0][0])); break; case nrrdField_data_file: /* detached header was either requested or is required */ ret = (nio->detachedHeader || nio->dataFNFormat || nio->dataFNArr->len > 1); break; } return ret; } /* ** _nrrdSprintFieldInfo ** ** this prints ": " into *strP (after allocating it for ** big enough, usually with a stupidly big margin of error), in a form ** suitable to be written to NRRD or other image headers. This will always ** print something (for valid inputs), even stupid s like ** "(unknown endian)". It is up to the caller to decide which fields ** are worth writing, via _nrrdFieldInteresting(). ** ** NOTE: some of these fields make sense in non-NRRD files (e.g. all ** the per-axis information), but many only make sense in NRRD files. ** This is just one example of NRRD-format-specific stuff that is not ** in formatNRRD.c */ void _nrrdSprintFieldInfo(char **strP, const char *prefix, const Nrrd *nrrd, NrrdIoState *nio, int field) { static const char me[]="_nrrdSprintFieldInfo"; char buff[AIR_STRLEN_MED], *fnb, stmp[AIR_STRLEN_SMALL], *strtmp=NULL; double colvec[NRRD_SPACE_DIM_MAX]; const char *fs; unsigned int ii, dd, uintStrlen = 11, size_tStrlen = 33, doubleStrlen = 513; size_t fslen, fdlen, maxl; int endi; if (!( strP && prefix && nrrd && AIR_IN_CL(1, nrrd->dim, NRRD_DIM_MAX) && AIR_IN_OP(nrrdField_unknown, field, nrrdField_last) )) { return; } if (!_nrrdFieldInteresting(nrrd, nio, field)) { *strP = airStrdup(""); } fs = airEnumStr(nrrdField, field); fslen = strlen(prefix) + strlen(fs) + strlen(": ") + 1; switch (field) { case nrrdField_comment: case nrrdField_keyvalue: fprintf(stderr, "%s: CONFUSION: why are you calling me on \"%s\"?\n", me, airEnumStr(nrrdField, nrrdField_comment)); *strP = airStrdup(""); break; case nrrdField_content: strtmp = airOneLinify(airStrdup(nrrd->content)); *strP = AIR_CALLOC(fslen + strlen(strtmp), char); sprintf(*strP, "%s%s: %s", prefix, fs, strtmp); airFree(strtmp); strtmp = NULL; break; case nrrdField_number: *strP = AIR_CALLOC(fslen + size_tStrlen, char); sprintf(*strP, "%s%s: %s", prefix, fs, airSprintSize_t(stmp, nrrdElementNumber(nrrd))); break; case nrrdField_type: *strP = AIR_CALLOC(fslen + strlen(airEnumStr(nrrdType, nrrd->type)), char); sprintf(*strP, "%s%s: %s", prefix, fs, airEnumStr(nrrdType, nrrd->type)); break; case nrrdField_block_size: *strP = AIR_CALLOC(fslen + size_tStrlen, char); sprintf(*strP, "%s%s: %s", prefix, fs, airSprintSize_t(stmp, nrrd->blockSize)); break; case nrrdField_dimension: *strP = AIR_CALLOC(fslen + uintStrlen, char); sprintf(*strP, "%s%s: %d", prefix, fs, nrrd->dim); break; case nrrdField_space: *strP = AIR_CALLOC(fslen + strlen(airEnumStr(nrrdSpace, nrrd->space)), char); sprintf(*strP, "%s%s: %s", prefix, fs, airEnumStr(nrrdSpace, nrrd->space)); break; case nrrdField_space_dimension: *strP = AIR_CALLOC(fslen + uintStrlen, char); sprintf(*strP, "%s%s: %d", prefix, fs, nrrd->spaceDim); break; /* ---- begin per-axis fields ---- */ case nrrdField_sizes: *strP = AIR_CALLOC(fslen + nrrd->dim*(size_tStrlen + 1), char); sprintf(*strP, "%s%s:", prefix, fs); for (ii=0; iidim; ii++) { sprintf(buff, " %s", airSprintSize_t(stmp, nrrd->axis[ii].size)); strcat(*strP, buff); } break; case nrrdField_spacings: *strP = AIR_CALLOC(fslen + nrrd->dim*(doubleStrlen + 1), char); sprintf(*strP, "%s%s:", prefix, fs); for (ii=0; iidim; ii++) { airSinglePrintf(NULL, buff, " %.17g", nrrd->axis[ii].spacing); strcat(*strP, buff); } break; case nrrdField_thicknesses: *strP = AIR_CALLOC(fslen + nrrd->dim*(doubleStrlen + 1), char); sprintf(*strP, "%s%s:", prefix, fs); for (ii=0; iidim; ii++) { airSinglePrintf(NULL, buff, " %.17g", nrrd->axis[ii].thickness); strcat(*strP, buff); } break; case nrrdField_axis_mins: *strP = AIR_CALLOC(fslen + nrrd->dim*(doubleStrlen + 1), char); sprintf(*strP, "%s%s:", prefix, fs); for (ii=0; iidim; ii++) { airSinglePrintf(NULL, buff, " %.17g", nrrd->axis[ii].min); strcat(*strP, buff); } break; case nrrdField_axis_maxs: *strP = AIR_CALLOC(fslen + nrrd->dim*(doubleStrlen + 1), char); sprintf(*strP, "%s%s:", prefix, fs); for (ii=0; iidim; ii++) { airSinglePrintf(NULL, buff, " %.17g", nrrd->axis[ii].max); strcat(*strP, buff); } break; case nrrdField_space_directions: *strP = AIR_CALLOC(fslen + nrrd->dim*nrrd->spaceDim*(doubleStrlen + strlen("(,) ")), char); sprintf(*strP, "%s%s: ", prefix, fs); for (ii=0; iidim; ii++) { _nrrdStrcatSpaceVector(*strP, nrrd->spaceDim, nrrd->axis[ii].spaceDirection); if (ii < nrrd->dim-1) { strcat(*strP, " "); } } break; case nrrdField_centers: fdlen = 0; for (ii=0; iidim; ii++) { fdlen += 1 + airStrlen(nrrd->axis[ii].center ? airEnumStr(nrrdCenter, nrrd->axis[ii].center) : NRRD_UNKNOWN); } *strP = AIR_CALLOC(fslen + fdlen, char); sprintf(*strP, "%s%s:", prefix, fs); for (ii=0; iidim; ii++) { sprintf(buff, " %s", (nrrd->axis[ii].center ? airEnumStr(nrrdCenter, nrrd->axis[ii].center) : NRRD_UNKNOWN)); strcat(*strP, buff); } break; case nrrdField_kinds: fdlen = 0; for (ii=0; iidim; ii++) { fdlen += 1 + airStrlen(nrrd->axis[ii].kind ? airEnumStr(nrrdKind, nrrd->axis[ii].kind) : NRRD_UNKNOWN); } *strP = AIR_CALLOC(fslen + fdlen, char); sprintf(*strP, "%s%s:", prefix, fs); for (ii=0; iidim; ii++) { sprintf(buff, " %s", (nrrd->axis[ii].kind ? airEnumStr(nrrdKind, nrrd->axis[ii].kind) : NRRD_UNKNOWN)); strcat(*strP, buff); } break; case nrrdField_labels: case nrrdField_units: #define LABEL_OR_UNITS (nrrdField_labels == field \ ? nrrd->axis[ii].label \ : nrrd->axis[ii].units) fdlen = 0; for (ii=0; iidim; ii++) { /* The "2*" is because at worst every character needs escaping. The "+ 3" for the |" "| between each part */ fdlen += 2*airStrlen(LABEL_OR_UNITS) + 3; } fdlen += 1; /* for '\0' */ *strP = AIR_CALLOC(fslen + fdlen, char); sprintf(*strP, "%s%s:", prefix, fs); for (ii=0; iidim; ii++) { strcat(*strP, " \""); if (airStrlen(nrrd->axis[ii].label)) { _nrrdWriteEscaped(NULL, *strP, LABEL_OR_UNITS, "\"", _NRRD_WHITESPACE_NOTAB); } strcat(*strP, "\""); } #undef LABEL_OR_UNITS break; /* ---- end per-axis fields ---- */ case nrrdField_min: case nrrdField_max: /* we're basically a no-op, now that these fields became meaningless */ *strP = AIR_CALLOC(fslen + doubleStrlen, char); sprintf(*strP, "%s%s: 0.0", prefix, fs); strcat(*strP, buff); break; case nrrdField_old_min: *strP = AIR_CALLOC(fslen + doubleStrlen, char); sprintf(*strP, "%s%s: ", prefix, fs); airSinglePrintf(NULL, buff, "%.17g", nrrd->oldMin); strcat(*strP, buff); break; case nrrdField_old_max: *strP = AIR_CALLOC(fslen + doubleStrlen, char); sprintf(*strP, "%s%s: ", prefix, fs); airSinglePrintf(NULL, buff, "%.17g", nrrd->oldMax); strcat(*strP, buff); break; case nrrdField_endian: if (airEndianUnknown != nio->endian) { /* we know a specific endianness because either it was recorded as part of "unu make -h", or it was set (and data was possibly altered) as part of "unu save" */ endi = nio->endian; } else { /* we record our current architecture's endian because we're going to writing out data */ endi = airMyEndian(); } *strP = AIR_CALLOC(fslen + strlen(airEnumStr(airEndian, endi)), char); sprintf(*strP, "%s%s: %s", prefix, fs, airEnumStr(airEndian, endi)); break; case nrrdField_encoding: *strP = AIR_CALLOC(fslen + strlen(nio->encoding->name), char); sprintf(*strP, "%s%s: %s", prefix, fs, nio->encoding->name); break; case nrrdField_line_skip: *strP = AIR_CALLOC(fslen + uintStrlen, char); sprintf(*strP, "%s%s: %d", prefix, fs, nio->lineSkip); break; case nrrdField_byte_skip: *strP = AIR_CALLOC(fslen + uintStrlen, char); sprintf(*strP, "%s%s: %ld", prefix, fs, nio->byteSkip); break; case nrrdField_sample_units: strtmp = airOneLinify(airStrdup(nrrd->sampleUnits)); *strP = AIR_CALLOC(fslen + strlen(strtmp), char); sprintf(*strP, "%s%s: \"%s\"", prefix, fs, strtmp); airFree(strtmp); strtmp = NULL; break; case nrrdField_space_units: fdlen = 0; for (ii=0; iispaceDim; ii++) { /* The "2*" is because at worst every character needs escaping. See note in formatNRRD.c about how even though its not part of the format, we have worst-case scenario of having to escape a space units which is nothing but ". The "+ 3" for the |" "| between each part */ fdlen += 2*airStrlen(nrrd->spaceUnits[ii]) + 3; } fdlen += 1; /* for '\0' */ *strP = AIR_CALLOC(fslen + fdlen, char); sprintf(*strP, "%s%s:", prefix, fs); for (ii=0; iispaceDim; ii++) { strcat(*strP, " \""); if (airStrlen(nrrd->spaceUnits[ii])) { _nrrdWriteEscaped(NULL, *strP, nrrd->spaceUnits[ii], "\"", _NRRD_WHITESPACE_NOTAB); } strcat(*strP, "\""); } break; case nrrdField_space_origin: *strP = AIR_CALLOC(fslen + nrrd->spaceDim*(doubleStrlen + strlen("(,) ")), char); sprintf(*strP, "%s%s: ", prefix, fs); _nrrdStrcatSpaceVector(*strP, nrrd->spaceDim, nrrd->spaceOrigin); break; case nrrdField_measurement_frame: *strP = AIR_CALLOC(fslen + (nrrd->spaceDim* nrrd->spaceDim*(doubleStrlen + strlen("(,) "))), char); sprintf(*strP, "%s%s: ", prefix, fs); for (dd=0; ddspaceDim; dd++) { for (ii=0; iispaceDim; ii++) { colvec[ii] = nrrd->measurementFrame[dd][ii]; } _nrrdStrcatSpaceVector(*strP, nrrd->spaceDim, colvec); if (dd < nrrd->spaceDim-1) { strcat(*strP, " "); } } break; case nrrdField_data_file: /* NOTE: this comes last (nrrdField_data_file is the highest-valued member of the nrrdField* enum) because the "LIST" form of the data file specification requires that the following lines be the filenames */ /* error checking elsewhere: assumes there is data file info */ if (nio->dataFNFormat) { *strP = AIR_CALLOC(fslen + strlen(nio->dataFNFormat) + 4*uintStrlen, char); if (nio->dataFileDim == nrrd->dim-1) { sprintf(*strP, "%s%s: %s %d %d %d", prefix, fs, nio->dataFNFormat, nio->dataFNMin, nio->dataFNMax, nio->dataFNStep); } else { sprintf(*strP, "%s%s: %s %d %d %d %u", prefix, fs, nio->dataFNFormat, nio->dataFNMin, nio->dataFNMax, nio->dataFNStep, nio->dataFileDim); } } else if (nio->dataFNArr->len > 1) { maxl = 0; for (ii=0; iidataFNArr->len; ii++) { maxl = AIR_MAX(maxl, strlen(nio->dataFN[ii])); } *strP = AIR_CALLOC(fslen + strlen(NRRD_LIST_FLAG) + uintStrlen + nio->dataFNArr->len * (maxl + 1), char); fnb = AIR_CALLOC(fslen + strlen(NRRD_LIST_FLAG) + uintStrlen + maxl + 1, char); if (nio->dataFileDim == nrrd->dim-1) { sprintf(*strP, "%s%s: LIST\n", prefix, fs); } else { sprintf(*strP, "%s%s: LIST %u\n", prefix, fs, nio->dataFileDim); } for (ii=0; iidataFNArr->len; ii++) { sprintf(fnb, "%s%s", nio->dataFN[ii], iidataFNArr->len-1 ? "\n" : ""); strcat(*strP, fnb); } free(fnb); } else { /* there is some ambiguity between a "LIST" of length one, and a single explicit data filename, but that's harmless */ *strP = AIR_CALLOC(fslen + strlen("./") + strlen(nio->dataFN[0]) + 1, char); sprintf(*strP, "%s%s: %s%s", prefix, fs, /* this is a favor to older readers that can deal with this NRRD file because its being saved in a NRRD0003 (or below) version, so we don't want to confuse them by not having the old explicit header-relative flag */ (_nrrdFormatNRRD_whichVersion(nrrd, nio) < 4 ? "./" : ""), nio->dataFN[0]); } break; default: fprintf(stderr, "%s: CONFUSION: field %d unrecognized\n", me, field); break; } return; } /* ** _nrrdFprintFieldInfo ** ** convenience wrapper around _nrrdSprintFieldInfo, for writing into ** a file. Same caveats here: use _nrrdFieldInteresting */ void _nrrdFprintFieldInfo(FILE *file, const char *prefix, const Nrrd *nrrd, NrrdIoState *nio, int field) { char *line=NULL; _nrrdSprintFieldInfo(&line, prefix, nrrd, nio, field); if (line) { fprintf(file, "%s\n", line); free(line); } return; } int _nrrdEncodingMaybeSet(NrrdIoState *nio) { static const char me[]="_nrrdEncodingMaybeSet"; if (!nio) { biffAddf(NRRD, "%s: got NULL pointer", me); return 1; } if (!nio->encoding) { biffAddf(NRRD, "%s: invalid (NULL) encoding", me); return 1; } if (nrrdEncodingUnknown == nio->encoding) { nio->encoding = nrrdEncodingArray[nrrdDefaultWriteEncodingType]; } if (!nio->encoding->available()) { biffAddf(NRRD, "%s: %s encoding not available in this Teem build", me, nio->encoding->name); return 1; } return 0; } /* ** we can assume (via action of caller nrrdSave) that nio->encoding ** has been set ** ** we must set nio->format to something useful/non-trivial */ int _nrrdFormatMaybeGuess(const Nrrd *nrrd, NrrdIoState *nio, const char *filename) { static const char me[]="_nrrdFormatMaybeGuess"; char mesg[AIR_STRLEN_MED]; int fi, guessed, available, fits; if (!nio->format) { biffAddf(NRRD, "%s: got invalid (NULL) format", me); return 1; } if (nrrdFormatUnknown == nio->format) { for (fi = nrrdFormatTypeUnknown+1; fi < nrrdFormatTypeLast; fi++) { if (nrrdFormatArray[fi]->nameLooksLike(filename)) { nio->format = nrrdFormatArray[fi]; break; } } if (nrrdFormatUnknown == nio->format) { /* no nameLooksLike() returned non-zero, punt */ nio->format = nrrdFormatNRRD; } guessed = AIR_TRUE; } else { guessed = AIR_FALSE; } available = nio->format->available(); fits = nio->format->fitsInto(nrrd, nio->encoding, AIR_FALSE); /* !available ==> !fits, by the nature of fitsInto() */ if (!( available && fits )) { sprintf(mesg, "can not use %s format: %s", nio->format->name, (!available ? "not available in this Teem build" : "array doesn\'t fit")); if (guessed) { if (1 <= nrrdStateVerboseIO) { fprintf(stderr, "(%s: %s --> saving to NRRD format)\n", me, mesg); } nio->format = nrrdFormatNRRD; } else { /* problem: this was the format someone explicitly requested */ biffAddf(NRRD, "%s: %s", me, mesg); return 1; } } return 0; } int _nrrdFormatMaybeSet(NrrdIoState *nio) { static const char me[]="_nrrdFormatMaybeSet"; if (!nio->format) { biffAddf(NRRD, "%s: invalid (NULL) format", me); return 1; } if (nrrdFormatUnknown == nio->format) { nio->format = nrrdFormatNRRD; } if (!nio->format->available()) { biffAddf(NRRD, "%s: %s format not available in this Teem build", me, nio->format->name); return 1; } return 0; } /* ** _nrrdWrite ** ** Write a nrrd to given file or string (allocated by nrrd), using the ** format and and encoding indicated in nio. Cleverness should be ** isolated and collected here: by the time nio->format->write() is ** called, all writing parameters must be given explicitly, and their ** appropriateness is explicitly tested */ int _nrrdWrite(FILE *file, char **stringP, const Nrrd *nrrd, NrrdIoState *_nio) { static const char me[]="_nrrdWrite"; NrrdIoState *nio; airArray *mop; if (!((file || stringP) && nrrd)) { biffAddf(NRRD, "%s: got NULL pointer", me); return 1; } if (file && stringP) { biffAddf(NRRD, "%s: can't write to both file and string", me); return 1; } if (nrrdCheck(nrrd)) { biffAddf(NRRD, "%s:", me); return 1; } mop = airMopNew(); if (_nio) { nio = _nio; } else { nio = nrrdIoStateNew(); if (!nio) { biffAddf(NRRD, "%s: couldn't alloc local NrrdIoState", me); airMopError(mop); return 1; } airMopAdd(mop, nio, (airMopper)nrrdIoStateNix, airMopAlways); } if (_nrrdEncodingMaybeSet(nio) || _nrrdFormatMaybeSet(nio)) { biffAddf(NRRD, "%s: ", me); airMopError(mop); return 1; } if (nio->byteSkip || nio->lineSkip) { /* NOTE: unu make bypasses this by calling nrrdFormatNRRD->write() directly */ biffAddf(NRRD, "%s: can't generate line or byte skips on data write", me); airMopError(mop); return 1; } if (stringP) { if (nrrdFormatNRRD != nio->format) { biffAddf(NRRD, "%s: sorry, can only write %s files to strings (not %s)", me, nrrdFormatNRRD->name, nio->format->name); airMopError(mop); return 1; } /* we do this in two passes; first see how much room is needed for the header, then allocate, then write the header */ nio->learningHeaderStrlen = AIR_TRUE; if (nio->format->write(NULL, nrrd, nio)) { biffAddf(NRRD, "%s:", me); airMopError(mop); return 1; } *stringP = AIR_MALLOC(nio->headerStrlen + 1, char); if (!*stringP) { biffAddf(NRRD, "%s: couldn't allocate header string (%u len )", me, nio->headerStrlen); airMopError(mop); return 1; } nio->learningHeaderStrlen = AIR_FALSE; nio->headerStringWrite = *stringP; if (nio->format->write(NULL, nrrd, nio)) { biffAddf(NRRD, "%s:", me); airMopError(mop); return 1; } } else { /* call the writer appropriate for the format */ if (nio->format->write(file, nrrd, nio)) { biffAddf(NRRD, "%s:", me); airMopError(mop); return 1; } } airMopOkay(mop); return 0; } /* ******** nrrdWrite ** ** wrapper around _nrrdWrite; writes to a FILE* */ int nrrdWrite(FILE *file, const Nrrd *nrrd, NrrdIoState *_nio) { static const char me[]="nrrdWrite"; if (_nrrdWrite(file, NULL, nrrd, _nio)) { biffAddf(NRRD, "%s: trouble", me); return 1; } return 0; } /* ******** nrrdStringWrite ** ** wrapper around _nrrdWrite; *allocates* and writes to a string */ int nrrdStringWrite(char **stringP, const Nrrd *nrrd, NrrdIoState *_nio) { static const char me[]="nrrdStringWrite"; if (_nrrdWrite(NULL, stringP, nrrd, _nio)) { biffAddf(NRRD, "%s: trouble", me); return 1; } return 0; } /* ******** nrrdSave ** ** save a given nrrd to a given filename, with cleverness to guess ** format if not specified by the caller ** ** currently, for NRRD format files, we play the detached header game ** whenever the filename ends in NRRD_EXT_NHDR, and when we play this ** game, the data file is ALWAYS header relative. */ int nrrdSave(const char *filename, const Nrrd *nrrd, NrrdIoState *nio) { static const char me[]="nrrdSave"; FILE *file; airArray *mop; if (!(nrrd && filename)) { biffAddf(NRRD, "%s: got NULL pointer", me); return 1; } mop = airMopNew(); if (!nio) { nio = nrrdIoStateNew(); if (!nio) { biffAddf(NRRD, "%s: couldn't alloc local NrrdIoState", me); return 1; } airMopAdd(mop, nio, (airMopper)nrrdIoStateNix, airMopAlways); } if (_nrrdEncodingMaybeSet(nio) || _nrrdFormatMaybeGuess(nrrd, nio, filename)) { biffAddf(NRRD, "%s: ", me); airMopError(mop); return 1; } if (nrrdFormatNRRD == nio->format && airEndsWith(filename, NRRD_EXT_NHDR)) { nio->detachedHeader = AIR_TRUE; _nrrdSplitName(&(nio->path), &(nio->base), filename); /* nix the ".nhdr" suffix */ nio->base[strlen(nio->base) - strlen(NRRD_EXT_NHDR)] = 0; /* nrrdFormatNRRD->write will do the rest */ } else { nio->detachedHeader = AIR_FALSE; } if (!( file = airFopen(filename, stdout, "wb") )) { biffAddf(NRRD, "%s: couldn't fopen(\"%s\",\"wb\"): %s", me, filename, strerror(errno)); airMopError(mop); return 1; } airMopAdd(mop, file, (airMopper)airFclose, airMopAlways); if (nrrdWrite(file, nrrd, nio)) { biffAddf(NRRD, "%s:", me); airMopError(mop); return 1; } airMopOkay(mop); return 0; } int nrrdSaveMulti(const char *fnameFormat, const Nrrd *const *nin, unsigned int ninLen, unsigned int numStart, NrrdIoState *nio) { static const char me[]="nrrdSaveMulti"; char *fname; airArray *mop; unsigned int nii; if (!( fnameFormat && nin )) { biffAddf(NRRD, "%s: got NULL pointer", me); return 1; } if (!( _nrrdContainsPercentThisAndMore(fnameFormat, 'u') )) { biffAddf(NRRD, "%s: given format \"%s\" doesn't seem to " "have the \"%%u\" conversion specification to sprintf " "an unsigned int\n", me, fnameFormat); return 1; } mop = airMopNew(); /* should be big enough for the number replacing the format sequence */ fname = AIR_CALLOC(strlen(fnameFormat) + 128, char); if (!(fname)) { biffAddf(NRRD, "%s: couldn't allocate local fname buffer", me); airMopError(mop); return 1; } airMopAdd(mop, fname, airFree, airMopAlways); for (nii=0; nii" was always followed by a newline (STR #76) - The mxml.pc.in file was broken (STR #79) - The mxmldoc program now handles "typedef enum name {} name" correctly (STR #72) CHANGES IN Mini-XML 2.5 - The mxmldoc program now makes greater use of CSS and supports a --css option to embed an alternate stylesheet. - The mxmldoc program now supports --header and --footer options to insert documentation content before and after the generated content. - The mxmldoc program now supports a --framed option to generate framed HTML output. - The mxmldoc program now creates a table of contents including any headings in the --intro file when generating HTML output. - The man pages and man page output from mxmldoc did not use "\-" for dashes (STR #68) - The debug version of the Mini-XML DLL could not be built (STR #65) - Processing instructions and directives did not work when not at the top level of a document (STR #67) - Spaces around the "=" in attributes were not supported (STR #67) CHANGES IN Mini-XML 2.4 - Fixed shared library build problems on HP-UX and Mac OS X. - The mxmldoc program did not output argument descriptions for functions properly. - All global settings (custom, error, and entity callbacks and the wrap margin) are now managed separately for each thread. - Added mxmlElementDeleteAttr() function (STR #59) - mxmlElementSetAttrf() did not work (STR #57) - mxmlLoad*() incorrectly treated declarations as parent elements (STR #56) - mxmlLoad*() incorrectly allowed attributes without values (STR #47) - Fixed Visual C++ build problems (STR #49) - mxmlLoad*() did not return NULL when an element contained an error (STR #46) - Added support for the apos character entity (STR #54) - Fixed whitespace detection with Unicode characters (STR #48) - mxmlWalkNext() and mxmlWalkPrev() did not work correctly when called with a node with no children as the top node (STR #53) CHANGES IN Mini-XML 2.3 - Added two exceptions to the LGPL to support static linking of applications against Mini-XML - The mxmldoc utility can now generate man pages, too. - Added a mxmlNewXML() function - Added a mxmlElementSetAttrf() function (STR #43) - Added snprintf() emulation function for test program (STR #32) - Added the _CRT_SECURE_NO_DEPRECATE definition when building on VC++ 2005 (STR #36) - mxmlLoad*() did not detect missing > characters in elements (STR #41) - mxmlLoad*() did not detect missing close tags at the end of an XML document (STR #45) - Added user_data and ref_count members to mxml_node_t structure - Added mxmlReleaseNode() and mxmlRetainNode() APIs for reference-counted nodes - Added mxmlSetWrapMargin() to control the wrapping of XML output - Added conditional check for EINTR error code for certain Windows compilers that do not define it (STR #33) - The mxmldoc program now generates correct HTML 4.0 output - previously it generated invalid XHTML - The mxmldoc program now supports "@deprecated@, "@private@", and "@since version@" comments - Fixed function and enumeration type bugs in mxmldoc. - Fixed the XML schema for mxmldoc - The mxmldoc program now supports --intro, --section, and --title options - The mxmlLoad*() functions could leak a node on an error (STR #27) - The mxml_vsnprintf() function could get in an infinite loop on a buffer overflow (STR #25) - Added new mxmlNewCDATA() and mxmlSetCDATA() functions to create and set CDATA nodes, which are really just special element nodes - Added new MXML_IGNORE type and MXML_IGNORE_CB callback to ignore non-element nodes, e.g. whitespace - mxmlLoad*() crashed when reporting an error in some invalid XML (STR #23) CHANGES IN Mini-XML 2.2.2 - mxmlLoad*() did not treat custom data as opaque, so whitespace characters would be lost. CHANGES IN Mini-XML 2.2.1 - mxmlLoadFd(), mxmlLoadFile(), and mxmlLoadString() now correctly return NULL on error (STR #21) - mxmlNewInteger(), mxmlNewOpaque(), mxmlNewReal(), mxmlNewText(), and mxmlNewTextf() incorrectly required a parent node (STR #22) - Fixed an XML output bug in mxmldoc. - The "make install" target now uses the install command to set the proper permissions on UNIX/Linux/OSX. - Fixed a MingW/Cygwin compilation problem (STR #18) CHANGES IN Mini-XML 2.2 - Added shared library support (STR #17) - mxmlLoad*() now returns an error when an XML stream contains illegal control characters (STR #10) - mxmlLoad*() now returns an error when an element contains two attributes with the same name in conformance with the XML spec (STR #16) - Added support for CDATA (STR #14, STR #15) - Updated comment and processing instruction handling - no entity support per XML specification. - Added checking for invalid comment termination ("--->" is not allowed) CHANGES IN Mini-XML 2.1 - Added support for custom data nodes (STR #6) - Now treat UTF-8 sequences which are longer than necessary as an error (STR #4) - Fixed entity number support (STR #8) - Fixed mxmlLoadString() bug with UTF-8 (STR #7) - Fixed entity lookup bug (STR #5) - Added mxmlLoadFd() and mxmlSaveFd() functions. - Fixed multi-word UTF-16 handling. CHANGES IN Mini-XML 2.0 - New programmers manual. - Added Visual C++ project files for Microsoft Windows users. - Added optimizations to mxmldoc, mxmlSaveFile(), and mxmlIndexNew() (STR #2) - mxmlEntityAddCallback() now returns an integer status (STR #2) - Added UTF-16 support (input only; all output is UTF-8) - Added index functions to build a searchable index of XML nodes. - Added character entity callback interface to support additional character entities beyond those defined in the XHTML specification. - Added support for XHTML character entities. - The mxmldoc utility now produces XML output which conforms to an updated XML schema, described in the file "doc/mxmldoc.xsd". - Changed the whitespace callback interface to return strings instead of a single character, allowing for greater control over the formatting of XML files written using Mini-XML. THIS CHANGE WILL REQUIRE CHANGES TO YOUR 1.x CODE IF YOU USE WHITESPACE CALLBACKS. - The mxmldoc utility is now capable of documenting C++ classes, functions, and structures, and correctly handles C++ comments. - Added new modular tests for mxmldoc. - Updated the mxmldoc output to be more compatible with embedding in manuals produced with HTMLDOC. - The makefile incorrectly included a "/" separator between the destination path and install path. This caused problems when building and installing with MingW. CHANGES IN Mini-XML 1.3 - Fixes for mxmldoc. - Added support for reading standard HTML entity names. - mxmlLoadString/File() did not decode character entities in element names, attribute names, or attribute values. - mxmlLoadString/File() would crash when loading non- conformant XML data under an existing parent (top) node. - Fixed several bugs in the mxmldoc utility. - Added new error callback function to catch a variety of errors and log them to someplace other than stderr. - The mxmlElementSetAttr() function now allows for NULL attribute values. - The load and save functions now properly handle quoted element and attribute name strings properly, e.g. for !DOCTYPE declarations. CHANGES IN Mini-XML 1.2 - Added new "set" methods to set the value of a node. - Added new formatted text methods mxmlNewTextf() and mxmlSetTextf() to create/set a text node value using printf-style formats. - Added new standard callbacks for use with the mxmlLoad functions. - Updated the HTML documentation to include examples of the walk and load function output. - Added --with/without-ansi configure option to control the strdup() function check. - Added --with/without-snprintf configure option to control the snprintf() and vsnprintf() function checks. CHANGES IN Mini-XML 1.1.2 - The mxml(3) man page wasn't updated for the string functions. - mxmlSaveString() returned the wrong number of characters. - mxml_add_char() updated the buffer pointer in the wrong place. CHANGES IN Mini-XML 1.1.1 - The private mxml_add_ch() function did not update the start-of-buffer pointer which could cause a crash when using mxmlSaveString(). - The private mxml_write_ws() function called putc() instead of using the proper callback which could cause a crash when using mxmlSaveString(). - Added a mxmlSaveAllocString() convenience function for saving an XML node tree to an allocated string. CHANGES IN Mini-XML 1.1 - The mxmlLoadFile() function now uses dynamically allocated string buffers for element names, attribute names, and attribute values. Previously they were capped at 16383, 255, and 255 bytes, respectively. - Added a new mxmlLoadString() function for loading an XML node tree from a string. - Added a new mxmlSaveString() function for saving an XML node tree to a string. - Add emulation of strdup() if the local platform does not provide the function. CHANGES IN Mini-XML 1.0 - The mxmldoc program now handles function arguments, structures, unions, enumerations, classes, and typedefs properly. - Documentation provided via mxmldoc and more in-line comments in the code. - Added man pages and packaging files. CHANGES IN Mini-XML 0.93 - New mxmldoc example program that is also used to create and update code documentation using XML and produce HTML reference pages. - Added mxmlAdd() and mxmlRemove() functions to add and remove nodes from a tree. This provides more flexibility over where the nodes are inserted and allows nodes to be moved within the tree as needed. - mxmlLoadFile() now correctly handles comments. - mxmlLoadFile() now supports the required "gt", "quot", and "nbsp" character entities. - mxmlSaveFile() now uses newlines as whitespace when valid to do so. - mxmlFindElement() now also takes attribute name and attribute value string arguments to limit the search to specific elements with attributes and/or values. NULL pointers can be used as "wildcards". - Added uninstall target to makefile, and auto-reconfig if Makefile.in or configure.in are changed. - mxmlFindElement(), mxmlWalkNext(), and mxmlWalkPrev() now all provide "descend" arguments to control whether they descend into child nodes in the tree. - Fixed some whitespace issues in mxmlLoadFile(). - Fixed Unicode output and whitespace issues in mxmlSaveFile(). - mxmlSaveFile() now supports a whitespace callback to provide more human-readable XML output under program control. CHANGES IN Mini-XML 0.92 - mxmlSaveFile() didn't return a value on success. CHANGES IN Mini-XML 0.91 - mxmlWalkNext() would go into an infinite loop. CHANGES IN Mini-XML 0.9 - Initial public release. cmtk-3.3.1/Utilities/mxml/CMake/000077500000000000000000000000001276303427400163745ustar00rootroot00000000000000cmtk-3.3.1/Utilities/mxml/CMake/CheckCInline.c000066400000000000000000000003021276303427400210120ustar00rootroot00000000000000/* Test source lifted from /usr/share/autoconf/autoconf/c.m4 */ typedef int foo_t; static inline foo_t static_foo(){return 0;} foo_t foo(){return 0;} int main(int argc, char *argv[]){return 0;} cmtk-3.3.1/Utilities/mxml/CMake/CheckCInline.cmake000066400000000000000000000011461276303427400216570ustar00rootroot00000000000000# This handy test is from Jack Kelly on the cmake email list. # http://www.cmake.org/Wiki/CMakeTestInline # Inspired from /usr/share/autoconf/autoconf/c.m4 FOREACH(KEYWORD "inline" "__inline__" "__inline") IF(NOT DEFINED C_INLINE) TRY_COMPILE(C_HAS_KEYWORD "${CMAKE_CURRENT_BINARY_DIR}" "${CMAKE_CURRENT_SOURCE_DIR}/CMake/CheckCInline.c" COMPILE_DEFINITIONS "-Dinline=${KEYWORD}") IF(C_HAS_KEYWORD) SET(C_INLINE ${KEYWORD} CACHE STRING "C compiler keyword for inline functions") MARK_AS_ADVANCED(C_INLINE) ENDIF(C_HAS_KEYWORD) ENDIF(NOT DEFINED C_INLINE) ENDFOREACH(KEYWORD) cmtk-3.3.1/Utilities/mxml/CMakeLists.txt000066400000000000000000000050231276303427400201540ustar00rootroot00000000000000## ## Copyright 2009-2012 SRI International ## ## This file is part of the Computational Morphometry Toolkit. ## ## http://www.nitrc.org/projects/cmtk/ ## ## The Computational Morphometry Toolkit is free software: you can ## redistribute it and/or modify it under the terms of the GNU General Public ## License as published by the Free Software Foundation, either version 3 of ## the License, or (at your option) any later version. ## ## The Computational Morphometry Toolkit is distributed in the hope that it ## will be useful, but WITHOUT ANY WARRANTY; without even the implied ## warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License along ## with the Computational Morphometry Toolkit. If not, see ## . ## ## $Revision: 199 $ ## ## $LastChangedDate: 2009-07-13 16:38:02 -0700 (Mon, 13 Jul 2009) $ ## ## $LastChangedBy: torstenrohlfing $ ## PROJECT(MiniXML) INCLUDE_REGULAR_EXPRESSION("^.*.h$") SET(MXML_VERSION_MAJOR 2) SET(MXML_VERSION_MINOR 7) SET(MXML_VERSION "Mini-XML v2.7") SET(mxml_SRCS mxml-attr.c mxml-entity.c mxml-index.c mxml-private.c mxml-get.c mxml-set.c mxml-file.c mxml-node.c mxml-search.c mxml-string.c) INCLUDE(${CMAKE_CURRENT_SOURCE_DIR}/CMake/CheckCInline.cmake) CHECK_TYPE_SIZE("long long" LONG_LONG_SIZE) IF(LONG_LONG_SIZE) SET(HAVE_LONG_LONG 1) ENDIF(LONG_LONG_SIZE) INCLUDE(CheckFunctionExists) CHECK_FUNCTION_EXISTS(strdup HAVE_STRDUP) CHECK_FUNCTION_EXISTS(snprintf HAVE_SNPRINTF) CHECK_FUNCTION_EXISTS(vsnprintf HAVE_VSNPRINTF) CHECK_INCLUDE_FILES(pthread.h HAVE_PTHREAD_H) CONFIGURE_FILE(${CMAKE_CURRENT_SOURCE_DIR}/config.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config.h) INCLUDE_DIRECTORIES(${CMAKE_CURRENT_BINARY_DIR}) ADD_LIBRARY(cmtkMxml ${mxml_SRCS}) TARGET_LINK_LIBRARIES(cmtkMxml) INSTALL(TARGETS cmtkMxml RUNTIME DESTINATION ${CMTK_INSTALL_BIN_DIR} COMPONENT runtime LIBRARY DESTINATION ${CMTK_INSTALL_LIB_DIR} COMPONENT runtime ARCHIVE DESTINATION ${CMTK_INSTALL_LIB_DIR} COMPONENT libraries) FILE(GLOB files "${CMAKE_CURRENT_SOURCE_DIR}/*.h") INSTALL(FILES ${files} ${CMAKE_CURRENT_BINARY_DIR}/config.h DESTINATION ${CMTK_INSTALL_INCLUDE_DIR} COMPONENT headers) SET(MXML_LIBRARIES "cmtkMxml" CACHE INTERNAL "Libraries for the mxml library") SET(MXML_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}" CACHE PATH "Include directory for the mxml library headers.") cmtk-3.3.1/Utilities/mxml/COPYING000066400000000000000000000631101276303427400164500ustar00rootroot00000000000000 Mini-XML License September 18, 2010 The Mini-XML library and included programs are provided under the terms of the GNU Library General Public License version 2 (LGPL2) with the following exceptions: 1. Static linking of applications to the Mini-XML library does not constitute a derivative work and does not require the author to provide source code for the application, use the shared Mini-XML libraries, or link their applications against a user-supplied version of Mini-XML. If you link the application to a modified version of Mini-XML, then the changes to Mini-XML must be provided under the terms of the LGPL2 in sections 1, 2, and 4. 2. You do not have to provide a copy of the Mini-XML license with programs that are linked to the Mini-XML library, nor do you have to identify the Mini-XML license in your program or documentation as required by section 6 of the LGPL2. GNU LIBRARY GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1991 Free Software Foundation, Inc. 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. [This is the first released version of the library GPL. It is numbered 2 because it goes with version 2 of the ordinary GPL.] Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public Licenses are intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This license, the Library General Public License, applies to some specially designated Free Software Foundation software, and to any other libraries whose authors decide to use it. You can use it for your libraries, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the library, or if you modify it. For example, if you distribute copies of the library, whether gratis or for a fee, you must give the recipients all the rights that we gave you. You must make sure that they, too, receive or can get the source code. If you link a program with the library, you must provide complete object files to the recipients so that they can relink them with the library, after making changes to the library and recompiling it. And you must show them these terms so they know their rights. Our method of protecting your rights has two steps: (1) copyright the library, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the library. Also, for each distributor's protection, we want to make certain that everyone understands that there is no warranty for this free library. If the library is modified by someone else and passed on, we want its recipients to know that what they have is not the original version, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that companies distributing free software will individually obtain patent licenses, thus in effect transforming the program into proprietary software. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. Most GNU software, including some libraries, is covered by the ordinary GNU General Public License, which was designed for utility programs. This license, the GNU Library General Public License, applies to certain designated libraries. This license is quite different from the ordinary one; be sure to read it in full, and don't assume that anything in it is the same as in the ordinary license. The reason we have a separate public license for some libraries is that they blur the distinction we usually make between modifying or adding to a program and simply using it. Linking a program with a library, without changing the library, is in some sense simply using the library, and is analogous to running a utility program or application program. However, in a textual and legal sense, the linked executable is a combined work, a derivative of the original library, and the ordinary General Public License treats it as such. Because of this blurred distinction, using the ordinary General Public License for libraries did not effectively promote software sharing, because most developers did not use the libraries. We concluded that weaker conditions might promote sharing better. However, unrestricted linking of non-free programs would deprive the users of those programs of all benefit from the free status of the libraries themselves. This Library General Public License is intended to permit developers of non-free programs to use free libraries, while preserving your freedom as a user of such programs to change the free libraries that are incorporated in them. (We have not seen how to achieve this as regards changes in header files, but we have achieved it as regards changes in the actual functions of the Library.) The hope is that this will lead to faster development of free libraries. The precise terms and conditions for copying, distribution and modification follow. Pay close attention to the difference between a "work based on the library" and a "work that uses the library". The former contains code derived from the library, while the latter only works together with the library. Note that it is possible for a library to be covered by the ordinary General Public License rather than by this special one. GNU LIBRARY GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License Agreement applies to any software library which contains a notice placed by the copyright holder or other authorized party saying it may be distributed under the terms of this Library General Public License (also called "this License"). Each licensee is addressed as "you". A "library" means a collection of software functions and/or data prepared so as to be conveniently linked with application programs (which use some of those functions and data) to form executables. The "Library", below, refers to any such software library or work which has been distributed under these terms. A "work based on the Library" means either the Library or any derivative work under copyright law: that is to say, a work containing the Library or a portion of it, either verbatim or with modifications and/or translated straightforwardly into another language. (Hereinafter, translation is included without limitation in the term "modification".) "Source code" for a work means the preferred form of the work for making modifications to it. For a library, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the library. Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running a program using the Library is not restricted, and output from such a program is covered only if its contents constitute a work based on the Library (independent of the use of the Library in a tool for writing it). Whether that is true depends on what the Library does and what the program that uses the Library does. 1. You may copy and distribute verbatim copies of the Library's complete source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and distribute a copy of this License along with the Library. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Library or any portion of it, thus forming a work based on the Library, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) The modified work must itself be a software library. b) You must cause the files modified to carry prominent notices stating that you changed the files and the date of any change. c) You must cause the whole of the work to be licensed at no charge to all third parties under the terms of this License. d) If a facility in the modified Library refers to a function or a table of data to be supplied by an application program that uses the facility, other than as an argument passed when the facility is invoked, then you must make a good faith effort to ensure that, in the event an application does not supply such function or table, the facility still operates, and performs whatever part of its purpose remains meaningful. (For example, a function in a library to compute square roots has a purpose that is entirely well-defined independent of the application. Therefore, Subsection 2d requires that any application-supplied function or table used by this function must be optional: if the application does not supply it, the square root function must still compute square roots.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Library, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Library, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Library. In addition, mere aggregation of another work not based on the Library with the Library (or with a work based on the Library) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may opt to apply the terms of the ordinary GNU General Public License instead of this License to a given copy of the Library. To do this, you must alter all the notices that refer to this License, so that they refer to the ordinary GNU General Public License, version 2, instead of to this License. (If a newer version than version 2 of the ordinary GNU General Public License has appeared, then you can specify that version instead if you wish.) Do not make any other change in these notices. Once this change is made in a given copy, it is irreversible for that copy, so the ordinary GNU General Public License applies to all subsequent copies and derivative works made from that copy. This option is useful when you wish to copy part of the code of the Library into a program that is not a library. 4. You may copy and distribute the Library (or a portion or derivative of it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange. If distribution of object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place satisfies the requirement to distribute the source code, even though third parties are not compelled to copy the source along with the object code. 5. A program that contains no derivative of any portion of the Library, but is designed to work with the Library by being compiled or linked with it, is called a "work that uses the Library". Such a work, in isolation, is not a derivative work of the Library, and therefore falls outside the scope of this License. However, linking a "work that uses the Library" with the Library creates an executable that is a derivative of the Library (because it contains portions of the Library), rather than a "work that uses the library". The executable is therefore covered by this License. Section 6 states terms for distribution of such executables. When a "work that uses the Library" uses material from a header file that is part of the Library, the object code for the work may be a derivative work of the Library even though the source code is not. Whether this is true is especially significant if the work can be linked without the Library, or if the work is itself a library. The threshold for this to be true is not precisely defined by law. If such an object file uses only numerical parameters, data structure layouts and accessors, and small macros and small inline functions (ten lines or less in length), then the use of the object file is unrestricted, regardless of whether it is legally a derivative work. (Executables containing this object code plus portions of the Library will still fall under Section 6.) Otherwise, if the work is a derivative of the Library, you may distribute the object code for the work under the terms of Section 6. Any executables containing that work also fall under Section 6, whether or not they are linked directly with the Library itself. 6. As an exception to the Sections above, you may also compile or link a "work that uses the Library" with the Library to produce a work containing portions of the Library, and distribute that work under terms of your choice, provided that the terms permit modification of the work for the customer's own use and reverse engineering for debugging such modifications. You must give prominent notice with each copy of the work that the Library is used in it and that the Library and its use are covered by this License. You must supply a copy of this License. If the work during execution displays copyright notices, you must include the copyright notice for the Library among them, as well as a reference directing the user to the copy of this License. Also, you must do one of these things: a) Accompany the work with the complete corresponding machine-readable source code for the Library including whatever changes were used in the work (which must be distributed under Sections 1 and 2 above); and, if the work is an executable linked with the Library, with the complete machine-readable "work that uses the Library", as object code and/or source code, so that the user can modify the Library and then relink to produce a modified executable containing the modified Library. (It is understood that the user who changes the contents of definitions files in the Library will not necessarily be able to recompile the application to use the modified definitions.) b) Accompany the work with a written offer, valid for at least three years, to give the same user the materials specified in Subsection 6a, above, for a charge no more than the cost of performing this distribution. c) If distribution of the work is made by offering access to copy from a designated place, offer equivalent access to copy the above specified materials from the same place. d) Verify that the user has already received a copy of these materials or that you have already sent this user a copy. For an executable, the required form of the "work that uses the Library" must include any data and utility programs needed for reproducing the executable from it. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. It may happen that this requirement contradicts the license restrictions of other proprietary libraries that do not normally accompany the operating system. Such a contradiction means you cannot use both them and the Library together in an executable that you distribute. 7. You may place library facilities that are a work based on the Library side-by-side in a single library together with other library facilities not covered by this License, and distribute such a combined library, provided that the separate distribution of the work based on the Library and of the other library facilities is otherwise permitted, and provided that you do these two things: a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities. This must be distributed under the terms of the Sections above. b) Give prominent notice with the combined library of the fact that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. 8. You may not copy, modify, sublicense, link with, or distribute the Library except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense, link with, or distribute the Library is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 9. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Library or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Library (or any work based on the Library), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Library or works based on it. 10. Each time you redistribute the Library (or any work based on the Library), the recipient automatically receives a license from the original licensor to copy, distribute, link with or modify the Library subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 11. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Library at all. For example, if a patent license would not permit royalty-free redistribution of the Library by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Library. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply, and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 12. If the distribution and/or use of the Library is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Library under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 13. The Free Software Foundation may publish revised and/or new versions of the Library General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Library specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Library does not specify a license version number, you may choose any version ever published by the Free Software Foundation. 14. If you wish to incorporate parts of the Library into other free programs whose distribution conditions are incompatible with these, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS Appendix: How to Apply These Terms to Your New Libraries If you develop a new library, and you want it to be of the greatest possible use to the public, we recommend making it free software that everyone can redistribute and change. You can do so by permitting redistribution under these terms (or, alternatively, under the terms of the ordinary General Public License). To apply these terms, attach the following notices to the library. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. Also add information on how to contact you by electronic and paper mail. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the library, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the library `Frob' (a library for tweaking knobs) written by James Random Hacker. , 1 April 1990 Ty Coon, President of Vice That's all there is to it! cmtk-3.3.1/Utilities/mxml/Makefile.in000066400000000000000000000242511276303427400174650ustar00rootroot00000000000000# # "$Id: Makefile.in 439 2011-04-13 15:43:32Z mike $" # # Makefile for Mini-XML, a small XML-like file parsing library. # # Copyright 2003-2011 by Michael R Sweet. # # These coded instructions, statements, and computer programs are the # property of Michael R Sweet and are protected by Federal copyright # law. Distribution and use rights are outlined in the file "COPYING" # which should have been included with this file. If this file is # missing or damaged, see the license at: # # http://www.minixml.org/ # # # Compiler tools definitions... # AR = @AR@ ARFLAGS = @ARFLAGS@ ARCHFLAGS = @ARCHFLAGS@ CC = @CC@ CFLAGS = $(OPTIM) $(ARCHFLAGS) @CFLAGS@ @CPPFLAGS@ @PTHREAD_FLAGS@ CP = @CP@ DSO = @DSO@ DSOFLAGS = @DSOFLAGS@ LDFLAGS = $(OPTIM) $(ARCHFLAGS) @LDFLAGS@ INSTALL = @INSTALL@ LIBMXML = @LIBMXML@ LIBS = @LIBS@ @PTHREAD_LIBS@ LN = @LN@ -s MKDIR = @MKDIR@ OPTIM = @OPTIM@ RANLIB = @RANLIB@ RM = @RM@ -f SHELL = /bin/sh # # Configured directories... # prefix = @prefix@ exec_prefix = @exec_prefix@ bindir = @bindir@ datarootdir = @datarootdir@ includedir = @includedir@ libdir = @libdir@ mandir = @mandir@ docdir = @docdir@ BUILDROOT = $(DSTROOT) # # Install commands... # INSTALL_BIN = $(LIBTOOL) $(INSTALL) -m 755 -s INSTALL_DATA = $(INSTALL) -m 644 INSTALL_DIR = $(INSTALL) -d INSTALL_LIB = $(LIBTOOL) $(INSTALL) -m 755 INSTALL_MAN = $(INSTALL) -m 644 INSTALL_SCRIPT = $(INSTALL) -m 755 # # Rules... # .SILENT: .SUFFIXES: .c .man .o .c.o: echo Compiling $< $(CC) $(CFLAGS) -c -o $@ $< # # Targets... # DOCFILES = doc/0.gif doc/1.gif doc/2.gif doc/3.gif doc/4.gif \ doc/A.gif doc/B.gif doc/C.gif doc/D.gif \ doc/mxml.html doc/mxmldoc.xsd \ README COPYING CHANGES PUBLIBOBJS = mxml-attr.o mxml-entity.o mxml-file.o mxml-get.o \ mxml-index.o mxml-node.o mxml-search.o mxml-set.o LIBOBJS = $(PUBLIBOBJS) mxml-private.o mxml-string.o OBJS = mxmldoc.o testmxml.o $(LIBOBJS) TARGETS = $(LIBMXML) mxmldoc testmxml mxml.xml doc/mxml.man # # Make everything... # all: Makefile config.h $(TARGETS) # # Clean everything... # clean: echo Cleaning build files... $(RM) $(OBJS) $(TARGETS) $(RM) mxmldoc-static libmxml.a libmxml.so.1.5 libmxml.sl.1 libmxml.1.dylib # # Really clean everything... # distclean: clean echo Cleaning distribution files... $(RM) config.cache config.log config.status $(RM) Makefile config.h $(RM) -r autom4te*.cache $(RM) *.bck *.bak $(RM) -r clang # # Run the clang.llvm.org static code analysis tool on the C sources. # .PHONY: clang clang-changes clang: echo Doing static code analysis of all code using CLANG... $(RM) -r clang scan-build -V -k -o `pwd`/clang $(MAKE) $(MFLAGS) clean all clang-changes: echo Doing static code analysis of changed code using CLANG... scan-build -V -k -o `pwd`/clang $(MAKE) $(MFLAGS) all # # Install everything... # install: $(TARGETS) install-$(LIBMXML) install-libmxml.a echo Installing mxmldoc in $(BUILDROOT)$(bindir)... $(INSTALL_DIR) $(BUILDROOT)$(bindir) $(INSTALL_BIN) mxmldoc $(BUILDROOT)$(bindir) echo Installing documentation in $(BUILDROOT)$(docdir)... $(INSTALL_DIR) $(BUILDROOT)$(docdir) for file in $(DOCFILES); do \ $(INSTALL_MAN) $$file $(BUILDROOT)$(docdir); \ done echo Installing header files in $(BUILDROOT)$(includedir)... $(INSTALL_DIR) $(BUILDROOT)$(includedir) $(INSTALL_DATA) mxml.h $(BUILDROOT)$(includedir) echo Installing pkgconfig files in $(BUILDROOT)$(libdir)/pkgconfig... $(INSTALL_DIR) $(BUILDROOT)$(libdir)/pkgconfig $(INSTALL_DATA) mxml.pc $(BUILDROOT)$(libdir)/pkgconfig echo Installing man pages in $(BUILDROOT)$(mandir)... $(INSTALL_DIR) $(BUILDROOT)$(mandir)/man1 $(INSTALL_MAN) doc/mxmldoc.man $(BUILDROOT)$(mandir)/man1/mxmldoc.1 $(INSTALL_DIR) $(BUILDROOT)$(mandir)/man3 $(INSTALL_MAN) doc/mxml.man $(BUILDROOT)$(mandir)/man3/mxml.3 install-libmxml.a: echo Installing libmxml.a to $(BUILDROOT)$(libdir)... $(INSTALL_DIR) $(BUILDROOT)$(libdir) $(INSTALL_LIB) libmxml.a $(BUILDROOT)$(libdir) $(RANLIB) $(BUILDROOT)$(libdir)/libmxml.a install-libmxml.so.1.5: echo Installing libmxml.so to $(BUILDROOT)$(libdir)... $(INSTALL_DIR) $(BUILDROOT)$(libdir) $(INSTALL_LIB) libmxml.so.1.5 $(BUILDROOT)$(libdir) $(RM) $(BUILDROOT)$(libdir)/libmxml.so $(LN) libmxml.so.1.5 $(BUILDROOT)$(libdir)/libmxml.so $(RM) $(BUILDROOT)$(libdir)/libmxml.so.1 $(LN) libmxml.so.1.5 $(BUILDROOT)$(libdir)/libmxml.so.1 install-libmxml.sl.1: echo Installing libmxml.sl to $(BUILDROOT)$(libdir)... $(INSTALL_DIR) $(BUILDROOT)$(libdir) $(INSTALL_LIB) libmxml.sl.1 $(BUILDROOT)$(libdir) $(RM) $(BUILDROOT)$(libdir)/libmxml.so $(LN) libmxml.sl.1 $(BUILDROOT)$(libdir)/libmxml.sl install-libmxml.1.dylib: echo Installing libmxml.dylib to $(BUILDROOT)$(libdir)... $(INSTALL_DIR) $(BUILDROOT)$(libdir) $(INSTALL_LIB) libmxml.1.dylib $(BUILDROOT)$(libdir) $(RM) $(BUILDROOT)$(libdir)/libmxml.dylib $(LN) libmxml.1.dylib $(BUILDROOT)$(libdir)/libmxml.dylib # # Uninstall everything... # uninstall: uninstall-$(LIBMXML) uninstall-libmxml.a echo Uninstalling mxmldoc from $(BUILDROOT)$(bindir)... $(RM) $(BUILDROOT)$(bindir)/mxmldoc echo Uninstalling documentation from $(BUILDROOT)$(docdir)... $(RM) -r $(BUILDROOT)$(docdir) echo Uninstalling headers from $(BUILDROOT)$(includedir)... $(RM) $(BUILDROOT)$(includedir)/mxml.h echo Uninstalling pkgconfig files from $(BUILDROOT)$(libdir)/pkgconfig... $(RM) $(BUILDROOT)$(libdir)/pkgconfig/mxml.pc echo Uninstalling man pages from $(BUILDROOT)$(mandir)... $(RM) $(BUILDROOT)$(mandir)/man1/mxmldoc.1 $(RM) $(BUILDROOT)$(mandir)/man3/mxml.3 uninstall-libmxml.a: echo Uninstalling libmxml.a from $(BUILDROOT)$(libdir)... $(RM) $(BUILDROOT)$(libdir)/libmxml.a uninstall-libmxml.so.1.5: echo Uninstalling libmxml.so from $(BUILDROOT)$(libdir)... $(RM) $(BUILDROOT)$(libdir)/libmxml.so $(RM) $(BUILDROOT)$(libdir)/libmxml.so.1 $(RM) $(BUILDROOT)$(libdir)/libmxml.so.1.4 uninstall-libmxml.sl.1: echo Uninstalling libmxml.sl from $(BUILDROOT)$(libdir)... $(RM) $(BUILDROOT)$(libdir)/libmxml.sl $(RM) $(BUILDROOT)$(libdir)/libmxml.sl.1 uninstall-libmxml.1.dylib: echo Uninstalling libmxml.dylib from $(BUILDROOT)$(libdir)... $(RM) $(BUILDROOT)$(libdir)/libmxml.dylib $(RM) $(BUILDROOT)$(libdir)/libmxml.1.dylib # # Make packages using EPM (http://www.epmhome.org/) # epm: all echo Creating distribution packages... epm --output-dir dist -v -f native mxml epm --output-dir dist -v -f portable mxml # # autoconf stuff... # Makefile: configure Makefile.in echo Updating makefile... if test -f config.status; then \ ./config.status --recheck; \ ./config.status; \ else \ ./configure; \ fi touch config.h config.h: configure config.h.in echo Updating config.h... autoconf if test -f config.status; then \ ./config.status --recheck; \ ./config.status; \ else \ ./configure; \ fi touch config.h # # Figure out lines-of-code... # .PHONY: sloc sloc: echo "libmxml: \c" sloccount $(LIBOBJS:.o=.c) mxml-private.c mxml.h 2>/dev/null | \ grep "Total Physical" | awk '{print $$9}' # # libmxml.a # libmxml.a: $(LIBOBJS) echo Creating $@... $(RM) $@ $(AR) $(ARFLAGS) $@ $(LIBOBJS) $(RANLIB) $@ $(LIBOBJS): mxml.h mxml-entity.o mxml-file.o mxml-private.o: mxml-private.h # # libmxml.so.1.5 # libmxml.so.1.5: $(LIBOBJS) echo Creating $@... $(DSO) $(DSOFLAGS) -o libmxml.so.1.5 $(LIBOBJS) $(RM) libmxml.so libmxml.so.1 $(LN) libmxml.so.1.5 libmxml.so $(LN) libmxml.so.1.5 libmxml.so.1 # # libmxml.sl.1 # libmxml.sl.1: $(LIBOBJS) echo Creating $@... $(DSO) $(DSOFLAGS) -o libmxml.sl.1 $(LIBOBJS) $(RM) libmxml.sl $(LN) libmxml.sl.1 libmxml.sl # # libmxml.1.dylib # libmxml.1.dylib: $(LIBOBJS) echo Creating $@... $(DSO) $(DSOFLAGS) -o libmxml.1.dylib \ -install_name $(libdir)/libmxml.dylib \ -current_version 1.5.0 \ -compatibility_version 1.0.0 \ $(LIBOBJS) $(RM) libmxml.dylib $(LN) libmxml.1.dylib libmxml.dylib # # mxmldoc # mxmldoc: $(LIBMXML) mxmldoc.o echo Linking $@... $(CC) -L. $(LDFLAGS) -o $@ mxmldoc.o -lmxml $(LIBS) mxmldoc-static: libmxml.a mxmldoc.o echo Linking $@... $(CC) $(LDFLAGS) -o $@ mxmldoc.o libmxml.a $(LIBS) mxmldoc.o: mxml.h # # testmxml # testmxml: libmxml.a testmxml.o echo Linking $@... $(CC) $(LDFLAGS) -o $@ testmxml.o libmxml.a $(LIBS) @echo Testing library... ./testmxml test.xml >temp1.xml 2>temp1s.xml ./testmxml temp1.xml >temp2.xml 2>temp2s.xml @if cmp temp1.xml temp2.xml; then \ echo Stdio file test passed!; \ $(RM) temp2.xml temp2s.xml; \ else \ echo Stdio file test failed!; \ fi @if cmp temp1.xml temp1s.xml; then \ echo String test passed!; \ $(RM) temp1.xml temp1s.xml; \ else \ echo String test failed!; \ fi @if cmp test.xml test.xmlfd; then \ echo File descriptor test passed!; \ $(RM) test.xmlfd; \ else \ echo File descriptor test failed!; \ fi testmxml-vg: $(LIBOBJS) testmxml.o echo Linking $@... $(CC) $(LDFLAGS) -o $@ testmxml.o $(LIBOBJS) $(LIBS) testmxml.o: mxml.h # # mxml.xml # mxml.xml: mxmldoc-static mxml.h $(PUBLIBOBJS:.o=.c) echo Generating API documentation... $(RM) mxml.xml ./mxmldoc-static --header doc/reference.heading mxml.xml mxml.h $(PUBLIBOBJS:.o=.c) >doc/reference.html if test "x`uname`" = xDarwin; then \ ./mxmldoc-static --docset org.minixml.docset \ --docversion @VERSION@ --feedname minixml.org \ --feedurl http://www.minixml.org/org.minixml.atom \ --header doc/docset.header --intro doc/docset.intro \ --css doc/docset.css --title "Mini-XML API Reference" \ mxml.xml || exit 1; \ $(RM) org.minixml.atom; \ /Developer/usr/bin/docsetutil package --output org.minixml.xar \ --atom org.minixml.atom \ --download-url http://www.minixml.org/org.minixml.xar \ org.minixml.docset || exit 1; \ fi valgrind: mxmldoc-static echo Doing dynamic code analysis using Valgrind... $(RM) valgrind.xml valgrind --tool=memcheck --leak-check=yes ./mxmldoc-static \ valgrind.xml mxml.h $(PUBLIBOBJS:.o=.c) \ >valgrind.html 2>valgrind.out # # doc/mxml.man # doc/mxml.man: mxmldoc-static mxml.xml echo "Generating mxml(3) man page..." $(RM) doc/mxml.man ./mxmldoc-static --man mxml --title "Mini-XML API" \ --intro doc/intro.man --footer doc/footer.man \ mxml.xml >doc/mxml.man # # All object files depend on the makefile... # $(OBJS): Makefile config.h # # End of "$Id: Makefile.in 439 2011-04-13 15:43:32Z mike $". # cmtk-3.3.1/Utilities/mxml/README000066400000000000000000000141521276303427400162770ustar00rootroot00000000000000README - 2011-12-20 ------------------- INTRODUCTION This README file describes the Mini-XML library version 2.7. Mini-XML is a small XML parsing library that you can use to read XML and XML-like data files in your application without requiring large non-standard libraries. Mini-XML only requires an ANSI C compatible compiler (GCC works, as do most vendors' ANSI C compilers) and a "make" program. Mini-XML provides the following functionality: - Reading of UTF-8 and UTF-16 and writing of UTF-8 encoded XML files and strings. - Data is stored in a linked-list tree structure, preserving the XML data hierarchy. - Supports arbitrary element names, attributes, and attribute values with no preset limits, just available memory. - Supports integer, real, opaque ("cdata"), and text data types in "leaf" nodes. - Functions for creating and managing trees of data. - "Find" and "walk" functions for easily locating and navigating trees of data. Mini-XML doesn't do validation or other types of processing on the data based upon schema files or other sources of definition information. BUILDING Mini-XML Mini-XML comes with an autoconf-based configure script; just type the following command to get things going: ./configure The default install prefix is /usr/local, which can be overridden using the --prefix option: ./configure --prefix=/foo Other configure options can be found using the --help option: ./configure --help Once you have configured the software, type "make" to do the build and run the test program to verify that things are working, as follows: make If you are using Mini-XML under Microsoft Windows with Visual C++ 2008, use the included project files in the "vcnet" subdirectory to build the library instead. INSTALLING Mini-XML The "install" target will install Mini-XML in the lib and include directories: make install Once you have installed it, use the "-lmxml" option to link your application against it. DOCUMENTATION The documentation is available in the "doc" subdirectory in the files "mxml.html" (HTML) and "mxml.pdf" (PDF). You can also look at the "testmxml.c" and "mxmldoc.c" source files for examples of using Mini-XML. Mini-XML provides a single header file which you include: #include Nodes are defined by the "mxml_node_t" structure; the "type" member defines the node type (element, integer, opaque, real, or text) which determines which value you want to look at in the "value" union. New nodes can be created using the "mxmlNewElement()", "mxmlNewInteger()", "mxmlNewOpaque()", "mxmlNewReal()", and "mxmlNewText()" functions. Only elements can have child nodes, and the top node must be an element, usually "?xml". You load an XML file using the "mxmlLoadFile()" function: FILE *fp; mxml_node_t *tree; fp = fopen("filename.xml", "r"); tree = mxmlLoadFile(NULL, fp, MXML_NO_CALLBACK); fclose(fp); Similarly, you save an XML file using the "mxmlSaveFile()" function: FILE *fp; mxml_node_t *tree; fp = fopen("filename.xml", "w"); mxmlSaveFile(tree, fp, MXML_NO_CALLBACK); fclose(fp); The "mxmlLoadString()", "mxmlSaveAllocString()", and "mxmlSaveString()" functions load XML node trees from and save XML node trees to strings: char buffer[8192]; char *ptr; mxml_node_t *tree; ... tree = mxmlLoadString(NULL, buffer, MXML_NO_CALLBACK); ... mxmlSaveString(tree, buffer, sizeof(buffer), MXML_NO_CALLBACK); ... ptr = mxmlSaveAllocString(tree, MXML_NO_CALLBACK); You can find a named element/node using the "mxmlFindElement()" function: mxml_node_t *node = mxmlFindElement(tree, tree, "name", "attr", "value", MXML_DESCEND); The "name", "attr", and "value" arguments can be passed as NULL to act as wildcards, e.g.: /* Find the first "a" element */ node = mxmlFindElement(tree, tree, "a", NULL, NULL, MXML_DESCEND); /* Find the first "a" element with "href" attribute */ node = mxmlFindElement(tree, tree, "a", "href", NULL, MXML_DESCEND); /* Find the first "a" element with "href" to a URL */ node = mxmlFindElement(tree, tree, "a", "href", "http://www.minixml.org/", MXML_DESCEND); /* Find the first element with a "src" attribute*/ node = mxmlFindElement(tree, tree, NULL, "src", NULL, MXML_DESCEND); /* Find the first element with a "src" = "foo.jpg" */ node = mxmlFindElement(tree, tree, NULL, "src", "foo.jpg", MXML_DESCEND); You can also iterate with the same function: mxml_node_t *node; for (node = mxmlFindElement(tree, tree, "name", NULL, NULL, MXML_DESCEND); node != NULL; node = mxmlFindElement(node, tree, "name", NULL, NULL, MXML_DESCEND)) { ... do something ... } The "mxmlFindPath()" function finds the (first) value node under a specific element using a "path": mxml_node_t *value = mxmlFindPath(tree, "path/to/*/foo/bar"); The "mxmlGetInteger()", "mxmlGetOpaque()", "mxmlGetReal()", and "mxmlGetText()" functions retrieve the value from a node: mxml_node_t *node; int intvalue = mxmlGetInteger(node); const char *opaquevalue = mxmlGetOpaque(node); double realvalue = mxmlGetReal(node); int whitespacevalue; const char *textvalue = mxmlGetText(node, &whitespacevalue); Finally, once you are done with the XML data, use the "mxmlDelete()" function to recursively free the memory that is used for a particular node or the entire tree: mxmlDelete(tree); GETTING HELP AND REPORTING PROBLEMS The Mini-XML web site provides access to a discussion forum and bug reporting page: http://www.minixml.org/ LEGAL STUFF The Mini-XML library is Copyright 2003-2011 by Michael Sweet. License terms are described in the file "COPYING". cmtk-3.3.1/Utilities/mxml/cmtk_mxml_mangle.h000066400000000000000000000106101276303427400211010ustar00rootroot00000000000000#ifndef cmtk_mxml_mangle_h #define cmtk_mxml_mangle_h /* This header file mangles all symbols exported from the mxml library. It is included in all files while building the mxml library. Due to namespace pollution, no mxml headers should be included in .h files in CMTK. The following command was used to obtain the symbol list: nm libcmtkMxml.so |grep " [TRD] " This is the way to recreate the whole list: nm libcmtkMxml.so |grep " [TRD] " | awk '{ print "#define "$3" cmtk_"$3 }' REMOVE the "_init" and "_fini" entries. */ #define cmtk__mxml_entity_cb cmtk_cmtk__mxml_entity_cb #define cmtk__mxml_global cmtk_cmtk__mxml_global #define cmtk__mxml_strdupf cmtk_cmtk__mxml_strdupf #define cmtk__mxml_vstrdupf cmtk_cmtk__mxml_vstrdupf #define cmtk_mxmlAdd cmtk_cmtk_mxmlAdd #define cmtk_mxmlDelete cmtk_cmtk_mxmlDelete #define cmtk_mxmlElementDeleteAttr cmtk_cmtk_mxmlElementDeleteAttr #define cmtk_mxmlElementGetAttr cmtk_cmtk_mxmlElementGetAttr #define cmtk_mxmlElementSetAttr cmtk_cmtk_mxmlElementSetAttr #define cmtk_mxmlElementSetAttrf cmtk_cmtk_mxmlElementSetAttrf #define cmtk_mxmlEntityAddCallback cmtk_cmtk_mxmlEntityAddCallback #define cmtk_mxmlEntityGetName cmtk_cmtk_mxmlEntityGetName #define cmtk_mxmlEntityGetValue cmtk_cmtk_mxmlEntityGetValue #define cmtk_mxmlEntityRemoveCallback cmtk_cmtk_mxmlEntityRemoveCallback #define cmtk_mxmlFindElement cmtk_cmtk_mxmlFindElement #define cmtk_mxmlIndexDelete cmtk_cmtk_mxmlIndexDelete #define cmtk_mxmlIndexEnum cmtk_cmtk_mxmlIndexEnum #define cmtk_mxmlIndexFind cmtk_cmtk_mxmlIndexFind #define cmtk_mxmlIndexNew cmtk_cmtk_mxmlIndexNew #define cmtk_mxmlIndexReset cmtk_cmtk_mxmlIndexReset #define cmtk_mxmlLoadFd cmtk_cmtk_mxmlLoadFd #define cmtk_mxmlLoadFile cmtk_cmtk_mxmlLoadFile #define cmtk_mxmlLoadString cmtk_cmtk_mxmlLoadString #define cmtk_mxmlNewCDATA cmtk_cmtk_mxmlNewCDATA #define cmtk_mxmlNewCustom cmtk_cmtk_mxmlNewCustom #define cmtk_mxmlNewElement cmtk_cmtk_mxmlNewElement #define cmtk_mxmlNewInteger cmtk_cmtk_mxmlNewInteger #define cmtk_mxmlNewOpaque cmtk_cmtk_mxmlNewOpaque #define cmtk_mxmlNewReal cmtk_cmtk_mxmlNewReal #define cmtk_mxmlNewText cmtk_cmtk_mxmlNewText #define cmtk_mxmlNewTextf cmtk_cmtk_mxmlNewTextf #define cmtk_mxmlNewXML cmtk_cmtk_mxmlNewXML #define cmtk_mxmlRelease cmtk_cmtk_mxmlRelease #define cmtk_mxmlRemove cmtk_cmtk_mxmlRemove #define cmtk_mxmlRetain cmtk_cmtk_mxmlRetain #define cmtk_mxmlSAXLoadFd cmtk_cmtk_mxmlSAXLoadFd #define cmtk_mxmlSAXLoadFile cmtk_cmtk_mxmlSAXLoadFile #define cmtk_mxmlSAXLoadString cmtk_cmtk_mxmlSAXLoadString #define cmtk_mxmlSaveAllocString cmtk_cmtk_mxmlSaveAllocString #define cmtk_mxmlSaveFd cmtk_cmtk_mxmlSaveFd #define cmtk_mxmlSaveFile cmtk_cmtk_mxmlSaveFile #define cmtk_mxmlSaveString cmtk_cmtk_mxmlSaveString #define cmtk_mxmlSetCDATA cmtk_cmtk_mxmlSetCDATA #define cmtk_mxmlSetCustom cmtk_cmtk_mxmlSetCustom #define cmtk_mxmlSetCustomHandlers cmtk_cmtk_mxmlSetCustomHandlers #define cmtk_mxmlSetElement cmtk_cmtk_mxmlSetElement #define cmtk_mxmlSetErrorCallback cmtk_cmtk_mxmlSetErrorCallback #define cmtk_mxmlSetInteger cmtk_cmtk_mxmlSetInteger #define cmtk_mxmlSetOpaque cmtk_cmtk_mxmlSetOpaque #define cmtk_mxmlSetReal cmtk_cmtk_mxmlSetReal #define cmtk_mxmlSetText cmtk_cmtk_mxmlSetText #define cmtk_mxmlSetTextf cmtk_cmtk_mxmlSetTextf #define cmtk_mxmlSetWrapMargin cmtk_cmtk_mxmlSetWrapMargin #define cmtk_mxmlWalkNext cmtk_cmtk_mxmlWalkNext #define cmtk_mxmlWalkPrev cmtk_cmtk_mxmlWalkPrev #define cmtk_mxml_error cmtk_cmtk_mxml_error #define cmtk_mxml_ignore_cb cmtk_cmtk_mxml_ignore_cb #define cmtk_mxml_integer_cb cmtk_cmtk_mxml_integer_cb #define cmtk_mxml_opaque_cb cmtk_cmtk_mxml_opaque_cb #define cmtk_mxml_real_cb cmtk_cmtk_mxml_real_cb #define mxmlFindPath cmtk_mxmlFindPath #define mxmlGetCDATA cmtk_mxmlGetCDATA #define mxmlGetCustom cmtk_mxmlGetCustom #define mxmlGetElement cmtk_mxmlGetElement #define mxmlGetFirstChild cmtk_mxmlGetFirstChild #define mxmlGetInteger cmtk_mxmlGetInteger #define mxmlGetLastChild cmtk_mxmlGetLastChild #define mxmlGetNextSibling cmtk_mxmlGetNextSibling #define mxmlGetOpaque cmtk_mxmlGetOpaque #define mxmlGetParent cmtk_mxmlGetParent #define mxmlGetPrevSibling cmtk_mxmlGetPrevSibling #define mxmlGetReal cmtk_mxmlGetReal #define mxmlGetRefCount cmtk_mxmlGetRefCount #define mxmlGetText cmtk_mxmlGetText #define mxmlGetType cmtk_mxmlGetType #define mxmlGetUserData cmtk_mxmlGetUserData #define mxmlIndexGetCount cmtk_mxmlIndexGetCount #define mxmlSetUserData cmtk_mxmlSetUserData #endif cmtk-3.3.1/Utilities/mxml/config.h.cmake000066400000000000000000000042711276303427400201150ustar00rootroot00000000000000/* * "$Id: config.h.in 387 2009-04-18 17:05:52Z mike $" * * Configuration file for Mini-XML, a small XML-like file parsing library. * * Copyright 2003-2009 by Michael Sweet. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ /* * Include necessary headers... */ /* * add "cmtk_" prefix to exported symbols - we are building this library bundled with CMTK */ #include #include #include #include #include #include /* * Version number... */ #define MXML_VERSION "@MXML_VERSION@" /* * Inline function support... */ #define inline @C_INLINE@ /* * Long long support... */ #cmakedefine HAVE_LONG_LONG /* * Do we have the snprintf() and vsnprintf() functions? */ #cmakedefine HAVE_SNPRINTF #cmakedefine HAVE_VSNPRINTF /* * Do we have the strXXX() functions? */ #cmakedefine HAVE_STRDUP /* * Do we have threading support? */ #cmakedefine HAVE_PTHREAD_H /* * Define prototypes for string functions as needed... */ # ifndef HAVE_STRDUP extern char *_mxml_strdup(const char *); # define strdup _mxml_strdup # endif /* !HAVE_STRDUP */ extern char *_mxml_strdupf(const char *, ...); extern char *_mxml_vstrdupf(const char *, va_list); # ifndef HAVE_SNPRINTF extern int _mxml_snprintf(char *, size_t, const char *, ...); # define snprintf _mxml_snprintf # endif /* !HAVE_SNPRINTF */ # ifndef HAVE_VSNPRINTF extern int _mxml_vsnprintf(char *, size_t, const char *, va_list); # define vsnprintf _mxml_vsnprintf # endif /* !HAVE_VSNPRINTF */ #ifdef _MSC_VER #if _MSC_VER >= 1400 /* disable warnings about "deprecated" C runtime functions */ #pragma warning( disable : 4996 ) #endif #endif /* * End of "$Id: config.h.in 387 2009-04-18 17:05:52Z mike $". */ cmtk-3.3.1/Utilities/mxml/config.h.in000066400000000000000000000033451276303427400174440ustar00rootroot00000000000000/* * "$Id: config.h.in 408 2010-09-19 05:26:46Z mike $" * * Configuration file for Mini-XML, a small XML-like file parsing library. * * Copyright 2003-2010 by Michael R Sweet. * * These coded instructions, statements, and computer programs are the * property of Michael R Sweet and are protected by Federal copyright * law. Distribution and use rights are outlined in the file "COPYING" * which should have been included with this file. If this file is * missing or damaged, see the license at: * * http://www.minixml.org/ */ /* * Include necessary headers... */ #include #include #include #include #include /* * Version number... */ #define MXML_VERSION "" /* * Inline function support... */ #define inline /* * Long long support... */ #undef HAVE_LONG_LONG /* * Do we have the snprintf() and vsnprintf() functions? */ #undef HAVE_SNPRINTF #undef HAVE_VSNPRINTF /* * Do we have the strXXX() functions? */ #undef HAVE_STRDUP /* * Do we have threading support? */ #undef HAVE_PTHREAD_H /* * Define prototypes for string functions as needed... */ # ifndef HAVE_STRDUP extern char *_mxml_strdup(const char *); # define strdup _mxml_strdup # endif /* !HAVE_STRDUP */ extern char *_mxml_strdupf(const char *, ...); extern char *_mxml_vstrdupf(const char *, va_list); # ifndef HAVE_SNPRINTF extern int _mxml_snprintf(char *, size_t, const char *, ...); # define snprintf _mxml_snprintf # endif /* !HAVE_SNPRINTF */ # ifndef HAVE_VSNPRINTF extern int _mxml_vsnprintf(char *, size_t, const char *, va_list); # define vsnprintf _mxml_vsnprintf # endif /* !HAVE_VSNPRINTF */ /* * End of "$Id: config.h.in 408 2010-09-19 05:26:46Z mike $". */ cmtk-3.3.1/Utilities/mxml/configure000077500000000000000000004774561276303427400173520ustar00rootroot00000000000000#! /bin/sh # Guess values for system-dependent variables and create Makefiles. # Generated by GNU Autoconf 2.61. # # Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001, # 2002, 2003, 2004, 2005, 2006 Free Software Foundation, Inc. # This configure script is free software; the Free Software Foundation # gives unlimited permission to copy, distribute and modify it. ## --------------------- ## ## M4sh Initialization. ## ## --------------------- ## # Be more Bourne compatible DUALCASE=1; export DUALCASE # for MKS sh if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then emulate sh NULLCMD=: # Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which # is contrary to our usage. Disable this feature. alias -g '${1+"$@"}'='"$@"' setopt NO_GLOB_SUBST else case `(set -o) 2>/dev/null` in *posix*) set -o posix ;; esac fi # PATH needs CR # Avoid depending upon Character Ranges. as_cr_letters='abcdefghijklmnopqrstuvwxyz' as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' as_cr_Letters=$as_cr_letters$as_cr_LETTERS as_cr_digits='0123456789' as_cr_alnum=$as_cr_Letters$as_cr_digits # The user is always right. if test "${PATH_SEPARATOR+set}" != set; then echo "#! /bin/sh" >conf$$.sh echo "exit 0" >>conf$$.sh chmod +x conf$$.sh if (PATH="/nonexistent;."; conf$$.sh) >/dev/null 2>&1; then PATH_SEPARATOR=';' else PATH_SEPARATOR=: fi rm -f conf$$.sh fi # Support unset when possible. if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then as_unset=unset else as_unset=false fi # IFS # We need space, tab and new line, in precisely that order. Quoting is # there to prevent editors from complaining about space-tab. # (If _AS_PATH_WALK were called with IFS unset, it would disable word # splitting by setting IFS to empty value.) as_nl=' ' IFS=" "" $as_nl" # Find who we are. Look in the path if we contain no directory separator. case $0 in *[\\/]* ) as_myself=$0 ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break done IFS=$as_save_IFS ;; esac # We did not find ourselves, most probably we were run as `sh COMMAND' # in which case we are not to be found in the path. if test "x$as_myself" = x; then as_myself=$0 fi if test ! -f "$as_myself"; then echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 { (exit 1); exit 1; } fi # Work around bugs in pre-3.0 UWIN ksh. for as_var in ENV MAIL MAILPATH do ($as_unset $as_var) >/dev/null 2>&1 && $as_unset $as_var done PS1='$ ' PS2='> ' PS4='+ ' # NLS nuisances. for as_var in \ LANG LANGUAGE LC_ADDRESS LC_ALL LC_COLLATE LC_CTYPE LC_IDENTIFICATION \ LC_MEASUREMENT LC_MESSAGES LC_MONETARY LC_NAME LC_NUMERIC LC_PAPER \ LC_TELEPHONE LC_TIME do if (set +x; test -z "`(eval $as_var=C; export $as_var) 2>&1`"); then eval $as_var=C; export $as_var else ($as_unset $as_var) >/dev/null 2>&1 && $as_unset $as_var fi done # Required to use basename. if expr a : '\(a\)' >/dev/null 2>&1 && test "X`expr 00001 : '.*\(...\)'`" = X001; then as_expr=expr else as_expr=false fi if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then as_basename=basename else as_basename=false fi # Name of the executable. as_me=`$as_basename -- "$0" || $as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ X"$0" : 'X\(//\)$' \| \ X"$0" : 'X\(/\)' \| . 2>/dev/null || echo X/"$0" | sed '/^.*\/\([^/][^/]*\)\/*$/{ s//\1/ q } /^X\/\(\/\/\)$/{ s//\1/ q } /^X\/\(\/\).*/{ s//\1/ q } s/.*/./; q'` # CDPATH. $as_unset CDPATH if test "x$CONFIG_SHELL" = x; then if (eval ":") 2>/dev/null; then as_have_required=yes else as_have_required=no fi if test $as_have_required = yes && (eval ": (as_func_return () { (exit \$1) } as_func_success () { as_func_return 0 } as_func_failure () { as_func_return 1 } as_func_ret_success () { return 0 } as_func_ret_failure () { return 1 } exitcode=0 if as_func_success; then : else exitcode=1 echo as_func_success failed. fi if as_func_failure; then exitcode=1 echo as_func_failure succeeded. fi if as_func_ret_success; then : else exitcode=1 echo as_func_ret_success failed. fi if as_func_ret_failure; then exitcode=1 echo as_func_ret_failure succeeded. fi if ( set x; as_func_ret_success y && test x = \"\$1\" ); then : else exitcode=1 echo positional parameters were not saved. fi test \$exitcode = 0) || { (exit 1); exit 1; } ( as_lineno_1=\$LINENO as_lineno_2=\$LINENO test \"x\$as_lineno_1\" != \"x\$as_lineno_2\" && test \"x\`expr \$as_lineno_1 + 1\`\" = \"x\$as_lineno_2\") || { (exit 1); exit 1; } ") 2> /dev/null; then : else as_candidate_shells= as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. case $as_dir in /*) for as_base in sh bash ksh sh5; do as_candidate_shells="$as_candidate_shells $as_dir/$as_base" done;; esac done IFS=$as_save_IFS for as_shell in $as_candidate_shells $SHELL; do # Try only shells that exist, to save several forks. if { test -f "$as_shell" || test -f "$as_shell.exe"; } && { ("$as_shell") 2> /dev/null <<\_ASEOF if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then emulate sh NULLCMD=: # Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which # is contrary to our usage. Disable this feature. alias -g '${1+"$@"}'='"$@"' setopt NO_GLOB_SUBST else case `(set -o) 2>/dev/null` in *posix*) set -o posix ;; esac fi : _ASEOF }; then CONFIG_SHELL=$as_shell as_have_required=yes if { "$as_shell" 2> /dev/null <<\_ASEOF if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then emulate sh NULLCMD=: # Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which # is contrary to our usage. Disable this feature. alias -g '${1+"$@"}'='"$@"' setopt NO_GLOB_SUBST else case `(set -o) 2>/dev/null` in *posix*) set -o posix ;; esac fi : (as_func_return () { (exit $1) } as_func_success () { as_func_return 0 } as_func_failure () { as_func_return 1 } as_func_ret_success () { return 0 } as_func_ret_failure () { return 1 } exitcode=0 if as_func_success; then : else exitcode=1 echo as_func_success failed. fi if as_func_failure; then exitcode=1 echo as_func_failure succeeded. fi if as_func_ret_success; then : else exitcode=1 echo as_func_ret_success failed. fi if as_func_ret_failure; then exitcode=1 echo as_func_ret_failure succeeded. fi if ( set x; as_func_ret_success y && test x = "$1" ); then : else exitcode=1 echo positional parameters were not saved. fi test $exitcode = 0) || { (exit 1); exit 1; } ( as_lineno_1=$LINENO as_lineno_2=$LINENO test "x$as_lineno_1" != "x$as_lineno_2" && test "x`expr $as_lineno_1 + 1`" = "x$as_lineno_2") || { (exit 1); exit 1; } _ASEOF }; then break fi fi done if test "x$CONFIG_SHELL" != x; then for as_var in BASH_ENV ENV do ($as_unset $as_var) >/dev/null 2>&1 && $as_unset $as_var done export CONFIG_SHELL exec "$CONFIG_SHELL" "$as_myself" ${1+"$@"} fi if test $as_have_required = no; then echo This script requires a shell more modern than all the echo shells that I found on your system. Please install a echo modern shell, or manually run the script under such a echo shell if you do have one. { (exit 1); exit 1; } fi fi fi (eval "as_func_return () { (exit \$1) } as_func_success () { as_func_return 0 } as_func_failure () { as_func_return 1 } as_func_ret_success () { return 0 } as_func_ret_failure () { return 1 } exitcode=0 if as_func_success; then : else exitcode=1 echo as_func_success failed. fi if as_func_failure; then exitcode=1 echo as_func_failure succeeded. fi if as_func_ret_success; then : else exitcode=1 echo as_func_ret_success failed. fi if as_func_ret_failure; then exitcode=1 echo as_func_ret_failure succeeded. fi if ( set x; as_func_ret_success y && test x = \"\$1\" ); then : else exitcode=1 echo positional parameters were not saved. fi test \$exitcode = 0") || { echo No shell found that supports shell functions. echo Please tell autoconf@gnu.org about your system, echo including any error possibly output before this echo message } as_lineno_1=$LINENO as_lineno_2=$LINENO test "x$as_lineno_1" != "x$as_lineno_2" && test "x`expr $as_lineno_1 + 1`" = "x$as_lineno_2" || { # Create $as_me.lineno as a copy of $as_myself, but with $LINENO # uniformly replaced by the line number. The first 'sed' inserts a # line-number line after each line using $LINENO; the second 'sed' # does the real work. The second script uses 'N' to pair each # line-number line with the line containing $LINENO, and appends # trailing '-' during substitution so that $LINENO is not a special # case at line end. # (Raja R Harinath suggested sed '=', and Paul Eggert wrote the # scripts with optimization help from Paolo Bonzini. Blame Lee # E. McMahon (1931-1989) for sed's syntax. :-) sed -n ' p /[$]LINENO/= ' <$as_myself | sed ' s/[$]LINENO.*/&-/ t lineno b :lineno N :loop s/[$]LINENO\([^'$as_cr_alnum'_].*\n\)\(.*\)/\2\1\2/ t loop s/-\n.*// ' >$as_me.lineno && chmod +x "$as_me.lineno" || { echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2 { (exit 1); exit 1; }; } # Don't try to exec as it changes $[0], causing all sort of problems # (the dirname of $[0] is not the place where we might find the # original and so on. Autoconf is especially sensitive to this). . "./$as_me.lineno" # Exit status is that of the last command. exit } if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then as_dirname=dirname else as_dirname=false fi ECHO_C= ECHO_N= ECHO_T= case `echo -n x` in -n*) case `echo 'x\c'` in *c*) ECHO_T=' ';; # ECHO_T is single tab character. *) ECHO_C='\c';; esac;; *) ECHO_N='-n';; esac if expr a : '\(a\)' >/dev/null 2>&1 && test "X`expr 00001 : '.*\(...\)'`" = X001; then as_expr=expr else as_expr=false fi rm -f conf$$ conf$$.exe conf$$.file if test -d conf$$.dir; then rm -f conf$$.dir/conf$$.file else rm -f conf$$.dir mkdir conf$$.dir fi echo >conf$$.file if ln -s conf$$.file conf$$ 2>/dev/null; then as_ln_s='ln -s' # ... but there are two gotchas: # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. # In both cases, we have to default to `cp -p'. ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || as_ln_s='cp -p' elif ln conf$$.file conf$$ 2>/dev/null; then as_ln_s=ln else as_ln_s='cp -p' fi rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file rmdir conf$$.dir 2>/dev/null if mkdir -p . 2>/dev/null; then as_mkdir_p=: else test -d ./-p && rmdir ./-p as_mkdir_p=false fi if test -x / >/dev/null 2>&1; then as_test_x='test -x' else if ls -dL / >/dev/null 2>&1; then as_ls_L_option=L else as_ls_L_option= fi as_test_x=' eval sh -c '\'' if test -d "$1"; then test -d "$1/."; else case $1 in -*)set "./$1";; esac; case `ls -ld'$as_ls_L_option' "$1" 2>/dev/null` in ???[sx]*):;;*)false;;esac;fi '\'' sh ' fi as_executable_p=$as_test_x # Sed expression to map a string onto a valid CPP name. as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" # Sed expression to map a string onto a valid variable name. as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" exec 7<&0 &1 # Name of the host. # hostname on some systems (SVR3.2, Linux) returns a bogus exit status, # so uname gets run too. ac_hostname=`(hostname || uname -n) 2>/dev/null | sed 1q` # # Initializations. # ac_default_prefix=/usr/local ac_clean_files= ac_config_libobj_dir=. LIBOBJS= cross_compiling=no subdirs= MFLAGS= MAKEFLAGS= SHELL=${CONFIG_SHELL-/bin/sh} # Identity of this package. PACKAGE_NAME= PACKAGE_TARNAME= PACKAGE_VERSION= PACKAGE_STRING= PACKAGE_BUGREPORT= ac_unique_file="mxml.h" # Factoring default headers for most tests. ac_includes_default="\ #include #ifdef HAVE_SYS_TYPES_H # include #endif #ifdef HAVE_SYS_STAT_H # include #endif #ifdef STDC_HEADERS # include # include #else # ifdef HAVE_STDLIB_H # include # endif #endif #ifdef HAVE_STRING_H # if !defined STDC_HEADERS && defined HAVE_MEMORY_H # include # endif # include #endif #ifdef HAVE_STRINGS_H # include #endif #ifdef HAVE_INTTYPES_H # include #endif #ifdef HAVE_STDINT_H # include #endif #ifdef HAVE_UNISTD_H # include #endif" ac_subst_vars='SHELL PATH_SEPARATOR PACKAGE_NAME PACKAGE_TARNAME PACKAGE_VERSION PACKAGE_STRING PACKAGE_BUGREPORT exec_prefix prefix program_transform_name bindir sbindir libexecdir datarootdir datadir sysconfdir sharedstatedir localstatedir includedir oldincludedir docdir infodir htmldir dvidir pdfdir psdir libdir localedir mandir DEFS ECHO_C ECHO_N ECHO_T LIBS build_alias host_alias target_alias VERSION LDFLAGS OPTIM ARCHFLAGS CC CFLAGS CPPFLAGS ac_ct_CC EXEEXT OBJEXT CXX CXXFLAGS ac_ct_CXX INSTALL_PROGRAM INSTALL_SCRIPT INSTALL_DATA RANLIB AR CP LN MKDIR RM ARFLAGS CPP GREP EGREP PTHREAD_FLAGS PTHREAD_LIBS DSO DSOFLAGS LIBMXML PICFLAG PC_CFLAGS PC_LIBS LIBOBJS LTLIBOBJS' ac_subst_files='' ac_precious_vars='build_alias host_alias target_alias CC CFLAGS LDFLAGS LIBS CPPFLAGS CXX CXXFLAGS CCC CPP' # Initialize some variables set by options. ac_init_help= ac_init_version=false # The variables have the same names as the options, with # dashes changed to underlines. cache_file=/dev/null exec_prefix=NONE no_create= no_recursion= prefix=NONE program_prefix=NONE program_suffix=NONE program_transform_name=s,x,x, silent= site= srcdir= verbose= x_includes=NONE x_libraries=NONE # Installation directory options. # These are left unexpanded so users can "make install exec_prefix=/foo" # and all the variables that are supposed to be based on exec_prefix # by default will actually change. # Use braces instead of parens because sh, perl, etc. also accept them. # (The list follows the same order as the GNU Coding Standards.) bindir='${exec_prefix}/bin' sbindir='${exec_prefix}/sbin' libexecdir='${exec_prefix}/libexec' datarootdir='${prefix}/share' datadir='${datarootdir}' sysconfdir='${prefix}/etc' sharedstatedir='${prefix}/com' localstatedir='${prefix}/var' includedir='${prefix}/include' oldincludedir='/usr/include' docdir='${datarootdir}/doc/${PACKAGE}' infodir='${datarootdir}/info' htmldir='${docdir}' dvidir='${docdir}' pdfdir='${docdir}' psdir='${docdir}' libdir='${exec_prefix}/lib' localedir='${datarootdir}/locale' mandir='${datarootdir}/man' ac_prev= ac_dashdash= for ac_option do # If the previous option needs an argument, assign it. if test -n "$ac_prev"; then eval $ac_prev=\$ac_option ac_prev= continue fi case $ac_option in *=*) ac_optarg=`expr "X$ac_option" : '[^=]*=\(.*\)'` ;; *) ac_optarg=yes ;; esac # Accept the important Cygnus configure options, so we can diagnose typos. case $ac_dashdash$ac_option in --) ac_dashdash=yes ;; -bindir | --bindir | --bindi | --bind | --bin | --bi) ac_prev=bindir ;; -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*) bindir=$ac_optarg ;; -build | --build | --buil | --bui | --bu) ac_prev=build_alias ;; -build=* | --build=* | --buil=* | --bui=* | --bu=*) build_alias=$ac_optarg ;; -cache-file | --cache-file | --cache-fil | --cache-fi \ | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c) ac_prev=cache_file ;; -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \ | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*) cache_file=$ac_optarg ;; --config-cache | -C) cache_file=config.cache ;; -datadir | --datadir | --datadi | --datad) ac_prev=datadir ;; -datadir=* | --datadir=* | --datadi=* | --datad=*) datadir=$ac_optarg ;; -datarootdir | --datarootdir | --datarootdi | --datarootd | --dataroot \ | --dataroo | --dataro | --datar) ac_prev=datarootdir ;; -datarootdir=* | --datarootdir=* | --datarootdi=* | --datarootd=* \ | --dataroot=* | --dataroo=* | --dataro=* | --datar=*) datarootdir=$ac_optarg ;; -disable-* | --disable-*) ac_feature=`expr "x$ac_option" : 'x-*disable-\(.*\)'` # Reject names that are not valid shell variable names. expr "x$ac_feature" : ".*[^-._$as_cr_alnum]" >/dev/null && { echo "$as_me: error: invalid feature name: $ac_feature" >&2 { (exit 1); exit 1; }; } ac_feature=`echo $ac_feature | sed 's/[-.]/_/g'` eval enable_$ac_feature=no ;; -docdir | --docdir | --docdi | --doc | --do) ac_prev=docdir ;; -docdir=* | --docdir=* | --docdi=* | --doc=* | --do=*) docdir=$ac_optarg ;; -dvidir | --dvidir | --dvidi | --dvid | --dvi | --dv) ac_prev=dvidir ;; -dvidir=* | --dvidir=* | --dvidi=* | --dvid=* | --dvi=* | --dv=*) dvidir=$ac_optarg ;; -enable-* | --enable-*) ac_feature=`expr "x$ac_option" : 'x-*enable-\([^=]*\)'` # Reject names that are not valid shell variable names. expr "x$ac_feature" : ".*[^-._$as_cr_alnum]" >/dev/null && { echo "$as_me: error: invalid feature name: $ac_feature" >&2 { (exit 1); exit 1; }; } ac_feature=`echo $ac_feature | sed 's/[-.]/_/g'` eval enable_$ac_feature=\$ac_optarg ;; -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \ | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \ | --exec | --exe | --ex) ac_prev=exec_prefix ;; -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \ | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \ | --exec=* | --exe=* | --ex=*) exec_prefix=$ac_optarg ;; -gas | --gas | --ga | --g) # Obsolete; use --with-gas. with_gas=yes ;; -help | --help | --hel | --he | -h) ac_init_help=long ;; -help=r* | --help=r* | --hel=r* | --he=r* | -hr*) ac_init_help=recursive ;; -help=s* | --help=s* | --hel=s* | --he=s* | -hs*) ac_init_help=short ;; -host | --host | --hos | --ho) ac_prev=host_alias ;; -host=* | --host=* | --hos=* | --ho=*) host_alias=$ac_optarg ;; -htmldir | --htmldir | --htmldi | --htmld | --html | --htm | --ht) ac_prev=htmldir ;; -htmldir=* | --htmldir=* | --htmldi=* | --htmld=* | --html=* | --htm=* \ | --ht=*) htmldir=$ac_optarg ;; -includedir | --includedir | --includedi | --included | --include \ | --includ | --inclu | --incl | --inc) ac_prev=includedir ;; -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \ | --includ=* | --inclu=* | --incl=* | --inc=*) includedir=$ac_optarg ;; -infodir | --infodir | --infodi | --infod | --info | --inf) ac_prev=infodir ;; -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*) infodir=$ac_optarg ;; -libdir | --libdir | --libdi | --libd) ac_prev=libdir ;; -libdir=* | --libdir=* | --libdi=* | --libd=*) libdir=$ac_optarg ;; -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \ | --libexe | --libex | --libe) ac_prev=libexecdir ;; -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \ | --libexe=* | --libex=* | --libe=*) libexecdir=$ac_optarg ;; -localedir | --localedir | --localedi | --localed | --locale) ac_prev=localedir ;; -localedir=* | --localedir=* | --localedi=* | --localed=* | --locale=*) localedir=$ac_optarg ;; -localstatedir | --localstatedir | --localstatedi | --localstated \ | --localstate | --localstat | --localsta | --localst | --locals) ac_prev=localstatedir ;; -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \ | --localstate=* | --localstat=* | --localsta=* | --localst=* | --locals=*) localstatedir=$ac_optarg ;; -mandir | --mandir | --mandi | --mand | --man | --ma | --m) ac_prev=mandir ;; -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*) mandir=$ac_optarg ;; -nfp | --nfp | --nf) # Obsolete; use --without-fp. with_fp=no ;; -no-create | --no-create | --no-creat | --no-crea | --no-cre \ | --no-cr | --no-c | -n) no_create=yes ;; -no-recursion | --no-recursion | --no-recursio | --no-recursi \ | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r) no_recursion=yes ;; -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \ | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \ | --oldin | --oldi | --old | --ol | --o) ac_prev=oldincludedir ;; -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \ | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \ | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*) oldincludedir=$ac_optarg ;; -prefix | --prefix | --prefi | --pref | --pre | --pr | --p) ac_prev=prefix ;; -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*) prefix=$ac_optarg ;; -program-prefix | --program-prefix | --program-prefi | --program-pref \ | --program-pre | --program-pr | --program-p) ac_prev=program_prefix ;; -program-prefix=* | --program-prefix=* | --program-prefi=* \ | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*) program_prefix=$ac_optarg ;; -program-suffix | --program-suffix | --program-suffi | --program-suff \ | --program-suf | --program-su | --program-s) ac_prev=program_suffix ;; -program-suffix=* | --program-suffix=* | --program-suffi=* \ | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*) program_suffix=$ac_optarg ;; -program-transform-name | --program-transform-name \ | --program-transform-nam | --program-transform-na \ | --program-transform-n | --program-transform- \ | --program-transform | --program-transfor \ | --program-transfo | --program-transf \ | --program-trans | --program-tran \ | --progr-tra | --program-tr | --program-t) ac_prev=program_transform_name ;; -program-transform-name=* | --program-transform-name=* \ | --program-transform-nam=* | --program-transform-na=* \ | --program-transform-n=* | --program-transform-=* \ | --program-transform=* | --program-transfor=* \ | --program-transfo=* | --program-transf=* \ | --program-trans=* | --program-tran=* \ | --progr-tra=* | --program-tr=* | --program-t=*) program_transform_name=$ac_optarg ;; -pdfdir | --pdfdir | --pdfdi | --pdfd | --pdf | --pd) ac_prev=pdfdir ;; -pdfdir=* | --pdfdir=* | --pdfdi=* | --pdfd=* | --pdf=* | --pd=*) pdfdir=$ac_optarg ;; -psdir | --psdir | --psdi | --psd | --ps) ac_prev=psdir ;; -psdir=* | --psdir=* | --psdi=* | --psd=* | --ps=*) psdir=$ac_optarg ;; -q | -quiet | --quiet | --quie | --qui | --qu | --q \ | -silent | --silent | --silen | --sile | --sil) silent=yes ;; -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb) ac_prev=sbindir ;; -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \ | --sbi=* | --sb=*) sbindir=$ac_optarg ;; -sharedstatedir | --sharedstatedir | --sharedstatedi \ | --sharedstated | --sharedstate | --sharedstat | --sharedsta \ | --sharedst | --shareds | --shared | --share | --shar \ | --sha | --sh) ac_prev=sharedstatedir ;; -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \ | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \ | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \ | --sha=* | --sh=*) sharedstatedir=$ac_optarg ;; -site | --site | --sit) ac_prev=site ;; -site=* | --site=* | --sit=*) site=$ac_optarg ;; -srcdir | --srcdir | --srcdi | --srcd | --src | --sr) ac_prev=srcdir ;; -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*) srcdir=$ac_optarg ;; -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \ | --syscon | --sysco | --sysc | --sys | --sy) ac_prev=sysconfdir ;; -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \ | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*) sysconfdir=$ac_optarg ;; -target | --target | --targe | --targ | --tar | --ta | --t) ac_prev=target_alias ;; -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*) target_alias=$ac_optarg ;; -v | -verbose | --verbose | --verbos | --verbo | --verb) verbose=yes ;; -version | --version | --versio | --versi | --vers | -V) ac_init_version=: ;; -with-* | --with-*) ac_package=`expr "x$ac_option" : 'x-*with-\([^=]*\)'` # Reject names that are not valid shell variable names. expr "x$ac_package" : ".*[^-._$as_cr_alnum]" >/dev/null && { echo "$as_me: error: invalid package name: $ac_package" >&2 { (exit 1); exit 1; }; } ac_package=`echo $ac_package | sed 's/[-.]/_/g'` eval with_$ac_package=\$ac_optarg ;; -without-* | --without-*) ac_package=`expr "x$ac_option" : 'x-*without-\(.*\)'` # Reject names that are not valid shell variable names. expr "x$ac_package" : ".*[^-._$as_cr_alnum]" >/dev/null && { echo "$as_me: error: invalid package name: $ac_package" >&2 { (exit 1); exit 1; }; } ac_package=`echo $ac_package | sed 's/[-.]/_/g'` eval with_$ac_package=no ;; --x) # Obsolete; use --with-x. with_x=yes ;; -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \ | --x-incl | --x-inc | --x-in | --x-i) ac_prev=x_includes ;; -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \ | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*) x_includes=$ac_optarg ;; -x-libraries | --x-libraries | --x-librarie | --x-librari \ | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l) ac_prev=x_libraries ;; -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \ | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*) x_libraries=$ac_optarg ;; -*) { echo "$as_me: error: unrecognized option: $ac_option Try \`$0 --help' for more information." >&2 { (exit 1); exit 1; }; } ;; *=*) ac_envvar=`expr "x$ac_option" : 'x\([^=]*\)='` # Reject names that are not valid shell variable names. expr "x$ac_envvar" : ".*[^_$as_cr_alnum]" >/dev/null && { echo "$as_me: error: invalid variable name: $ac_envvar" >&2 { (exit 1); exit 1; }; } eval $ac_envvar=\$ac_optarg export $ac_envvar ;; *) # FIXME: should be removed in autoconf 3.0. echo "$as_me: WARNING: you should use --build, --host, --target" >&2 expr "x$ac_option" : ".*[^-._$as_cr_alnum]" >/dev/null && echo "$as_me: WARNING: invalid host type: $ac_option" >&2 : ${build_alias=$ac_option} ${host_alias=$ac_option} ${target_alias=$ac_option} ;; esac done if test -n "$ac_prev"; then ac_option=--`echo $ac_prev | sed 's/_/-/g'` { echo "$as_me: error: missing argument to $ac_option" >&2 { (exit 1); exit 1; }; } fi # Be sure to have absolute directory names. for ac_var in exec_prefix prefix bindir sbindir libexecdir datarootdir \ datadir sysconfdir sharedstatedir localstatedir includedir \ oldincludedir docdir infodir htmldir dvidir pdfdir psdir \ libdir localedir mandir do eval ac_val=\$$ac_var case $ac_val in [\\/$]* | ?:[\\/]* ) continue;; NONE | '' ) case $ac_var in *prefix ) continue;; esac;; esac { echo "$as_me: error: expected an absolute directory name for --$ac_var: $ac_val" >&2 { (exit 1); exit 1; }; } done # There might be people who depend on the old broken behavior: `$host' # used to hold the argument of --host etc. # FIXME: To remove some day. build=$build_alias host=$host_alias target=$target_alias # FIXME: To remove some day. if test "x$host_alias" != x; then if test "x$build_alias" = x; then cross_compiling=maybe echo "$as_me: WARNING: If you wanted to set the --build type, don't use --host. If a cross compiler is detected then cross compile mode will be used." >&2 elif test "x$build_alias" != "x$host_alias"; then cross_compiling=yes fi fi ac_tool_prefix= test -n "$host_alias" && ac_tool_prefix=$host_alias- test "$silent" = yes && exec 6>/dev/null ac_pwd=`pwd` && test -n "$ac_pwd" && ac_ls_di=`ls -di .` && ac_pwd_ls_di=`cd "$ac_pwd" && ls -di .` || { echo "$as_me: error: Working directory cannot be determined" >&2 { (exit 1); exit 1; }; } test "X$ac_ls_di" = "X$ac_pwd_ls_di" || { echo "$as_me: error: pwd does not report name of working directory" >&2 { (exit 1); exit 1; }; } # Find the source files, if location was not specified. if test -z "$srcdir"; then ac_srcdir_defaulted=yes # Try the directory containing this script, then the parent directory. ac_confdir=`$as_dirname -- "$0" || $as_expr X"$0" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$0" : 'X\(//\)[^/]' \| \ X"$0" : 'X\(//\)$' \| \ X"$0" : 'X\(/\)' \| . 2>/dev/null || echo X"$0" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q'` srcdir=$ac_confdir if test ! -r "$srcdir/$ac_unique_file"; then srcdir=.. fi else ac_srcdir_defaulted=no fi if test ! -r "$srcdir/$ac_unique_file"; then test "$ac_srcdir_defaulted" = yes && srcdir="$ac_confdir or .." { echo "$as_me: error: cannot find sources ($ac_unique_file) in $srcdir" >&2 { (exit 1); exit 1; }; } fi ac_msg="sources are in $srcdir, but \`cd $srcdir' does not work" ac_abs_confdir=`( cd "$srcdir" && test -r "./$ac_unique_file" || { echo "$as_me: error: $ac_msg" >&2 { (exit 1); exit 1; }; } pwd)` # When building in place, set srcdir=. if test "$ac_abs_confdir" = "$ac_pwd"; then srcdir=. fi # Remove unnecessary trailing slashes from srcdir. # Double slashes in file names in object file debugging info # mess up M-x gdb in Emacs. case $srcdir in */) srcdir=`expr "X$srcdir" : 'X\(.*[^/]\)' \| "X$srcdir" : 'X\(.*\)'`;; esac for ac_var in $ac_precious_vars; do eval ac_env_${ac_var}_set=\${${ac_var}+set} eval ac_env_${ac_var}_value=\$${ac_var} eval ac_cv_env_${ac_var}_set=\${${ac_var}+set} eval ac_cv_env_${ac_var}_value=\$${ac_var} done # # Report the --help message. # if test "$ac_init_help" = "long"; then # Omit some internal or obsolete options to make the list less imposing. # This message is too long to be a string in the A/UX 3.1 sh. cat <<_ACEOF \`configure' configures this package to adapt to many kinds of systems. Usage: $0 [OPTION]... [VAR=VALUE]... To assign environment variables (e.g., CC, CFLAGS...), specify them as VAR=VALUE. See below for descriptions of some of the useful variables. Defaults for the options are specified in brackets. Configuration: -h, --help display this help and exit --help=short display options specific to this package --help=recursive display the short help of all the included packages -V, --version display version information and exit -q, --quiet, --silent do not print \`checking...' messages --cache-file=FILE cache test results in FILE [disabled] -C, --config-cache alias for \`--cache-file=config.cache' -n, --no-create do not create output files --srcdir=DIR find the sources in DIR [configure dir or \`..'] Installation directories: --prefix=PREFIX install architecture-independent files in PREFIX [$ac_default_prefix] --exec-prefix=EPREFIX install architecture-dependent files in EPREFIX [PREFIX] By default, \`make install' will install all the files in \`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc. You can specify an installation prefix other than \`$ac_default_prefix' using \`--prefix', for instance \`--prefix=\$HOME'. For better control, use the options below. Fine tuning of the installation directories: --bindir=DIR user executables [EPREFIX/bin] --sbindir=DIR system admin executables [EPREFIX/sbin] --libexecdir=DIR program executables [EPREFIX/libexec] --sysconfdir=DIR read-only single-machine data [PREFIX/etc] --sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com] --localstatedir=DIR modifiable single-machine data [PREFIX/var] --libdir=DIR object code libraries [EPREFIX/lib] --includedir=DIR C header files [PREFIX/include] --oldincludedir=DIR C header files for non-gcc [/usr/include] --datarootdir=DIR read-only arch.-independent data root [PREFIX/share] --datadir=DIR read-only architecture-independent data [DATAROOTDIR] --infodir=DIR info documentation [DATAROOTDIR/info] --localedir=DIR locale-dependent data [DATAROOTDIR/locale] --mandir=DIR man documentation [DATAROOTDIR/man] --docdir=DIR documentation root [DATAROOTDIR/doc/PACKAGE] --htmldir=DIR html documentation [DOCDIR] --dvidir=DIR dvi documentation [DOCDIR] --pdfdir=DIR pdf documentation [DOCDIR] --psdir=DIR ps documentation [DOCDIR] _ACEOF cat <<\_ACEOF _ACEOF fi if test -n "$ac_init_help"; then cat <<\_ACEOF Optional Features: --disable-FEATURE do not include FEATURE (same as --enable-FEATURE=no) --enable-FEATURE[=ARG] include FEATURE [ARG=yes] --enable-debug turn on debugging, default=no --enable-threads enable multi-threading support --enable-shared turn on shared libraries, default=no Optional Packages: --with-PACKAGE[=ARG] use PACKAGE [ARG=yes] --without-PACKAGE do not use PACKAGE (same as --with-PACKAGE=no) --with-ansi set full ANSI C mode, default=no --with-archflags set additional architecture flags, default=none --with-docdir set directory for documentation, default=${prefix}/share/doc/mxml --with-vsnprintf use vsnprintf emulation functions, default=auto Some influential environment variables: CC C compiler command CFLAGS C compiler flags LDFLAGS linker flags, e.g. -L if you have libraries in a nonstandard directory LIBS libraries to pass to the linker, e.g. -l CPPFLAGS C/C++/Objective C preprocessor flags, e.g. -I if you have headers in a nonstandard directory CXX C++ compiler command CXXFLAGS C++ compiler flags CPP C preprocessor Use these variables to override the choices made by `configure' or to help it to find libraries and programs with nonstandard names/locations. _ACEOF ac_status=$? fi if test "$ac_init_help" = "recursive"; then # If there are subdirs, report their specific --help. for ac_dir in : $ac_subdirs_all; do test "x$ac_dir" = x: && continue test -d "$ac_dir" || continue ac_builddir=. case "$ac_dir" in .) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;; *) ac_dir_suffix=/`echo "$ac_dir" | sed 's,^\.[\\/],,'` # A ".." for each directory in $ac_dir_suffix. ac_top_builddir_sub=`echo "$ac_dir_suffix" | sed 's,/[^\\/]*,/..,g;s,/,,'` case $ac_top_builddir_sub in "") ac_top_builddir_sub=. ac_top_build_prefix= ;; *) ac_top_build_prefix=$ac_top_builddir_sub/ ;; esac ;; esac ac_abs_top_builddir=$ac_pwd ac_abs_builddir=$ac_pwd$ac_dir_suffix # for backward compatibility: ac_top_builddir=$ac_top_build_prefix case $srcdir in .) # We are building in place. ac_srcdir=. ac_top_srcdir=$ac_top_builddir_sub ac_abs_top_srcdir=$ac_pwd ;; [\\/]* | ?:[\\/]* ) # Absolute name. ac_srcdir=$srcdir$ac_dir_suffix; ac_top_srcdir=$srcdir ac_abs_top_srcdir=$srcdir ;; *) # Relative name. ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix ac_top_srcdir=$ac_top_build_prefix$srcdir ac_abs_top_srcdir=$ac_pwd/$srcdir ;; esac ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix cd "$ac_dir" || { ac_status=$?; continue; } # Check for guested configure. if test -f "$ac_srcdir/configure.gnu"; then echo && $SHELL "$ac_srcdir/configure.gnu" --help=recursive elif test -f "$ac_srcdir/configure"; then echo && $SHELL "$ac_srcdir/configure" --help=recursive else echo "$as_me: WARNING: no configuration information is in $ac_dir" >&2 fi || ac_status=$? cd "$ac_pwd" || { ac_status=$?; break; } done fi test -n "$ac_init_help" && exit $ac_status if $ac_init_version; then cat <<\_ACEOF configure generated by GNU Autoconf 2.61 Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006 Free Software Foundation, Inc. This configure script is free software; the Free Software Foundation gives unlimited permission to copy, distribute and modify it. _ACEOF exit fi cat >config.log <<_ACEOF This file contains any messages produced by compilers while running configure, to aid debugging if configure makes a mistake. It was created by $as_me, which was generated by GNU Autoconf 2.61. Invocation command line was $ $0 $@ _ACEOF exec 5>>config.log { cat <<_ASUNAME ## --------- ## ## Platform. ## ## --------- ## hostname = `(hostname || uname -n) 2>/dev/null | sed 1q` uname -m = `(uname -m) 2>/dev/null || echo unknown` uname -r = `(uname -r) 2>/dev/null || echo unknown` uname -s = `(uname -s) 2>/dev/null || echo unknown` uname -v = `(uname -v) 2>/dev/null || echo unknown` /usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null || echo unknown` /bin/uname -X = `(/bin/uname -X) 2>/dev/null || echo unknown` /bin/arch = `(/bin/arch) 2>/dev/null || echo unknown` /usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null || echo unknown` /usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null || echo unknown` /usr/bin/hostinfo = `(/usr/bin/hostinfo) 2>/dev/null || echo unknown` /bin/machine = `(/bin/machine) 2>/dev/null || echo unknown` /usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null || echo unknown` /bin/universe = `(/bin/universe) 2>/dev/null || echo unknown` _ASUNAME as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. echo "PATH: $as_dir" done IFS=$as_save_IFS } >&5 cat >&5 <<_ACEOF ## ----------- ## ## Core tests. ## ## ----------- ## _ACEOF # Keep a trace of the command line. # Strip out --no-create and --no-recursion so they do not pile up. # Strip out --silent because we don't want to record it for future runs. # Also quote any args containing shell meta-characters. # Make two passes to allow for proper duplicate-argument suppression. ac_configure_args= ac_configure_args0= ac_configure_args1= ac_must_keep_next=false for ac_pass in 1 2 do for ac_arg do case $ac_arg in -no-create | --no-c* | -n | -no-recursion | --no-r*) continue ;; -q | -quiet | --quiet | --quie | --qui | --qu | --q \ | -silent | --silent | --silen | --sile | --sil) continue ;; *\'*) ac_arg=`echo "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;; esac case $ac_pass in 1) ac_configure_args0="$ac_configure_args0 '$ac_arg'" ;; 2) ac_configure_args1="$ac_configure_args1 '$ac_arg'" if test $ac_must_keep_next = true; then ac_must_keep_next=false # Got value, back to normal. else case $ac_arg in *=* | --config-cache | -C | -disable-* | --disable-* \ | -enable-* | --enable-* | -gas | --g* | -nfp | --nf* \ | -q | -quiet | --q* | -silent | --sil* | -v | -verb* \ | -with-* | --with-* | -without-* | --without-* | --x) case "$ac_configure_args0 " in "$ac_configure_args1"*" '$ac_arg' "* ) continue ;; esac ;; -* ) ac_must_keep_next=true ;; esac fi ac_configure_args="$ac_configure_args '$ac_arg'" ;; esac done done $as_unset ac_configure_args0 || test "${ac_configure_args0+set}" != set || { ac_configure_args0=; export ac_configure_args0; } $as_unset ac_configure_args1 || test "${ac_configure_args1+set}" != set || { ac_configure_args1=; export ac_configure_args1; } # When interrupted or exit'd, cleanup temporary files, and complete # config.log. We remove comments because anyway the quotes in there # would cause problems or look ugly. # WARNING: Use '\'' to represent an apostrophe within the trap. # WARNING: Do not start the trap code with a newline, due to a FreeBSD 4.0 bug. trap 'exit_status=$? # Save into config.log some information that might help in debugging. { echo cat <<\_ASBOX ## ---------------- ## ## Cache variables. ## ## ---------------- ## _ASBOX echo # The following way of writing the cache mishandles newlines in values, ( for ac_var in `(set) 2>&1 | sed -n '\''s/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'\''`; do eval ac_val=\$$ac_var case $ac_val in #( *${as_nl}*) case $ac_var in #( *_cv_*) { echo "$as_me:$LINENO: WARNING: Cache variable $ac_var contains a newline." >&5 echo "$as_me: WARNING: Cache variable $ac_var contains a newline." >&2;} ;; esac case $ac_var in #( _ | IFS | as_nl) ;; #( *) $as_unset $ac_var ;; esac ;; esac done (set) 2>&1 | case $as_nl`(ac_space='\'' '\''; set) 2>&1` in #( *${as_nl}ac_space=\ *) sed -n \ "s/'\''/'\''\\\\'\'''\''/g; s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\''\\2'\''/p" ;; #( *) sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p" ;; esac | sort ) echo cat <<\_ASBOX ## ----------------- ## ## Output variables. ## ## ----------------- ## _ASBOX echo for ac_var in $ac_subst_vars do eval ac_val=\$$ac_var case $ac_val in *\'\''*) ac_val=`echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;; esac echo "$ac_var='\''$ac_val'\''" done | sort echo if test -n "$ac_subst_files"; then cat <<\_ASBOX ## ------------------- ## ## File substitutions. ## ## ------------------- ## _ASBOX echo for ac_var in $ac_subst_files do eval ac_val=\$$ac_var case $ac_val in *\'\''*) ac_val=`echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;; esac echo "$ac_var='\''$ac_val'\''" done | sort echo fi if test -s confdefs.h; then cat <<\_ASBOX ## ----------- ## ## confdefs.h. ## ## ----------- ## _ASBOX echo cat confdefs.h echo fi test "$ac_signal" != 0 && echo "$as_me: caught signal $ac_signal" echo "$as_me: exit $exit_status" } >&5 rm -f core *.core core.conftest.* && rm -f -r conftest* confdefs* conf$$* $ac_clean_files && exit $exit_status ' 0 for ac_signal in 1 2 13 15; do trap 'ac_signal='$ac_signal'; { (exit 1); exit 1; }' $ac_signal done ac_signal=0 # confdefs.h avoids OS command line length limits that DEFS can exceed. rm -f -r conftest* confdefs.h # Predefined preprocessor variables. cat >>confdefs.h <<_ACEOF #define PACKAGE_NAME "$PACKAGE_NAME" _ACEOF cat >>confdefs.h <<_ACEOF #define PACKAGE_TARNAME "$PACKAGE_TARNAME" _ACEOF cat >>confdefs.h <<_ACEOF #define PACKAGE_VERSION "$PACKAGE_VERSION" _ACEOF cat >>confdefs.h <<_ACEOF #define PACKAGE_STRING "$PACKAGE_STRING" _ACEOF cat >>confdefs.h <<_ACEOF #define PACKAGE_BUGREPORT "$PACKAGE_BUGREPORT" _ACEOF # Let the site file select an alternate cache file if it wants to. # Prefer explicitly selected file to automatically selected ones. if test -n "$CONFIG_SITE"; then set x "$CONFIG_SITE" elif test "x$prefix" != xNONE; then set x "$prefix/share/config.site" "$prefix/etc/config.site" else set x "$ac_default_prefix/share/config.site" \ "$ac_default_prefix/etc/config.site" fi shift for ac_site_file do if test -r "$ac_site_file"; then { echo "$as_me:$LINENO: loading site script $ac_site_file" >&5 echo "$as_me: loading site script $ac_site_file" >&6;} sed 's/^/| /' "$ac_site_file" >&5 . "$ac_site_file" fi done if test -r "$cache_file"; then # Some versions of bash will fail to source /dev/null (special # files actually), so we avoid doing that. if test -f "$cache_file"; then { echo "$as_me:$LINENO: loading cache $cache_file" >&5 echo "$as_me: loading cache $cache_file" >&6;} case $cache_file in [\\/]* | ?:[\\/]* ) . "$cache_file";; *) . "./$cache_file";; esac fi else { echo "$as_me:$LINENO: creating cache $cache_file" >&5 echo "$as_me: creating cache $cache_file" >&6;} >$cache_file fi # Check that the precious variables saved in the cache have kept the same # value. ac_cache_corrupted=false for ac_var in $ac_precious_vars; do eval ac_old_set=\$ac_cv_env_${ac_var}_set eval ac_new_set=\$ac_env_${ac_var}_set eval ac_old_val=\$ac_cv_env_${ac_var}_value eval ac_new_val=\$ac_env_${ac_var}_value case $ac_old_set,$ac_new_set in set,) { echo "$as_me:$LINENO: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&5 echo "$as_me: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&2;} ac_cache_corrupted=: ;; ,set) { echo "$as_me:$LINENO: error: \`$ac_var' was not set in the previous run" >&5 echo "$as_me: error: \`$ac_var' was not set in the previous run" >&2;} ac_cache_corrupted=: ;; ,);; *) if test "x$ac_old_val" != "x$ac_new_val"; then { echo "$as_me:$LINENO: error: \`$ac_var' has changed since the previous run:" >&5 echo "$as_me: error: \`$ac_var' has changed since the previous run:" >&2;} { echo "$as_me:$LINENO: former value: $ac_old_val" >&5 echo "$as_me: former value: $ac_old_val" >&2;} { echo "$as_me:$LINENO: current value: $ac_new_val" >&5 echo "$as_me: current value: $ac_new_val" >&2;} ac_cache_corrupted=: fi;; esac # Pass precious variables to config.status. if test "$ac_new_set" = set; then case $ac_new_val in *\'*) ac_arg=$ac_var=`echo "$ac_new_val" | sed "s/'/'\\\\\\\\''/g"` ;; *) ac_arg=$ac_var=$ac_new_val ;; esac case " $ac_configure_args " in *" '$ac_arg' "*) ;; # Avoid dups. Use of quotes ensures accuracy. *) ac_configure_args="$ac_configure_args '$ac_arg'" ;; esac fi done if $ac_cache_corrupted; then { echo "$as_me:$LINENO: error: changes in the environment can compromise the build" >&5 echo "$as_me: error: changes in the environment can compromise the build" >&2;} { { echo "$as_me:$LINENO: error: run \`make distclean' and/or \`rm $cache_file' and start over" >&5 echo "$as_me: error: run \`make distclean' and/or \`rm $cache_file' and start over" >&2;} { (exit 1); exit 1; }; } fi ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu ac_config_headers="$ac_config_headers config.h" VERSION=2.7 cat >>confdefs.h <<_ACEOF #define MXML_VERSION "Mini-XML v$VERSION" _ACEOF CFLAGS="${CFLAGS:=}" CXXFLAGS="${CXXFLAGS:=}" LDFLAGS="${LDFLAGS:=}" OPTIM="" # Check whether --with-ansi was given. if test "${with_ansi+set}" = set; then withval=$with_ansi; use_ansi="$withval" else use_ansi="no" fi # Check whether --with-archflags was given. if test "${with_archflags+set}" = set; then withval=$with_archflags; ARCHFLAGS="$withval" else ARCHFLAGS="" fi # Check whether --enable-debug was given. if test "${enable_debug+set}" = set; then enableval=$enable_debug; if eval "test x$enable_debug = xyes"; then OPTIM="-g" fi fi # Check whether --with-docdir was given. if test "${with_docdir+set}" = set; then withval=$with_docdir; docdir="$withval" else docdir="NONE" fi # Check whether --with-vsnprintf was given. if test "${with_vsnprintf+set}" = set; then withval=$with_vsnprintf; use_vsnprintf="$withval" else use_vsnprintf="no" fi uname=`uname` uversion=`uname -r | sed -e '1,$s/[^0-9]//g'` if test x$uname = xIRIX64; then uname="IRIX" fi ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args. set dummy ${ac_tool_prefix}gcc; ac_word=$2 { echo "$as_me:$LINENO: checking for $ac_word" >&5 echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; } if test "${ac_cv_prog_CC+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_prog_CC="${ac_tool_prefix}gcc" echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then { echo "$as_me:$LINENO: result: $CC" >&5 echo "${ECHO_T}$CC" >&6; } else { echo "$as_me:$LINENO: result: no" >&5 echo "${ECHO_T}no" >&6; } fi fi if test -z "$ac_cv_prog_CC"; then ac_ct_CC=$CC # Extract the first word of "gcc", so it can be a program name with args. set dummy gcc; ac_word=$2 { echo "$as_me:$LINENO: checking for $ac_word" >&5 echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; } if test "${ac_cv_prog_ac_ct_CC+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else if test -n "$ac_ct_CC"; then ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_prog_ac_ct_CC="gcc" echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_CC=$ac_cv_prog_ac_ct_CC if test -n "$ac_ct_CC"; then { echo "$as_me:$LINENO: result: $ac_ct_CC" >&5 echo "${ECHO_T}$ac_ct_CC" >&6; } else { echo "$as_me:$LINENO: result: no" >&5 echo "${ECHO_T}no" >&6; } fi if test "x$ac_ct_CC" = x; then CC="" else case $cross_compiling:$ac_tool_warned in yes:) { echo "$as_me:$LINENO: WARNING: In the future, Autoconf will not detect cross-tools whose name does not start with the host triplet. If you think this configuration is useful to you, please write to autoconf@gnu.org." >&5 echo "$as_me: WARNING: In the future, Autoconf will not detect cross-tools whose name does not start with the host triplet. If you think this configuration is useful to you, please write to autoconf@gnu.org." >&2;} ac_tool_warned=yes ;; esac CC=$ac_ct_CC fi else CC="$ac_cv_prog_CC" fi if test -z "$CC"; then if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args. set dummy ${ac_tool_prefix}cc; ac_word=$2 { echo "$as_me:$LINENO: checking for $ac_word" >&5 echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; } if test "${ac_cv_prog_CC+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_prog_CC="${ac_tool_prefix}cc" echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then { echo "$as_me:$LINENO: result: $CC" >&5 echo "${ECHO_T}$CC" >&6; } else { echo "$as_me:$LINENO: result: no" >&5 echo "${ECHO_T}no" >&6; } fi fi fi if test -z "$CC"; then # Extract the first word of "cc", so it can be a program name with args. set dummy cc; ac_word=$2 { echo "$as_me:$LINENO: checking for $ac_word" >&5 echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; } if test "${ac_cv_prog_CC+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else ac_prog_rejected=no as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then if test "$as_dir/$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then ac_prog_rejected=yes continue fi ac_cv_prog_CC="cc" echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS if test $ac_prog_rejected = yes; then # We found a bogon in the path, so make sure we never use it. set dummy $ac_cv_prog_CC shift if test $# != 0; then # We chose a different compiler from the bogus one. # However, it has the same basename, so the bogon will be chosen # first if we set CC to just the basename; use the full file name. shift ac_cv_prog_CC="$as_dir/$ac_word${1+' '}$@" fi fi fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then { echo "$as_me:$LINENO: result: $CC" >&5 echo "${ECHO_T}$CC" >&6; } else { echo "$as_me:$LINENO: result: no" >&5 echo "${ECHO_T}no" >&6; } fi fi if test -z "$CC"; then if test -n "$ac_tool_prefix"; then for ac_prog in cl.exe do # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. set dummy $ac_tool_prefix$ac_prog; ac_word=$2 { echo "$as_me:$LINENO: checking for $ac_word" >&5 echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; } if test "${ac_cv_prog_CC+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_prog_CC="$ac_tool_prefix$ac_prog" echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then { echo "$as_me:$LINENO: result: $CC" >&5 echo "${ECHO_T}$CC" >&6; } else { echo "$as_me:$LINENO: result: no" >&5 echo "${ECHO_T}no" >&6; } fi test -n "$CC" && break done fi if test -z "$CC"; then ac_ct_CC=$CC for ac_prog in cl.exe do # Extract the first word of "$ac_prog", so it can be a program name with args. set dummy $ac_prog; ac_word=$2 { echo "$as_me:$LINENO: checking for $ac_word" >&5 echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; } if test "${ac_cv_prog_ac_ct_CC+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else if test -n "$ac_ct_CC"; then ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_prog_ac_ct_CC="$ac_prog" echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_CC=$ac_cv_prog_ac_ct_CC if test -n "$ac_ct_CC"; then { echo "$as_me:$LINENO: result: $ac_ct_CC" >&5 echo "${ECHO_T}$ac_ct_CC" >&6; } else { echo "$as_me:$LINENO: result: no" >&5 echo "${ECHO_T}no" >&6; } fi test -n "$ac_ct_CC" && break done if test "x$ac_ct_CC" = x; then CC="" else case $cross_compiling:$ac_tool_warned in yes:) { echo "$as_me:$LINENO: WARNING: In the future, Autoconf will not detect cross-tools whose name does not start with the host triplet. If you think this configuration is useful to you, please write to autoconf@gnu.org." >&5 echo "$as_me: WARNING: In the future, Autoconf will not detect cross-tools whose name does not start with the host triplet. If you think this configuration is useful to you, please write to autoconf@gnu.org." >&2;} ac_tool_warned=yes ;; esac CC=$ac_ct_CC fi fi fi test -z "$CC" && { { echo "$as_me:$LINENO: error: no acceptable C compiler found in \$PATH See \`config.log' for more details." >&5 echo "$as_me: error: no acceptable C compiler found in \$PATH See \`config.log' for more details." >&2;} { (exit 1); exit 1; }; } # Provide some information about the compiler. echo "$as_me:$LINENO: checking for C compiler version" >&5 ac_compiler=`set X $ac_compile; echo $2` { (ac_try="$ac_compiler --version >&5" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 (eval "$ac_compiler --version >&5") 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } { (ac_try="$ac_compiler -v >&5" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 (eval "$ac_compiler -v >&5") 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } { (ac_try="$ac_compiler -V >&5" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 (eval "$ac_compiler -V >&5") 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ int main () { ; return 0; } _ACEOF ac_clean_files_save=$ac_clean_files ac_clean_files="$ac_clean_files a.out a.exe b.out" # Try to create an executable without -o first, disregard a.out. # It will help us diagnose broken compilers, and finding out an intuition # of exeext. { echo "$as_me:$LINENO: checking for C compiler default output file name" >&5 echo $ECHO_N "checking for C compiler default output file name... $ECHO_C" >&6; } ac_link_default=`echo "$ac_link" | sed 's/ -o *conftest[^ ]*//'` # # List of possible output files, starting from the most likely. # The algorithm is not robust to junk in `.', hence go to wildcards (a.*) # only as a last resort. b.out is created by i960 compilers. ac_files='a_out.exe a.exe conftest.exe a.out conftest a.* conftest.* b.out' # # The IRIX 6 linker writes into existing files which may not be # executable, retaining their permissions. Remove them first so a # subsequent execution test works. ac_rmfiles= for ac_file in $ac_files do case $ac_file in *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.o | *.obj ) ;; * ) ac_rmfiles="$ac_rmfiles $ac_file";; esac done rm -f $ac_rmfiles if { (ac_try="$ac_link_default" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 (eval "$ac_link_default") 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; then # Autoconf-2.13 could set the ac_cv_exeext variable to `no'. # So ignore a value of `no', otherwise this would lead to `EXEEXT = no' # in a Makefile. We should not override ac_cv_exeext if it was cached, # so that the user can short-circuit this test for compilers unknown to # Autoconf. for ac_file in $ac_files '' do test -f "$ac_file" || continue case $ac_file in *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.o | *.obj ) ;; [ab].out ) # We found the default executable, but exeext='' is most # certainly right. break;; *.* ) if test "${ac_cv_exeext+set}" = set && test "$ac_cv_exeext" != no; then :; else ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` fi # We set ac_cv_exeext here because the later test for it is not # safe: cross compilers may not add the suffix if given an `-o' # argument, so we may need to know it at that point already. # Even if this section looks crufty: it has the advantage of # actually working. break;; * ) break;; esac done test "$ac_cv_exeext" = no && ac_cv_exeext= else ac_file='' fi { echo "$as_me:$LINENO: result: $ac_file" >&5 echo "${ECHO_T}$ac_file" >&6; } if test -z "$ac_file"; then echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 { { echo "$as_me:$LINENO: error: C compiler cannot create executables See \`config.log' for more details." >&5 echo "$as_me: error: C compiler cannot create executables See \`config.log' for more details." >&2;} { (exit 77); exit 77; }; } fi ac_exeext=$ac_cv_exeext # Check that the compiler produces executables we can run. If not, either # the compiler is broken, or we cross compile. { echo "$as_me:$LINENO: checking whether the C compiler works" >&5 echo $ECHO_N "checking whether the C compiler works... $ECHO_C" >&6; } # FIXME: These cross compiler hacks should be removed for Autoconf 3.0 # If not cross compiling, check that we can run a simple program. if test "$cross_compiling" != yes; then if { ac_try='./$ac_file' { (case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 (eval "$ac_try") 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then cross_compiling=no else if test "$cross_compiling" = maybe; then cross_compiling=yes else { { echo "$as_me:$LINENO: error: cannot run C compiled programs. If you meant to cross compile, use \`--host'. See \`config.log' for more details." >&5 echo "$as_me: error: cannot run C compiled programs. If you meant to cross compile, use \`--host'. See \`config.log' for more details." >&2;} { (exit 1); exit 1; }; } fi fi fi { echo "$as_me:$LINENO: result: yes" >&5 echo "${ECHO_T}yes" >&6; } rm -f a.out a.exe conftest$ac_cv_exeext b.out ac_clean_files=$ac_clean_files_save # Check that the compiler produces executables we can run. If not, either # the compiler is broken, or we cross compile. { echo "$as_me:$LINENO: checking whether we are cross compiling" >&5 echo $ECHO_N "checking whether we are cross compiling... $ECHO_C" >&6; } { echo "$as_me:$LINENO: result: $cross_compiling" >&5 echo "${ECHO_T}$cross_compiling" >&6; } { echo "$as_me:$LINENO: checking for suffix of executables" >&5 echo $ECHO_N "checking for suffix of executables... $ECHO_C" >&6; } if { (ac_try="$ac_link" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 (eval "$ac_link") 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; then # If both `conftest.exe' and `conftest' are `present' (well, observable) # catch `conftest.exe'. For instance with Cygwin, `ls conftest' will # work properly (i.e., refer to `conftest.exe'), while it won't with # `rm'. for ac_file in conftest.exe conftest conftest.*; do test -f "$ac_file" || continue case $ac_file in *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.o | *.obj ) ;; *.* ) ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` break;; * ) break;; esac done else { { echo "$as_me:$LINENO: error: cannot compute suffix of executables: cannot compile and link See \`config.log' for more details." >&5 echo "$as_me: error: cannot compute suffix of executables: cannot compile and link See \`config.log' for more details." >&2;} { (exit 1); exit 1; }; } fi rm -f conftest$ac_cv_exeext { echo "$as_me:$LINENO: result: $ac_cv_exeext" >&5 echo "${ECHO_T}$ac_cv_exeext" >&6; } rm -f conftest.$ac_ext EXEEXT=$ac_cv_exeext ac_exeext=$EXEEXT { echo "$as_me:$LINENO: checking for suffix of object files" >&5 echo $ECHO_N "checking for suffix of object files... $ECHO_C" >&6; } if test "${ac_cv_objext+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ int main () { ; return 0; } _ACEOF rm -f conftest.o conftest.obj if { (ac_try="$ac_compile" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 (eval "$ac_compile") 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; then for ac_file in conftest.o conftest.obj conftest.*; do test -f "$ac_file" || continue; case $ac_file in *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf ) ;; *) ac_cv_objext=`expr "$ac_file" : '.*\.\(.*\)'` break;; esac done else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 { { echo "$as_me:$LINENO: error: cannot compute suffix of object files: cannot compile See \`config.log' for more details." >&5 echo "$as_me: error: cannot compute suffix of object files: cannot compile See \`config.log' for more details." >&2;} { (exit 1); exit 1; }; } fi rm -f conftest.$ac_cv_objext conftest.$ac_ext fi { echo "$as_me:$LINENO: result: $ac_cv_objext" >&5 echo "${ECHO_T}$ac_cv_objext" >&6; } OBJEXT=$ac_cv_objext ac_objext=$OBJEXT { echo "$as_me:$LINENO: checking whether we are using the GNU C compiler" >&5 echo $ECHO_N "checking whether we are using the GNU C compiler... $ECHO_C" >&6; } if test "${ac_cv_c_compiler_gnu+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ int main () { #ifndef __GNUC__ choke me #endif ; return 0; } _ACEOF rm -f conftest.$ac_objext if { (ac_try="$ac_compile" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 (eval "$ac_compile") 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { test -z "$ac_c_werror_flag" || test ! -s conftest.err } && test -s conftest.$ac_objext; then ac_compiler_gnu=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_compiler_gnu=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext ac_cv_c_compiler_gnu=$ac_compiler_gnu fi { echo "$as_me:$LINENO: result: $ac_cv_c_compiler_gnu" >&5 echo "${ECHO_T}$ac_cv_c_compiler_gnu" >&6; } GCC=`test $ac_compiler_gnu = yes && echo yes` ac_test_CFLAGS=${CFLAGS+set} ac_save_CFLAGS=$CFLAGS { echo "$as_me:$LINENO: checking whether $CC accepts -g" >&5 echo $ECHO_N "checking whether $CC accepts -g... $ECHO_C" >&6; } if test "${ac_cv_prog_cc_g+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else ac_save_c_werror_flag=$ac_c_werror_flag ac_c_werror_flag=yes ac_cv_prog_cc_g=no CFLAGS="-g" cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ int main () { ; return 0; } _ACEOF rm -f conftest.$ac_objext if { (ac_try="$ac_compile" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 (eval "$ac_compile") 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { test -z "$ac_c_werror_flag" || test ! -s conftest.err } && test -s conftest.$ac_objext; then ac_cv_prog_cc_g=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 CFLAGS="" cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ int main () { ; return 0; } _ACEOF rm -f conftest.$ac_objext if { (ac_try="$ac_compile" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 (eval "$ac_compile") 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { test -z "$ac_c_werror_flag" || test ! -s conftest.err } && test -s conftest.$ac_objext; then : else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_c_werror_flag=$ac_save_c_werror_flag CFLAGS="-g" cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ int main () { ; return 0; } _ACEOF rm -f conftest.$ac_objext if { (ac_try="$ac_compile" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 (eval "$ac_compile") 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { test -z "$ac_c_werror_flag" || test ! -s conftest.err } && test -s conftest.$ac_objext; then ac_cv_prog_cc_g=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext ac_c_werror_flag=$ac_save_c_werror_flag fi { echo "$as_me:$LINENO: result: $ac_cv_prog_cc_g" >&5 echo "${ECHO_T}$ac_cv_prog_cc_g" >&6; } if test "$ac_test_CFLAGS" = set; then CFLAGS=$ac_save_CFLAGS elif test $ac_cv_prog_cc_g = yes; then if test "$GCC" = yes; then CFLAGS="-g -O2" else CFLAGS="-g" fi else if test "$GCC" = yes; then CFLAGS="-O2" else CFLAGS= fi fi { echo "$as_me:$LINENO: checking for $CC option to accept ISO C89" >&5 echo $ECHO_N "checking for $CC option to accept ISO C89... $ECHO_C" >&6; } if test "${ac_cv_prog_cc_c89+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else ac_cv_prog_cc_c89=no ac_save_CC=$CC cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #include #include #include #include /* Most of the following tests are stolen from RCS 5.7's src/conf.sh. */ struct buf { int x; }; FILE * (*rcsopen) (struct buf *, struct stat *, int); static char *e (p, i) char **p; int i; { return p[i]; } static char *f (char * (*g) (char **, int), char **p, ...) { char *s; va_list v; va_start (v,p); s = g (p, va_arg (v,int)); va_end (v); return s; } /* OSF 4.0 Compaq cc is some sort of almost-ANSI by default. It has function prototypes and stuff, but not '\xHH' hex character constants. These don't provoke an error unfortunately, instead are silently treated as 'x'. The following induces an error, until -std is added to get proper ANSI mode. Curiously '\x00'!='x' always comes out true, for an array size at least. It's necessary to write '\x00'==0 to get something that's true only with -std. */ int osf4_cc_array ['\x00' == 0 ? 1 : -1]; /* IBM C 6 for AIX is almost-ANSI by default, but it replaces macro parameters inside strings and character constants. */ #define FOO(x) 'x' int xlc6_cc_array[FOO(a) == 'x' ? 1 : -1]; int test (int i, double x); struct s1 {int (*f) (int a);}; struct s2 {int (*f) (double a);}; int pairnames (int, char **, FILE *(*)(struct buf *, struct stat *, int), int, int); int argc; char **argv; int main () { return f (e, argv, 0) != argv[0] || f (e, argv, 1) != argv[1]; ; return 0; } _ACEOF for ac_arg in '' -qlanglvl=extc89 -qlanglvl=ansi -std \ -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__" do CC="$ac_save_CC $ac_arg" rm -f conftest.$ac_objext if { (ac_try="$ac_compile" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 (eval "$ac_compile") 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { test -z "$ac_c_werror_flag" || test ! -s conftest.err } && test -s conftest.$ac_objext; then ac_cv_prog_cc_c89=$ac_arg else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 fi rm -f core conftest.err conftest.$ac_objext test "x$ac_cv_prog_cc_c89" != "xno" && break done rm -f conftest.$ac_ext CC=$ac_save_CC fi # AC_CACHE_VAL case "x$ac_cv_prog_cc_c89" in x) { echo "$as_me:$LINENO: result: none needed" >&5 echo "${ECHO_T}none needed" >&6; } ;; xno) { echo "$as_me:$LINENO: result: unsupported" >&5 echo "${ECHO_T}unsupported" >&6; } ;; *) CC="$CC $ac_cv_prog_cc_c89" { echo "$as_me:$LINENO: result: $ac_cv_prog_cc_c89" >&5 echo "${ECHO_T}$ac_cv_prog_cc_c89" >&6; } ;; esac ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu ac_ext=cpp ac_cpp='$CXXCPP $CPPFLAGS' ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_cxx_compiler_gnu if test -z "$CXX"; then if test -n "$CCC"; then CXX=$CCC else if test -n "$ac_tool_prefix"; then for ac_prog in g++ c++ gpp aCC CC cxx cc++ cl.exe FCC KCC RCC xlC_r xlC do # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. set dummy $ac_tool_prefix$ac_prog; ac_word=$2 { echo "$as_me:$LINENO: checking for $ac_word" >&5 echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; } if test "${ac_cv_prog_CXX+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else if test -n "$CXX"; then ac_cv_prog_CXX="$CXX" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_prog_CXX="$ac_tool_prefix$ac_prog" echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi CXX=$ac_cv_prog_CXX if test -n "$CXX"; then { echo "$as_me:$LINENO: result: $CXX" >&5 echo "${ECHO_T}$CXX" >&6; } else { echo "$as_me:$LINENO: result: no" >&5 echo "${ECHO_T}no" >&6; } fi test -n "$CXX" && break done fi if test -z "$CXX"; then ac_ct_CXX=$CXX for ac_prog in g++ c++ gpp aCC CC cxx cc++ cl.exe FCC KCC RCC xlC_r xlC do # Extract the first word of "$ac_prog", so it can be a program name with args. set dummy $ac_prog; ac_word=$2 { echo "$as_me:$LINENO: checking for $ac_word" >&5 echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; } if test "${ac_cv_prog_ac_ct_CXX+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else if test -n "$ac_ct_CXX"; then ac_cv_prog_ac_ct_CXX="$ac_ct_CXX" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_prog_ac_ct_CXX="$ac_prog" echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_CXX=$ac_cv_prog_ac_ct_CXX if test -n "$ac_ct_CXX"; then { echo "$as_me:$LINENO: result: $ac_ct_CXX" >&5 echo "${ECHO_T}$ac_ct_CXX" >&6; } else { echo "$as_me:$LINENO: result: no" >&5 echo "${ECHO_T}no" >&6; } fi test -n "$ac_ct_CXX" && break done if test "x$ac_ct_CXX" = x; then CXX="g++" else case $cross_compiling:$ac_tool_warned in yes:) { echo "$as_me:$LINENO: WARNING: In the future, Autoconf will not detect cross-tools whose name does not start with the host triplet. If you think this configuration is useful to you, please write to autoconf@gnu.org." >&5 echo "$as_me: WARNING: In the future, Autoconf will not detect cross-tools whose name does not start with the host triplet. If you think this configuration is useful to you, please write to autoconf@gnu.org." >&2;} ac_tool_warned=yes ;; esac CXX=$ac_ct_CXX fi fi fi fi # Provide some information about the compiler. echo "$as_me:$LINENO: checking for C++ compiler version" >&5 ac_compiler=`set X $ac_compile; echo $2` { (ac_try="$ac_compiler --version >&5" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 (eval "$ac_compiler --version >&5") 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } { (ac_try="$ac_compiler -v >&5" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 (eval "$ac_compiler -v >&5") 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } { (ac_try="$ac_compiler -V >&5" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 (eval "$ac_compiler -V >&5") 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } { echo "$as_me:$LINENO: checking whether we are using the GNU C++ compiler" >&5 echo $ECHO_N "checking whether we are using the GNU C++ compiler... $ECHO_C" >&6; } if test "${ac_cv_cxx_compiler_gnu+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ int main () { #ifndef __GNUC__ choke me #endif ; return 0; } _ACEOF rm -f conftest.$ac_objext if { (ac_try="$ac_compile" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 (eval "$ac_compile") 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { test -z "$ac_cxx_werror_flag" || test ! -s conftest.err } && test -s conftest.$ac_objext; then ac_compiler_gnu=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_compiler_gnu=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext ac_cv_cxx_compiler_gnu=$ac_compiler_gnu fi { echo "$as_me:$LINENO: result: $ac_cv_cxx_compiler_gnu" >&5 echo "${ECHO_T}$ac_cv_cxx_compiler_gnu" >&6; } GXX=`test $ac_compiler_gnu = yes && echo yes` ac_test_CXXFLAGS=${CXXFLAGS+set} ac_save_CXXFLAGS=$CXXFLAGS { echo "$as_me:$LINENO: checking whether $CXX accepts -g" >&5 echo $ECHO_N "checking whether $CXX accepts -g... $ECHO_C" >&6; } if test "${ac_cv_prog_cxx_g+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else ac_save_cxx_werror_flag=$ac_cxx_werror_flag ac_cxx_werror_flag=yes ac_cv_prog_cxx_g=no CXXFLAGS="-g" cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ int main () { ; return 0; } _ACEOF rm -f conftest.$ac_objext if { (ac_try="$ac_compile" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 (eval "$ac_compile") 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { test -z "$ac_cxx_werror_flag" || test ! -s conftest.err } && test -s conftest.$ac_objext; then ac_cv_prog_cxx_g=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 CXXFLAGS="" cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ int main () { ; return 0; } _ACEOF rm -f conftest.$ac_objext if { (ac_try="$ac_compile" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 (eval "$ac_compile") 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { test -z "$ac_cxx_werror_flag" || test ! -s conftest.err } && test -s conftest.$ac_objext; then : else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_cxx_werror_flag=$ac_save_cxx_werror_flag CXXFLAGS="-g" cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ int main () { ; return 0; } _ACEOF rm -f conftest.$ac_objext if { (ac_try="$ac_compile" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 (eval "$ac_compile") 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { test -z "$ac_cxx_werror_flag" || test ! -s conftest.err } && test -s conftest.$ac_objext; then ac_cv_prog_cxx_g=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext ac_cxx_werror_flag=$ac_save_cxx_werror_flag fi { echo "$as_me:$LINENO: result: $ac_cv_prog_cxx_g" >&5 echo "${ECHO_T}$ac_cv_prog_cxx_g" >&6; } if test "$ac_test_CXXFLAGS" = set; then CXXFLAGS=$ac_save_CXXFLAGS elif test $ac_cv_prog_cxx_g = yes; then if test "$GXX" = yes; then CXXFLAGS="-g -O2" else CXXFLAGS="-g" fi else if test "$GXX" = yes; then CXXFLAGS="-O2" else CXXFLAGS= fi fi ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu ac_aux_dir= for ac_dir in "$srcdir" "$srcdir/.." "$srcdir/../.."; do if test -f "$ac_dir/install-sh"; then ac_aux_dir=$ac_dir ac_install_sh="$ac_aux_dir/install-sh -c" break elif test -f "$ac_dir/install.sh"; then ac_aux_dir=$ac_dir ac_install_sh="$ac_aux_dir/install.sh -c" break elif test -f "$ac_dir/shtool"; then ac_aux_dir=$ac_dir ac_install_sh="$ac_aux_dir/shtool install -c" break fi done if test -z "$ac_aux_dir"; then { { echo "$as_me:$LINENO: error: cannot find install-sh or install.sh in \"$srcdir\" \"$srcdir/..\" \"$srcdir/../..\"" >&5 echo "$as_me: error: cannot find install-sh or install.sh in \"$srcdir\" \"$srcdir/..\" \"$srcdir/../..\"" >&2;} { (exit 1); exit 1; }; } fi # These three variables are undocumented and unsupported, # and are intended to be withdrawn in a future Autoconf release. # They can cause serious problems if a builder's source tree is in a directory # whose full name contains unusual characters. ac_config_guess="$SHELL $ac_aux_dir/config.guess" # Please don't use this var. ac_config_sub="$SHELL $ac_aux_dir/config.sub" # Please don't use this var. ac_configure="$SHELL $ac_aux_dir/configure" # Please don't use this var. # Find a good install program. We prefer a C program (faster), # so one script is as good as another. But avoid the broken or # incompatible versions: # SysV /etc/install, /usr/sbin/install # SunOS /usr/etc/install # IRIX /sbin/install # AIX /bin/install # AmigaOS /C/install, which installs bootblocks on floppy discs # AIX 4 /usr/bin/installbsd, which doesn't work without a -g flag # AFS /usr/afsws/bin/install, which mishandles nonexistent args # SVR4 /usr/ucb/install, which tries to use the nonexistent group "staff" # OS/2's system install, which has a completely different semantic # ./install, which can be erroneously created by make from ./install.sh. { echo "$as_me:$LINENO: checking for a BSD-compatible install" >&5 echo $ECHO_N "checking for a BSD-compatible install... $ECHO_C" >&6; } if test -z "$INSTALL"; then if test "${ac_cv_path_install+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. # Account for people who put trailing slashes in PATH elements. case $as_dir/ in ./ | .// | /cC/* | \ /etc/* | /usr/sbin/* | /usr/etc/* | /sbin/* | /usr/afsws/bin/* | \ ?:\\/os2\\/install\\/* | ?:\\/OS2\\/INSTALL\\/* | \ /usr/ucb/* ) ;; *) # OSF1 and SCO ODT 3.0 have their own names for install. # Don't use installbsd from OSF since it installs stuff as root # by default. for ac_prog in ginstall scoinst install; do for ac_exec_ext in '' $ac_executable_extensions; do if { test -f "$as_dir/$ac_prog$ac_exec_ext" && $as_test_x "$as_dir/$ac_prog$ac_exec_ext"; }; then if test $ac_prog = install && grep dspmsg "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then # AIX install. It has an incompatible calling convention. : elif test $ac_prog = install && grep pwplus "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then # program-specific install script used by HP pwplus--don't use. : else ac_cv_path_install="$as_dir/$ac_prog$ac_exec_ext -c" break 3 fi fi done done ;; esac done IFS=$as_save_IFS fi if test "${ac_cv_path_install+set}" = set; then INSTALL=$ac_cv_path_install else # As a last resort, use the slow shell script. Don't cache a # value for INSTALL within a source directory, because that will # break other packages using the cache if that directory is # removed, or if the value is a relative name. INSTALL=$ac_install_sh fi fi { echo "$as_me:$LINENO: result: $INSTALL" >&5 echo "${ECHO_T}$INSTALL" >&6; } # Use test -z because SunOS4 sh mishandles braces in ${var-val}. # It thinks the first close brace ends the variable substitution. test -z "$INSTALL_PROGRAM" && INSTALL_PROGRAM='${INSTALL}' test -z "$INSTALL_SCRIPT" && INSTALL_SCRIPT='${INSTALL}' test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644' if test "$INSTALL" = "$ac_install_sh"; then # Use full path to install-sh script... INSTALL="`pwd`/install-sh -c" fi if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}ranlib", so it can be a program name with args. set dummy ${ac_tool_prefix}ranlib; ac_word=$2 { echo "$as_me:$LINENO: checking for $ac_word" >&5 echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; } if test "${ac_cv_prog_RANLIB+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else if test -n "$RANLIB"; then ac_cv_prog_RANLIB="$RANLIB" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_prog_RANLIB="${ac_tool_prefix}ranlib" echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi RANLIB=$ac_cv_prog_RANLIB if test -n "$RANLIB"; then { echo "$as_me:$LINENO: result: $RANLIB" >&5 echo "${ECHO_T}$RANLIB" >&6; } else { echo "$as_me:$LINENO: result: no" >&5 echo "${ECHO_T}no" >&6; } fi fi if test -z "$ac_cv_prog_RANLIB"; then ac_ct_RANLIB=$RANLIB # Extract the first word of "ranlib", so it can be a program name with args. set dummy ranlib; ac_word=$2 { echo "$as_me:$LINENO: checking for $ac_word" >&5 echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; } if test "${ac_cv_prog_ac_ct_RANLIB+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else if test -n "$ac_ct_RANLIB"; then ac_cv_prog_ac_ct_RANLIB="$ac_ct_RANLIB" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_prog_ac_ct_RANLIB="ranlib" echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_RANLIB=$ac_cv_prog_ac_ct_RANLIB if test -n "$ac_ct_RANLIB"; then { echo "$as_me:$LINENO: result: $ac_ct_RANLIB" >&5 echo "${ECHO_T}$ac_ct_RANLIB" >&6; } else { echo "$as_me:$LINENO: result: no" >&5 echo "${ECHO_T}no" >&6; } fi if test "x$ac_ct_RANLIB" = x; then RANLIB=":" else case $cross_compiling:$ac_tool_warned in yes:) { echo "$as_me:$LINENO: WARNING: In the future, Autoconf will not detect cross-tools whose name does not start with the host triplet. If you think this configuration is useful to you, please write to autoconf@gnu.org." >&5 echo "$as_me: WARNING: In the future, Autoconf will not detect cross-tools whose name does not start with the host triplet. If you think this configuration is useful to you, please write to autoconf@gnu.org." >&2;} ac_tool_warned=yes ;; esac RANLIB=$ac_ct_RANLIB fi else RANLIB="$ac_cv_prog_RANLIB" fi # Extract the first word of "ar", so it can be a program name with args. set dummy ar; ac_word=$2 { echo "$as_me:$LINENO: checking for $ac_word" >&5 echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; } if test "${ac_cv_path_AR+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else case $AR in [\\/]* | ?:[\\/]*) ac_cv_path_AR="$AR" # Let the user override the test with a path. ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_path_AR="$as_dir/$ac_word$ac_exec_ext" echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS ;; esac fi AR=$ac_cv_path_AR if test -n "$AR"; then { echo "$as_me:$LINENO: result: $AR" >&5 echo "${ECHO_T}$AR" >&6; } else { echo "$as_me:$LINENO: result: no" >&5 echo "${ECHO_T}no" >&6; } fi # Extract the first word of "cp", so it can be a program name with args. set dummy cp; ac_word=$2 { echo "$as_me:$LINENO: checking for $ac_word" >&5 echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; } if test "${ac_cv_path_CP+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else case $CP in [\\/]* | ?:[\\/]*) ac_cv_path_CP="$CP" # Let the user override the test with a path. ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_path_CP="$as_dir/$ac_word$ac_exec_ext" echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS ;; esac fi CP=$ac_cv_path_CP if test -n "$CP"; then { echo "$as_me:$LINENO: result: $CP" >&5 echo "${ECHO_T}$CP" >&6; } else { echo "$as_me:$LINENO: result: no" >&5 echo "${ECHO_T}no" >&6; } fi # Extract the first word of "ln", so it can be a program name with args. set dummy ln; ac_word=$2 { echo "$as_me:$LINENO: checking for $ac_word" >&5 echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; } if test "${ac_cv_path_LN+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else case $LN in [\\/]* | ?:[\\/]*) ac_cv_path_LN="$LN" # Let the user override the test with a path. ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_path_LN="$as_dir/$ac_word$ac_exec_ext" echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS ;; esac fi LN=$ac_cv_path_LN if test -n "$LN"; then { echo "$as_me:$LINENO: result: $LN" >&5 echo "${ECHO_T}$LN" >&6; } else { echo "$as_me:$LINENO: result: no" >&5 echo "${ECHO_T}no" >&6; } fi # Extract the first word of "mkdir", so it can be a program name with args. set dummy mkdir; ac_word=$2 { echo "$as_me:$LINENO: checking for $ac_word" >&5 echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; } if test "${ac_cv_path_MKDIR+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else case $MKDIR in [\\/]* | ?:[\\/]*) ac_cv_path_MKDIR="$MKDIR" # Let the user override the test with a path. ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_path_MKDIR="$as_dir/$ac_word$ac_exec_ext" echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS ;; esac fi MKDIR=$ac_cv_path_MKDIR if test -n "$MKDIR"; then { echo "$as_me:$LINENO: result: $MKDIR" >&5 echo "${ECHO_T}$MKDIR" >&6; } else { echo "$as_me:$LINENO: result: no" >&5 echo "${ECHO_T}no" >&6; } fi # Extract the first word of "rm", so it can be a program name with args. set dummy rm; ac_word=$2 { echo "$as_me:$LINENO: checking for $ac_word" >&5 echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; } if test "${ac_cv_path_RM+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else case $RM in [\\/]* | ?:[\\/]*) ac_cv_path_RM="$RM" # Let the user override the test with a path. ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_path_RM="$as_dir/$ac_word$ac_exec_ext" echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS ;; esac fi RM=$ac_cv_path_RM if test -n "$RM"; then { echo "$as_me:$LINENO: result: $RM" >&5 echo "${ECHO_T}$RM" >&6; } else { echo "$as_me:$LINENO: result: no" >&5 echo "${ECHO_T}no" >&6; } fi case "$uname" in Darwin* | *BSD*) ARFLAGS="-rcv" ;; *) ARFLAGS="crvs" ;; esac { echo "$as_me:$LINENO: checking for inline" >&5 echo $ECHO_N "checking for inline... $ECHO_C" >&6; } if test "${ac_cv_c_inline+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else ac_cv_c_inline=no for ac_kw in inline __inline__ __inline; do cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #ifndef __cplusplus typedef int foo_t; static $ac_kw foo_t static_foo () {return 0; } $ac_kw foo_t foo () {return 0; } #endif _ACEOF rm -f conftest.$ac_objext if { (ac_try="$ac_compile" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 (eval "$ac_compile") 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { test -z "$ac_c_werror_flag" || test ! -s conftest.err } && test -s conftest.$ac_objext; then ac_cv_c_inline=$ac_kw else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext test "$ac_cv_c_inline" != no && break done fi { echo "$as_me:$LINENO: result: $ac_cv_c_inline" >&5 echo "${ECHO_T}$ac_cv_c_inline" >&6; } case $ac_cv_c_inline in inline | yes) ;; *) case $ac_cv_c_inline in no) ac_val=;; *) ac_val=$ac_cv_c_inline;; esac cat >>confdefs.h <<_ACEOF #ifndef __cplusplus #define inline $ac_val #endif _ACEOF ;; esac if test "x$use_ansi" != xyes; then for ac_func in strdup do as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh` { echo "$as_me:$LINENO: checking for $ac_func" >&5 echo $ECHO_N "checking for $ac_func... $ECHO_C" >&6; } if { as_var=$as_ac_var; eval "test \"\${$as_var+set}\" = set"; }; then echo $ECHO_N "(cached) $ECHO_C" >&6 else cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ /* Define $ac_func to an innocuous variant, in case declares $ac_func. For example, HP-UX 11i declares gettimeofday. */ #define $ac_func innocuous_$ac_func /* System header to define __stub macros and hopefully few prototypes, which can conflict with char $ac_func (); below. Prefer to if __STDC__ is defined, since exists even on freestanding compilers. */ #ifdef __STDC__ # include #else # include #endif #undef $ac_func /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char $ac_func (); /* The GNU C library defines this for functions which it implements to always fail with ENOSYS. Some functions are actually named something starting with __ and the normal name is an alias. */ #if defined __stub_$ac_func || defined __stub___$ac_func choke me #endif int main () { return $ac_func (); ; return 0; } _ACEOF rm -f conftest.$ac_objext conftest$ac_exeext if { (ac_try="$ac_link" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 (eval "$ac_link") 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { test -z "$ac_c_werror_flag" || test ! -s conftest.err } && test -s conftest$ac_exeext && $as_test_x conftest$ac_exeext; then eval "$as_ac_var=yes" else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 eval "$as_ac_var=no" fi rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \ conftest$ac_exeext conftest.$ac_ext fi ac_res=`eval echo '${'$as_ac_var'}'` { echo "$as_me:$LINENO: result: $ac_res" >&5 echo "${ECHO_T}$ac_res" >&6; } if test `eval echo '${'$as_ac_var'}'` = yes; then cat >>confdefs.h <<_ACEOF #define `echo "HAVE_$ac_func" | $as_tr_cpp` 1 _ACEOF fi done fi if test "x$use_vsnprintf" != xyes; then for ac_func in snprintf vsnprintf do as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh` { echo "$as_me:$LINENO: checking for $ac_func" >&5 echo $ECHO_N "checking for $ac_func... $ECHO_C" >&6; } if { as_var=$as_ac_var; eval "test \"\${$as_var+set}\" = set"; }; then echo $ECHO_N "(cached) $ECHO_C" >&6 else cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ /* Define $ac_func to an innocuous variant, in case declares $ac_func. For example, HP-UX 11i declares gettimeofday. */ #define $ac_func innocuous_$ac_func /* System header to define __stub macros and hopefully few prototypes, which can conflict with char $ac_func (); below. Prefer to if __STDC__ is defined, since exists even on freestanding compilers. */ #ifdef __STDC__ # include #else # include #endif #undef $ac_func /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char $ac_func (); /* The GNU C library defines this for functions which it implements to always fail with ENOSYS. Some functions are actually named something starting with __ and the normal name is an alias. */ #if defined __stub_$ac_func || defined __stub___$ac_func choke me #endif int main () { return $ac_func (); ; return 0; } _ACEOF rm -f conftest.$ac_objext conftest$ac_exeext if { (ac_try="$ac_link" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 (eval "$ac_link") 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { test -z "$ac_c_werror_flag" || test ! -s conftest.err } && test -s conftest$ac_exeext && $as_test_x conftest$ac_exeext; then eval "$as_ac_var=yes" else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 eval "$as_ac_var=no" fi rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \ conftest$ac_exeext conftest.$ac_ext fi ac_res=`eval echo '${'$as_ac_var'}'` { echo "$as_me:$LINENO: result: $ac_res" >&5 echo "${ECHO_T}$ac_res" >&6; } if test `eval echo '${'$as_ac_var'}'` = yes; then cat >>confdefs.h <<_ACEOF #define `echo "HAVE_$ac_func" | $as_tr_cpp` 1 _ACEOF fi done fi { echo "$as_me:$LINENO: checking for long long int" >&5 echo $ECHO_N "checking for long long int... $ECHO_C" >&6; } if test "${ac_cv_c_long_long+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else if test "$GCC" = yes; then ac_cv_c_long_long=yes else cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ int main () { long long int i; ; return 0; } _ACEOF rm -f conftest.$ac_objext if { (ac_try="$ac_compile" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 (eval "$ac_compile") 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { test -z "$ac_c_werror_flag" || test ! -s conftest.err } && test -s conftest.$ac_objext; then ac_cv_c_long_long=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_cv_c_long_long=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi fi { echo "$as_me:$LINENO: result: $ac_cv_c_long_long" >&5 echo "${ECHO_T}$ac_cv_c_long_long" >&6; } if test $ac_cv_c_long_long = yes; then cat >>confdefs.h <<\_ACEOF #define HAVE_LONG_LONG 1 _ACEOF fi # Check whether --enable-threads was given. if test "${enable_threads+set}" = set; then enableval=$enable_threads; fi have_pthread=no PTHREAD_FLAGS="" PTHREAD_LIBS="" if test "x$enable_threads" != xno; then ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu { echo "$as_me:$LINENO: checking how to run the C preprocessor" >&5 echo $ECHO_N "checking how to run the C preprocessor... $ECHO_C" >&6; } # On Suns, sometimes $CPP names a directory. if test -n "$CPP" && test -d "$CPP"; then CPP= fi if test -z "$CPP"; then if test "${ac_cv_prog_CPP+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else # Double quotes because CPP needs to be expanded for CPP in "$CC -E" "$CC -E -traditional-cpp" "/lib/cpp" do ac_preproc_ok=false for ac_c_preproc_warn_flag in '' yes do # Use a header file that comes with gcc, so configuring glibc # with a fresh cross-compiler works. # Prefer to if __STDC__ is defined, since # exists even on freestanding compilers. # On the NeXT, cc -E runs the code through the compiler's parser, # not just through cpp. "Syntax error" is here to catch this case. cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #ifdef __STDC__ # include #else # include #endif Syntax error _ACEOF if { (ac_try="$ac_cpp conftest.$ac_ext" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } >/dev/null && { test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" || test ! -s conftest.err }; then : else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 # Broken: fails on valid input. continue fi rm -f conftest.err conftest.$ac_ext # OK, works on sane cases. Now check whether nonexistent headers # can be detected and how. cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #include _ACEOF if { (ac_try="$ac_cpp conftest.$ac_ext" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } >/dev/null && { test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" || test ! -s conftest.err }; then # Broken: success on invalid input. continue else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 # Passes both tests. ac_preproc_ok=: break fi rm -f conftest.err conftest.$ac_ext done # Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. rm -f conftest.err conftest.$ac_ext if $ac_preproc_ok; then break fi done ac_cv_prog_CPP=$CPP fi CPP=$ac_cv_prog_CPP else ac_cv_prog_CPP=$CPP fi { echo "$as_me:$LINENO: result: $CPP" >&5 echo "${ECHO_T}$CPP" >&6; } ac_preproc_ok=false for ac_c_preproc_warn_flag in '' yes do # Use a header file that comes with gcc, so configuring glibc # with a fresh cross-compiler works. # Prefer to if __STDC__ is defined, since # exists even on freestanding compilers. # On the NeXT, cc -E runs the code through the compiler's parser, # not just through cpp. "Syntax error" is here to catch this case. cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #ifdef __STDC__ # include #else # include #endif Syntax error _ACEOF if { (ac_try="$ac_cpp conftest.$ac_ext" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } >/dev/null && { test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" || test ! -s conftest.err }; then : else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 # Broken: fails on valid input. continue fi rm -f conftest.err conftest.$ac_ext # OK, works on sane cases. Now check whether nonexistent headers # can be detected and how. cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #include _ACEOF if { (ac_try="$ac_cpp conftest.$ac_ext" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } >/dev/null && { test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" || test ! -s conftest.err }; then # Broken: success on invalid input. continue else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 # Passes both tests. ac_preproc_ok=: break fi rm -f conftest.err conftest.$ac_ext done # Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. rm -f conftest.err conftest.$ac_ext if $ac_preproc_ok; then : else { { echo "$as_me:$LINENO: error: C preprocessor \"$CPP\" fails sanity check See \`config.log' for more details." >&5 echo "$as_me: error: C preprocessor \"$CPP\" fails sanity check See \`config.log' for more details." >&2;} { (exit 1); exit 1; }; } fi ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu { echo "$as_me:$LINENO: checking for grep that handles long lines and -e" >&5 echo $ECHO_N "checking for grep that handles long lines and -e... $ECHO_C" >&6; } if test "${ac_cv_path_GREP+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else # Extract the first word of "grep ggrep" to use in msg output if test -z "$GREP"; then set dummy grep ggrep; ac_prog_name=$2 if test "${ac_cv_path_GREP+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else ac_path_GREP_found=false # Loop through the user's path and test for each of PROGNAME-LIST as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_prog in grep ggrep; do for ac_exec_ext in '' $ac_executable_extensions; do ac_path_GREP="$as_dir/$ac_prog$ac_exec_ext" { test -f "$ac_path_GREP" && $as_test_x "$ac_path_GREP"; } || continue # Check for GNU ac_path_GREP and select it if it is found. # Check for GNU $ac_path_GREP case `"$ac_path_GREP" --version 2>&1` in *GNU*) ac_cv_path_GREP="$ac_path_GREP" ac_path_GREP_found=:;; *) ac_count=0 echo $ECHO_N "0123456789$ECHO_C" >"conftest.in" while : do cat "conftest.in" "conftest.in" >"conftest.tmp" mv "conftest.tmp" "conftest.in" cp "conftest.in" "conftest.nl" echo 'GREP' >> "conftest.nl" "$ac_path_GREP" -e 'GREP$' -e '-(cannot match)-' < "conftest.nl" >"conftest.out" 2>/dev/null || break diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break ac_count=`expr $ac_count + 1` if test $ac_count -gt ${ac_path_GREP_max-0}; then # Best one so far, save it but keep looking for a better one ac_cv_path_GREP="$ac_path_GREP" ac_path_GREP_max=$ac_count fi # 10*(2^10) chars as input seems more than enough test $ac_count -gt 10 && break done rm -f conftest.in conftest.tmp conftest.nl conftest.out;; esac $ac_path_GREP_found && break 3 done done done IFS=$as_save_IFS fi GREP="$ac_cv_path_GREP" if test -z "$GREP"; then { { echo "$as_me:$LINENO: error: no acceptable $ac_prog_name could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" >&5 echo "$as_me: error: no acceptable $ac_prog_name could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" >&2;} { (exit 1); exit 1; }; } fi else ac_cv_path_GREP=$GREP fi fi { echo "$as_me:$LINENO: result: $ac_cv_path_GREP" >&5 echo "${ECHO_T}$ac_cv_path_GREP" >&6; } GREP="$ac_cv_path_GREP" { echo "$as_me:$LINENO: checking for egrep" >&5 echo $ECHO_N "checking for egrep... $ECHO_C" >&6; } if test "${ac_cv_path_EGREP+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else if echo a | $GREP -E '(a|b)' >/dev/null 2>&1 then ac_cv_path_EGREP="$GREP -E" else # Extract the first word of "egrep" to use in msg output if test -z "$EGREP"; then set dummy egrep; ac_prog_name=$2 if test "${ac_cv_path_EGREP+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else ac_path_EGREP_found=false # Loop through the user's path and test for each of PROGNAME-LIST as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_prog in egrep; do for ac_exec_ext in '' $ac_executable_extensions; do ac_path_EGREP="$as_dir/$ac_prog$ac_exec_ext" { test -f "$ac_path_EGREP" && $as_test_x "$ac_path_EGREP"; } || continue # Check for GNU ac_path_EGREP and select it if it is found. # Check for GNU $ac_path_EGREP case `"$ac_path_EGREP" --version 2>&1` in *GNU*) ac_cv_path_EGREP="$ac_path_EGREP" ac_path_EGREP_found=:;; *) ac_count=0 echo $ECHO_N "0123456789$ECHO_C" >"conftest.in" while : do cat "conftest.in" "conftest.in" >"conftest.tmp" mv "conftest.tmp" "conftest.in" cp "conftest.in" "conftest.nl" echo 'EGREP' >> "conftest.nl" "$ac_path_EGREP" 'EGREP$' < "conftest.nl" >"conftest.out" 2>/dev/null || break diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break ac_count=`expr $ac_count + 1` if test $ac_count -gt ${ac_path_EGREP_max-0}; then # Best one so far, save it but keep looking for a better one ac_cv_path_EGREP="$ac_path_EGREP" ac_path_EGREP_max=$ac_count fi # 10*(2^10) chars as input seems more than enough test $ac_count -gt 10 && break done rm -f conftest.in conftest.tmp conftest.nl conftest.out;; esac $ac_path_EGREP_found && break 3 done done done IFS=$as_save_IFS fi EGREP="$ac_cv_path_EGREP" if test -z "$EGREP"; then { { echo "$as_me:$LINENO: error: no acceptable $ac_prog_name could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" >&5 echo "$as_me: error: no acceptable $ac_prog_name could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" >&2;} { (exit 1); exit 1; }; } fi else ac_cv_path_EGREP=$EGREP fi fi fi { echo "$as_me:$LINENO: result: $ac_cv_path_EGREP" >&5 echo "${ECHO_T}$ac_cv_path_EGREP" >&6; } EGREP="$ac_cv_path_EGREP" { echo "$as_me:$LINENO: checking for ANSI C header files" >&5 echo $ECHO_N "checking for ANSI C header files... $ECHO_C" >&6; } if test "${ac_cv_header_stdc+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #include #include #include #include int main () { ; return 0; } _ACEOF rm -f conftest.$ac_objext if { (ac_try="$ac_compile" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 (eval "$ac_compile") 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { test -z "$ac_c_werror_flag" || test ! -s conftest.err } && test -s conftest.$ac_objext; then ac_cv_header_stdc=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_cv_header_stdc=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext if test $ac_cv_header_stdc = yes; then # SunOS 4.x string.h does not declare mem*, contrary to ANSI. cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #include _ACEOF if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | $EGREP "memchr" >/dev/null 2>&1; then : else ac_cv_header_stdc=no fi rm -f -r conftest* fi if test $ac_cv_header_stdc = yes; then # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI. cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #include _ACEOF if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | $EGREP "free" >/dev/null 2>&1; then : else ac_cv_header_stdc=no fi rm -f -r conftest* fi if test $ac_cv_header_stdc = yes; then # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi. if test "$cross_compiling" = yes; then : else cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #include #include #if ((' ' & 0x0FF) == 0x020) # define ISLOWER(c) ('a' <= (c) && (c) <= 'z') # define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c)) #else # define ISLOWER(c) \ (('a' <= (c) && (c) <= 'i') \ || ('j' <= (c) && (c) <= 'r') \ || ('s' <= (c) && (c) <= 'z')) # define TOUPPER(c) (ISLOWER(c) ? ((c) | 0x40) : (c)) #endif #define XOR(e, f) (((e) && !(f)) || (!(e) && (f))) int main () { int i; for (i = 0; i < 256; i++) if (XOR (islower (i), ISLOWER (i)) || toupper (i) != TOUPPER (i)) return 2; return 0; } _ACEOF rm -f conftest$ac_exeext if { (ac_try="$ac_link" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 (eval "$ac_link") 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='./conftest$ac_exeext' { (case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 (eval "$ac_try") 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then : else echo "$as_me: program exited with status $ac_status" >&5 echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ( exit $ac_status ) ac_cv_header_stdc=no fi rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext fi fi fi { echo "$as_me:$LINENO: result: $ac_cv_header_stdc" >&5 echo "${ECHO_T}$ac_cv_header_stdc" >&6; } if test $ac_cv_header_stdc = yes; then cat >>confdefs.h <<\_ACEOF #define STDC_HEADERS 1 _ACEOF fi # On IRIX 5.3, sys/types and inttypes.h are conflicting. for ac_header in sys/types.h sys/stat.h stdlib.h string.h memory.h strings.h \ inttypes.h stdint.h unistd.h do as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh` { echo "$as_me:$LINENO: checking for $ac_header" >&5 echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6; } if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then echo $ECHO_N "(cached) $ECHO_C" >&6 else cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ $ac_includes_default #include <$ac_header> _ACEOF rm -f conftest.$ac_objext if { (ac_try="$ac_compile" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 (eval "$ac_compile") 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { test -z "$ac_c_werror_flag" || test ! -s conftest.err } && test -s conftest.$ac_objext; then eval "$as_ac_Header=yes" else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 eval "$as_ac_Header=no" fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi ac_res=`eval echo '${'$as_ac_Header'}'` { echo "$as_me:$LINENO: result: $ac_res" >&5 echo "${ECHO_T}$ac_res" >&6; } if test `eval echo '${'$as_ac_Header'}'` = yes; then cat >>confdefs.h <<_ACEOF #define `echo "HAVE_$ac_header" | $as_tr_cpp` 1 _ACEOF fi done if test "${ac_cv_header_pthread_h+set}" = set; then { echo "$as_me:$LINENO: checking for pthread.h" >&5 echo $ECHO_N "checking for pthread.h... $ECHO_C" >&6; } if test "${ac_cv_header_pthread_h+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 fi { echo "$as_me:$LINENO: result: $ac_cv_header_pthread_h" >&5 echo "${ECHO_T}$ac_cv_header_pthread_h" >&6; } else # Is the header compilable? { echo "$as_me:$LINENO: checking pthread.h usability" >&5 echo $ECHO_N "checking pthread.h usability... $ECHO_C" >&6; } cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ $ac_includes_default #include _ACEOF rm -f conftest.$ac_objext if { (ac_try="$ac_compile" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 (eval "$ac_compile") 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { test -z "$ac_c_werror_flag" || test ! -s conftest.err } && test -s conftest.$ac_objext; then ac_header_compiler=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_header_compiler=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext { echo "$as_me:$LINENO: result: $ac_header_compiler" >&5 echo "${ECHO_T}$ac_header_compiler" >&6; } # Is the header present? { echo "$as_me:$LINENO: checking pthread.h presence" >&5 echo $ECHO_N "checking pthread.h presence... $ECHO_C" >&6; } cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #include _ACEOF if { (ac_try="$ac_cpp conftest.$ac_ext" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } >/dev/null && { test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" || test ! -s conftest.err }; then ac_header_preproc=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_header_preproc=no fi rm -f conftest.err conftest.$ac_ext { echo "$as_me:$LINENO: result: $ac_header_preproc" >&5 echo "${ECHO_T}$ac_header_preproc" >&6; } # So? What about this header? case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in yes:no: ) { echo "$as_me:$LINENO: WARNING: pthread.h: accepted by the compiler, rejected by the preprocessor!" >&5 echo "$as_me: WARNING: pthread.h: accepted by the compiler, rejected by the preprocessor!" >&2;} { echo "$as_me:$LINENO: WARNING: pthread.h: proceeding with the compiler's result" >&5 echo "$as_me: WARNING: pthread.h: proceeding with the compiler's result" >&2;} ac_header_preproc=yes ;; no:yes:* ) { echo "$as_me:$LINENO: WARNING: pthread.h: present but cannot be compiled" >&5 echo "$as_me: WARNING: pthread.h: present but cannot be compiled" >&2;} { echo "$as_me:$LINENO: WARNING: pthread.h: check for missing prerequisite headers?" >&5 echo "$as_me: WARNING: pthread.h: check for missing prerequisite headers?" >&2;} { echo "$as_me:$LINENO: WARNING: pthread.h: see the Autoconf documentation" >&5 echo "$as_me: WARNING: pthread.h: see the Autoconf documentation" >&2;} { echo "$as_me:$LINENO: WARNING: pthread.h: section \"Present But Cannot Be Compiled\"" >&5 echo "$as_me: WARNING: pthread.h: section \"Present But Cannot Be Compiled\"" >&2;} { echo "$as_me:$LINENO: WARNING: pthread.h: proceeding with the preprocessor's result" >&5 echo "$as_me: WARNING: pthread.h: proceeding with the preprocessor's result" >&2;} { echo "$as_me:$LINENO: WARNING: pthread.h: in the future, the compiler will take precedence" >&5 echo "$as_me: WARNING: pthread.h: in the future, the compiler will take precedence" >&2;} ;; esac { echo "$as_me:$LINENO: checking for pthread.h" >&5 echo $ECHO_N "checking for pthread.h... $ECHO_C" >&6; } if test "${ac_cv_header_pthread_h+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else ac_cv_header_pthread_h=$ac_header_preproc fi { echo "$as_me:$LINENO: result: $ac_cv_header_pthread_h" >&5 echo "${ECHO_T}$ac_cv_header_pthread_h" >&6; } fi if test $ac_cv_header_pthread_h = yes; then cat >>confdefs.h <<\_ACEOF #define HAVE_PTHREAD_H 1 _ACEOF fi if test x$ac_cv_header_pthread_h = xyes; then for flag in -lpthreads -lpthread -pthread; do { echo "$as_me:$LINENO: checking for pthread_create using $flag" >&5 echo $ECHO_N "checking for pthread_create using $flag... $ECHO_C" >&6; } SAVELIBS="$LIBS" LIBS="$flag $LIBS" cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #include int main () { pthread_create(0, 0, 0, 0); ; return 0; } _ACEOF rm -f conftest.$ac_objext conftest$ac_exeext if { (ac_try="$ac_link" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 (eval "$ac_link") 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { test -z "$ac_c_werror_flag" || test ! -s conftest.err } && test -s conftest$ac_exeext && $as_test_x conftest$ac_exeext; then have_pthread=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 fi rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \ conftest$ac_exeext conftest.$ac_ext { echo "$as_me:$LINENO: result: $have_pthread" >&5 echo "${ECHO_T}$have_pthread" >&6; } LIBS="$SAVELIBS" if test $have_pthread = yes; then PTHREAD_FLAGS="-D_THREAD_SAFE -D_REENTRANT" PTHREAD_LIBS="$flag" # Solaris requires -D_POSIX_PTHREAD_SEMANTICS to # be POSIX-compliant... :( if test $uname = SunOS; then PTHREAD_FLAGS="$PTHREAD_FLAGS -D_POSIX_PTHREAD_SEMANTICS" fi break fi done fi fi DSO="${DSO:=:}" DSOFLAGS="${DSOFLAGS:=}" # Check whether --enable-shared was given. if test "${enable_shared+set}" = set; then enableval=$enable_shared; fi if test x$enable_shared != xno; then { echo "$as_me:$LINENO: checking for shared library support" >&5 echo $ECHO_N "checking for shared library support... $ECHO_C" >&6; } PICFLAG=1 case "$uname" in SunOS* | UNIX_S*) { echo "$as_me:$LINENO: result: yes" >&5 echo "${ECHO_T}yes" >&6; } LIBMXML="libmxml.so.1.5" DSO="\$(CC)" DSOFLAGS="$DSOFLAGS -Wl,-h,libmxml.so.1 -G -R\$(libdir) \$(OPTIM)" LDFLAGS="$LDFLAGS -R\$(libdir)" ;; HP-UX*) { echo "$as_me:$LINENO: result: yes" >&5 echo "${ECHO_T}yes" >&6; } LIBMXML="libmxml.sl.1" DSO="ld" DSOFLAGS="$DSOFLAGS -b -z +h libmxml.sl.1 +s +b \$(libdir)" LDFLAGS="$LDFLAGS -Wl,+s,+b,\$(libdir)" ;; IRIX) { echo "$as_me:$LINENO: result: yes" >&5 echo "${ECHO_T}yes" >&6; } LIBMXML="libmxml.so.1.5" DSO="\$(CC)" DSOFLAGS="$DSOFLAGS -Wl,-rpath,\$(libdir),-set_version,sgi1.0,-soname,libmxml.so.1 -shared \$(OPTIM)" ;; OSF1* | Linux | GNU) { echo "$as_me:$LINENO: result: yes" >&5 echo "${ECHO_T}yes" >&6; } LIBMXML="libmxml.so.1.5" DSO="\$(CC)" DSOFLAGS="$DSOFLAGS -Wl,-soname,libmxml.so.1,-rpath,\$(libdir) -shared \$(OPTIM)" LDFLAGS="$LDFLAGS -Wl,-rpath,\$(libdir)" ;; *BSD*) { echo "$as_me:$LINENO: result: yes" >&5 echo "${ECHO_T}yes" >&6; } LIBMXML="libmxml.so.1.5" DSO="\$(CC)" DSOFLAGS="$DSOFLAGS -Wl,-soname,libmxml.so.1,-R\$(libdir) -shared \$(OPTIM)" LDFLAGS="$LDFLAGS -Wl,-R\$(libdir)" ;; Darwin*) { echo "$as_me:$LINENO: result: yes" >&5 echo "${ECHO_T}yes" >&6; } LIBMXML="libmxml.1.dylib" DSO="\$(CC)" DSOFLAGS="$DSOFLAGS \$(RC_CFLAGS) -dynamiclib -lc" ;; *) { echo "$as_me:$LINENO: result: no" >&5 echo "${ECHO_T}no" >&6; } { echo "$as_me:$LINENO: WARNING: shared libraries not supported on this platform." >&5 echo "$as_me: WARNING: shared libraries not supported on this platform." >&2;} PICFLAG=0 LIBMXML="libmxml.a" ;; esac else PICFLAG=0 LIBMXML="libmxml.a" fi if test -n "$GCC"; then CFLAGS="-Wall $CFLAGS" if test "x$OPTIM" = x; then OPTIM="-Os -g" fi if test "x$use_ansi" = xyes; then CFLAGS="-ansi -pedantic $CFLAGS" fi if test $PICFLAG = 1 -a $uname != AIX; then OPTIM="-fPIC $OPTIM" fi else case $uname in HP-UX*) CFLAGS="-Ae $CFLAGS" if test "x$OPTIM" = x; then OPTIM="-O" fi OPTIM="+DAportable $OPTIM" if test $PICFLAG = 1; then OPTIM="+z $OPTIM" fi ;; UNIX_SVR* | SunOS*) if test "x$OPTIM" = x; then OPTIM="-O" fi if test $PICFLAG = 1; then OPTIM="-KPIC $OPTIM" fi ;; *) if test "x$OPTIM" = x; then OPTIM="-O" fi ;; esac fi if test "$prefix" = "NONE"; then prefix="/usr/local" fi if test "$exec_prefix" = "NONE"; then exec_prefix="$prefix" fi if test "$docdir" = "NONE"; then docdir="$datadir/doc/mxml" fi if test "$mandir" = "\${prefix}/man" -a "$prefix" = "/usr"; then case "$uname" in *BSD* | Darwin* | Linux*) # BSD, Darwin (MacOS X), and Linux mandir="/usr/share/man" ;; IRIX*) # SGI IRIX mandir="/usr/share/catman/u_man" ;; *) # All others mandir="/usr/man" ;; esac fi if test "$includedir" != /usr/include; then PC_CFLAGS="-I$includedir" else PC_CFLAGS="" fi if test "$libdir" != /usr/lib; then PC_LIBS="-L$libdir -lmxml" else PC_LIBS="-lmxml" fi ac_config_files="$ac_config_files Makefile mxml.list mxml.pc" cat >confcache <<\_ACEOF # This file is a shell script that caches the results of configure # tests run on this system so they can be shared between configure # scripts and configure runs, see configure's option --config-cache. # It is not useful on other systems. If it contains results you don't # want to keep, you may remove or edit it. # # config.status only pays attention to the cache file if you give it # the --recheck option to rerun configure. # # `ac_cv_env_foo' variables (set or unset) will be overridden when # loading this file, other *unset* `ac_cv_foo' will be assigned the # following values. _ACEOF # The following way of writing the cache mishandles newlines in values, # but we know of no workaround that is simple, portable, and efficient. # So, we kill variables containing newlines. # Ultrix sh set writes to stderr and can't be redirected directly, # and sets the high bit in the cache file unless we assign to the vars. ( for ac_var in `(set) 2>&1 | sed -n 's/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'`; do eval ac_val=\$$ac_var case $ac_val in #( *${as_nl}*) case $ac_var in #( *_cv_*) { echo "$as_me:$LINENO: WARNING: Cache variable $ac_var contains a newline." >&5 echo "$as_me: WARNING: Cache variable $ac_var contains a newline." >&2;} ;; esac case $ac_var in #( _ | IFS | as_nl) ;; #( *) $as_unset $ac_var ;; esac ;; esac done (set) 2>&1 | case $as_nl`(ac_space=' '; set) 2>&1` in #( *${as_nl}ac_space=\ *) # `set' does not quote correctly, so add quotes (double-quote # substitution turns \\\\ into \\, and sed turns \\ into \). sed -n \ "s/'/'\\\\''/g; s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\\2'/p" ;; #( *) # `set' quotes correctly as required by POSIX, so do not add quotes. sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p" ;; esac | sort ) | sed ' /^ac_cv_env_/b end t clear :clear s/^\([^=]*\)=\(.*[{}].*\)$/test "${\1+set}" = set || &/ t end s/^\([^=]*\)=\(.*\)$/\1=${\1=\2}/ :end' >>confcache if diff "$cache_file" confcache >/dev/null 2>&1; then :; else if test -w "$cache_file"; then test "x$cache_file" != "x/dev/null" && { echo "$as_me:$LINENO: updating cache $cache_file" >&5 echo "$as_me: updating cache $cache_file" >&6;} cat confcache >$cache_file else { echo "$as_me:$LINENO: not updating unwritable cache $cache_file" >&5 echo "$as_me: not updating unwritable cache $cache_file" >&6;} fi fi rm -f confcache test "x$prefix" = xNONE && prefix=$ac_default_prefix # Let make expand exec_prefix. test "x$exec_prefix" = xNONE && exec_prefix='${prefix}' DEFS=-DHAVE_CONFIG_H ac_libobjs= ac_ltlibobjs= for ac_i in : $LIBOBJS; do test "x$ac_i" = x: && continue # 1. Remove the extension, and $U if already installed. ac_script='s/\$U\././;s/\.o$//;s/\.obj$//' ac_i=`echo "$ac_i" | sed "$ac_script"` # 2. Prepend LIBOBJDIR. When used with automake>=1.10 LIBOBJDIR # will be set to the directory where LIBOBJS objects are built. ac_libobjs="$ac_libobjs \${LIBOBJDIR}$ac_i\$U.$ac_objext" ac_ltlibobjs="$ac_ltlibobjs \${LIBOBJDIR}$ac_i"'$U.lo' done LIBOBJS=$ac_libobjs LTLIBOBJS=$ac_ltlibobjs : ${CONFIG_STATUS=./config.status} ac_clean_files_save=$ac_clean_files ac_clean_files="$ac_clean_files $CONFIG_STATUS" { echo "$as_me:$LINENO: creating $CONFIG_STATUS" >&5 echo "$as_me: creating $CONFIG_STATUS" >&6;} cat >$CONFIG_STATUS <<_ACEOF #! $SHELL # Generated by $as_me. # Run this file to recreate the current configuration. # Compiler output produced by configure, useful for debugging # configure, is in config.log if it exists. debug=false ac_cs_recheck=false ac_cs_silent=false SHELL=\${CONFIG_SHELL-$SHELL} _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF ## --------------------- ## ## M4sh Initialization. ## ## --------------------- ## # Be more Bourne compatible DUALCASE=1; export DUALCASE # for MKS sh if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then emulate sh NULLCMD=: # Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which # is contrary to our usage. Disable this feature. alias -g '${1+"$@"}'='"$@"' setopt NO_GLOB_SUBST else case `(set -o) 2>/dev/null` in *posix*) set -o posix ;; esac fi # PATH needs CR # Avoid depending upon Character Ranges. as_cr_letters='abcdefghijklmnopqrstuvwxyz' as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' as_cr_Letters=$as_cr_letters$as_cr_LETTERS as_cr_digits='0123456789' as_cr_alnum=$as_cr_Letters$as_cr_digits # The user is always right. if test "${PATH_SEPARATOR+set}" != set; then echo "#! /bin/sh" >conf$$.sh echo "exit 0" >>conf$$.sh chmod +x conf$$.sh if (PATH="/nonexistent;."; conf$$.sh) >/dev/null 2>&1; then PATH_SEPARATOR=';' else PATH_SEPARATOR=: fi rm -f conf$$.sh fi # Support unset when possible. if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then as_unset=unset else as_unset=false fi # IFS # We need space, tab and new line, in precisely that order. Quoting is # there to prevent editors from complaining about space-tab. # (If _AS_PATH_WALK were called with IFS unset, it would disable word # splitting by setting IFS to empty value.) as_nl=' ' IFS=" "" $as_nl" # Find who we are. Look in the path if we contain no directory separator. case $0 in *[\\/]* ) as_myself=$0 ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break done IFS=$as_save_IFS ;; esac # We did not find ourselves, most probably we were run as `sh COMMAND' # in which case we are not to be found in the path. if test "x$as_myself" = x; then as_myself=$0 fi if test ! -f "$as_myself"; then echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 { (exit 1); exit 1; } fi # Work around bugs in pre-3.0 UWIN ksh. for as_var in ENV MAIL MAILPATH do ($as_unset $as_var) >/dev/null 2>&1 && $as_unset $as_var done PS1='$ ' PS2='> ' PS4='+ ' # NLS nuisances. for as_var in \ LANG LANGUAGE LC_ADDRESS LC_ALL LC_COLLATE LC_CTYPE LC_IDENTIFICATION \ LC_MEASUREMENT LC_MESSAGES LC_MONETARY LC_NAME LC_NUMERIC LC_PAPER \ LC_TELEPHONE LC_TIME do if (set +x; test -z "`(eval $as_var=C; export $as_var) 2>&1`"); then eval $as_var=C; export $as_var else ($as_unset $as_var) >/dev/null 2>&1 && $as_unset $as_var fi done # Required to use basename. if expr a : '\(a\)' >/dev/null 2>&1 && test "X`expr 00001 : '.*\(...\)'`" = X001; then as_expr=expr else as_expr=false fi if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then as_basename=basename else as_basename=false fi # Name of the executable. as_me=`$as_basename -- "$0" || $as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ X"$0" : 'X\(//\)$' \| \ X"$0" : 'X\(/\)' \| . 2>/dev/null || echo X/"$0" | sed '/^.*\/\([^/][^/]*\)\/*$/{ s//\1/ q } /^X\/\(\/\/\)$/{ s//\1/ q } /^X\/\(\/\).*/{ s//\1/ q } s/.*/./; q'` # CDPATH. $as_unset CDPATH as_lineno_1=$LINENO as_lineno_2=$LINENO test "x$as_lineno_1" != "x$as_lineno_2" && test "x`expr $as_lineno_1 + 1`" = "x$as_lineno_2" || { # Create $as_me.lineno as a copy of $as_myself, but with $LINENO # uniformly replaced by the line number. The first 'sed' inserts a # line-number line after each line using $LINENO; the second 'sed' # does the real work. The second script uses 'N' to pair each # line-number line with the line containing $LINENO, and appends # trailing '-' during substitution so that $LINENO is not a special # case at line end. # (Raja R Harinath suggested sed '=', and Paul Eggert wrote the # scripts with optimization help from Paolo Bonzini. Blame Lee # E. McMahon (1931-1989) for sed's syntax. :-) sed -n ' p /[$]LINENO/= ' <$as_myself | sed ' s/[$]LINENO.*/&-/ t lineno b :lineno N :loop s/[$]LINENO\([^'$as_cr_alnum'_].*\n\)\(.*\)/\2\1\2/ t loop s/-\n.*// ' >$as_me.lineno && chmod +x "$as_me.lineno" || { echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2 { (exit 1); exit 1; }; } # Don't try to exec as it changes $[0], causing all sort of problems # (the dirname of $[0] is not the place where we might find the # original and so on. Autoconf is especially sensitive to this). . "./$as_me.lineno" # Exit status is that of the last command. exit } if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then as_dirname=dirname else as_dirname=false fi ECHO_C= ECHO_N= ECHO_T= case `echo -n x` in -n*) case `echo 'x\c'` in *c*) ECHO_T=' ';; # ECHO_T is single tab character. *) ECHO_C='\c';; esac;; *) ECHO_N='-n';; esac if expr a : '\(a\)' >/dev/null 2>&1 && test "X`expr 00001 : '.*\(...\)'`" = X001; then as_expr=expr else as_expr=false fi rm -f conf$$ conf$$.exe conf$$.file if test -d conf$$.dir; then rm -f conf$$.dir/conf$$.file else rm -f conf$$.dir mkdir conf$$.dir fi echo >conf$$.file if ln -s conf$$.file conf$$ 2>/dev/null; then as_ln_s='ln -s' # ... but there are two gotchas: # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. # In both cases, we have to default to `cp -p'. ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || as_ln_s='cp -p' elif ln conf$$.file conf$$ 2>/dev/null; then as_ln_s=ln else as_ln_s='cp -p' fi rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file rmdir conf$$.dir 2>/dev/null if mkdir -p . 2>/dev/null; then as_mkdir_p=: else test -d ./-p && rmdir ./-p as_mkdir_p=false fi if test -x / >/dev/null 2>&1; then as_test_x='test -x' else if ls -dL / >/dev/null 2>&1; then as_ls_L_option=L else as_ls_L_option= fi as_test_x=' eval sh -c '\'' if test -d "$1"; then test -d "$1/."; else case $1 in -*)set "./$1";; esac; case `ls -ld'$as_ls_L_option' "$1" 2>/dev/null` in ???[sx]*):;;*)false;;esac;fi '\'' sh ' fi as_executable_p=$as_test_x # Sed expression to map a string onto a valid CPP name. as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" # Sed expression to map a string onto a valid variable name. as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" exec 6>&1 # Save the log message, to keep $[0] and so on meaningful, and to # report actual input values of CONFIG_FILES etc. instead of their # values after options handling. ac_log=" This file was extended by $as_me, which was generated by GNU Autoconf 2.61. Invocation command line was CONFIG_FILES = $CONFIG_FILES CONFIG_HEADERS = $CONFIG_HEADERS CONFIG_LINKS = $CONFIG_LINKS CONFIG_COMMANDS = $CONFIG_COMMANDS $ $0 $@ on `(hostname || uname -n) 2>/dev/null | sed 1q` " _ACEOF cat >>$CONFIG_STATUS <<_ACEOF # Files that config.status was made for. config_files="$ac_config_files" config_headers="$ac_config_headers" _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF ac_cs_usage="\ \`$as_me' instantiates files from templates according to the current configuration. Usage: $0 [OPTIONS] [FILE]... -h, --help print this help, then exit -V, --version print version number and configuration settings, then exit -q, --quiet do not print progress messages -d, --debug don't remove temporary files --recheck update $as_me by reconfiguring in the same conditions --file=FILE[:TEMPLATE] instantiate the configuration file FILE --header=FILE[:TEMPLATE] instantiate the configuration header FILE Configuration files: $config_files Configuration headers: $config_headers Report bugs to ." _ACEOF cat >>$CONFIG_STATUS <<_ACEOF ac_cs_version="\\ config.status configured by $0, generated by GNU Autoconf 2.61, with options \\"`echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`\\" Copyright (C) 2006 Free Software Foundation, Inc. This config.status script is free software; the Free Software Foundation gives unlimited permission to copy, distribute and modify it." ac_pwd='$ac_pwd' srcdir='$srcdir' INSTALL='$INSTALL' _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF # If no file are specified by the user, then we need to provide default # value. By we need to know if files were specified by the user. ac_need_defaults=: while test $# != 0 do case $1 in --*=*) ac_option=`expr "X$1" : 'X\([^=]*\)='` ac_optarg=`expr "X$1" : 'X[^=]*=\(.*\)'` ac_shift=: ;; *) ac_option=$1 ac_optarg=$2 ac_shift=shift ;; esac case $ac_option in # Handling of the options. -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r) ac_cs_recheck=: ;; --version | --versio | --versi | --vers | --ver | --ve | --v | -V ) echo "$ac_cs_version"; exit ;; --debug | --debu | --deb | --de | --d | -d ) debug=: ;; --file | --fil | --fi | --f ) $ac_shift CONFIG_FILES="$CONFIG_FILES $ac_optarg" ac_need_defaults=false;; --header | --heade | --head | --hea ) $ac_shift CONFIG_HEADERS="$CONFIG_HEADERS $ac_optarg" ac_need_defaults=false;; --he | --h) # Conflict between --help and --header { echo "$as_me: error: ambiguous option: $1 Try \`$0 --help' for more information." >&2 { (exit 1); exit 1; }; };; --help | --hel | -h ) echo "$ac_cs_usage"; exit ;; -q | -quiet | --quiet | --quie | --qui | --qu | --q \ | -silent | --silent | --silen | --sile | --sil | --si | --s) ac_cs_silent=: ;; # This is an error. -*) { echo "$as_me: error: unrecognized option: $1 Try \`$0 --help' for more information." >&2 { (exit 1); exit 1; }; } ;; *) ac_config_targets="$ac_config_targets $1" ac_need_defaults=false ;; esac shift done ac_configure_extra_args= if $ac_cs_silent; then exec 6>/dev/null ac_configure_extra_args="$ac_configure_extra_args --silent" fi _ACEOF cat >>$CONFIG_STATUS <<_ACEOF if \$ac_cs_recheck; then echo "running CONFIG_SHELL=$SHELL $SHELL $0 "$ac_configure_args \$ac_configure_extra_args " --no-create --no-recursion" >&6 CONFIG_SHELL=$SHELL export CONFIG_SHELL exec $SHELL "$0"$ac_configure_args \$ac_configure_extra_args --no-create --no-recursion fi _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF exec 5>>config.log { echo sed 'h;s/./-/g;s/^.../## /;s/...$/ ##/;p;x;p;x' <<_ASBOX ## Running $as_me. ## _ASBOX echo "$ac_log" } >&5 _ACEOF cat >>$CONFIG_STATUS <<_ACEOF _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF # Handling of arguments. for ac_config_target in $ac_config_targets do case $ac_config_target in "config.h") CONFIG_HEADERS="$CONFIG_HEADERS config.h" ;; "Makefile") CONFIG_FILES="$CONFIG_FILES Makefile" ;; "mxml.list") CONFIG_FILES="$CONFIG_FILES mxml.list" ;; "mxml.pc") CONFIG_FILES="$CONFIG_FILES mxml.pc" ;; *) { { echo "$as_me:$LINENO: error: invalid argument: $ac_config_target" >&5 echo "$as_me: error: invalid argument: $ac_config_target" >&2;} { (exit 1); exit 1; }; };; esac done # If the user did not use the arguments to specify the items to instantiate, # then the envvar interface is used. Set only those that are not. # We use the long form for the default assignment because of an extremely # bizarre bug on SunOS 4.1.3. if $ac_need_defaults; then test "${CONFIG_FILES+set}" = set || CONFIG_FILES=$config_files test "${CONFIG_HEADERS+set}" = set || CONFIG_HEADERS=$config_headers fi # Have a temporary directory for convenience. Make it in the build tree # simply because there is no reason against having it here, and in addition, # creating and moving files from /tmp can sometimes cause problems. # Hook for its removal unless debugging. # Note that there is a small window in which the directory will not be cleaned: # after its creation but before its name has been assigned to `$tmp'. $debug || { tmp= trap 'exit_status=$? { test -z "$tmp" || test ! -d "$tmp" || rm -fr "$tmp"; } && exit $exit_status ' 0 trap '{ (exit 1); exit 1; }' 1 2 13 15 } # Create a (secure) tmp directory for tmp files. { tmp=`(umask 077 && mktemp -d "./confXXXXXX") 2>/dev/null` && test -n "$tmp" && test -d "$tmp" } || { tmp=./conf$$-$RANDOM (umask 077 && mkdir "$tmp") } || { echo "$me: cannot create a temporary directory in ." >&2 { (exit 1); exit 1; } } # # Set up the sed scripts for CONFIG_FILES section. # # No need to generate the scripts if there are no CONFIG_FILES. # This happens for instance when ./config.status config.h if test -n "$CONFIG_FILES"; then _ACEOF ac_delim='%!_!# ' for ac_last_try in false false false false false :; do cat >conf$$subs.sed <<_ACEOF SHELL!$SHELL$ac_delim PATH_SEPARATOR!$PATH_SEPARATOR$ac_delim PACKAGE_NAME!$PACKAGE_NAME$ac_delim PACKAGE_TARNAME!$PACKAGE_TARNAME$ac_delim PACKAGE_VERSION!$PACKAGE_VERSION$ac_delim PACKAGE_STRING!$PACKAGE_STRING$ac_delim PACKAGE_BUGREPORT!$PACKAGE_BUGREPORT$ac_delim exec_prefix!$exec_prefix$ac_delim prefix!$prefix$ac_delim program_transform_name!$program_transform_name$ac_delim bindir!$bindir$ac_delim sbindir!$sbindir$ac_delim libexecdir!$libexecdir$ac_delim datarootdir!$datarootdir$ac_delim datadir!$datadir$ac_delim sysconfdir!$sysconfdir$ac_delim sharedstatedir!$sharedstatedir$ac_delim localstatedir!$localstatedir$ac_delim includedir!$includedir$ac_delim oldincludedir!$oldincludedir$ac_delim docdir!$docdir$ac_delim infodir!$infodir$ac_delim htmldir!$htmldir$ac_delim dvidir!$dvidir$ac_delim pdfdir!$pdfdir$ac_delim psdir!$psdir$ac_delim libdir!$libdir$ac_delim localedir!$localedir$ac_delim mandir!$mandir$ac_delim DEFS!$DEFS$ac_delim ECHO_C!$ECHO_C$ac_delim ECHO_N!$ECHO_N$ac_delim ECHO_T!$ECHO_T$ac_delim LIBS!$LIBS$ac_delim build_alias!$build_alias$ac_delim host_alias!$host_alias$ac_delim target_alias!$target_alias$ac_delim VERSION!$VERSION$ac_delim LDFLAGS!$LDFLAGS$ac_delim OPTIM!$OPTIM$ac_delim ARCHFLAGS!$ARCHFLAGS$ac_delim CC!$CC$ac_delim CFLAGS!$CFLAGS$ac_delim CPPFLAGS!$CPPFLAGS$ac_delim ac_ct_CC!$ac_ct_CC$ac_delim EXEEXT!$EXEEXT$ac_delim OBJEXT!$OBJEXT$ac_delim CXX!$CXX$ac_delim CXXFLAGS!$CXXFLAGS$ac_delim ac_ct_CXX!$ac_ct_CXX$ac_delim INSTALL_PROGRAM!$INSTALL_PROGRAM$ac_delim INSTALL_SCRIPT!$INSTALL_SCRIPT$ac_delim INSTALL_DATA!$INSTALL_DATA$ac_delim RANLIB!$RANLIB$ac_delim AR!$AR$ac_delim CP!$CP$ac_delim LN!$LN$ac_delim MKDIR!$MKDIR$ac_delim RM!$RM$ac_delim ARFLAGS!$ARFLAGS$ac_delim CPP!$CPP$ac_delim GREP!$GREP$ac_delim EGREP!$EGREP$ac_delim PTHREAD_FLAGS!$PTHREAD_FLAGS$ac_delim PTHREAD_LIBS!$PTHREAD_LIBS$ac_delim DSO!$DSO$ac_delim DSOFLAGS!$DSOFLAGS$ac_delim LIBMXML!$LIBMXML$ac_delim PICFLAG!$PICFLAG$ac_delim PC_CFLAGS!$PC_CFLAGS$ac_delim PC_LIBS!$PC_LIBS$ac_delim LIBOBJS!$LIBOBJS$ac_delim LTLIBOBJS!$LTLIBOBJS$ac_delim _ACEOF if test `sed -n "s/.*$ac_delim\$/X/p" conf$$subs.sed | grep -c X` = 73; then break elif $ac_last_try; then { { echo "$as_me:$LINENO: error: could not make $CONFIG_STATUS" >&5 echo "$as_me: error: could not make $CONFIG_STATUS" >&2;} { (exit 1); exit 1; }; } else ac_delim="$ac_delim!$ac_delim _$ac_delim!! " fi done ac_eof=`sed -n '/^CEOF[0-9]*$/s/CEOF/0/p' conf$$subs.sed` if test -n "$ac_eof"; then ac_eof=`echo "$ac_eof" | sort -nru | sed 1q` ac_eof=`expr $ac_eof + 1` fi cat >>$CONFIG_STATUS <<_ACEOF cat >"\$tmp/subs-1.sed" <<\CEOF$ac_eof /@[a-zA-Z_][a-zA-Z_0-9]*@/!b end _ACEOF sed ' s/[,\\&]/\\&/g; s/@/@|#_!!_#|/g s/^/s,@/; s/!/@,|#_!!_#|/ :n t n s/'"$ac_delim"'$/,g/; t s/$/\\/; p N; s/^.*\n//; s/[,\\&]/\\&/g; s/@/@|#_!!_#|/g; b n ' >>$CONFIG_STATUS >$CONFIG_STATUS <<_ACEOF :end s/|#_!!_#|//g CEOF$ac_eof _ACEOF # VPATH may cause trouble with some makes, so we remove $(srcdir), # ${srcdir} and @srcdir@ from VPATH if srcdir is ".", strip leading and # trailing colons and then remove the whole line if VPATH becomes empty # (actually we leave an empty line to preserve line numbers). if test "x$srcdir" = x.; then ac_vpsub='/^[ ]*VPATH[ ]*=/{ s/:*\$(srcdir):*/:/ s/:*\${srcdir}:*/:/ s/:*@srcdir@:*/:/ s/^\([^=]*=[ ]*\):*/\1/ s/:*$// s/^[^=]*=[ ]*$// }' fi cat >>$CONFIG_STATUS <<\_ACEOF fi # test -n "$CONFIG_FILES" for ac_tag in :F $CONFIG_FILES :H $CONFIG_HEADERS do case $ac_tag in :[FHLC]) ac_mode=$ac_tag; continue;; esac case $ac_mode$ac_tag in :[FHL]*:*);; :L* | :C*:*) { { echo "$as_me:$LINENO: error: Invalid tag $ac_tag." >&5 echo "$as_me: error: Invalid tag $ac_tag." >&2;} { (exit 1); exit 1; }; };; :[FH]-) ac_tag=-:-;; :[FH]*) ac_tag=$ac_tag:$ac_tag.in;; esac ac_save_IFS=$IFS IFS=: set x $ac_tag IFS=$ac_save_IFS shift ac_file=$1 shift case $ac_mode in :L) ac_source=$1;; :[FH]) ac_file_inputs= for ac_f do case $ac_f in -) ac_f="$tmp/stdin";; *) # Look for the file first in the build tree, then in the source tree # (if the path is not absolute). The absolute path cannot be DOS-style, # because $ac_f cannot contain `:'. test -f "$ac_f" || case $ac_f in [\\/$]*) false;; *) test -f "$srcdir/$ac_f" && ac_f="$srcdir/$ac_f";; esac || { { echo "$as_me:$LINENO: error: cannot find input file: $ac_f" >&5 echo "$as_me: error: cannot find input file: $ac_f" >&2;} { (exit 1); exit 1; }; };; esac ac_file_inputs="$ac_file_inputs $ac_f" done # Let's still pretend it is `configure' which instantiates (i.e., don't # use $as_me), people would be surprised to read: # /* config.h. Generated by config.status. */ configure_input="Generated from "`IFS=: echo $* | sed 's|^[^:]*/||;s|:[^:]*/|, |g'`" by configure." if test x"$ac_file" != x-; then configure_input="$ac_file. $configure_input" { echo "$as_me:$LINENO: creating $ac_file" >&5 echo "$as_me: creating $ac_file" >&6;} fi case $ac_tag in *:-:* | *:-) cat >"$tmp/stdin";; esac ;; esac ac_dir=`$as_dirname -- "$ac_file" || $as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$ac_file" : 'X\(//\)[^/]' \| \ X"$ac_file" : 'X\(//\)$' \| \ X"$ac_file" : 'X\(/\)' \| . 2>/dev/null || echo X"$ac_file" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q'` { as_dir="$ac_dir" case $as_dir in #( -*) as_dir=./$as_dir;; esac test -d "$as_dir" || { $as_mkdir_p && mkdir -p "$as_dir"; } || { as_dirs= while :; do case $as_dir in #( *\'*) as_qdir=`echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #( *) as_qdir=$as_dir;; esac as_dirs="'$as_qdir' $as_dirs" as_dir=`$as_dirname -- "$as_dir" || $as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$as_dir" : 'X\(//\)[^/]' \| \ X"$as_dir" : 'X\(//\)$' \| \ X"$as_dir" : 'X\(/\)' \| . 2>/dev/null || echo X"$as_dir" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q'` test -d "$as_dir" && break done test -z "$as_dirs" || eval "mkdir $as_dirs" } || test -d "$as_dir" || { { echo "$as_me:$LINENO: error: cannot create directory $as_dir" >&5 echo "$as_me: error: cannot create directory $as_dir" >&2;} { (exit 1); exit 1; }; }; } ac_builddir=. case "$ac_dir" in .) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;; *) ac_dir_suffix=/`echo "$ac_dir" | sed 's,^\.[\\/],,'` # A ".." for each directory in $ac_dir_suffix. ac_top_builddir_sub=`echo "$ac_dir_suffix" | sed 's,/[^\\/]*,/..,g;s,/,,'` case $ac_top_builddir_sub in "") ac_top_builddir_sub=. ac_top_build_prefix= ;; *) ac_top_build_prefix=$ac_top_builddir_sub/ ;; esac ;; esac ac_abs_top_builddir=$ac_pwd ac_abs_builddir=$ac_pwd$ac_dir_suffix # for backward compatibility: ac_top_builddir=$ac_top_build_prefix case $srcdir in .) # We are building in place. ac_srcdir=. ac_top_srcdir=$ac_top_builddir_sub ac_abs_top_srcdir=$ac_pwd ;; [\\/]* | ?:[\\/]* ) # Absolute name. ac_srcdir=$srcdir$ac_dir_suffix; ac_top_srcdir=$srcdir ac_abs_top_srcdir=$srcdir ;; *) # Relative name. ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix ac_top_srcdir=$ac_top_build_prefix$srcdir ac_abs_top_srcdir=$ac_pwd/$srcdir ;; esac ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix case $ac_mode in :F) # # CONFIG_FILE # case $INSTALL in [\\/$]* | ?:[\\/]* ) ac_INSTALL=$INSTALL ;; *) ac_INSTALL=$ac_top_build_prefix$INSTALL ;; esac _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF # If the template does not know about datarootdir, expand it. # FIXME: This hack should be removed a few years after 2.60. ac_datarootdir_hack=; ac_datarootdir_seen= case `sed -n '/datarootdir/ { p q } /@datadir@/p /@docdir@/p /@infodir@/p /@localedir@/p /@mandir@/p ' $ac_file_inputs` in *datarootdir*) ac_datarootdir_seen=yes;; *@datadir@*|*@docdir@*|*@infodir@*|*@localedir@*|*@mandir@*) { echo "$as_me:$LINENO: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&5 echo "$as_me: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&2;} _ACEOF cat >>$CONFIG_STATUS <<_ACEOF ac_datarootdir_hack=' s&@datadir@&$datadir&g s&@docdir@&$docdir&g s&@infodir@&$infodir&g s&@localedir@&$localedir&g s&@mandir@&$mandir&g s&\\\${datarootdir}&$datarootdir&g' ;; esac _ACEOF # Neutralize VPATH when `$srcdir' = `.'. # Shell code in configure.ac might set extrasub. # FIXME: do we really want to maintain this feature? cat >>$CONFIG_STATUS <<_ACEOF sed "$ac_vpsub $extrasub _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF :t /@[a-zA-Z_][a-zA-Z_0-9]*@/!b s&@configure_input@&$configure_input&;t t s&@top_builddir@&$ac_top_builddir_sub&;t t s&@srcdir@&$ac_srcdir&;t t s&@abs_srcdir@&$ac_abs_srcdir&;t t s&@top_srcdir@&$ac_top_srcdir&;t t s&@abs_top_srcdir@&$ac_abs_top_srcdir&;t t s&@builddir@&$ac_builddir&;t t s&@abs_builddir@&$ac_abs_builddir&;t t s&@abs_top_builddir@&$ac_abs_top_builddir&;t t s&@INSTALL@&$ac_INSTALL&;t t $ac_datarootdir_hack " $ac_file_inputs | sed -f "$tmp/subs-1.sed" >$tmp/out test -z "$ac_datarootdir_hack$ac_datarootdir_seen" && { ac_out=`sed -n '/\${datarootdir}/p' "$tmp/out"`; test -n "$ac_out"; } && { ac_out=`sed -n '/^[ ]*datarootdir[ ]*:*=/p' "$tmp/out"`; test -z "$ac_out"; } && { echo "$as_me:$LINENO: WARNING: $ac_file contains a reference to the variable \`datarootdir' which seems to be undefined. Please make sure it is defined." >&5 echo "$as_me: WARNING: $ac_file contains a reference to the variable \`datarootdir' which seems to be undefined. Please make sure it is defined." >&2;} rm -f "$tmp/stdin" case $ac_file in -) cat "$tmp/out"; rm -f "$tmp/out";; *) rm -f "$ac_file"; mv "$tmp/out" $ac_file;; esac ;; :H) # # CONFIG_HEADER # _ACEOF # Transform confdefs.h into a sed script `conftest.defines', that # substitutes the proper values into config.h.in to produce config.h. rm -f conftest.defines conftest.tail # First, append a space to every undef/define line, to ease matching. echo 's/$/ /' >conftest.defines # Then, protect against being on the right side of a sed subst, or in # an unquoted here document, in config.status. If some macros were # called several times there might be several #defines for the same # symbol, which is useless. But do not sort them, since the last # AC_DEFINE must be honored. ac_word_re=[_$as_cr_Letters][_$as_cr_alnum]* # These sed commands are passed to sed as "A NAME B PARAMS C VALUE D", where # NAME is the cpp macro being defined, VALUE is the value it is being given. # PARAMS is the parameter list in the macro definition--in most cases, it's # just an empty string. ac_dA='s,^\\([ #]*\\)[^ ]*\\([ ]*' ac_dB='\\)[ (].*,\\1define\\2' ac_dC=' ' ac_dD=' ,' uniq confdefs.h | sed -n ' t rset :rset s/^[ ]*#[ ]*define[ ][ ]*// t ok d :ok s/[\\&,]/\\&/g s/^\('"$ac_word_re"'\)\(([^()]*)\)[ ]*\(.*\)/ '"$ac_dA"'\1'"$ac_dB"'\2'"${ac_dC}"'\3'"$ac_dD"'/p s/^\('"$ac_word_re"'\)[ ]*\(.*\)/'"$ac_dA"'\1'"$ac_dB$ac_dC"'\2'"$ac_dD"'/p ' >>conftest.defines # Remove the space that was appended to ease matching. # Then replace #undef with comments. This is necessary, for # example, in the case of _POSIX_SOURCE, which is predefined and required # on some systems where configure will not decide to define it. # (The regexp can be short, since the line contains either #define or #undef.) echo 's/ $// s,^[ #]*u.*,/* & */,' >>conftest.defines # Break up conftest.defines: ac_max_sed_lines=50 # First sed command is: sed -f defines.sed $ac_file_inputs >"$tmp/out1" # Second one is: sed -f defines.sed "$tmp/out1" >"$tmp/out2" # Third one will be: sed -f defines.sed "$tmp/out2" >"$tmp/out1" # et cetera. ac_in='$ac_file_inputs' ac_out='"$tmp/out1"' ac_nxt='"$tmp/out2"' while : do # Write a here document: cat >>$CONFIG_STATUS <<_ACEOF # First, check the format of the line: cat >"\$tmp/defines.sed" <<\\CEOF /^[ ]*#[ ]*undef[ ][ ]*$ac_word_re[ ]*\$/b def /^[ ]*#[ ]*define[ ][ ]*$ac_word_re[( ]/b def b :def _ACEOF sed ${ac_max_sed_lines}q conftest.defines >>$CONFIG_STATUS echo 'CEOF sed -f "$tmp/defines.sed"' "$ac_in >$ac_out" >>$CONFIG_STATUS ac_in=$ac_out; ac_out=$ac_nxt; ac_nxt=$ac_in sed 1,${ac_max_sed_lines}d conftest.defines >conftest.tail grep . conftest.tail >/dev/null || break rm -f conftest.defines mv conftest.tail conftest.defines done rm -f conftest.defines conftest.tail echo "ac_result=$ac_in" >>$CONFIG_STATUS cat >>$CONFIG_STATUS <<\_ACEOF if test x"$ac_file" != x-; then echo "/* $configure_input */" >"$tmp/config.h" cat "$ac_result" >>"$tmp/config.h" if diff $ac_file "$tmp/config.h" >/dev/null 2>&1; then { echo "$as_me:$LINENO: $ac_file is unchanged" >&5 echo "$as_me: $ac_file is unchanged" >&6;} else rm -f $ac_file mv "$tmp/config.h" $ac_file fi else echo "/* $configure_input */" cat "$ac_result" fi rm -f "$tmp/out12" ;; esac done # for ac_tag { (exit 0); exit 0; } _ACEOF chmod +x $CONFIG_STATUS ac_clean_files=$ac_clean_files_save # configure is writing to config.log, and then calls config.status. # config.status does its own redirection, appending to config.log. # Unfortunately, on DOS this fails, as config.log is still kept open # by configure, so config.status won't be able to write to it; its # output is simply discarded. So we exec the FD to /dev/null, # effectively closing config.log, so it can be properly (re)opened and # appended to by config.status. When coming back to configure, we # need to make the FD available again. if test "$no_create" != yes; then ac_cs_success=: ac_config_status_args= test "$silent" = yes && ac_config_status_args="$ac_config_status_args --quiet" exec 5>/dev/null $SHELL $CONFIG_STATUS $ac_config_status_args || ac_cs_success=false exec 5>>config.log # Use ||, not &&, to avoid exiting from the if with $? = 1, which # would make configure fail if this is the last instruction. $ac_cs_success || { (exit 1); exit 1; } fi cmtk-3.3.1/Utilities/mxml/configure.in000066400000000000000000000167711276303427400177410ustar00rootroot00000000000000dnl dnl "$Id: configure.in 427 2011-01-03 02:03:29Z mike $" dnl dnl Configuration script for Mini-XML, a small XML-like file parsing library. dnl dnl Copyright 2003-2011 by Michael R Sweet. dnl dnl These coded instructions, statements, and computer programs are the dnl property of Michael R Sweet and are protected by Federal copyright dnl law. Distribution and use rights are outlined in the file "COPYING" dnl which should have been included with this file. If this file is dnl missing or damaged, see the license at: dnl dnl http://www.minixml.org/ dnl dnl Specify a source file from the distribution... AC_INIT(mxml.h) dnl Set the name of the config header file... AC_CONFIG_HEADER(config.h) dnl Version number... VERSION=2.7 AC_SUBST(VERSION) AC_DEFINE_UNQUOTED(MXML_VERSION, "Mini-XML v$VERSION") dnl Clear default debugging options and set normal optimization by dnl default unless the user asks for debugging specifically. CFLAGS="${CFLAGS:=}" CXXFLAGS="${CXXFLAGS:=}" LDFLAGS="${LDFLAGS:=}" AC_SUBST(LDFLAGS) OPTIM="" AC_SUBST(OPTIM) AC_ARG_WITH(ansi, [ --with-ansi set full ANSI C mode, default=no], use_ansi="$withval", use_ansi="no") AC_ARG_WITH(archflags, [ --with-archflags set additional architecture flags, default=none], ARCHFLAGS="$withval", ARCHFLAGS="") AC_SUBST(ARCHFLAGS) AC_ARG_ENABLE(debug, [ --enable-debug turn on debugging, default=no], if eval "test x$enable_debug = xyes"; then OPTIM="-g" fi) AC_ARG_WITH(docdir, [ --with-docdir set directory for documentation, default=${prefix}/share/doc/mxml], docdir="$withval", docdir="NONE") AC_SUBST(docdir) AC_ARG_WITH(vsnprintf, [ --with-vsnprintf use vsnprintf emulation functions, default=auto], use_vsnprintf="$withval", use_vsnprintf="no") dnl Get the operating system and version number... uname=`uname` uversion=`uname -r | sed -e '1,$s/[[^0-9]]//g'` if test x$uname = xIRIX64; then uname="IRIX" fi dnl Checks for programs... AC_PROG_CC AC_PROG_CXX AC_PROG_INSTALL if test "$INSTALL" = "$ac_install_sh"; then # Use full path to install-sh script... INSTALL="`pwd`/install-sh -c" fi AC_PROG_RANLIB AC_PATH_PROG(AR,ar) AC_PATH_PROG(CP,cp) AC_PATH_PROG(LN,ln) AC_PATH_PROG(MKDIR,mkdir) AC_PATH_PROG(RM,rm) dnl Flags for "ar" command... case "$uname" in Darwin* | *BSD*) ARFLAGS="-rcv" ;; *) ARFLAGS="crvs" ;; esac AC_SUBST(ARFLAGS) dnl Inline functions... AC_C_INLINE dnl Checks for string functions. if test "x$use_ansi" != xyes; then AC_CHECK_FUNCS(strdup) fi if test "x$use_vsnprintf" != xyes; then AC_CHECK_FUNCS(snprintf vsnprintf) fi dnl Check for "long long" support... AC_CACHE_CHECK(for long long int, ac_cv_c_long_long, [if test "$GCC" = yes; then ac_cv_c_long_long=yes else AC_TRY_COMPILE(,[long long int i;], ac_cv_c_long_long=yes, ac_cv_c_long_long=no) fi]) if test $ac_cv_c_long_long = yes; then AC_DEFINE(HAVE_LONG_LONG) fi dnl Threading support AC_ARG_ENABLE(threads, [ --enable-threads enable multi-threading support]) have_pthread=no PTHREAD_FLAGS="" PTHREAD_LIBS="" if test "x$enable_threads" != xno; then AC_CHECK_HEADER(pthread.h, AC_DEFINE(HAVE_PTHREAD_H)) if test x$ac_cv_header_pthread_h = xyes; then dnl Check various threading options for the platforms we support for flag in -lpthreads -lpthread -pthread; do AC_MSG_CHECKING([for pthread_create using $flag]) SAVELIBS="$LIBS" LIBS="$flag $LIBS" AC_TRY_LINK([#include ], [pthread_create(0, 0, 0, 0);], have_pthread=yes) AC_MSG_RESULT([$have_pthread]) LIBS="$SAVELIBS" if test $have_pthread = yes; then PTHREAD_FLAGS="-D_THREAD_SAFE -D_REENTRANT" PTHREAD_LIBS="$flag" # Solaris requires -D_POSIX_PTHREAD_SEMANTICS to # be POSIX-compliant... :( if test $uname = SunOS; then PTHREAD_FLAGS="$PTHREAD_FLAGS -D_POSIX_PTHREAD_SEMANTICS" fi break fi done fi fi AC_SUBST(PTHREAD_FLAGS) AC_SUBST(PTHREAD_LIBS) dnl Shared library support... DSO="${DSO:=:}" DSOFLAGS="${DSOFLAGS:=}" AC_ARG_ENABLE(shared, [ --enable-shared turn on shared libraries, default=no]) if test x$enable_shared != xno; then AC_MSG_CHECKING(for shared library support) PICFLAG=1 case "$uname" in SunOS* | UNIX_S*) AC_MSG_RESULT(yes) LIBMXML="libmxml.so.1.5" DSO="\$(CC)" DSOFLAGS="$DSOFLAGS -Wl,-h,libmxml.so.1 -G -R\$(libdir) \$(OPTIM)" LDFLAGS="$LDFLAGS -R\$(libdir)" ;; HP-UX*) AC_MSG_RESULT(yes) LIBMXML="libmxml.sl.1" DSO="ld" DSOFLAGS="$DSOFLAGS -b -z +h libmxml.sl.1 +s +b \$(libdir)" LDFLAGS="$LDFLAGS -Wl,+s,+b,\$(libdir)" ;; IRIX) AC_MSG_RESULT(yes) LIBMXML="libmxml.so.1.5" DSO="\$(CC)" DSOFLAGS="$DSOFLAGS -Wl,-rpath,\$(libdir),-set_version,sgi1.0,-soname,libmxml.so.1 -shared \$(OPTIM)" ;; OSF1* | Linux | GNU) AC_MSG_RESULT(yes) LIBMXML="libmxml.so.1.5" DSO="\$(CC)" DSOFLAGS="$DSOFLAGS -Wl,-soname,libmxml.so.1,-rpath,\$(libdir) -shared \$(OPTIM)" LDFLAGS="$LDFLAGS -Wl,-rpath,\$(libdir)" ;; *BSD*) AC_MSG_RESULT(yes) LIBMXML="libmxml.so.1.5" DSO="\$(CC)" DSOFLAGS="$DSOFLAGS -Wl,-soname,libmxml.so.1,-R\$(libdir) -shared \$(OPTIM)" LDFLAGS="$LDFLAGS -Wl,-R\$(libdir)" ;; Darwin*) AC_MSG_RESULT(yes) LIBMXML="libmxml.1.dylib" DSO="\$(CC)" DSOFLAGS="$DSOFLAGS \$(RC_CFLAGS) -dynamiclib -lc" ;; *) AC_MSG_RESULT(no) AC_MSG_WARN(shared libraries not supported on this platform.) PICFLAG=0 LIBMXML="libmxml.a" ;; esac else PICFLAG=0 LIBMXML="libmxml.a" fi AC_SUBST(DSO) AC_SUBST(DSOFLAGS) AC_SUBST(LIBMXML) AC_SUBST(PICFLAG) dnl Add -Wall for GCC... if test -n "$GCC"; then CFLAGS="-Wall $CFLAGS" if test "x$OPTIM" = x; then OPTIM="-Os -g" fi if test "x$use_ansi" = xyes; then CFLAGS="-ansi -pedantic $CFLAGS" fi if test $PICFLAG = 1 -a $uname != AIX; then OPTIM="-fPIC $OPTIM" fi else case $uname in HP-UX*) CFLAGS="-Ae $CFLAGS" if test "x$OPTIM" = x; then OPTIM="-O" fi OPTIM="+DAportable $OPTIM" if test $PICFLAG = 1; then OPTIM="+z $OPTIM" fi ;; UNIX_SVR* | SunOS*) if test "x$OPTIM" = x; then OPTIM="-O" fi if test $PICFLAG = 1; then OPTIM="-KPIC $OPTIM" fi ;; *) if test "x$OPTIM" = x; then OPTIM="-O" fi ;; esac fi dnl Fix "prefix" variable if it hasn't been specified... if test "$prefix" = "NONE"; then prefix="/usr/local" fi dnl Fix "exec_prefix" variable if it hasn't been specified... if test "$exec_prefix" = "NONE"; then exec_prefix="$prefix" fi dnl Fix "docdir" variable if it hasn't been specified... if test "$docdir" = "NONE"; then docdir="$datadir/doc/mxml" fi dnl Fix "mandir" variable if it hasn't been specified... if test "$mandir" = "\${prefix}/man" -a "$prefix" = "/usr"; then case "$uname" in *BSD* | Darwin* | Linux*) # BSD, Darwin (MacOS X), and Linux mandir="/usr/share/man" ;; IRIX*) # SGI IRIX mandir="/usr/share/catman/u_man" ;; *) # All others mandir="/usr/man" ;; esac fi dnl pkg-config stuff... if test "$includedir" != /usr/include; then PC_CFLAGS="-I$includedir" else PC_CFLAGS="" fi if test "$libdir" != /usr/lib; then PC_LIBS="-L$libdir -lmxml" else PC_LIBS="-lmxml" fi AC_SUBST(PC_CFLAGS) AC_SUBST(PC_LIBS) dnl Output the makefile, etc... AC_OUTPUT(Makefile mxml.list mxml.pc) dnl dnl End of "$Id: configure.in 427 2011-01-03 02:03:29Z mike $". dnl cmtk-3.3.1/Utilities/mxml/doc/000077500000000000000000000000001276303427400161615ustar00rootroot00000000000000cmtk-3.3.1/Utilities/mxml/doc/0.gif000066400000000000000000000027131276303427400170120ustar00rootroot00000000000000GIF89addç  !!!"""###$$$%%%&&&'''((()))***+++,,,---...///000111222333444555666777888999:::;;;<<<===>>>???@@@AAABBBCCCDDDEEEFFFGGGHHHIIIJJJKKKLLLMMMNNNOOOPPPQQQRRRSSSTTTUUUVVVWWWXXXYYYZZZ[[[\\\]]]^^^___```aaabbbcccdddeeefffggghhhiiijjjkkklllmmmnnnooopppqqqrrrssstttuuuvvvwwwxxxyyyzzz{{{|||}}}~~~€€€‚‚‚ƒƒƒ„„„………†††‡‡‡ˆˆˆ‰‰‰ŠŠŠ‹‹‹ŒŒŒŽŽŽ‘‘‘’’’“““”””•••–––———˜˜˜™™™ššš›››œœœžžžŸŸŸ   ¡¡¡¢¢¢£££¤¤¤¥¥¥¦¦¦§§§¨¨¨©©©ªªª«««¬¬¬­­­®®®¯¯¯°°°±±±²²²³³³´´´µµµ¶¶¶···¸¸¸¹¹¹ººº»»»¼¼¼½½½¾¾¾¿¿¿ÀÀÀÁÁÁÂÂÂÃÃÃÄÄÄÅÅÅÆÆÆÇÇÇÈÈÈÉÉÉÊÊÊËËËÌÌÌÍÍÍÎÎÎÏÏÏÐÐÐÑÑÑÒÒÒÓÓÓÔÔÔÕÕÕÖÖÖ×××ØØØÙÙÙÚÚÚÛÛÛÜÜÜÝÝÝÞÞÞßßßàààáááâââãããäääåååæææçççèèèéééêêêëëëìììíííîîîïïïðððñññòòòóóóôôôõõõööö÷÷÷øøøùùùúúúûûûüüüýýýþþþÿÿÿ,ddþÿ H°`A5*\Ȱ¡Ã‡#*Tc°¢Å‹‡IÜȱ£ÇÃ0мhå£É“(­Œ\ù¯Ê—0;öay1¦Í›i$…³§Ï„¤tþkò³(Î&4a]jÆJ¢L£¾DŠ‘§Ô«'ƒÖÄÊõãÖ® ydªæÎÝ5MDÀ*¬è’k–”ºÎ,ˆÐ°,)uß]ºRU”*äoE?RjdzͰÅ{QCþ‹ªÏ1ƨ™Þ³,’if£’9_<·ô¢'DLbôtQÕ+ÕÂ4{$dÙ'uâhD'n“ìXÆ‹(%¿ß…oŒÇ9Ç1Í9²äã\"K !D¯þp»ôØÜþ¦©äc••6Â7üÞëHtêzï8_}ý÷¹¯|vÒWûø3¬äšGçTC| ²v&Ù5Ò&ÐÏO„ùáG¡~ÒסsZ÷!r!FT¢l'v7âo):ÔbW/Ê·"n1.TãU7&”#f3æÔ£Z;îèÙ. £‘2Ž„!’62‰£“:BÉ£’( Yš”V¤”CR™—WziR–¯a ¦–fŠÞ–jrئ‡o:çMšá+)eU"¡ƒ"]rR,+åµdœ&žfgc*Š&¢>BŠœ&+…ðQ+qaB€2 §§›ÆH&SÉ>›*”K»lÔ K²¤ºKÄDD M²*dŠN4T‚N‚ä:km æj±•!l“ÈV´l’ÍöìB6D;ÐL øÓ\Í~‘ídLÙÑ-R­@¬‰›®hzý³˜THX6V¡uuÄ]U)_]É1 j &í´]ÔÁEqKÂİOU=|“VA%1J|b¤çÅ9Å’Åo”ñJV…ÅB™©a«|€Â–ñ˱Àµ½ËphÖš¶,E;cmtk-3.3.1/Utilities/mxml/doc/1.gif000066400000000000000000000023701276303427400170120ustar00rootroot00000000000000GIF89addç  !!!"""###$$$%%%&&&'''((()))***+++,,,---...///000111222333444555666777888999:::;;;<<<===>>>???@@@AAABBBCCCDDDEEEFFFGGGHHHIIIJJJKKKLLLMMMNNNOOOPPPQQQRRRSSSTTTUUUVVVWWWXXXYYYZZZ[[[\\\]]]^^^___```aaabbbcccdddeeefffggghhhiiijjjkkklllmmmnnnooopppqqqrrrssstttuuuvvvwwwxxxyyyzzz{{{|||}}}~~~€€€‚‚‚ƒƒƒ„„„………†††‡‡‡ˆˆˆ‰‰‰ŠŠŠ‹‹‹ŒŒŒŽŽŽ‘‘‘’’’“““”””•••–––———˜˜˜™™™ššš›››œœœžžžŸŸŸ   ¡¡¡¢¢¢£££¤¤¤¥¥¥¦¦¦§§§¨¨¨©©©ªªª«««¬¬¬­­­®®®¯¯¯°°°±±±²²²³³³´´´µµµ¶¶¶···¸¸¸¹¹¹ººº»»»¼¼¼½½½¾¾¾¿¿¿ÀÀÀÁÁÁÂÂÂÃÃÃÄÄÄÅÅÅÆÆÆÇÇÇÈÈÈÉÉÉÊÊÊËËËÌÌÌÍÍÍÎÎÎÏÏÏÐÐÐÑÑÑÒÒÒÓÓÓÔÔÔÕÕÕÖÖÖ×××ØØØÙÙÙÚÚÚÛÛÛÜÜÜÝÝÝÞÞÞßßßàààáááâââãããäääåååæææçççèèèéééêêêëëëìììíííîîîïïïðððñññòòòóóóôôôõõõööö÷÷÷øøøùùùúúúûûûüüüýýýþþþÿÿÿ,ddþÿ H°`A5*\Ȱ¡Ã‡#*Tc°¢Å‹‡IÜȱ£ÇÃ0мhå£É“(­Œ\ù¯Ê—0;öay1¦Í›i$…³§Ï„¤tþkò³(Î&4a]jÆJ¢L£¾DŠ‘§Ô«'ƒÖÄÊõãÖ®`9Vt¶lÄ™Í6ü*µ`Iµ ©°ªr Ü…»Ôx÷€…¼\Cþë›P$X„iÖ;ø.‘‡Âý±¬dµ½XÆÚ@çæ¨„6þìsD:шIŸT EIèPT}RöbÚmÆÝQ÷\Þ}[Þ[ølâG\9Aæ?‡žSzjê75cÏ^y»MíÞaÞ‚r>>???@@@AAABBBCCCDDDEEEFFFGGGHHHIIIJJJKKKLLLMMMNNNOOOPPPQQQRRRSSSTTTUUUVVVWWWXXXYYYZZZ[[[\\\]]]^^^___```aaabbbcccdddeeefffggghhhiiijjjkkklllmmmnnnooopppqqqrrrssstttuuuvvvwwwxxxyyyzzz{{{|||}}}~~~€€€‚‚‚ƒƒƒ„„„………†††‡‡‡ˆˆˆ‰‰‰ŠŠŠ‹‹‹ŒŒŒŽŽŽ‘‘‘’’’“““”””•••–––———˜˜˜™™™ššš›››œœœžžžŸŸŸ   ¡¡¡¢¢¢£££¤¤¤¥¥¥¦¦¦§§§¨¨¨©©©ªªª«««¬¬¬­­­®®®¯¯¯°°°±±±²²²³³³´´´µµµ¶¶¶···¸¸¸¹¹¹ººº»»»¼¼¼½½½¾¾¾¿¿¿ÀÀÀÁÁÁÂÂÂÃÃÃÄÄÄÅÅÅÆÆÆÇÇÇÈÈÈÉÉÉÊÊÊËËËÌÌÌÍÍÍÎÎÎÏÏÏÐÐÐÑÑÑÒÒÒÓÓÓÔÔÔÕÕÕÖÖÖ×××ØØØÙÙÙÚÚÚÛÛÛÜÜÜÝÝÝÞÞÞßßßàààáááâââãããäääåååæææçççèèèéééêêêëëëìììíííîîîïïïðððñññòòòóóóôôôõõõööö÷÷÷øøøùùùúúúûûûüüüýýýþþþÿÿÿ,ddþÿ H°`A5*\Ȱ¡Ã‡#*Tc°¢Å‹‡IÜȱ£ÇÃ0мhå£É“(­Œ\ù¯Ê—0;öay1¦Í›i$…³§Ï„¤tþkò³(Î&4a]jÆJ¢L£¾DŠ‘§Ô«'ƒÖÄÊõãÖ®`9VtÉÕ‹¿•vºÎ,ˆuŽÐÁ°,µÕÛŠRUŒ:âîEwQj\ͯH KCþcÊÏðÈ0K.Ýçx¥ £’‹&«Ì2HÑjàL³h蟤u†=y)5M<«½êôW‚á’·±;fQ QN9¹7Ò\'¶tðˆÆ‹¯<ÑËXbî0ÞJv&“SWøÜäîÛ¹þ/? >üòÑo_ ¥zê+¸oþüÈóGÖO¸2A~‘ûÙàH_vøY‚ ö„À{ ÚÏJÑD¸àHþYØÓ=jxÒw+‰âáMv8bz,½qbL¼­xR-ºøQ :a £I¡èDÅ5FÓ<¢H“Ar„…P7¹Q=BA dD0 õdD•5Ï”9 5–9'\6dNô„ÙÐ[;š©Rª©"BçfB›é8gB…é樴yg:Ás§x,Y3(4•sèpjwgŒwŠBÓ'‹ è¤Ûap)nûµ)¦ð} *s¢Žœ ¥:JÝ,©š[«p®zt‹°êwE8Õj«M™ñªk7õJë¯åúÏ`ÃkÐMŠ-V©pÑõìCzí5íC‘uíBk±µíB_m[Õ·Z]Õ³T‰¤T¥N±tîé²dÕœåúifeÚNÙmeÒY­kȺØlª³*HÑ];cmtk-3.3.1/Utilities/mxml/doc/3.gif000066400000000000000000000026521276303427400170170ustar00rootroot00000000000000GIF89addç  !!!"""###$$$%%%&&&'''((()))***+++,,,---...///000111222333444555666777888999:::;;;<<<===>>>???@@@AAABBBCCCDDDEEEFFFGGGHHHIIIJJJKKKLLLMMMNNNOOOPPPQQQRRRSSSTTTUUUVVVWWWXXXYYYZZZ[[[\\\]]]^^^___```aaabbbcccdddeeefffggghhhiiijjjkkklllmmmnnnooopppqqqrrrssstttuuuvvvwwwxxxyyyzzz{{{|||}}}~~~€€€‚‚‚ƒƒƒ„„„………†††‡‡‡ˆˆˆ‰‰‰ŠŠŠ‹‹‹ŒŒŒŽŽŽ‘‘‘’’’“““”””•••–––———˜˜˜™™™ššš›››œœœžžžŸŸŸ   ¡¡¡¢¢¢£££¤¤¤¥¥¥¦¦¦§§§¨¨¨©©©ªªª«««¬¬¬­­­®®®¯¯¯°°°±±±²²²³³³´´´µµµ¶¶¶···¸¸¸¹¹¹ººº»»»¼¼¼½½½¾¾¾¿¿¿ÀÀÀÁÁÁÂÂÂÃÃÃÄÄÄÅÅÅÆÆÆÇÇÇÈÈÈÉÉÉÊÊÊËËËÌÌÌÍÍÍÎÎÎÏÏÏÐÐÐÑÑÑÒÒÒÓÓÓÔÔÔÕÕÕÖÖÖ×××ØØØÙÙÙÚÚÚÛÛÛÜÜÜÝÝÝÞÞÞßßßàààáááâââãããäääåååæææçççèèèéééêêêëëëìììíííîîîïïïðððñññòòòóóóôôôõõõööö÷÷÷øøøùùùúúúûûûüüüýýýþþþÿÿÿ,ddþÿ H°`A5*\Ȱ¡Ã‡#*Tc°¢Å‹‡IÜȱ£ÇÃ0мhå£É“(­Œ\ù¯Ê—0;öay1¦Í›i$…³§Ï„¤tþkò³(Î&4a]jÆJ¢L£¾DŠ‘§Ô«'ƒÖÄÊõãÖ®búUÎ^»g¨€UXÑ%V]4ûÕà:³àUDB ÚÃZ°$SGy-JU9)‚ÀñD¨Ñ(Ä"ï1 ùoi(È+™ 4ºóÊ~K7ÿ¼à™e*£ÿþ,MÓ¨jŸåX·^{R6Í´?*Ó™À¡(¡¹=Ò¬ QgðŽ,‘³$~<âï‘':F`É«yÄä±[w¸rÊÇ9Ú·þ3Ìl2¼x¶#k“?¿p}öôìÛÃ÷ê>~}ŽæÙß߸’_|©$¬ÆQͳ=å‡ NѬÄÜ‚>™  „0=1!…(Ñ4†19ðŠN,pXdˆ8b`&ª'”.)ª¨Ó$-ž˜—1*‡˜?5òç9æT=rÇA.„ÀxCË V$G…Ï“i “Tâ7[–Å]È¥B®TÈ—]HfB.xy¦šd²ù¥›`ÁÙœ\Ñ)ß|çÙ‰žâ陟‹™Ñ€j&æIþºÔ=v¦¨QC°4æ{#AAàp6 zœNOè¸e|'UÂCô‚¶·` ©²´…‘­Ž¤‡±b$b¨µDäO¯ùdF®=èEK=,n¨U¶”¹*Õ>©*hcLAÀš1WQ¦¬TØ€| äV IJ’87€EXagÊ([írT—]ñjYQ½eVd¾ iuTü*D•HJ|€S,\ïÀ,íÛ®¿ª®‰¼TÎëÙ¸=®kµ-j l¯RX@;cmtk-3.3.1/Utilities/mxml/doc/4.gif000066400000000000000000000025441276303427400170200ustar00rootroot00000000000000GIF89addç  !!!"""###$$$%%%&&&'''((()))***+++,,,---...///000111222333444555666777888999:::;;;<<<===>>>???@@@AAABBBCCCDDDEEEFFFGGGHHHIIIJJJKKKLLLMMMNNNOOOPPPQQQRRRSSSTTTUUUVVVWWWXXXYYYZZZ[[[\\\]]]^^^___```aaabbbcccdddeeefffggghhhiiijjjkkklllmmmnnnooopppqqqrrrssstttuuuvvvwwwxxxyyyzzz{{{|||}}}~~~€€€‚‚‚ƒƒƒ„„„………†††‡‡‡ˆˆˆ‰‰‰ŠŠŠ‹‹‹ŒŒŒŽŽŽ‘‘‘’’’“““”””•••–––———˜˜˜™™™ššš›››œœœžžžŸŸŸ   ¡¡¡¢¢¢£££¤¤¤¥¥¥¦¦¦§§§¨¨¨©©©ªªª«««¬¬¬­­­®®®¯¯¯°°°±±±²²²³³³´´´µµµ¶¶¶···¸¸¸¹¹¹ººº»»»¼¼¼½½½¾¾¾¿¿¿ÀÀÀÁÁÁÂÂÂÃÃÃÄÄÄÅÅÅÆÆÆÇÇÇÈÈÈÉÉÉÊÊÊËËËÌÌÌÍÍÍÎÎÎÏÏÏÐÐÐÑÑÑÒÒÒÓÓÓÔÔÔÕÕÕÖÖÖ×××ØØØÙÙÙÚÚÚÛÛÛÜÜÜÝÝÝÞÞÞßßßàààáááâââãããäääåååæææçççèèèéééêêêëëëìììíííîîîïïïðððñññòòòóóóôôôõõõööö÷÷÷øøøùùùúúúûûûüüüýýýþþþÿÿÿ,ddþÿ H°`A5*\Ȱ¡Ã‡#*Tc°¢Å‹‡IÜȱ£ÇÃ0мhå£É“(­Œ\ù¯Ê—0;öay1¦Í›i$…³§Ï„¤tþkò³(Î&4a]jÆJ¢L£¾DŠ‘§Ô«'ƒÖÄÊõãÖ®`9Vt¶lÄ™Í2¤HÓhÁ’j 5ªr`\…éæº¨ñ.¡ÿ–† |÷`Â{ S9ÌT`áÈ‹þCwd¦”ãBŽ\øçfÎqùZ2éÒ=—}úåjÖ­MbœÆ2vÌ ¯´ #Ý#y£´•¸Há'‹WŽ\ìÅ\rw7ç‚yíé1ŽXx;ÄPÌ—þõî0¼x‹äú»¨©a÷ô -˜?_>CŒÊK·`ÑÅ|½ß| H>í‘Ó~é9@` ’‡Ñ8]µ#‘…Í=]péÁsQÙEØ\~xœ„ãUh¢p`\´MGÚ–"w/ÚFÎE@xT#jµ¸aŽ­Ýx‘¥-qQ_>"š‘G‚ˆÜkTÂfV•¯¡†åjZn¹Y—^2VZ˜—I&``žÙ–™j®ùd›nvgœü-(e²9‰g€zîé^Ÿ~âxg %J¨‹†Êg¢ŠþÉh£‚®©’•›•þDQl?ÆäX‘€&g¥Zöö“o>Ó`£NŠè?Kp¹êQ]vÉÚ$AdÙ*Zié:d}¾.Z‘UÁ.¤ÕEP{U")U¬S,%k+³,ëê±zMºZ®„òºZ¬xÒŠ%ªé±ªæ¥¼±X@;cmtk-3.3.1/Utilities/mxml/doc/5.gif000066400000000000000000000026301276303427400170150ustar00rootroot00000000000000GIF89addç        ! "! #"!$#"%$#&%#'&$'&$('%)(&*)'+*(,+)-,*.-,0/-10.21/320431542653665886997::8;;9<<:=<;>==@?>A@?BA@CBADCBEDCFEDGFEHGFIHHKJILKJMLKNMLONNQPORQPSRQTSRUTSUUTVVUWWVXXWYYY[[Z\\[]]\^^]__^``_a``baacbbdcbedcfedgfehgfihgjihkjilkjmlknmlonmponqporqpsrqtsrttsuutvvuwwvxxwyyxzzy{{z||{}}|~~}~€€€‚‚ƒƒ‚„ƒƒ…„„†……‡††ˆ‡ˆŠ‰‰‹ŠŠŒ‹‹ŒŒŽŽŽ‘’‘’“““””•–––———˜˜˜™™™ššš››œžžžŸŸŸ   ¡¡¡¢¢¡££¢¤¤£¥¥¤¦¦¥§¦¦¨§§©¨¨ª©©«ªª¬««­¬¬®­­¯®®°¯¯±°°²±±²²²³³³´´´µµµ¶¶¶···¸¸¸¹¹¹ººº»»»¼¼¼½½½¾¾¾¿¿¿ÀÀÀÁÁÁÂÂÂÃÃÃÄÄÄÅÅÅÆÆÆÇÇÇÈÈÈÉÉÊËÊËÌËÌÍÌÍÎÍÎÏÎÏÐÏÐÑÐÑÑÑÒÒÒÓÓÓÔÔÔÕÕÕÖÖÖ×××ØØØÙÙÙÚÚÚÛÛÛÜÜÜÝÝÝÞÞÞßßßààààáááââãäääåååææçèèèéééêêêëëëìììíííîíîïîïðïðððñññòòòóóóôôôõõõööö÷÷÷øøøùùùúúúûûûüüüýýýþþþÿÿÿ,ddþé H°`A1*\Ȱ¡Ã‡#*c°¢Å‹sIÜȱ£G¹0мøä£É“(ŸŒ\IÏÊ—0;Úay1¦Í›iÔ„³§Ï„štÒ3ò³(N#4U]jSÅJ¢L£¾DŠ‘§Ô«'ƒÖÄÊõãÖ®`9Vt¶lÄ™"ÊV`Ï‚%‹új˶§ÊKéÖ}+P£Q½B}†¤Ç°ÎŸnóf‰˜°Q‹û¤‡Ð(©È+Vþ‹y¤Y”=6ZähÒ¥¿žÞh!µêÕa„mÖ4í°¶owÍ­+ÆU½¹À¸#8Ö9³_=—\yTÞÎ9¿ŽÞØ"·†^äU´&„ºGŒlþÚ ùÇ{D,Ð6˜y‡Ïf—ŒãýB×ÃÙOˆßàþþþ½`€Ô1`A¢xWË@ƒ*ÇÖ*9°€©´Ct4­Q69Ç’  “Ê¡°R ß±r›ŒôÍGÐ$¡hIÃ’zûÅÄR‚=Âdc/ ‘ e†d’8.é•’N>Ùd”bAIe•S^Ñ‘ZJÄe—9Y d–i¦™2 ÝjóäJY|¹Ú-+Í¡&F¤ô††œÀRš· À§—ƒžÆ']¶’:ÆÑ”@GiШn¡Ð”ÂFÚv#M==@?>A@?BA@CBADCBEDCFEDGFEHGFIHGJIHKJILKJMLKNMLONMPONQPORQPSRQTSRUTSUUTVVUWWVXXWYYXZZY[[Z\\[]]\^^]__^``_a``baacbbdcbedcfedgfehgfihgjihkjilkjmlknmlonmponqporqpsrqtsrttsuutvvuwwvxxwyyxzzy{{{}}|~~~€€€‚‚ƒƒ‚„ƒƒ…„„†……‡††ˆ‡‡‰ˆˆŠ‰‰‹ŠŠŒ‹‹ŒŒŽŽ‘’‘‘“’’“““”””•••–––———˜˜˜™™™ššš›››œœœžžžŸŸ ¡¡¡¢¢¡££¢¤¤£¥¥¤¦¦¥§¦¦¨§§©¨¨ª©©«ªª¬««­¬¬®­­¯®®°¯¯±°°²±±²²²³³³´´´µµµ¶¶¶···¸¸¸¹¹¹ººº»»»¼¼¼½½½¾¾¾¿¿¿ÀÀÀÁÁÁÂÂÂÃÃÃÄÄÅÆÆÇÈÈÈÉÉÉÊÊÊËÊËÌËÌÍÌÍÎÍÎÏÎÏÐÏÐÑÐÑÑÑÒÒÒÓÓÓÔÔÔÕÕÕÖÖÖ×××ØØØÙÙÙÚÚÚÛÛÛÜÜÜÝÝÝÞÞÞßßßààààáááâââãããäääåååæææçççèèèéééêêêëëëìììíííîíîïîïðïðððñññòòòóóóôôôõõõööö÷÷÷øøøùùùúúúûûûüüüýýýþþþÿÿÿ,ddþó H°`A3*\Ȱ¡Ã‡#*4c°¢Å‹{IÜȱ£G½0м(å£É“(¥Œ\™/Ê—0;æay1¦Í›iô„³§Ï„žtæCò³(N$4U]jSÅJ¢L£¾DŠ‘§Ô«'ƒÖÄÊõãÖ® s0R6n¶R[€­è¬Ö‘é\Y°k ¡YI-Xòª¼1U9«8À™Ô¸ñÅFCæ“*À±ÈF6¶Œ1BQÍŠ9üŒp©è•ø~–6 éôJ`aºf¥ÎRuÖ6YµD*, íöÈG8´‡s\iøJåÍ=÷:zÄ•WL®ÑÕ-Æ=IÊñ£YõñK¡V$çÈQ%’R({äTœ-s¤2K"ÇìPÉÂÚ¬gÛ¬1g ,ñlþRLðµéÞG`;cmtk-3.3.1/Utilities/mxml/doc/7.gif000066400000000000000000000024521276303427400170210ustar00rootroot00000000000000GIF89addç        ! #"!$#"%$#&%#'&$'&$('%)(&*)'+*)-,*.-+/.,0/-10.21/320431542653664775886997::8;;9<<:=<;>==@?>A@ADCBEDFIHGJIHKJILKKNMLONMPONQPORQPSRQTSRUTSUUUWWWYYXZZY[[[]]\^^]__^``_a`acbbdcbedehgfihgjihkjilkjmlknmlonorqpsrqtsrttsuutvvuwwvxxwyyxzzy{{{}}|~~}~€€€‚‚‚„ƒƒ…„„†……‡††ˆ‡ˆŠ‰ŠŒ‹‹ŒŽ’‘’““”•••––˜™™™ššš›››œœœžžŸ   ¡¡¡¢¢£¥¥¥§¦¦¨§§©¨¨ª©©«ªª¬««­¬­¯®®°¯¯±°°²±²³³µ¶¶·¸¸¸¹¹¹ººº»»»¼¼½¾¾¿ÀÀÀÁÁÂÃÃÃÄÄÄÅÅÅÆÆÆÇÇÈÉÉÊËÊËÌËÌÍÌÍÎÍÎÏÎÏÐÏÐÑÐÑÑÑÒÒÒÓÓÓÔÔÔÕÕÕÖÖÖ×××ØØØÙÙÙÚÚÚÛÛÛÜÜÜÝÝÝÞÞÞßßßààààáááâââãããääåæææçççèèèéééêêêëëìíííîíîïîðððòòòóóóôôôõõõööö÷÷÷øøøùùùúúúûûûüüüýýýþþþÿÿÿ,ddþ£ H°`Á+*\Ȱ¡Ã‡#*¼b°¢Å‹3IÜȱ£G™0мˆä£É“(‘Œ\ Ê—0;¢ay1¦Í›i„³§Ï„ƒtFûñ³(Î4Q]jÅJ¢L£¾DŠ‘§Ô«'ƒÖÄÊõãÖ®`9Vt¶lÄ™m ]KfÁ’1ÙÊ SåÀ›sóVŒ+P£Z½€Æ  gàÀ Ö›ïâ¼j:~,צäɔך}˜¹òæ†Ù~Zçè°V*8 våÖ]Wö‚ÍÕØJÚX·ÜÆ-5õȼ¥®ì<ê®Ýņ@ž¼èÊÍ6É,zQÌ­+éA»Ïc#þyï)!ûx·#yœ¿ Éüú“îß{ùJ>Ìò#í£ÉF?Ê#ñù'ÑJDh’]t8ŸHB(è:Hva… %3-nta‡`|ˆáJCt8`~&B¤‰ˆ®¤TŠœ¡cCŒ°(áJ\ÌèòÀ£‚ÏühàJ`è8!FF2dƒÚ¤,%¹ÐJ‰H©ÐJ9X ÀOê÷I—öE©¥˜V’)¥™£]ÆT hnFÑU}´i–`RA#gYtFÕk} w'X„¦eDÁ5hCæy蔑µhBh¥õhB_ZÕ¢Z]••T‰ôb’N±´éŒ²dŒ™ £^Ž:©^†ú—èa~Éhij&G[;cmtk-3.3.1/Utilities/mxml/doc/8.gif000066400000000000000000000027761276303427400170330ustar00rootroot00000000000000GIF89addç        ! "! #"!$#"%$#&%#'&$'&$('%)(&*)'+*(,+)-,*.-+/.,0/-10.21/320431542653664775886997::8;;9<<:=<;>==@?>A@?BA@CBADCBEDCFEDGFEHGFIHGJIHKJILKJMLKNMLONMPONQPORQPSRQTSRUTSUUTVVUWWVXXXZZY[[Z\\[]]\^^]__^``_a``baacbbdcbedcfedgfehgfihgjihkjilkjmlknmlonmponqporqpsrqtsrttsuutvvuwwvxxwyyxzzy{{z||{}}|~~}~€€€‚‚ƒƒ‚„ƒƒ…„„†……‡††ˆ‡‡‰ˆˆŠ‰‰‹ŠŠŒ‹‹ŒŒŽŽŽ‘’‘‘“’’“““”””•••–––———˜˜˜™™™ššš›››œœœžžžŸŸŸ   ¡¡¡¢¢¡££¢¤¤£¥¥¥§¦¦¨§§©¨¨ª©ª¬««­¬¬®­­¯®¯±°°²±±²²²³³³´´´µµµ¶¶¶···¸¸¸¹¹¹ººº»»»¼¼¼½½½¾¾¾¿¿¿ÀÀÀÁÁÁÂÂÂÃÃÃÄÄÄÅÅÅÆÆÆÇÇÇÈÈÈÉÉÉÊÊÊËÊËÌËÌÍÌÍÎÍÎÏÎÏÐÏÐÑÐÑÑÑÒÒÒÓÓÓÔÔÔÕÕÕÖÖÖ×××ØØØÙÙÙÚÚÚÛÛÛÜÜÜÝÝÝÞÞÞßßßààààáááâââãããäääåååæææçççèèèéééêêêëëëìììíííîíîïîïðïðððñññòòòóóóôôôõõõööö÷÷÷øøøùùùúúúûûûüüüýýýþþþÿÿÿ,ddþù H°`A3*\Ȱ¡Ã‡#*4c°¢Å‹}IÜȱ£G¾0м8å£É“(§Œ\ÉOÊ—0;êay1¦Í›i…³§Ï„¢tòKò³(Î$4W]jsÅJ¢L£¾DŠ‘§Ô«'ƒÖÄÊõãÖ® yT ö-Þ7Ym€MXÑ%Öâh¢á:³ VhB R¸Z°dTvy+:ˆªr`TŠ-Ò‹:PãÒz‰EX’ÓÈ+5,¸óJF9õL34ŸH—þyú§jš”ÖžÔ÷zµl:M= 0æ¨Û…±,ÆÑ6ðˆ,kxl·òøÆ•`L®Ôäâ†æ'±Wo¨iä”òFþnwÈL„ÂŽ-Ö#DáÙÈÀS›HBTî4Ùq %PY-Mú ¥Að Y×ŃãBÒU4Ý2£NHãJp˜ KútD†qðñ)Q,¥`0,Ô›ŒÛ±´„~+MÖ'¢61ðõ±Ò5/ P¨z,ÅùÒ¦ãʨˆ²‰š¤À™š!…Ω:!©åk¹ú¬`ÉÚ‘­¥®”…{¨ÞægG=àºÖ"ÂæTl­+ÙsR ,¥øèJ:¬*ÒwðÙC“³ö œ¦4}²Ñ:9k_^v<C^B@&F娸îW¾k8r ï@ã yǽ–ÕšOŠÈkEKAf”¡õkTZªq™ÂKŲ£T9U8ª=UeGµo`ÜpE_\ÏJˆ€U˜aW&L[-÷T—]1÷oÍŸ^dÎ(iuT<Ä&FJÝ‘S,m4DC¯´óÒ ù,ÔSbóÒ3{F2Î+×fñ•ù/‡ˆå;cmtk-3.3.1/Utilities/mxml/doc/9.gif000066400000000000000000000027721276303427400170300ustar00rootroot00000000000000GIF89addç        ! "! #"!$#"%$#&%#'&$'&$('%)(&*)'+*(,+)-,*.-+/.,0/-10.21/320431542653664775886997::8;;9<<:=<;>==@?>A@?BA@CBADCCFEDGFEHGFIHGJIHKJILKKNMLONMPONQPORQPSRQTSRUTSUUTVVUWWVXXWYYXZZY[[Z\\[]]\^^]__^``_a``baacbbdcbedcfedgfehgfihgjihkjilkjmlknmlonmponqporqpsrqtsrttsuutvvuwwvxxwyyxzzy{{z||{}}|~~}~€€€‚‚ƒƒ‚„ƒƒ…„„†……‡††ˆ‡‡‰ˆˆŠ‰‰‹ŠŠŒ‹‹ŒŒŽŽ‘’‘‘“’’“““”””•••–––———˜˜˜™™™ššš›››œœœžžžŸŸ ¡¡¡¢¢¡££¢¤¤£¥¥¤¦¦¥§¦¦¨§§©¨¨ª©©«ªª¬««­¬¬®­­¯®®°¯¯±°°²±±²²²³³³´´´µµµ¶¶¶···¸¸¸¹¹¹ººº»»»¼¼¼½½½¾¾¾¿¿¿ÀÀÀÁÁÁÂÂÂÃÃÃÄÄÄÅÅÅÆÆÆÇÇÇÈÈÈÉÉÉÊÊÊËÊËÌËÌÍÌÍÎÍÎÏÎÏÐÏÐÑÐÑÑÑÒÒÒÓÓÓÔÔÔÕÕÕÖÖÖ×××ØØØÙÙÙÚÚÚÛÛÛÜÜÜÝÝÝÞÞÞßßßààààáááâââãããäääåååæææçççèèèéééêêêëëëìììíííîíîïîïðïðððñññòòòóóóôôôõõõööö÷÷÷øøøùùùúúúûûûüüüýýýþþþÿÿÿ,ddþù H°`Á2*\Ȱ¡Ã‡#*,c°¢Å‹}IÜȱ£G¾0мå£É“(£Œ\É/Ê—0;æay1¦Í›iü„³§Ï„Ÿtò;ò³(Î#4W]jsÅJ¢L£¾DŠ‘§Ô«'ƒÖÄÊõãÖ®~Xb–Ž]5MHÀ.¬è«)–˜ÀÎ,xU‡Ð¸,É4Ã]ƒXUŒzïoE5Wj\jø¢;©!ù-eÐX¤TF TsÑÍ#á1å‡ð'è•|––ö‰ëôJµ0]³„}Rg;‡tZ¡í•e‚ˆhòîÈn6G–|‡GdyÀ£qåA¬ ÷ñÍkèé]ï-òv‡Ï=þεüáÊ(·—O¨ž;F¨ë´÷˜oä»õ ;Íïxh¿òhþmÔC€¼ÅC`D›> >¤ÙH àçMƒ°’ø CaCg¬ ~lÈ4áa‡ƒˆk•ˆÝJ¤§"t+åsÂá÷¢D:á€=+=ó‘9â§K8¤76t—ŽìCÓ!ð“ èDC6%“²m©£] ÅdB †™äŠf¢–ir6fBzÈ6Fò¼©~ 1ÀJªØ©›Ý·çHWø©P­£ÐJEªPw­6(yŽ.äKÇ(éf¥ ñME±à jrêP&¥šjÞHŒ¨jÓJá¸Sª²Ð­²–‰Q/câ’˯À lm#ÅÊd9(r‚kT¥0êÞE¢éÃJûÐçì›,)µÑ-g¢¹ÒÃÒ~Š£“¨ YW£ŸmZdˆ£íVÄi¼=Àé1ôòÓŒªùJ%iOñú+™Qm&60Á]Þƒ•gK)âzñ³SøTvW‘U5w £AÉI%@.#B›`ƒÕê“Am©lÓœ)»<«E2Çv‘U5¤ÕEðå¼U"iësDN±ÔóÐ ÍÎH+´³˜M³WYË9ÃÜXȵ¢,ÛĪfÜî¿LRôW@;cmtk-3.3.1/Utilities/mxml/doc/A.gif000066400000000000000000000027061276303427400170350ustar00rootroot00000000000000GIF89addç       ! "! #""%$#&%$'&$('%)(&*)'+*)-,*.-,0/-10.21/320431542653664775887::8;;9<<:=<;>==@?>A@?BA@CBADCBEDCFEDGFEHGFIHGJIHKJJMLKNMLONMPONQPPSRQTSRUTSUUTVVUWWVXXWYYY[[Z\\]__^``_a``babdcbedehgfihhkjknmmponqporqqtstvvuwwvxxwyyxzzz|||~~}~€€€‚‚‚„ƒƒ…„„†……‡††ˆ‡‡‰ˆˆŠ‰‰‹ŠŠŒ‹ŒŽŽŽ‘’‘‘“’’“““”””•••–––———˜˜˜™™™ššš››œžŸŸŸ   ¡¡¡££¢¤¤£¥¥¤¦¦¦¨§§©¨ª¬««­¬­¯®®°¯¯±°°²±³´´µ¶¶¶···¸¸¸¹¹¹ºº»¼¼¼½½½¾¾¾¿¿¿ÀÀÀÁÁÂÃÃÃÄÄÅÆÆÇÈÈÈÉÉÉÊÊËÌËÌÍÌÎÏÎÏÐÏÐÑÐÑÑÑÒÒÒÔÔÔÕÕÕÖÖÖ×××ØØØÙÙÙÚÚÚÛÛÛÜÜÜÝÝÝÞÞÞßßßààààáááâââãããäääåååæææçççèèéêêêëëëìììíííîíïðïðððñññóóóôôôõõõööö÷÷÷øøøùùùúúúûûûüüüýýýþþþÿÿÿ,ddþ¥ H°`Á+*\Ȱ¡Ã‡#*¼b°¢Å‹9IÜȱ£Gœ0м¨ä£É“(•Œ\)m Ê—0;Žay1¦Í›i4„³§Ï„†tJ ò³(Î 4K]j³ÄJ¢L£¾DŠ‘§Ô«'ƒÖÄÊõãÖ®`9VtÉU(×™»~Ú¢,Á’nÍbU9¬P]jäšè®´"\CJ³ëw°[Â~óÆÚª°4,eÆ-ÌU2VŽñ†½Ê,³46›¥zÖÚ¨ÓÑ¥—ŽH(uÑÕ¤]ß„{±l›µ¥]º}“e,–¼cÒY +ðà/YÀ²r”ǯ|nRñ„ÆW îQ¹B–¸þsÌ>r!¬ãâ#[ùj!y‘ÒGô¾¥3ù+ Wx~:þ†,¹ÒÐ{yðCô!¸ß<,¨Ð+ÊàC^Tà %`„Ü­åßC~ˆ_sUh‘jèMÿ!ÂáB®¼‰™XQ#®´ÜF,¦×˜ˆýX^z,±Ò‘Õ!f@JÔ#u5¤Œ©ù’)B T®ØeXOzÄÊ—ˆå&”'ÈÑffb3®é׎·¹é—-¼Í!'›²Ýé—qê)”1²Yç§\U*Ô©aihEѤ¶Þ¢: iR¡Q0)¡eŠ$€Oa*&%L4ýVƒ+íò“^EÓ¦?¥*™­^±DEW^°”‹QF¸ªK¬¾+n¿ÆºX¹¸.L©ùiD–uDœFé E&I‹M~Äúј+ÑÖn(ìHÐ—{mÄä¹=ÑIG‚ÙÑ2,Õ‚Uá~7\2EW]ý2eYÿ„VZ·jQÂí^dÃ0iuTŸD•HJUüÑiOiÜÑÅ,=ìñCc:r}™|òÁžñ[ñ¿µ¥[p¼w6+E~;cmtk-3.3.1/Utilities/mxml/doc/B.gif000066400000000000000000000026231276303427400170340ustar00rootroot00000000000000GIF89addç        ! #"!$#"%$#&%#'&$'&%)(&*)'+*(,+)-,*.-+/.,0/-10.21/320431542653664775886997::8;;9<<:=<;>==@??BA@CBADCBEDCFEDGFFIHGJIHKJJMLKNMLONMPONQPORQPSRQTSRUTSUUTVVUWWVXXWYYXZZY[[Z\\[]]\^^]__^``_a`acbbdcbeddgfehggjihkjilkjmlknmlonmponqporqpsrqtssuutvvuwwvxxwyyxzzy{{z||{}}|~~}€‚‚ƒƒ‚„ƒƒ…„„†……‡†‡‰ˆˆŠ‰‰‹ŠŠŒ‹ŒŽŽ‘’‘‘“’’“““”””••–———˜˜˜™™š››œžžžŸŸ ¡¡¡¢¢¡££¢¤¤£¥¥¤¦¦¦¨§§©¨¨ª©©«ªª¬««­¬¬®­­¯®®°¯¯±°±²²²³³³´´´µµµ¶¶·¸¸¸¹¹¹ººº»»»¼¼¼½½½¾¾¾¿¿¿ÀÀÀÁÁÁÂÂÂÃÃÃÄÄÄÅÅÅÆÆÆÇÇÇÈÈÈÉÉËÌËÌÍÌÍÎÍÎÏÎÏÐÏÐÑÐÑÑÑÒÒÒÓÓÓÔÔÔÕÕÕÖÖÖ×××ØØØÙÙÙÚÚÚÜÜÜÝÝÝßßßààààáááâââãããäääåååæææçççèèèéééêêêëëëìììíííîíîïîïðïðððñññòòòóóóôôôõõõööö÷÷÷øøøùùùúúúûûûüüüýýýþþþÿÿÿ,ddþÑ H°`Á/*\Ȱ¡Ã‡#*üb°¢Å‹]IÜȱ£G®0мèä£É“(Œ\‰NÊ—0;Êay1¦Í›i”„³§Ï„’t¢#ò³(N"4S]j3ÅJ¢L£¾DŠ‘§Ô«'ƒÖÄÊõãÖ®`9VtiR¨Ù‚¤0YåÙ· -Xò$Ü»üT9ð%Þ¿ß~Ôèöïß=C¢ƒiØð…ž7þ»gd¿“ÿZFˆ93Þ›œ;{†Ë(lÃÑšM/Dw˜j…4?ÈÅÔÛ× cÃì!tnÝ1ƒéüͧ0঑ÇT–ùK>Å_;‘øJŸÑUO/<ÒzwÈ×¥þg¿9>ìv“nV’ó.üÈÊâÃÛ´UÞ|ý“¹XFÀ àüÆ49Ã_÷A4ÀMˆbÖ€²¶ ƒ>a„,Ià lŽtÄ…fø‡ zX æ&âEДxb{®(Ò º(ÒÊxTìÙhPüùç1ÔŽ„Úø64•“ã‡0 @Óñ}g™‘Rùè‘TFe¥G,uR¤|8y‘åR[r fWev4æ\kzu&WiŠõ&VqnÔ¦`wª9çUuFÄIžîI9åžU%}Ú”hC9ÐtÇ—‚Â4Êp²¢BE©£E2hº©AK~*P ¡ŠÚ£¨ žŠê@ð•jc z8…fתètëbÔmŠ®Ümª ž’é¨ ›„Í*ã K)ÆkY"~BŸÕU¢i|õ5mr‘u-Wlµµ-œ} ­EV‰k”V7š[U")¥.NN±„ã»S U.½&¡›)¾nÂ¥-¿u‹—´/TídŬ¬ƒ²nKÑY;cmtk-3.3.1/Utilities/mxml/doc/C.gif000066400000000000000000000027071276303427400170400ustar00rootroot00000000000000GIF89addç        ! "! #"!$#"%$#&%#'&$'&$('%)(&*)'+*(,+)-,*.-+/.,0/-10.21/320431542653664775886997::8;;9<<:=<;>==@?>A@?BA@CBADCBEDCFEDGFEHGFIHGJIHKJILKJMLKNMLONMPONQPQTSRUTSUUTVVUWWVXXWYYXZZY[[Z\\[]]\^^]__^``_a``baacbbdccfedgfehgfihgjihkjilkjmlknmlonmponqporqpsrqtsrttsuutvvuwwvxxwyyxzzy{{z||{}}|~~}~€€€‚‚ƒƒ‚„ƒƒ…„„†……‡†ˆŠ‰‰‹ŠŠŒ‹‹ŒŒŽŽŽ‘’‘‘“’’“““”””•••–––———˜˜˜™™™ššš›››œœœžžžŸŸŸ   ¡¡¡¢¢¡££¢¤¤£¥¥¤¦¦¥§¦¦¨§§©¨¨ª©©«ªª¬««­¬­¯®®°¯¯±°°²±±²²²³³³´´µ¶¶¶···¸¸¸¹¹¹ººº»»»¼¼¼½½½¾¾¾¿¿¿ÀÀÀÁÁÁÂÂÂÃÃÃÄÄÄÅÅÅÆÆÆÇÇÇÈÈÈÉÉÉÊÊÊËÊËÌËÌÍÌÍÎÍÎÏÎÏÐÏÐÑÐÑÑÑÒÒÒÓÓÓÔÔÔÕÕÕÖÖÖ×××ØØØÙÙÙÚÚÚÛÛÛÜÜÜÝÝÝÞÞÞßßßààààáááâââãããäääåååæææçççèèèéééêêêëëëìììíííîíîïîïðïðððñññòòòóóóôôôõõõööö÷÷÷øøøùùùúúúûûûüüüýýýþþþÿÿÿ,ddþó H°`A2*\Ȱ¡Ã‡#*$c°¢Å‹wIÜȱ£G»0мå£É“(£Œ\™Ê—0;âay1¦Í›iô„³§Ï„žtæKò³(Î$4W]jsÅJ¢L£¾DŠ‘§Ô«'ƒÖÄÊõãV®ˆhvpµ¢K©<„üÂtfA©âÔb`´`É¥òä®$ëSåÀ¥¨ôÒ”÷s Æ¢‚Õšè2_Q$‰åë)ðgàÈrñá¬Ü³æÄ7ó!ÄéêsbÍ1Gß¼b:²³®´Æ öÃÈìè舰`Fv m3§wÊF:z…+, —ÃG(jù W›í¥Zå/„f‰)T¹Ð!¡iþæ±=Aç)Ê4mwï)(=lˆYÂÖC3UQcò»Â¯»²…~–Â,ÅÒ\µà#ÑÁÕ9*÷K‹88]~>¸R…°áÐ †R]ÂÒ2rEKZ„ˆÕ†& HaŠL¡Èba+¾ß…2ªHc3Ž„#‚:îã>ncÜ I¤uFyƒ+¡¤M‡°Ë“1à"•]‰%GZn)Mf0ÕÌJ·pEL—EŽô W   Kkü·Ò%6ŠäWûýÔH’KAÓ9Фž›±@¨OîäÞJÓLÈ!8ýB޲$„M>(Ú•l:‰Ó:aã Zè Ìz£ªUbG6ÈU!!®z%DÜ’†³a ‡¹Ç!s½^—b ÁêD‹5»Ò8©†ÓÊb”C³Ž­@ ·T8× „š¶Kµ­ ñ¶”ÁÞaTcÕFuBk/´8Ð]W­!ØvQùõ—mG#R((È9ÐY^ÞäÖ[/jPÂiVdÃ&iuTwD•HJU¼‘S,Q¬±C³ôðÇ IŒ*ÉDFpÅGFoÁú¶f.•ì*ëlÉ;cmtk-3.3.1/Utilities/mxml/doc/D.gif000066400000000000000000000026671276303427400170460ustar00rootroot00000000000000GIF89addç        ! "! #"!$#"%$#&%#'&$'&$('%)(&*)'+*(,+)-,*.--10.21/320432653664776997::8;;9<<:=<=@?>A@?BA@CBADCBEDCFEDGFEHGFIHGJIHKJILKJMLKNMLONMPONQPORQPSRQTSRUTTVVUWWVXXWYYXZZY[[Z\\[]]\^^]___a``baacbbdcbedcfedgfehgfihgjihkjilkjmlknmlonmponqporqpsrqtsrttsuutvvuwwwyyxzzy{{z|||~~}~€€ƒƒ‚„ƒƒ…„…‡††ˆ‡‡‰ˆˆŠ‰‰‹ŠŠŒ‹‹ŒŒŽŽŽ‘’‘’“““”””•••––—˜˜™ššš›››œœœžžžŸŸŸ   ¡¡¡¢¢¡£££¥¥¤¦¦¥§¦¦¨§§©¨¨ª©©«ªª¬««­¬¬®­­¯®®°¯¯±°°²±±²²³´´´µµµ¶¶¶···¸¸¸¹¹¹ººº»»»¼¼¼½½½¾¾¾¿¿¿ÀÀÁÂÂÂÃÃÃÄÄÄÅÅÅÆÆÆÇÇÇÈÈÈÉÉÉÊÊÊËÊËÌËÌÍÌÍÎÍÎÏÎÏÐÏÐÑÐÑÑÑÒÒÒÓÓÓÔÔÔÕÕÕ×××ÙÙÙÚÚÚÛÛÛÜÜÜÝÝÝÞÞÞààààáááâââãããäääåååæææçççèèèéééêêêëëëìììíííîíîïîïðïðððñññòòòóóóôôôõõõööö÷÷÷øøøùùùúúúûûûüüüýýýþþþÿÿÿ,ddþ× H°`Á/*\Ȱ¡Ã‡#*üb°¢Å‹aIÜȱ£G°0мÈä£É“(™Œ\¹.Ê—0;Æay1¦Í›iœ„³§Ï„“t®ò³(Î!4S]j3ÅJ¢L£¾DŠ‘§Ô«'ƒÖÄÊõãÖ®`9VtéU¨ÙuäÆ`YðäÙ·5D-XÒ$Ü»ë -U9%^¼TŒÔèö/Þ¢!×½4ü7ÀO‹ã-à²_ɇ{®CxóÝžœ;{†Öáh¼J3šblâÉEgÆ=žW|ÊjEh<¢UŒÚCZDi(¦MOyÇ#Õ.V¯nɪ5 ×®O¾‚m"vì’²fçMMëmZ·f᎕ –nW»Zñ^ÕK•¯H¿¡~*¸ÊÍÂF#&¢x±ÆŽ!/¾#¤ckp¨hŒçñ¸É1[æÌŒØhÐo”‰ötÙs7Zlïìâ»jRµß¬b•û3íÞ¬­XÎEÓHÄ£P£(ùGl;W²œÍðéÆMp wîÝ·`§ÝÌtWÕì*O¤r*;cmtk-3.3.1/Utilities/mxml/doc/F.gif000066400000000000000000000012011276303427400170270ustar00rootroot00000000000000GIF89add¥  $'&&*)-100437:::=F *tœÄ¢ E"ÝVr)S”Nƒ}Œ*ê(Õ.D¯f¥º5jW§_—†E:¶hY¡g¦Í¸ÖbÛ‰o!Æm8Wa݃wi‹8"•¤MÕòä«”íàR}é¦F8°Åb‡±Z…;DÖU;¸r]¶c„Ôf4ªb~–jdtã=¦»hº)5Jb¹vâˆMëÙI`³±„{µ ܵ

3More Mini-XML Programming Techniques

This chapter shows additional ways to use the Mini-XML library in your programs.

Load Callbacks

Chapter 2 introduced the mxmlLoadFile() and mxmlLoadString() functions. The last argument to these functions is a callback function which is used to determine the value type of each data node in an XML document.

Mini-XML defines several standard callbacks for simple XML data files:

  • MXML_INTEGER_CALLBACK - All data nodes contain whitespace-separated integers.
  • MXML_OPAQUE_CALLBACK - All data nodes contain opaque strings ("CDATA").
  • MXML_REAL_CALLBACK - All data nodes contain whitespace-separated floating-point numbers.
  • MXML_TEXT_CALLBACK - All data nodes contain whitespace-separated strings.

You can provide your own callback functions for more complex XML documents. Your callback function will receive a pointer to the current element node and must return the value type of the immediate children for that element node: MXML_INTEGER, MXML_OPAQUE, MXML_REAL, or MXML_TEXT. The function is called after the element and its attributes have been read, so you can look at the element name, attributes, and attribute values to determine the proper value type to return.

The following callback function looks for an attribute named "type" or the element name to determine the value type for its child nodes:

    mxml_type_t
    type_cb(mxml_node_t *node)
    {
      const char *type;

     /*
      * You can lookup attributes and/or use the
      * element name, hierarchy, etc...
      */

      type = mxmlElementGetAttr(node, "type");
      if (type == NULL)
	type = mxmlGetElement(node);

      if (!strcmp(type, "integer"))
	return (MXML_INTEGER);
      else if (!strcmp(type, "opaque"))
	return (MXML_OPAQUE);
      else if (!strcmp(type, "real"))
	return (MXML_REAL);
      else
	return (MXML_TEXT);
    }

To use this callback function, simply use the name when you call any of the load functions:

    FILE *fp;
    mxml_node_t *tree;

    fp = fopen("filename.xml", "r");
    tree = mxmlLoadFile(NULL, fp, type_cb);
    fclose(fp);

Save Callbacks

Chapter 2 also introduced the mxmlSaveFile(), mxmlSaveString(), and mxmlSaveAllocString() functions. The last argument to these functions is a callback function which is used to automatically insert whitespace in an XML document.

Your callback function will be called up to four times for each element node with a pointer to the node and a "where" value of MXML_WS_BEFORE_OPEN, MXML_WS_AFTER_OPEN, MXML_WS_BEFORE_CLOSE, or MXML_WS_AFTER_CLOSE. The callback function should return NULL if no whitespace should be added and the string to insert (spaces, tabs, carriage returns, and newlines) otherwise.

The following whitespace callback can be used to add whitespace to XHTML output to make it more readable in a standard text editor:

    const char *
    whitespace_cb(mxml_node_t *node,
                  int where)
    {
      const char *name;

     /*
      * We can conditionally break to a new line
      * before or after any element. These are
      * just common HTML elements...
      */

      name = mxmlGetElement(node);

      if (!strcmp(name, "html") ||
          !strcmp(name, "head") ||
          !strcmp(name, "body") ||
	  !strcmp(name, "pre") ||
          !strcmp(name, "p") ||
	  !strcmp(name, "h1") ||
          !strcmp(name, "h2") ||
          !strcmp(name, "h3") ||
	  !strcmp(name, "h4") ||
          !strcmp(name, "h5") ||
          !strcmp(name, "h6"))
      {
       /*
	* Newlines before open and after
        * close...
	*/

	if (where == MXML_WS_BEFORE_OPEN ||
            where == MXML_WS_AFTER_CLOSE)
	  return ("\n");
      }
      else if (!strcmp(name, "dl") ||
               !strcmp(name, "ol") ||
               !strcmp(name, "ul"))
      {
       /*
	* Put a newline before and after list
        * elements...
	*/

	return ("\n");
      }
      else if (!strcmp(name, "dd") ||
               !strcmp(name, "dt") ||
               !strcmp(name, "li"))
      {
       /*
	* Put a tab before <li>'s, * <dd>'s,
        * and <dt>'s, and a newline after them...
	*/

	if (where == MXML_WS_BEFORE_OPEN)
	  return ("\t");
	else if (where == MXML_WS_AFTER_CLOSE)
	  return ("\n");
      }

     /*
      * Return NULL for no added whitespace...
      */

      return (NULL);
    }

To use this callback function, simply use the name when you call any of the save functions:

    FILE *fp;
    mxml_node_t *tree;

    fp = fopen("filename.xml", "w");
    mxmlSaveFile(tree, fp, whitespace_cb);
    fclose(fp);

Custom Data Types

Mini-XML supports custom data types via global load and save callbacks. Only a single set of callbacks can be active at any time, however your callbacks can store additional information in order to support multiple custom data types as needed. The MXML_CUSTOM node type identifies custom data nodes.

The load callback receives a pointer to the current data node and a string of opaque character data from the XML source with character entities converted to the corresponding UTF-8 characters. For example, if we wanted to support a custom date/time type whose value is encoded as "yyyy-mm-ddThh:mm:ssZ" (ISO format), the load callback would look like the following:

    typedef struct
    {
      unsigned      year,    /* Year */
                    month,   /* Month */
                    day,     /* Day */
                    hour,    /* Hour */
                    minute,  /* Minute */
                    second;  /* Second */
      time_t        unix;    /* UNIX time */
    } iso_date_time_t;

    int
    load_custom(mxml_node_t *node,
                const char *data)
    {
      iso_date_time_t *dt;
      struct tm tmdata;

     /*
      * Allocate data structure...
      */

      dt = calloc(1, sizeof(iso_date_time_t));

     /*
      * Try reading 6 unsigned integers from the
      * data string...
      */

      if (sscanf(data, "%u-%u-%uT%u:%u:%uZ",
                 &(dt->year), &(dt->month),
                 &(dt->day), &(dt->hour),
                 &(dt->minute),
                 &(dt->second)) != 6)
      {
       /*
        * Unable to read numbers, free the data
        * structure and return an error...
        */

        free(dt);

        return (-1);
      }

     /*
      * Range check values...
      */

      if (dt->month < 1 || dt->month > 12 ||
          dt->day < 1 || dt->day > 31 ||
          dt->hour < 0 || dt->hour > 23 ||
          dt->minute < 0 || dt->minute > 59 ||
          dt->second < 0 || dt->second > 59)
      {
       /*
        * Date information is out of range...
        */

        free(dt);

        return (-1);
      }

     /*
      * Convert ISO time to UNIX time in
      * seconds...
      */

      tmdata.tm_year = dt->year - 1900;
      tmdata.tm_mon  = dt->month - 1;
      tmdata.tm_day  = dt->day;
      tmdata.tm_hour = dt->hour;
      tmdata.tm_min  = dt->minute;
      tmdata.tm_sec  = dt->second;

      dt->unix = gmtime(&tmdata);

     /*
      * Assign custom node data and destroy
      * function pointers...
      */

      mxmlSetCustom(node, data, destroy);

     /*
      * Return with no errors...
      */

      return (0);
    }

The function itself can return 0 on success or -1 if it is unable to decode the custom data or the data contains an error. Custom data nodes contain a void pointer to the allocated custom data for the node and a pointer to a destructor function which will free the custom data when the node is deleted.

The save callback receives the node pointer and returns an allocated string containing the custom data value. The following save callback could be used for our ISO date/time type:

    char *
    save_custom(mxml_node_t *node)
    {
      char data[255];
      iso_date_time_t *dt;


      dt = (iso_date_time_t *)mxmlGetCustom(node);

      snprintf(data, sizeof(data),
               "%04u-%02u-%02uT%02u:%02u:%02uZ",
               dt->year, dt->month, dt->day,
               dt->hour, dt->minute, dt->second);

      return (strdup(data));
    }

You register the callback functions using the mxmlSetCustomHandlers() function:

    mxmlSetCustomHandlers(load_custom,
                          save_custom);

Changing Node Values

All of the examples so far have concentrated on creating and loading new XML data nodes. Many applications, however, need to manipulate or change the nodes during their operation, so Mini-XML provides functions to change node values safely and without leaking memory.

Existing nodes can be changed using the mxmlSetElement(), mxmlSetInteger(), mxmlSetOpaque(), mxmlSetReal(), mxmlSetText(), and mxmlSetTextf() functions. For example, use the following function call to change a text node to contain the text "new" with leading whitespace:

    mxml_node_t *node;

    mxmlSetText(node, 1, "new");

Formatted Text

The mxmlNewTextf() and mxmlSetTextf() functions create and change text nodes, respectively, using printf-style format strings and arguments. For example, use the following function call to create a new text node containing a constructed filename:

    mxml_node_t *node;

    node = mxmlNewTextf(node, 1, "%s/%s",
                        path, filename);

Indexing

Mini-XML provides functions for managing indices of nodes. The current implementation provides the same functionality as mxmlFindElement(). The advantage of using an index is that searching and enumeration of elements is significantly faster. The only disadvantage is that each index is a static snapshot of the XML document, so indices are not well suited to XML data that is updated more often than it is searched. The overhead of creating an index is approximately equal to walking the XML document tree. Nodes in the index are sorted by element name and attribute value.

Indices are stored in mxml_index_t structures. The mxmlIndexNew() function creates a new index:

    mxml_node_t *tree;
    mxml_index_t *ind;

    ind = mxmlIndexNew(tree, "element",
                       "attribute");

The first argument is the XML node tree to index. Normally this will be a pointer to the ?xml element.

The second argument contains the element to index; passing NULL indexes all element nodes alphabetically.

The third argument contains the attribute to index; passing NULL causes only the element name to be indexed.

Once the index is created, the mxmlIndexEnum(), mxmlIndexFind(), and mxmlIndexReset() functions are used to access the nodes in the index. The mxmlIndexReset() function resets the "current" node pointer in the index, allowing you to do new searches and enumerations on the same index. Typically you will call this function prior to your calls to mxmlIndexEnum() and mxmlIndexFind().

The mxmlIndexEnum() function enumerates each of the nodes in the index and can be used in a loop as follows:

    mxml_node_t *node;

    mxmlIndexReset(ind);

    while ((node = mxmlIndexEnum(ind)) != NULL)
    {
      // do something with node
    }

The mxmlIndexFind() function locates the next occurrence of the named element and attribute value in the index. It can be used to find all matching elements in an index, as follows:

    mxml_node_t *node;

    mxmlIndexReset(ind);

    while ((node = mxmlIndexFind(ind, "element",
                                 "attr-value"))
                != NULL)
    {
      // do something with node
    }

The second and third arguments represent the element name and attribute value, respectively. A NULL pointer is used to return all elements or attributes in the index. Passing NULL for both the element name and attribute value is equivalent to calling mxmlIndexEnum.

When you are done using the index, delete it using the mxmlIndexDelete() function:

    mxmlIndexDelete(ind);

SAX (Stream) Loading of Documents

Mini-XML supports an implementation of the Simple API for XML (SAX) which allows you to load and process an XML document as a stream of nodes. Aside from allowing you to process XML documents of any size, the Mini-XML implementation also allows you to retain portions of the document in memory for later processing.

The mxmlSAXLoadFd, mxmlSAXLoadFile, and mxmlSAXLoadString functions provide the SAX loading APIs. Each function works like the corresponding mxmlLoad function but uses a callback to process each node as it is read.

The callback function receives the node, an event code, and a user data pointer you supply:

    void
    sax_cb(mxml_node_t *node,
           mxml_sax_event_t event,
           void *data)
    {
      ... do something ...
    }

The event will be one of the following:

  • MXML_SAX_CDATA - CDATA was just read
  • MXML_SAX_COMMENT - A comment was just read
  • MXML_SAX_DATA - Data (custom, integer, opaque, real, or text) was just read
  • MXML_SAX_DIRECTIVE - A processing directive was just read
  • MXML_SAX_ELEMENT_CLOSE - A close element was just read (</element>)
  • MXML_SAX_ELEMENT_OPEN - An open element was just read (<element>)

Elements are released after the close element is processed. All other nodes are released after they are processed. The SAX callback can retain the node using the mxmlRetain function. For example, the following SAX callback will retain all nodes, effectively simulating a normal in-memory load:

    void
    sax_cb(mxml_node_t *node,
           mxml_sax_event_t event,
           void *data)
    {
      if (event != MXML_SAX_ELEMENT_CLOSE)
        mxmlRetain(node);
    }

More typically the SAX callback will only retain a small portion of the document that is needed for post-processing. For example, the following SAX callback will retain the title and headings in an XHTML file. It also retains the (parent) elements like <html>, <head>, and <body>, and processing directives like <?xml ... ?> and <!DOCTYPE ... >:

    void
    sax_cb(mxml_node_t *node,
           mxml_sax_event_t event,
           void *data)
    {
      if (event == MXML_SAX_ELEMENT_OPEN)
      {
       /*
        * Retain headings and titles...
        */

        char *name = mxmlGetElement(node);

        if (!strcmp(name, "html") ||
            !strcmp(name, "head") ||
            !strcmp(name, "title") ||
            !strcmp(name, "body") ||
            !strcmp(name, "h1") ||
            !strcmp(name, "h2") ||
            !strcmp(name, "h3") ||
            !strcmp(name, "h4") ||
            !strcmp(name, "h5") ||
            !strcmp(name, "h6"))
          mxmlRetain(node);
      }
      else if (event == MXML_SAX_DIRECTIVE)
        mxmlRetain(node);
      else if (event == MXML_SAX_DATA)
      {
        if (mxmlGetRefCount(mxmlGetParent(node)) > 1)
        {
         /*
          * If the parent was retained, then retain
          * this data node as well.
          */

          mxmlRetain(node);
        }
      }
    }

The resulting skeleton document tree can then be searched just like one loaded using the mxmlLoad functions. For example, a filter that reads an XHTML document from stdin and then shows the title and headings in the document would look like:

    mxml_node_t *doc, *title, *body, *heading;

    doc = mxmlSAXLoadFd(NULL, 0,
                        MXML_TEXT_CALLBACK,
                        sax_cb, NULL);

    title = mxmlFindElement(doc, doc, "title",
                            NULL, NULL,
                            MXML_DESCEND);

    if (title)
      print_children(title);

    body = mxmlFindElement(doc, doc, "body",
                           NULL, NULL,
                           MXML_DESCEND);

    if (body)
    {
      for (heading = mxmlGetFirstChild(body);
           heading;
           heading = mxmlGetNextSibling(heading))
        print_children(heading);
    }
cmtk-3.3.1/Utilities/mxml/doc/basics.html000066400000000000000000000465211276303427400203230ustar00rootroot00000000000000

2Getting Started with Mini-XML

This chapter describes how to write programs that use Mini-XML to access data in an XML file. Mini-XML provides the following functionality:

  • Functions for creating and managing XML documents in memory.
  • Reading of UTF-8 and UTF-16 encoded XML files and strings.
  • Writing of UTF-8 encoded XML files and strings.
  • Support for arbitrary element names, attributes, and attribute values with no preset limits, just available memory.
  • Support for integer, real, opaque ("CDATA"), and text data types in "leaf" nodes.
  • "Find", "index", and "walk" functions for easily accessing data in an XML document.

Mini-XML doesn't do validation or other types of processing on the data based upon schema files or other sources of definition information, nor does it support character entities other than those required by the XML specification.

The Basics

Mini-XML provides a single header file which you include:

    #include <mxml.h>

The Mini-XML library is included with your program using the -lmxml option:

    gcc -o myprogram myprogram.c -lmxml ENTER

If you have the pkg-config(1) software installed, you can use it to determine the proper compiler and linker options for your installation:

    pkg-config --cflags mxml ENTER
    pkg-config --libs mxml ENTER

Nodes

Every piece of information in an XML file is stored in memory in "nodes". Nodes are defined by the mxml_node_t structure. Each node has a typed value, optional user data, a parent node, sibling nodes (previous and next), and potentially child nodes.

For example, if you have an XML file like the following:

    <?xml version="1.0" encoding="utf-8"?>
    <data>
        <node>val1</node>
        <node>val2</node>
        <node>val3</node>
        <group>
            <node>val4</node>
            <node>val5</node>
            <node>val6</node>
        </group>
        <node>val7</node>
        <node>val8</node>
    </data>

the node tree for the file would look like the following in memory:

    ?xml version="1.0" encoding="utf-8"?
      |
    data
      |
    node - node - node - group - node - node
      |      |      |      |       |      |
    val1   val2   val3     |     val7   val8
                           |
                         node - node - node
                           |      |      |
                         val4   val5   val6

where "-" is a pointer to the sibling node and "|" is a pointer to the first child or parent node.

The mxmlGetType function gets the type of a node, one of MXML_CUSTOM, MXML_ELEMENT, MXML_INTEGER, MXML_OPAQUE, MXML_REAL, or MXML_TEXT. The parent and sibling nodes are accessed using the mxmlGetParent, mxmlGetNext, and mxmlGetPrevious functions. The mxmlGetUserData function gets any user data associated with the node.

CDATA Nodes

CDATA (MXML_ELEMENT) nodes are created using the mxmlNewCDATA function. The mxmlGetCDATA function retrieves the CDATA string pointer for a node.

Note:

CDATA nodes are currently stored in memory as special elements. This will be changed in a future major release of Mini-XML.

Custom Nodes

Custom (MXML_CUSTOM) nodes are created using the mxmlNewCustom function or using a custom load callback specified using the mxmlSetCustomHandlers function. The mxmlGetCustom function retrieves the custom value pointer for a node.

Comment Nodes

Comment (MXML_ELEMENT) nodes are created using the mxmlNewElement function. The mxmlGetElement function retrieves the comment string pointer for a node, including the surrounding "!--" and "--" characters.

Note:

Comment nodes are currently stored in memory as special elements. This will be changed in a future major release of Mini-XML.

Element Nodes

Element (MXML_ELEMENT) nodes are created using the mxmlNewElement function. The mxmlGetElement function retrieves the element name, the mxmlElementGetAttr function retrieves the value string for a named attribute associated with the element, and the mxmlGetFirstChild and mxmlGetLastChild functions retrieve the first and last child nodes for the element, respectively.

Integer Nodes

Integer (MXML_INTEGER) nodes are created using the mxmlNewInteger function. The mxmlGetInteger function retrieves the integer value for a node.

Opaque Nodes

Opaque (MXML_OPAQUE) nodes are created using the mxmlNewOpaque function. The mxmlGetOpaque function retrieves the opaque string pointer for a node. Opaque nodes are like string nodes but preserve all whitespace between nodes.

Text Nodes

Text (MXML_TEXT) nodes are created using the mxmlNewText and mxmlNewTextf functions. Each text node consists of a text string and (leading) whitespace value - the mxmlGetText function retrieves the text string pointer and whitespace value for a node.

Processing Instruction Nodes

Processing instruction (MXML_ELEMENT) nodes are created using the mxmlNewElement function. The mxmlGetElement function retrieves the processing instruction string for a node, including the surrounding "?" characters.

Note:

Processing instruction nodes are currently stored in memory as special elements. This will be changed in a future major release of Mini-XML.

Real Number Nodes

Real number (MXML_REAL) nodes are created using the mxmlNewReal function. The mxmlGetReal function retrieves the CDATA string pointer for a node.

XML Declaration Nodes

XML declaration (MXML_ELEMENT) nodes are created using the mxmlNewXML function. The mxmlGetElement function retrieves the XML declaration string for a node, including the surrounding "?" characters.

Note:

XML declaration nodes are currently stored in memory as special elements. This will be changed in a future major release of Mini-XML.

Creating XML Documents

You can create and update XML documents in memory using the various mxmlNew functions. The following code will create the XML document described in the previous section:

    mxml_node_t *xml;    /* <?xml ... ?> */
    mxml_node_t *data;   /* <data> */
    mxml_node_t *node;   /* <node> */
    mxml_node_t *group;  /* <group> */

    xml = mxmlNewXML("1.0");

    data = mxmlNewElement(xml, "data");

        node = mxmlNewElement(data, "node");
        mxmlNewText(node, 0, "val1");
        node = mxmlNewElement(data, "node");
        mxmlNewText(node, 0, "val2");
        node = mxmlNewElement(data, "node");
        mxmlNewText(node, 0, "val3");

        group = mxmlNewElement(data, "group");

            node = mxmlNewElement(group, "node");
            mxmlNewText(node, 0, "val4");
            node = mxmlNewElement(group, "node");
            mxmlNewText(node, 0, "val5");
            node = mxmlNewElement(group, "node");
            mxmlNewText(node, 0, "val6");

        node = mxmlNewElement(data, "node");
        mxmlNewText(node, 0, "val7");
        node = mxmlNewElement(data, "node");
        mxmlNewText(node, 0, "val8");

We start by creating the declaration node common to all XML files using the mxmlNewXML function:

    xml = mxmlNewXML("1.0");

We then create the <data> node used for this document using the mxmlNewElement function. The first argument specifies the parent node (xml) while the second specifies the element name (data):

    data = mxmlNewElement(xml, "data");

Each <node>...</node> in the file is created using the mxmlNewElement and mxmlNewText functions. The first argument of mxmlNewText specifies the parent node (node). The second argument specifies whether whitespace appears before the text - 0 or false in this case. The last argument specifies the actual text to add:

    node = mxmlNewElement(data, "node");
    mxmlNewText(node, 0, "val1");

The resulting in-memory XML document can then be saved or processed just like one loaded from disk or a string.

Loading XML

You load an XML file using the mxmlLoadFile function:

    FILE *fp;
    mxml_node_t *tree;

    fp = fopen("filename.xml", "r");
    tree = mxmlLoadFile(NULL, fp,
                        MXML_TEXT_CALLBACK);
    fclose(fp);

The first argument specifies an existing XML parent node, if any. Normally you will pass NULL for this argument unless you are combining multiple XML sources. The XML file must contain a complete XML document including the ?xml element if the parent node is NULL.

The second argument specifies the stdio file to read from, as opened by fopen() or popen(). You can also use stdin if you are implementing an XML filter program.

The third argument specifies a callback function which returns the value type of the immediate children for a new element node: MXML_CUSTOM, MXML_IGNORE, MXML_INTEGER, MXML_OPAQUE, MXML_REAL, or MXML_TEXT. Load callbacks are described in detail in Chapter 3. The example code uses the MXML_TEXT_CALLBACK constant which specifies that all data nodes in the document contain whitespace-separated text values. Other standard callbacks include MXML_IGNORE_CALLBACK, MXML_INTEGER_CALLBACK, MXML_OPAQUE_CALLBACK, and MXML_REAL_CALLBACK.

The mxmlLoadString function loads XML node trees from a string:

    char buffer[8192];
    mxml_node_t *tree;

    ...
    tree = mxmlLoadString(NULL, buffer,
                          MXML_TEXT_CALLBACK);

The first and third arguments are the same as used for mxmlLoadFile(). The second argument specifies the string or character buffer to load and must be a complete XML document including the ?xml element if the parent node is NULL.

Saving XML

You save an XML file using the mxmlSaveFile function:

    FILE *fp;
    mxml_node_t *tree;

    fp = fopen("filename.xml", "w");
    mxmlSaveFile(tree, fp, MXML_NO_CALLBACK);
    fclose(fp);

The first argument is the XML node tree to save. It should normally be a pointer to the top-level ?xml node in your XML document.

The second argument is the stdio file to write to, as opened by fopen() or popen(). You can also use stdout if you are implementing an XML filter program.

The third argument is the whitespace callback to use when saving the file. Whitespace callbacks are covered in detail in Chapter 3. The previous example code uses the MXML_NO_CALLBACK constant to specify that no special whitespace handling is required.

The mxmlSaveAllocString, and mxmlSaveString functions save XML node trees to strings:

    char buffer[8192];
    char *ptr;
    mxml_node_t *tree;

    ...
    mxmlSaveString(tree, buffer, sizeof(buffer),
                   MXML_NO_CALLBACK);

    ...
    ptr = mxmlSaveAllocString(tree, MXML_NO_CALLBACK);

The first and last arguments are the same as used for mxmlSaveFile(). The mxmlSaveString function takes pointer and size arguments for saving the XML document to a fixed-size buffer, while mxmlSaveAllocString() returns a string buffer that was allocated using malloc().

Controlling Line Wrapping

When saving XML documents, Mini-XML normally wraps output lines at column 75 so that the text is readable in terminal windows. The mxmlSetWrapMargin function overrides the default wrap margin:

    /* Set the margin to 132 columns */
    mxmlSetWrapMargin(132);

    /* Disable wrapping */
    mxmlSetWrapMargin(0);

Memory Management

Once you are done with the XML data, use the mxmlDelete function to recursively free the memory that is used for a particular node or the entire tree:

    mxmlDelete(tree);

You can also use reference counting to manage memory usage. The mxmlRetain and mxmlRelease functions increment and decrement a node's use count, respectively. When the use count goes to 0, mxmlRelease will automatically call mxmlDelete to actually free the memory used by the node tree. New nodes automatically start with a use count of 1.

Finding and Iterating Nodes

The mxmlWalkPrev and mxmlWalkNextfunctions can be used to iterate through the XML node tree:

    mxml_node_t *node;
    
    node = mxmlWalkPrev(current, tree,
                        MXML_DESCEND);

    node = mxmlWalkNext(current, tree,
                        MXML_DESCEND);

In addition, you can find a named element/node using the mxmlFindElement function:

    mxml_node_t *node;
    
    node = mxmlFindElement(tree, tree, "name",
                           "attr", "value",
                           MXML_DESCEND);

The name, attr, and value arguments can be passed as NULL to act as wildcards, e.g.:

    /* Find the first "a" element */
    node = mxmlFindElement(tree, tree, "a",
                           NULL, NULL,
                           MXML_DESCEND);
    /* Find the first "a" element with "href"
       attribute */
    node = mxmlFindElement(tree, tree, "a",
                           "href", NULL,
                           MXML_DESCEND);
    /* Find the first "a" element with "href"
       to a URL */
    node = mxmlFindElement(tree, tree, "a",
                           "href",
                           "http://www.easysw.com/",
                           MXML_DESCEND);
    /* Find the first element with a "src"
       attribute */
    node = mxmlFindElement(tree, tree, NULL,
                           "src", NULL,
                           MXML_DESCEND);
    /* Find the first element with a "src"
       = "foo.jpg" */
    node = mxmlFindElement(tree, tree, NULL,
                           "src", "foo.jpg",
                           MXML_DESCEND);

You can also iterate with the same function:

    mxml_node_t *node;

    for (node = mxmlFindElement(tree, tree,
                                "name",
                                NULL, NULL,
                                MXML_DESCEND);
         node != NULL;
         node = mxmlFindElement(node, tree,
                                "name",
                                NULL, NULL,
                                MXML_DESCEND))
    {
      ... do something ...
    }

The MXML_DESCEND argument can actually be one of three constants:

  • MXML_NO_DESCEND means to not to look at any child nodes in the element hierarchy, just look at siblings at the same level or parent nodes until the top node or top-of-tree is reached.

    The previous node from "group" would be the "node" element to the left, while the next node from "group" would be the "node" element to the right.

  • MXML_DESCEND_FIRST means that it is OK to descend to the first child of a node, but not to descend further when searching. You'll normally use this when iterating through direct children of a parent node, e.g. all of the "node" and "group" elements under the "?xml" parent node in the example above.

    This mode is only applicable to the search function; the walk functions treat this as MXML_DESCEND since every call is a first time.

  • MXML_DESCEND means to keep descending until you hit the bottom of the tree. The previous node from "group" would be the "val3" node and the next node would be the first node element under "group".

    If you were to walk from the root node "?xml" to the end of the tree with mxmlWalkNext(), the order would be:

    ?xml data node val1 node val2 node val3 group node val4 node val5 node val6 node val7 node val8

    If you started at "val8" and walked using mxmlWalkPrev(), the order would be reversed, ending at "?xml".

Finding Specific Nodes

You can find specific nodes in the tree using the mxmlFindPath, for example:

    mxml_node_t *value;

    value = mxmlFindPath(tree, "path/to/*/foo/bar");

The second argument is a "path" to the parent node. Each component of the path is separated by a slash (/) and represents a named element in the document tree or a wildcard (*) path representing 0 or more intervening nodes.

cmtk-3.3.1/Utilities/mxml/doc/chapters.xcf.gz000066400000000000000000000146451276303427400211250ustar00rootroot00000000000000‹µY x•Źž“L˜IÈ ä„$'$8‚¶ÚÛZ)¸A±>UHB€”Å‚õ†VkÕkoëm+¹]½VPÛZm+¨m-VêÒ6t×.6¸`Ep’fîûÍr’ˆ}žÞ{ŸËó̼_þÿüËüïÌ7ï÷²º³ûòø–öUñUë:c±é®Ùt †®Mžu3cÔðoGË@+ö1ýn<à `Îjܶ¡}}wwGÏ&æORcùöLgwëꎆÕ:Wú“1Y·qÓÖuñΞM6v´oê\ß³1‘U·juûúuë7Äë,4lXÝÖoœÑhÿ½G0Ë \Úö—Îz÷O èÒ-/omïìYŸ[ßë ?Ö°¹§s^¾}M½õ–õ«VmìØyõ©‡Ü‘Ñ—²á±=ø¸3F¸Çõ2­‰Ž¤ù3c<†xÁG¦é_Öˆxìˆ8{Dœ3"Îó÷ú€1æ q.ðŸî9?/Å즎-›ÖµníØ˜mª£cñʦJ¢s}ÂÅÝm›7Æ/jíÙÿHü¬õëV†s ;¯êˆ75ŽøJ©ÃîC]Þ¹¥c}ã5˜*ÄÆVûÅ[7oZOGŸ8׺®³u£ÿ{]kÏê͘sñÊŽžåK/¢ÇµµnìhXÙ¹Á͵øºMpð=cÖnÞ¸©sÕÖøºŽU›èy= a¾4Ì1œ¶õ[º×¯ìˆ¯ÜÚÓÚÝÙîÛÎÓ¿õ.òñÙÓ`bàGÅXzÅ•£ÏóÙhg¡]ˆÖ†ø5hŸÇ,Yÿ¬Þƒ™…<ö`÷ÈÍÛ–ûŸ°Ò7ƒÝ[Áêh =†ãã  Òƒ¶å33¢úwæÃfDû¿ý>]±(}zkaïˆ÷_÷ããÆ°f6TqùÞ· 7³Y¿ò #ÍL‰e˜±¦E%×39f‹jv½còL‚Eå}ʘZåÝôŽ)2SY[vØŒ7ÕLU>h&˜JXqÜ”˜)L~ÛL4q¦¦ÿÕ”™ ¦–œ0åf2ÓWšÉ¦œ©´Ï™ Süª‰›‰Lžl¦˜RàvS Ô[L•)aú|Sm&05󰘩‚gÌT3¿ºÇÔõZSkÆ1Uõ&°‡dê€z™I˜B¦²ž·ˆGÖ›¦ò^±ˆ[O3ùLÉ-êf:Ác€<¦g˜‚›pN:ËX¥=ËÔž\–Ëô| 7ǰ¢õx”Ò—"³ë? Ô+)šDÑ Ï¼ o¤N§ðkxWub R¿~¡¾Š¢:x8“¡ƒW"èz» b4uײ粧³êÚ¾»ô«gž?òê[o¾vôÅ?ÿòáïíºvÝy3rX‹vÜßû䑟|aÍÁJY•<ùø¿ŸW€¤RkÕ½1¼à43ôì¿ÍŒa(3LôÛî|¤¬F½õ•zä´&£î‡åà/ öïw@“uäËfúë G ÏÖ¿ h1ž«b¯cÙ©·2inŽ££?¤ós(ÚLÑEͧ¨›~¨³)¼¡þ†¦¿BÑ>Šî¡èKí§è“ý–¢6ŠþJÑ"ŠþNÑé½AQEïP§hˆ¢bŠ4E¹ˆ°r„î fa[ÖöQã«ez/¦`½ƒ: *vÄŒ3uL?ààF+|ØAƒƒ‰ H鲌wPá`†ƒ…:\ïà^=þïõd÷R—8ØÀÄ•Ç-êóÜt~€I>ÓÍõÛXI¬,ˆô~‹z+ÖÖÉY ˆu?âRð”—‚«Ù¬Î}ë 98ã—ƒßÿ,F,ÄÆ;‘{‹±M|p¾oJ›»ó0’S³z¾ý2²i:{#6&g\YÝ™í¾á»OGÈ–šQÙ!1œdætja&i†vco2H#Ç—Ç>æ"mT@ïÌ1Ñ+8À¨»'A·˜èäÍ%83)éú—i^¼ÌešÇNyŠïF.mÌìkÜÿ†ƒüÂÁ3Ž98îà¤=ê/O:øƒ¯9èup±ƒ&@QÑX‹ºß-Á/;¸€ÕŠL¨wkø“â,÷‡Ýª¿ kÔ c,êMXWHGs-ênÌq¤‘?±¨ÏEVKC.AæQ/‘‰ÔÙHA™tÿ‰HSê¾4 ÒÖ/³‘¯ÆþV‚4†´v¸iãQ5HsH~ÏMEÖC2üm9’ąç³),2}9R¯bÛbŸaú4$ÏH}. ‡ð‹?ÍÇ/ÒL¤oʰyS˜ì2æ±…¸õ¨,ëÃ,ñqM©L+ìz EïP”KÑqŠŠ):J}ý Es(úEçSôcŠ:)º›¢ë)úE÷Qô Š(ú8íÆù´/ è\ŠŠ)Ú‰HÅ^¥þŽ~øs:XM§o¥èŠVÑþ>F»z&>ò$Ę·éè´•â-pôÃõ"ROIqö<\Ÿ1jÜï5mÿaw„ÅÒÒy†ÈÊ-,©H4Ÿ½¤ó3·=þžPÄNƪ?öù_)¬Í6”÷±»Þ„d*cQn×ïüzþo쥵u;ç¸ÛíÎÅÚßv{eÓKn“¬ü³Û' ¸DXõ’MLjÁt]‚é. *öÖT=S“߀î›F †@e>kòÍt¦×XPY/C60}ƒ:À ¦Ÿ4¹W;h± Ò_‡¤œÉôOÜì ÃÁ9¦;(që ÃtPä ÂA£ƒ lvp«ƒƒ<7WYÄ#ìÛ|ÐÀÉ¢>Û½øwNµ¶¨¿„Ña°õß ¶ƒ¯gú£ø$ø4“_â‹eìǧ™„Æ`)\³â@Ý’îdÁ}™V¨}™N­¸?ÃI€]1·÷o¶ 0Ù쮬ÜmðVGжO Þ©²ÙFý †)WD‹‚@=•]:ÎèK,¨?¦Cw7z‰ƒÅFWAÒÓš!PÏÆ '=ÛÁ5~ (1zŠÛšønp°ß‚z-ÅXjôg:Xãà³¾âàûžpðŒƒÃ^qð¦å rð–ƒ×qðO:Øçà79èqpƒFÞWeZÄõv÷:èuð!€ ”ìG¸ÙÁBÀȵœ…$ð­÷¡9´ÞwSAp¸Œ ‚K¨dx’Ê•’âÇ ¨d¸u…ê/Ç÷F1Ó€$~.î¨ÄQ3ƒQ6‰Ž™eÆ#¹$ ,$y”dl9rOÛgKÕ¨qû*,T1Õ5…$[TÚOl²P¯#WA¸]jAžŽt‡Ò)³;:J©)/C¼¡´š÷¶Í'êŠ …W`"¥¿J’ÒRaÉI›bÔÙ¯¡|ƒ„¨yÊIõü{­Tb—:>û÷V£GâÚ!—ifüÔfTŠÏÚe^þw›T¢¬M/Øü¹´ß.Ë¡XË®ã¨=g±¡ìKî9‰r´™òÜmGL†iaƒiM¿{ÄÄÌéøaÕEײm|Û»DžŠ}Õ ªkÕðìØ§ðld½´[ð.ÐIÅûI ÆKb~c %ßD¦Œ^]Ÿ……ö¾ŽáEÇ®¯À²Œ¦í¶¸j~ëg¿ÿšÙ–ñ¼:…LùÍlå=Ý"ª«zT­S=_v:çÕñNçôXe£žà@ä¶ÁéNÓü«Ó4?¥â™ðå2|rdFõA§fzœšAb´jæ{  õ'ÒɘŸå:ó£±NÆ ÁZs ©#ê *â NŽœeõËÏÊ!VÔUéø ?üŸû(±÷™íÔ¿Ù#fD#kç"´g½Ÿ”@»b„Ý7×›’#ýá±gµ¶w­Þ°~sÏÊÿo2ü{/2møtñïßõšé8ö&c³¾ˆöM´ïŒ>?¡ƒ±òCŒM½&Å›ŒûU>ÃXÕhÿö ´o3V}ÚZ´+Ñ®AÛ¶‹±šçÐ^Eb¬ï–8—±†f´n´­ŒÍXFO»Ê öïZ“Ì3§™¡þí‹‹x:o2ÑÞ®j.x£‰öw•ò,Þ`¢£ÛyŸf¢þ5<#_ï_Ê y­Q^ÈÇñ©F=½œó*L¸«y ¯4êÎ^ÊãF½´œ—ñ £î(åå|²Ñ]|2/Goá¼Ì¨Û2yœOD]Î+9rH¯âŒ:¾Ws¬­’|*Çjhä5ëg ×r,œyÇŠ9žä  ^Èëy]=çÓ½¦sˆþÛxÇéÏ´¨[ø žCÏŸÉ!òïàËã¥R‹z9ŸÅ³Œ¾“Ïæ¤õk,ê«y—F?Í› –„ÑòÓ.ŒÁþÍç,d @á-kXú]¶_cû¤íóЃӃ}] ŠˆÓƒ;/˜ Ó%8Ý×S+…§ô”Ë, Níl–9œìÊ”yœ>²LJpz`©'‰ÓVY,‰Ó^Y"ÁéÝõ²T‚Ó#­²L‚Ó=å²\‚Ó9Y‚ÓƒIY!Áéí92.Áé§e¥§+e•$NËj N/S%qÚ,k$q:KÖJpz¸YÖIâtLõbY/ èêi2Ÿî5]‚ÓÛeƒ§s,ꤜ!sèù3%8Ý#%8=RnQ·ÊYœÞ-gKâ´Þ¢î•M’8•Í­y@žF°2‘s–@æAÙBЧÇ2-êr.A3 ®˜GÐã ÷É$AƒZ@Œîv:ÁN8˜Pæ œ¾Ïö]¶_`û"ô´NûÖÎ/´ëtÇùÅ"]§Ý5Bâ´»Ld âtG“È´N×J‘',§¢P§KÄ8Aœ®ÅÂr*J8½+!JqºB” pº»L” pÚ-& Z§IQ!ˆÓlÄ©¨à´]T ât‘¨Äé|1U§M¢F§¢V§M¢N§óE¨‰zQ@WOÄ©˜.ˆSÑ hf[ÔI1CPQ.f pº[4 â´Ì¢^!f pz—˜-ˆÓ„EÝ+šq*š VˆSqÁq*æ,Ð:-kÄ©´¨wˆ¹MâTÌ#èvP NE’ ÛA €Ö©8`‡ƒó°NÅ|ô}¶_kûù¶/DbàÁ]W·-l©)ÍËäÛòNðõõ®\œ¬//Ê‘8 íöEÉDYa¶ÀÝ”£I4ç'2óJkZ¶]½ëÁHÀi|0/¹fW?*³F>T´x{¿É4Í<ªîÚ‹"ª…G¥]ûM:Å`ãö£Æ Dkúù¶Ü3y\žÈ)*¯O.^ÙÛ÷½—,ZÐÕGÕ^£špÁ΃t/Õöì£{ɨ¼çº—lÞyÌÌß(³ë Ä½d\œÈ.,K$µ‡{‰ÁÂùkûì{‰¡âówØ÷QM·½—ˆÊºí½Ä`ÓÜ ß-’kûî%¶ÅšZºŠ;ÆÕ…Bi§sµüi(ì ®®VÐÔc¸ª¹u»Äñ— ©3¹*½¢s,GÍ=ÉdsÕÒEšÃUæmPzy\oƒVÍçÐå%¦€«…Ç¡á ¹J¾‰[ÄUãï8®P†ŽÇß/`Å8Õê®Jèê|SJ÷Ê3¹¾ r{"§R Ç”qÔæ9¦œžŸm&q}ø$¼ÕK&ËLæz9 ‚ë;ñ *ðî ª<ÎQÕK3…ë§ X¨äúQ|À…€*®÷Cµ–ª¹î7œ`•qp*×Û4jèŠ4‚.¥€Z®÷Bñ×ÒA Õ€:õc:Ôòh»ƒÅŠu\õÛ~—íר>I½Î3–e;R-=@ìHÕjÙ‘ª×²#UýÝÄŽ!v¤*ßCìHÝCìH•Ø‘z±ƒ·:BìHÝJìH™`ïnÙ‘º—Ø‘Žú Ø‘ú±#ÁØ‘úbGêeÄŽÔ‰‰L 2-RïtÐLìÐi=ʉ‰Š4FÐã –Ø‘ÑA,°Zítpƒ €:©Ú¾Ïö]¶_@½.ÉŽPK,;B­°ìÏŽP‰»ˆ·ìU¶›Øº›Ø*i׎PÙ–áØºØj‘eG¨ù–¡š,;‚ÖØÁß–œ·ì½ˆØ¡«ÁŽpìÇž`׎pìÐóÁŽÐ»‰¼•eGèÄŽÐw;xwËŽpìÇýìÇŽÐKˆáØŽáÖŽÐk-(iÙz‡ƒ&bG8vè=,”;±C-Ô;‚Ö6»h‡ƒóêÖõ}¶_kûùÔk2Áù®¤nuWS×FÝBêZ¨«¡®”º<ê°Iäôr= Hýu}ÔõR·’ºÅÔ%©«§®œº"ê°™äôJ= ýu}ÔõR×NÝ"ê’Ô%¨+£®:l:9½£6×ã½ Å1  (AJЀ4  (AJЀ4 „Pf¯Ç›AñaH R‚†” !%hH R‚†” !%hH ;¤Ì2Y/0¨ *AƒJР4¨ *AƒJР4¨ *a•YöO jT<ñXÄcË÷ÑË^JS\µ¤økKqº+Å31îzPëkF¨xRô^Þ‹}/ý©ðeÁp‘@%ƒ/ †Ë *.|©‘*<\âË_¤`ôà )OÍšdj&­LÍ®¾ÔŒ£¹çi§]#D+ X¯f‡µ­Wº¤{½ ÖĤ½^VϤ¥½²Nél'»½ ÷šC 2Ã\-KÍßdjN·§æy_jîÓ*pú+h±µ#4é5/Þ†¥œv$ó¼è–€$½<‹$½LÉJ§2½èôôŸšx¤ÉÖLijµ¤ÖV[j½íJ­ÁÚ¨ó†·k¿uï [ºß߇w{¿÷“¨qº`X%fð bXOºðZÃ+ DRºÄfZ €¦ZX¹å©ÕœL­ð•©Uß—Ê´— ïh~wë »žß‡7D¿=ÒfYã¶Îá”·ßd‡·\Jñ~;ö›sØ«S[·ÝÈ1šh!”¥rJ2•gÚS¹§/•(Ý'}¿ô…ÁïÃ{†ßAh?©q»Ëð^càö¡á]ÉÀíX~ÿ ÛYjw³ x»Þ»ðN†÷5¼Ëá=`xGÄû#Á.ñî‰÷R¼³Œï»xÆ{2Þ¡ †M0p‚¡ ž`øÈûAÞ fQ0¼—¬¥`5ë)XQÁš VU°®R^VÊÝJù])lNÊKŸì ãïx×À{ÞQðþ‚w‚ùà½ïL£ÂûÞÅðžF°8¼ãáýï†xo$X%Á: VJ°V‚Õ¬ïÄx_&Ø4Á¶ñ.N0u‚ÉLŸ`S(˜DÁ4J¹H)_)å4¥¼§9);**÷5]iNº¯×}õîky_Ùû:?”ýÞðž@°¼càýï&sÁ{ Þyð>„w%‚IL‹`bS#˜ÁôðˆwD‚A ïŸ;%Ø+Án öK°c‚=ìš”“rtROÊõ™“2‚’Á:ÃlËz˜²š8ÉQÆB“Ä!1ÊZh-o2ÿÕ9ó‚S0'x)7aØ`á:œâHàNΘ|‚9Á9Hy ÃöÂÏá?wr®À¼àÌ ¾AÊI6F8§¸¾„öµ/¯}±íKo_ˆû²÷Õº¯ÝC)JûPê‡Ò?XÁðN÷ ‚lï2Ó!˜Á”&E0-‚‰AE³ý(®`ôå£/&}ié M_vú"4Ô¤¾Dõk¨_}9ë‹[_ê†Ê×¾,öE²/™C*êPa‡Š;Tà¡"÷º/×Cõªy_܇Z?ÔþÁ Þ@ð ‚w> c싾ѿ…¾Ñ¿©¾±ÿd º1cmtk-3.3.1/Utilities/mxml/doc/docset.css000066400000000000000000000037541276303427400201650ustar00rootroot00000000000000body { background: white; color: black; font-family: "lucida grande", geneva, helvetica, arial, sans-serif; } h1, h2, h3, h4, h5, h6, p, td, th { font-family: "lucida grande", geneva, helvetica, arial, sans-serif; } kbd { color: #006600; font-family: monaco, courier, monospace; font-weight: bold; } pre { font-family: monaco, courier, monospace; } pre.example { background: white; border: dotted thin #999999; margin-left: 36pt; padding: 10px; } pre.example em { color: #3f0000; font-family: "lucida grande", geneva, helvetica, arial, sans-serif; } div.summary table { border: solid thin #999999; border-collapse: collapse; border-spacing: 0; margin: 10px; width: 33%; } div.summary table td, div.summary table th { background: white; border: solid thin #999999; border-spacing: 0; padding: 5px; text-align: left; vertical-align: top; } div.summary table thead th { background: #f0f0f0; } div.body h1 { margin: 0; } div.body h2 { margin-top: 1.5em; } div.body h3, div.body h4, div.body h5 { margin-bottom: 0.5em; margin-top: 1.5em; } .class, .enumeration, .function, .struct, .typedef, .union { border-bottom: solid thin #999999; margin-bottom: 0; margin-top: 2em; } .description { margin-top: 0.5em; } code, p.code, pre, ul.code li { font-family: monaco, courier, monospace; font-size: 90%; } ul.code, ul.contents, ul.subcontents { list-style-type: none; margin: 0; padding-left: 0; } ul.code li { margin: 0; } ul.contents > li { margin-top: 1em; } ul.contents li ul.code, ul.contents li ul.subcontents { padding-left: 2em; } div.body dl { margin-left: 0; margin-top: 0; } div.body dt { font-style: italic; margin-left: 0; margin-top: 0; } div.body dd { margin-bottom: 0.5em; } span.info { background: black; border: thin solid black; color: white; font-size: 80%; font-style: italic; font-weight: bold; white-space: nowrap; } h2 span.info, h3 span.info, h4 span.info { float: right; font-size: 100%; } cmtk-3.3.1/Utilities/mxml/doc/docset.header000066400000000000000000000003501276303427400206120ustar00rootroot00000000000000

Mini-XML API Reference

Header mxml.h
Library -lmxml
cmtk-3.3.1/Utilities/mxml/doc/docset.intro000066400000000000000000000121571276303427400205250ustar00rootroot00000000000000

Introduction

Mini-XML is a small XML parsing library that you can use to read XML and XML-like data files in your application without requiring large non-standard libraries. Mini-XML only requires an ANSI C compatible compiler (GCC works, as do most vendors' ANSI C compilers) and a "make" program.

Mini-XML provides the following functionality:

  • Reading of UTF-8 and UTF-16 and writing of UTF-8 encoded XML files and strings.
  • Data is stored in a linked-list tree structure, preserving the XML data hierarchy.
  • Supports arbitrary element names, attributes, and attribute values with no preset limits, just available memory.
  • Supports integer, real, opaque ("CDATA"), and text data types in "leaf" nodes.
  • Functions for creating, indexing, and managing trees of data.
  • "Find" and "walk" functions for easily locating and navigating trees of data.

Mini-XML doesn't do validation or other types of processing on the data based upon schema files or other sources of definition information, nor does it support character entities other than those required by the XML specification.

Using Mini-XML

Mini-XML provides a single header file which you include:

#include <mxml.h>

Nodes are defined by the "mxml_node_t" structure; the "type" member defines the node type (element, integer, opaque, real, or text) which determines which value you want to look at in the "value" union. New nodes can be created using the "mxmlNewElement()", "mxmlNewInteger()", "mxmlNewOpaque()", "mxmlNewReal()", and "mxmlNewText()" functions. Only elements can have child nodes, and the top node must be an element, usually "?xml".

You load an XML file using the "mxmlLoadFile()" function:

FILE *fp;
mxml_node_t *tree;

fp = fopen("filename.xml", "r");
tree = mxmlLoadFile(NULL, fp, MXML_NO_CALLBACK);
fclose(fp);

Similarly, you save an XML file using the "mxmlSaveFile()" function:

FILE *fp;
mxml_node_t *tree;

fp = fopen("filename.xml", "w");
mxmlSaveFile(tree, fp, MXML_NO_CALLBACK);
fclose(fp);

The "mxmlLoadString()", "mxmlSaveAllocString()", and "mxmlSaveString()" functions load XML node trees from and save XML node trees to strings:

char buffer[8192];
char *ptr;
mxml_node_t *tree;

...
tree = mxmlLoadString(NULL, buffer, MXML_NO_CALLBACK);

...
mxmlSaveString(tree, buffer, sizeof(buffer),
	       MXML_NO_CALLBACK);

...
ptr = mxmlSaveAllocString(tree, MXML_NO_CALLBACK);

You can find a named element/node using the "mxmlFindElement()" function:

mxml_node_t *node = mxmlFindElement(tree, tree, "name",
				    "attr", "value",
				    MXML_DESCEND);

The "name", "attr", and "value" arguments can be passed as NULL to act as wildcards, e.g.:

/* Find the first "a" element */
node = mxmlFindElement(tree, tree, "a", NULL, NULL,
		       MXML_DESCEND);

/* Find the first "a" element with "href" attribute */
node = mxmlFindElement(tree, tree, "a", "href", NULL,
		       MXML_DESCEND);

/* Find the first "a" element with "href" to a URL */
node = mxmlFindElement(tree, tree, "a", "href",
		       "http://www.easysw.com/~mike/mxml/",
		       MXML_DESCEND);

/* Find the first element with a "src" attribute*/
node = mxmlFindElement(tree, tree, NULL, "src", NULL,
		       MXML_DESCEND);

/* Find the first element with a "src" = "foo.jpg" */
node = mxmlFindElement(tree, tree, NULL, "src",
		       "foo.jpg", MXML_DESCEND);

You can also iterate with the same function:

mxml_node_t *node;

for (node = mxmlFindElement(tree, tree, "name", NULL,
			    NULL, MXML_DESCEND);
     node != NULL;
     node = mxmlFindElement(node, tree, "name", NULL,
			    NULL, MXML_DESCEND))
{
  ... do something ...
}

The "mxmlFindPath()" function finds the (first) value node under a specific element using a "path":

mxml_node_t *value = mxmlFindPath(tree, "path/to/*/foo/bar");

The "mxmlGetInteger()", "mxmlGetOpaque()", "mxmlGetReal()", and "mxmlGetText()" functions retrieve the value from a node:

mxml_node_t *node;

int intvalue = mxmlGetInteger(node);

const char *opaquevalue = mxmlGetOpaque(node);

double realvalue = mxmlGetReal(node);

int whitespacevalue;
const char *textvalue = mxmlGetText(node, &whitespacevalue);

Finally, once you are done with the XML data, use the "mxmlDelete()" function to recursively free the memory that is used for a particular node or the entire tree:

mxmlDelete(tree);
cmtk-3.3.1/Utilities/mxml/doc/footer.man000066400000000000000000000002021276303427400201460ustar00rootroot00000000000000.SH SEE ALSO mxmldoc(1), Mini-XML Programmers Manual, http://www.minixml.org/ .SH COPYRIGHT Copyright 2003-2011 by Michael Sweet. cmtk-3.3.1/Utilities/mxml/doc/install.html000066400000000000000000000071411276303427400205200ustar00rootroot00000000000000

1Building, Installing, and Packaging Mini-XML

This chapter describes how to build, install, and package Mini-XML on your system from the source archive. You will need an ANSI/ISO-C compatible compiler to build Mini-XML - GCC works, as do most vendors' C compilers. If you are building Mini-XML on Windows, we recommend using the Visual C++ environment with the supplied solution file. For other operating systems, you'll need a POSIX-compatible shell and make program in addition to the C compiler.

Compiling Mini-XML

Mini-XML comes with both an autoconf-based configure script and a Visual C++ solution that can be used to compile the library and associated tools.

Compiling with Visual C++

Open the mxml.sln solution in the vcnet folder. Choose the desired build configuration, "Debug" (the default) or "Release", and then choose Build Solution from the Build menu.

Compiling with Command-Line Tools

Type the following command to configure the Mini-XML source code for your system:

    ./configure ENTER

The default install prefix is /usr/local, which can be overridden using the --prefix option:

    ./configure --prefix=/foo ENTER

Other configure options can be found using the --help option:

    ./configure --help ENTER

Once you have configured the software, use the make(1) program to do the build and run the test program to verify that things are working, as follows:

    make ENTER

Installing Mini-XML

If you are using Visual C++, copy the mxml.lib and and mxml.h files to the Visual C++ lib and include directories, respectively.

Otherwise, use the make command with the install target to install Mini-XML in the configured directories:

    make install ENTER

Creating Mini-XML Packages

Mini-XML includes two files that can be used to create binary packages. The first file is mxml.spec which is used by the rpmbuild(8) software to create Red Hat Package Manager ("RPM") packages which are commonly used on Linux. Since rpmbuild wants to compile the software on its own, you can provide it with the Mini-XML tar file to build the package:

    rpmbuild -ta mxml-version.tar.gz ENTER

The second file is mxml.list which is used by the epm(1) program to create software packages in a variety of formats. The epm program is available from the following URL:

    http://www.epmhome.org/

Use the make command with the epm target to create portable and native packages for your system:

    make epm ENTER

The packages are stored in a subdirectory named dist for your convenience. The portable packages utilize scripts and tar files to install the software on the target system. After extracting the package archive, use the mxml.install script to install the software.

The native packages will be in the local OS's native format: RPM for Red Hat Linux, DPKG for Debian Linux, PKG for Solaris, and so forth. Use the corresponding commands to install the native packages.

cmtk-3.3.1/Utilities/mxml/doc/intro.html000066400000000000000000000125071276303427400202070ustar00rootroot00000000000000 Mini-XML Programmers Manual, Version 2.7

0Introduction

This programmers manual describes Mini-XML version 2.7, a small XML parsing library that you can use to read and write XML data files in your C and C++ applications.

Mini-XML was initially developed for the Gutenprint project to replace the rather large and unwieldy libxml2 library with something substantially smaller and easier-to-use. It all began one morning in June of 2003 when Robert posted the following sentence to the developer's list:

It's bad enough that we require libxml2, but rolling our own XML parser is a bit more than we can handle.

I then replied with:

Given the limited scope of what you use in XML, it should be trivial to code a mini-XML API in a few hundred lines of code.

I took my own challenge and coded furiously for two days to produced the initial public release of Mini-XML, total lines of code: 696. Robert promptly integrated Mini-XML into Gutenprint and removed libxml2.

Thanks to lots of feedback and support from various developers, Mini-XML has evolved since then to provide a more complete XML implementation and now stands at a whopping 3,965 lines of code, compared to 103,893 lines of code for libxml2 version 2.6.9.

Aside from Gutenprint, Mini-XML is used for the following projects/software applications:

Please email me (mxml @ easysw . com) if you would like your project added or removed from this list, or if you have any comments/quotes you would like me to publish about your experiences with Mini-XML.

Organization of This Document

This manual is organized into the following chapters and appendices:

Notation Conventions

Various font and syntax conventions are used in this guide. Examples and their meanings and uses are explained below:

mxmldoc
mxmldoc(1)
The names of commands; the first mention of a command or function in a chapter is followed by a manual page section number.

/var
/etc/hosts
File and directory names.

Request ID is Printer-123
Screen output.

lp -d printer filename ENTER
Literal user input; special keys like ENTER are in ALL CAPS.

12.3
Numbers in the text are written using the period (.) to indicate the decimal point.

Abbreviations

The following abbreviations are used throughout this manual:

Gb
Gigabytes, or 1073741824 bytes

kb
Kilobytes, or 1024 bytes

Mb
Megabytes, or 1048576 bytes

UTF-8, UTF-16
Unicode Transformation Format, 8-bit or 16-bit

W3C
World Wide Web Consortium

XML
Extensible Markup Language

Other References

The Unicode Standard, Version 4.0, Addison-Wesley, ISBN 0-321-18578-1
The definition of the Unicode character set which is used for XML.

Extensible Markup Language (XML) 1.0 (Third Edition)
The XML specification from the World Wide Web Consortium (W3C)

Legal Stuff

The Mini-XML library is copyright 2003-2011 by Michael Sweet. License terms are described in Appendix A - Mini-XML License.

cmtk-3.3.1/Utilities/mxml/doc/intro.man000066400000000000000000000115161276303427400200150ustar00rootroot00000000000000.SH INCLUDE FILE #include .SH LIBRARY \-lmxml .SH DESCRIPTION Mini-XML is a small XML parsing library that you can use to read XML and XML-like data files in your application without requiring large non-standard libraries. Mini-XML only requires an ANSI C compatible compiler (GCC works, as do most vendors' ANSI C compilers) and a "make" program. .PP Mini-XML provides the following functionality: .IP \(bu 4 Reading of UTF-8 and UTF-16 and writing of UTF-8 encoded XML files and strings. .IP \(bu 4 Data is stored in a linked-list tree structure, preserving the XML data hierarchy. .IP \(bu 4 Supports arbitrary element names, attributes, and attribute values with no preset limits, just available memory. .IP \(bu 4 Supports integer, real, opaque ("CDATA"), and text data types in "leaf" nodes. .IP \(bu 4 Functions for creating, indexing, and managing trees of data. .IP \(bu 4 "Find" and "walk" functions for easily locating and navigating trees of data. .PP Mini-XML doesn't do validation or other types of processing on the data based upon schema files or other sources of definition information, nor does it support character entities other than those required by the XML specification. .SH USING MINI-XML Mini-XML provides a single header file which you include: .nf #include .fi .PP Nodes are defined by the "mxml_node_t" structure; the "type" member defines the node type (element, integer, opaque, real, or text) which determines which value you want to look at in the "value" union. New nodes can be created using the "mxmlNewElement()", "mxmlNewInteger()", "mxmlNewOpaque()", "mxmlNewReal()", and "mxmlNewText()" functions. Only elements can have child nodes, and the top node must be an element, usually "?xml". .PP You load an XML file using the "mxmlLoadFile()" function: .nf FILE *fp; mxml_node_t *tree; fp = fopen("filename.xml", "r"); tree = mxmlLoadFile(NULL, fp, MXML_NO_CALLBACK); fclose(fp); .fi .PP Similarly, you save an XML file using the "mxmlSaveFile()" function: .nf FILE *fp; mxml_node_t *tree; fp = fopen("filename.xml", "w"); mxmlSaveFile(tree, fp, MXML_NO_CALLBACK); fclose(fp); .fi .PP The "mxmlLoadString()", "mxmlSaveAllocString()", and "mxmlSaveString()" functions load XML node trees from and save XML node trees to strings: .nf char buffer[8192]; char *ptr; mxml_node_t *tree; ... tree = mxmlLoadString(NULL, buffer, MXML_NO_CALLBACK); ... mxmlSaveString(tree, buffer, sizeof(buffer), MXML_NO_CALLBACK); ... ptr = mxmlSaveAllocString(tree, MXML_NO_CALLBACK); .fi .PP You can find a named element/node using the "mxmlFindElement()" function: .nf mxml_node_t *node = mxmlFindElement(tree, tree, "name", "attr", "value", MXML_DESCEND); .fi .PP The "name", "attr", and "value" arguments can be passed as NULL to act as wildcards, e.g.: .nf /* Find the first "a" element */ node = mxmlFindElement(tree, tree, "a", NULL, NULL, MXML_DESCEND); /* Find the first "a" element with "href" attribute */ node = mxmlFindElement(tree, tree, "a", "href", NULL, MXML_DESCEND); /* Find the first "a" element with "href" to a URL */ node = mxmlFindElement(tree, tree, "a", "href", "http://www.easysw.com/~mike/mxml/", MXML_DESCEND); /* Find the first element with a "src" attribute*/ node = mxmlFindElement(tree, tree, NULL, "src", NULL, MXML_DESCEND); /* Find the first element with a "src" = "foo.jpg" */ node = mxmlFindElement(tree, tree, NULL, "src", "foo.jpg", MXML_DESCEND); .fi .PP You can also iterate with the same function: .nf mxml_node_t *node; for (node = mxmlFindElement(tree, tree, "name", NULL, NULL, MXML_DESCEND); node != NULL; node = mxmlFindElement(node, tree, "name", NULL, NULL, MXML_DESCEND)) { ... do something ... } .fi .PP To find the value of a specific node in the tree, use the "mxmlFindPath()" function: .nf mxml_node_t *value = mxmlFindPath(tree, "path/to/*/foo/bar"); .fi .PP The "mxmlGetInteger()", "mxmlGetOpaque()", "mxmlGetReal()", and "mxmlGetText()" functions retrieve the value from a node: .nf mxml_node_t *node; int intvalue = mxmlGetInteger(node); const char *opaquevalue = mxmlGetOpaque(node); double realvalue = mxmlGetReal(node); int whitespacevalue; const char *textvalue = mxmlGetText(node, &whitespacevalue); .fi .PP Finally, once you are done with the XML data, use the "mxmlDelete()" function to recursively free the memory that is used for a particular node or the entire tree: .nf mxmlDelete(tree); .fi cmtk-3.3.1/Utilities/mxml/doc/license.html000066400000000000000000000652311276303427400205000ustar00rootroot00000000000000

AMini-XML License

The Mini-XML library and included programs are provided under the terms of the GNU Library General Public License version 2 (LGPL2) with the following exceptions:

1. Static linking of applications to the Mini-XML library does not constitute a derivative work and does not require the author to provide source code for the application, use the shared Mini-XML libraries, or link their applications against a user-supplied version of Mini-XML.

If you link the application to a modified version of Mini-XML, then the changes to Mini-XML must be provided under the terms of the LGPL2 in sections 1, 2, and 4.

2. You do not have to provide a copy of the Mini-XML license with programs that are linked to the Mini-XML library, nor do you have to identify the Mini-XML license in your program or documentation as required by section 6 of the LGPL2.

 

GNU LIBRARY GENERAL PUBLIC LICENSE

Version 2, June 1991
Copyright (C) 1991 Free Software Foundation, Inc.
59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed.
[This is the first released version of the library GPL. It is numbered 2 because it goes with version 2 of the ordinary GPL.]

Preamble

The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public Licenses are intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users.

This license, the Library General Public License, applies to some specially designated Free Software Foundation software, and to any other libraries whose authors decide to use it. You can use it for your libraries, too.

When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things.

To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the library, or if you modify it.

For example, if you distribute copies of the library, whether gratis or for a fee, you must give the recipients all the rights that we gave you. You must make sure that they, too, receive or can get the source code. If you link a program with the library, you must provide complete object files to the recipients so that they can relink them with the library, after making changes to the library and recompiling it. And you must show them these terms so they know their rights.

Our method of protecting your rights has two steps: (1) copyright the library, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the library.

Also, for each distributor's protection, we want to make certain that everyone understands that there is no warranty for this free library. If the library is modified by someone else and passed on, we want its recipients to know that what they have is not the original version, so that any problems introduced by others will not reflect on the original authors' reputations.

Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that companies distributing free software will individually obtain patent licenses, thus in effect transforming the program into proprietary software. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all.

Most GNU software, including some libraries, is covered by the ordinary GNU General Public License, which was designed for utility programs. This license, the GNU Library General Public License, applies to certain designated libraries. This license is quite different from the ordinary one; be sure to read it in full, and don't assume that anything in it is the same as in the ordinary license.

The reason we have a separate public license for some libraries is that they blur the distinction we usually make between modifying or adding to a program and simply using it. Linking a program with a library, without changing the library, is in some sense simply using the library, and is analogous to running a utility program or application program. However, in a textual and legal sense, the linked executable is a combined work, a derivative of the original library, and the ordinary General Public License treats it as such.

Because of this blurred distinction, using the ordinary General Public License for libraries did not effectively promote software sharing, because most developers did not use the libraries. We concluded that weaker conditions might promote sharing better.

However, unrestricted linking of non-free programs would deprive the users of those programs of all benefit from the free status of the libraries themselves. This Library General Public License is intended to permit developers of non-free programs to use free libraries, while preserving your freedom as a user of such programs to change the free libraries that are incorporated in them. (We have not seen how to achieve this as regards changes in header files, but we have achieved it as regards changes in the actual functions of the Library.) The hope is that this will lead to faster development of free libraries.

The precise terms and conditions for copying, distribution and modification follow. Pay close attention to the difference between a "work based on the libary" and a "work that uses the library". The former contains code derived from the library, while the latter only works together with the library.

Note that it is possible for a library to be covered by the ordinary General Public License rather than by this special one.

TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION

0. This License Agreement applies to any software library which contains a notice placed by the copyright holder or other authorized party saying it may be distributed under the terms of this Library General Public License (also called "this License"). Each licensee is addressed as "you".

A "library" means a collection of software functions and/or data prepared so as to be conveniently linked with application programs (which use some of those functions and data) to form executables.

The "Library", below, refers to any such software library or work which has been distributed under these terms. A "work based on the Library" means either the Library or any derivative work under copyright law: that is to say, a work containing the Library or a portion of it, either verbatim or with modifications and/or translated straightforwardly into another language. (Hereinafter, translation is included without limitation in the term "modification".)

"Source code" for a work means the preferred form of the work for making modifications to it. For a library, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the library.

Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running a program using the Library is not restricted, and output from such a program is covered only if its contents constitute a work based on the Library (independent of the use of the Library in a tool for writing it). Whether that is true depends on what the Library does and what the program that uses the Library does.

1. You may copy and distribute verbatim copies of the Library's complete source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and distribute a copy of this License along with the Library.

You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee.

2. You may modify your copy or copies of the Library or any portion of it, thus forming a work based on the Library, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions:

    a) The modified work must itself be a software library.

    b) You must cause the files modified to carry prominent notices stating that you changed the files and the date of any change.

    c) You must cause the whole of the work to be licensed at no charge to all third parties under the terms of this License.

    d) If a facility in the modified Library refers to a function or a table of data to be supplied by an application program that uses the facility, other than as an argument passed when the facility is invoked, then you must make a good faith effort to ensure that, in the event an application does not supply such function or table, the facility still operates, and performs whatever part of its purpose remains meaningful.

    (For example, a function in a library to compute square roots has a purpose that is entirely well-defined independent of the application. Therefore, Subsection 2d requires that any application-supplied function or table used by this function must be optional: if the application does not supply it, the square root function must still compute square roots.)

These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Library, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Library, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it.

Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Library.

In addition, mere aggregation of another work not based on the Library with the Library (or with a work based on the Library) on a volume of a storage or distribution medium does not bring the other work under the scope of this License.

3. You may opt to apply the terms of the ordinary GNU General Public License instead of this License to a given copy of the Library. To do this, you must alter all the notices that refer to this License, so that they refer to the ordinary GNU General Public License, version 2, instead of to this License. (If a newer version than version 2 of the ordinary GNU General Public License has appeared, then you can specify that version instead if you wish.) Do not make any other change in these notices.

Once this change is made in a given copy, it is irreversible for that copy, so the ordinary GNU General Public License applies to all subsequent copies and derivative works made from that copy.

This option is useful when you wish to copy part of the code of the Library into a program that is not a library.

4. You may copy and distribute the Library (or a portion or derivative of it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange.

If distribution of object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place satisfies the requirement to distribute the source code, even though third parties are not compelled to copy the source along with the object code.

5. A program that contains no derivative of any portion of the Library, but is designed to work with the Library by being compiled or linked with it, is called a "work that uses the Library". Such a work, in isolation, is not a derivative work of the Library, and therefore falls outside the scope of this License.

However, linking a "work that uses the Library" with the Library creates an executable that is a derivative of the Library (because it contains portions of the Library), rather than a "work that uses the library". The executable is therefore covered by this License. Section 6 states terms for distribution of such executables.

When a "work that uses the Library" uses material from a header file that is part of the Library, the object code for the work may be a derivative work of the Library even though the source code is not. Whether this is true is especially significant if the work can be linked without the Library, or if the work is itself a library. The threshold for this to be true is not precisely defined by law.

If such an object file uses only numerical parameters, data structure layouts and accessors, and small macros and small inline functions (ten lines or less in length), then the use of the object file is unrestricted, regardless of whether it is legally a derivative work. (Executables containing this object code plus portions of the Library will still fall under Section 6.)

Otherwise, if the work is a derivative of the Library, you may distribute the object code for the work under the terms of Section 6. Any executables containing that work also fall under Section 6, whether or not they are linked directly with the Library itself.

6. As an exception to the Sections above, you may also compile or link a "work that uses the Library" with the Library to produce a work containing portions of the Library, and distribute that work under terms of your choice, provided that the terms permit modification of the work for the customer's own use and reverse engineering for debugging such modifications.

You must give prominent notice with each copy of the work that the Library is used in it and that the Library and its use are covered by this License. You must supply a copy of this License. If the work during execution displays copyright notices, you must include the copyright notice for the Library among them, as well as a reference directing the user to the copy of this License. Also, you must do one of these things:

    a) Accompany the work with the complete corresponding machine-readable source code for the Library including whatever changes were used in the work (which must be distributed under Sections 1 and 2 above); and, if the work is an executable linked with the Library, with the complete machine-readable "work that uses the Library", as object code and/or source code, so that the user can modify the Library and then relink to produce a modified executable containing the modified Library. (It is understood that the user who changes the contents of definitions files in the Library will not necessarily be able to recompile the application to use the modified definitions.)

    b) Accompany the work with a written offer, valid for at least three years, to give the same user the materials specified in Subsection 6a, above, for a charge no more than the cost of performing this distribution.

    c) If distribution of the work is made by offering access to copy from a designated place, offer equivalent access to copy the above specified materials from the same place.

    d) Verify that the user has already received a copy of these materials or that you have already sent this user a copy.

For an executable, the required form of the "work that uses the Library" must include any data and utility programs needed for reproducing the executable from it. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable.

It may happen that this requirement contradicts the license restrictions of other proprietary libraries that do not normally accompany the operating system. Such a contradiction means you cannot use both them and the Library together in an executable that you distribute.

7. You may place library facilities that are a work based on the Library side-by-side in a single library together with other library facilities not covered by this License, and distribute such a combined library, provided that the separate distribution of the work based on the Library and of the other library facilities is otherwise permitted, and provided that you do these two things:

    a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities. This must be distributed under the terms of the Sections above.

    b) Give prominent notice with the combined library of the fact that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work.

8. You may not copy, modify, sublicense, link with, or distribute the Library except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense, link with, or distribute the Library is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance.

9. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Library or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Library (or any work based on the Library), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Library or works based on it.

10. Each time you redistribute the Library (or any work based on the Library), the recipient automatically receives a license from the original licensor to copy, distribute, link with or modify the Library subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License.

11. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Library at all. For example, if a patent license would not permit royalty-free redistribution of the Library by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Library.

If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply, and the section as a whole is intended to apply in other circumstances.

It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice.

This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License.

12. If the distribution and/or use of the Library is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Library under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License.

13. The Free Software Foundation may publish revised and/or new versions of the Library General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns.

Each version is given a distinguishing version number. If the Library specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Library does not specify a license version number, you may choose any version ever published by the Free Software Foundation.

14. If you wish to incorporate parts of the Library into other free programs whose distribution conditions are incompatible with these, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally.

NO WARRANTY

15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.

16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.

END OF TERMS AND CONDITIONS

How to Apply These Terms to Your New Libraries

If you develop a new library, and you want it to be of the greatest possible use to the public, we recommend making it free software that everyone can redistribute and change. You can do so by permitting redistribution under these terms (or, alternatively, under the terms of the ordinary General Public License).

To apply these terms, attach the following notices to the library. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found.

    one line to give the library's name and an idea of what it does.
    Copyright (C) year name of author

    This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version.

    This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA

Also add information on how to contact you by electronic and paper mail.

You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the library, if necessary. Here is a sample; alter the names:

    Yoyodyne, Inc., hereby disclaims all copyright interest in the library `Frob' (a library for tweaking knobs) written by James Random Hacker.

    signature of Ty Coon, 1 April 1990 Ty Coon, President of Vice

That's all there is to it! cmtk-3.3.1/Utilities/mxml/doc/logo.gif000066400000000000000000000132441276303427400176140ustar00rootroot00000000000000GIF87a,,çL  !!!"""###$$$%%%&&&'''((()))***+++,,,---...///000111222333444555666777888999:::;;;<<<===>>>???@@@AAABBBCCCDDDEEEFFFGGGHHHIIIJJJKKKLLLMMMNNNOOOPPPQQQRRRSSSTTTUUUVVVWWWXXXYYYZZZ[[[\\\]]]^^^___```aaabbbcccdddeeefffggghhhiiijjjkkklllmmmnnnooopppqqqrrrssstttuuuvvvwwwxxxyyyzzz{{{|||}}}~~~€€€‚‚‚ƒƒƒ„„„………†††‡‡‡ˆˆˆ‰‰‰ŠŠŠ‹‹‹ŒŒŒŽŽŽ‘‘‘’’’“““”””•••–––———˜˜˜™™™ššš›››œœœžžžŸŸŸ   ¡¡¡¢¢¢£££¤¤¤¥¥¥¦¦¦§§§¨¨¨©©©ªªª«««¬¬¬­­­®®®¯¯¯°°°±±±²²²³³³´´´µµµ¶¶¶···¸¸¸¹¹¹ººº»»»¼¼¼½½½¾¾¾¿¿¿ÀÀÀÁÁÁÂÂÂÃÃÃÄÄÄÅÅÅÆÆÆÇÇÇÈÈÈÉÉÉÊÊÊËËËÌÌÌÍÍÍÎÎÎÏÏÏÐÐÐÑÑÑÒÒÒÓÓÓÔÔÔÕÕÕÖÖÖ×××ØØØÙÙÙÚÚÚÛÛÛÜÜÜÝÝÝÞÞÞßßßàààáááâââãããäääåååæææçççèèèéééêêêëëëìììíííîîîïïïðððñññòòòóóóôôôõõõööö÷÷÷øøøùùùúúúûûûüüüýýýþþþÿÿÿ,,,@þÿ H° Áƒ*DøNÙ)Dl¨üpñBƒ2jÜȱ£Ç CŠI²¤É“%4 ðÁÅ*lRön¡Í›8sêÜI°ž# (ƒ J´¨Ñ£H“Ðà¨ϧPÎâ´ªÕ«X³jÍŠe^Ô¯{5ØJ¶¬Ù³hÑ6è6¡˜´pãÊK¥˜¨ŽêêÝË·o\G6×õK8+«Ãˆ'.LÔ‡âLJëðm°Î Ƙ¯âÌ4ËMV„ÑTƹ´éÓ¨5*£à—ÕÍ lÚ T•ñ‘lŸNnéÚf©nÿ3çÁ¤g›  ³n­3ƒ6àí\VdŒv8 ‘Ü-²7Î{—Eþv€–óÉÈã “§–ë]a—“è‚ÿyûM’íz?¡zý!ôßziå‡Pô!—`zöÙ„ßMvVŸH4 gxP„%UhÐ…!yXˆq’†q¸ B$n$"A-b¨Š©xÞ„â’‰ ÑH9*H¡Ž2šåã@@†HäBŠÄãGG ”äM¸d‘Xf©å–\véå—`†)æ˜d–iæ™h¦©¦Œ/䣓E…àNúÐ`eT¥õÇW1j¥”kî5„?9ñRR"9áÓB_Ý1mõ™ÑŸÿLè\[äÔŽYÀNõœÀN¬(£'= Bxú „þ—fF‡pòˆpÔ‹0€t u@Ò1 Qq®Iz¥–ÆêW#;¹ãkUÄ ¤ ¡# ¢Q´ÿ‹¬²™•ÑIq;à íÒQòqdÛ×91‘'9 BÔ»¹¡P#a@(BßxÄow8%+ïViääÍ`ÙÐON‰¬È"H•@Õ§»Ê[Âg²±—:i+Ô0:AÂd•±¥“J^Ù¥Â!„^±Å²ðÎ<‹ A÷,4Ïß$dOuC'}¦öìžÒPo)ÚmóØõÕœÙàUÍ õ2Ö`›5Ë\ „­¶H ÁkÙpõM#A(°v™ ÑHþÑq÷Ý÷:•ìpw ;TR™ßˆ/ Òƒ7¾‘?'þ”*;nùP ¨"¹@T]îùVXÜvLÚŸ—žÀæÄ‰é¬ë…/B·®Û}]FkìYÌÔ‡ìA=‰¥í…õñ°}ýy³F@â&NÍ’–à£S2C åṵ̂nt+çÔ¤£|aÄ¿yâ´5¸¼ç$8• ‚ýg=˜dNWØ/3G×sD©Õ(ÙÅMN¿‹É/{Œ2`ý£>…°/! ûÈøb?¬ý)‚.Ú_ÇXö‘þmä‚£â`G&x5Gƒ#DaF<è'ZL@dàãuBÞ©‚;¢Âh(>’wþ@ ¢‡HÄ"ñˆHL¢—ÈÄ@ÉB'›ÊtbŠò$/D¹ ŸÁ&& 9Á†Ý>"Œœx¢‡m CPN o¬‰y7蔂 Ì&•(Ê»¼¡˜¤ éÁÁNôF8z$‡ƒ #”¯ì#!øp€H4—5dd=*¤!=âmðä+—Ç2B…Hã#oPH&2¨Âcir“ØFÍÔ‘£`ò™J)6RÊ„ #…­üåøtdj7i$$3A(ä»Jˆ<Æè®QÒ…cú@_ МÌÂ$PGNà¡M+Â$ÁáÉ nˆÃL¶ÏfzùÀ;è>æD Gþ±MNæ1 Zó#äy%0mè¿w³.5ùGú6ÉÐIn¨¡ÝÖMꂈ2QNQ‰‡ ,DÄ£oª &GæJ´ Œ¨ÜH×Ôl”j`ÜJOãFlí¥8%H;TÌ”,*@ƒ*°“ӢĜxÁOKóN4ͨe;F–Ú¥¤ª9IÆF©z&€«YÆ2¹ .,#§ŸÀY‡–€OlNc]+Ö8 ¸%C¥r [¾ xÈ4¯óÀÛv ÀÊ:‰ay—;…øb±DôEBÞ·D®ç‡tÙAÚ!R%Z65˜¥‹ˆúslò³¨ m]Î!ŠBV‰!øþ"¾ä‚0¢©P(qt¶$  ‚,QŠU~è Â×%`¡–0Å*F‰?€jU"\À›/ ÒƒJ2j©âŽO1PòY6 `Y‡ÐØN¾¸ »{¡™pα<áPƒ¹9tÐÇlB ~”­ <'að›ß§|ƒrø‚è0ŠDB¥¨ÀCº‡N˜c'7h`B¸ˆ“v¼"fà‚òp qê„$QíÐ(ŽúÙÀ‰?€@\c€"F‰Ò¹’Ì'X¤ Kx“ÙÖp!Úx¡…‚|!ÄB‰ A%ºåž™0ÁS~ò’AÒ^l‚ùCæDóþ ‚A.×WÉ2t’¯Éæ¾¹š]æÙ—cÜJ7¹ƒsvg™ygVZh{N󈤬f9 x‡ƒ¦ïŸ:é¤%ze•ögžPf2zјŽ3“=­è\‰YÔd4”ÌÌç<ËѬf'ªC­à¯oÀ‘þ´©á\ë¨]š×a>3¨}kH+¤Íx>ô%ûüÚf;ûÙÐŽ¶´§Míj[ûÚØÎ¶¶·Íín{Ž`؉Ž"Öæä š†ŠŒBî=űæ]À¡“E ¥7 l½+ oDÆß¤̈3q"Œw}Ò $%þI…¸aƒ–Ï=aÐß…°#”q‚NP¥ë]sdF:è•B¤[ù—^ërŠŒ[Ÿ##ðT7 iw™6P±:8kb7€ñ€ÜU Ðy{«‚Qt s8¥æ~q<ñM²Æ€…WTÇC?@@u‘N81nDƒ7Ñ ¿åg·wJ ¤ÑoFg9µ‚sÑz27á”eu°V~§ç8!X,'w>BM0>õm£‘…^ˆ5Apy4†`“1 ¡Th¨4/pómÈ3pSÏ7Us¨,9`v:¡3y¨&‘#‹ô‡câRp³ þ_CˆZ2gu>ŸpƒŠˆ àVE%щ}auVo‰p!ʼn9Ñ àZ ˆ! |HŠmá Y°©x ’åŠPu±ÐM€ŠÕMб`n¸XŒ¡‹¼è‹ÔŒÂHŒÆUsS7Š˜7{óŒç‹²˜Šµx‹ÖS5‹"QSvØ8aŠÊŽB±Š­HŽq6ÿ‡ŽDÑ6ƒEŽže!ŠÝ˜Rö¨†È‰–¸s¡‰Xå5©cƒVXq1‰R Y%µ9ˆø™Áˆ~S5iZ7ƒ˜‘§Ñm¡Q¹%~8’ˆPñ4(I È´ý€þ‡-)#{¸5“Y‚Q8‡8©%uxGÓ“[Â46Á†B©%oxlGY; q†•¥j¿Ãld¡†4L„Z§ñjpÁ7!†H„•¦¡•iQ†±PW •E"–iaRÿÐ…M–¥¡–h†q’o‰–2"—h9E¸”a²…óä—dò.(˜br "Óuˆ1!ˆ0| ñ¥`ï¸ Ô°çÐ 8Œ‰$±˜¡éÁ`O“y QP€%á¡)…ÁŸÙ`Þ¥=@o<± -×Sàb;á˜Ð›³óh@ØE¸Á`+3×k|ñåÄÆsq€^OÑ ‰þˆš ÈÅYlÇy ´è{m18Ð~ÒÂqñ'°ŽOZW3öRÆflW3&x}йîùžìh}ik„k ·Á !g…k6 G7ùyR“àŸÁ6j9 J°q ãˆé€xµn“g@eq…q Qpƒ –ߘ|(8c91 $!…9± P…Ñ *Z,:ôà•wr¤·#/”’&ÑX &’ÿY"x‰{71'AË™gDkêk7‘ '¡ÏGy#¡¦›V¡ Q B(®&•Êò'2Ø Ñ êÙ-CJEŠnAÑþ>d§±òk'X¦dúƒ©fœ‚¦”„–ƒ•'©;ƒ¨ ¨¨>(‚€æ¨;Wqzê§‹:l ©Ÿú¤“z£§ä©¨ºl›¶0– …­j£YÊ?[ÚB‡¡Œ*«µ k¹š¨»š©JljlÈùœ˜J«ÉŠ£¿z©Á ¬¢Ú¨âù¨ÇÚ§’¢—à«”6«¶×«¡ªŪÞzªãZ©Ì×­ËÊ«éú­·ŠzÍ*¬ëªšª4Úª¬Ïê¬Ñº©ÓÚ©¤j­N6õŠ®÷ª®Ë®œÚ€žª«ùº­å «çŠƒ ëªÊ®)bg’Š­wú°K©»®+Ԯ˗°ÿ*¯Ã:ªÕ:²¥ú§Q¤È&þ° k¯/ˬ"‹¯ó ­5k«›‚ýzj㊱‡ª±äê±.{³© ²˰ë³—r´Ë­[°›³õ®6[²4kµPãY°µ\Ûµ\ëœ%!^;¶[kš&!¶dëµfK!iëµ$¡µmûµB¶q›k t·wk˜|Û·~û·€¸‚;¸„[¸†{¸ˆ›¸Š»¸ŒÛ¸Žû¸¹’;¹”[¹–›‡ :QüVÝ—U®É-‘˜E[„ ;+DtŠïð£C õ¯ Ñ Ga §k¬';Dì’öBÁ7±$kWG+`r¨›»CDôû°N&±Ö )qUµ6þq E È‹»«ŠD P 9áéIPsÔw™O«µyðH·;žÝ›DÆ—G0$¦Ù³<á¥A±‘R¤•ÒD$“È÷9  áÕ£¿<Bÿ›¼ñ«D67‘¢A9‘ëÕ§6±J(A¨̽áÚD0£7á(9¡ «¶€„1y(ш¡’”šL$@8ádA¥ ‘j÷‘¿& a„7µT{Dƒ˜l©ýÀûB*€—‚g® u°Ä\ÚÄGÄ9 Æ€äÀ¦â C7º%€ qµv:›ºpÚ'v9Ä "³[þ ‹Ƹ*ÆH´Ñpq€ü‚ Q¼!ÑšWËDÈîjÈIÔ `±5 Jb a #qdºÔ´ÜªÃ %.Pñ àéÉM ±¾!™áGA+´¨ÜPGȨ`ó"üëU.é³a<³ %ã ‹ìËÖÄ•áÀ2 Á~· µ!‹Ì›dlÇÁ§#m J6WÌD[ÈÚ GŸË ìÊ8”£(yjœ·ŽgÊåšË†„eÔðŒüO€`ÄÁ4³ÆŒÎÞ G@ZQa¥çzDÒ¾ZÌ`ˆÀ*}ΗœÎI„.ÂÍA8—¤{ ¢þqC«ÑÙ¼ÐKT81 QXßÌA"ðÇQŸñH‹ËâwD@Í6! ¼Ùr/Ì€ý—œc 1挵ËÑDÄ­­wм7¡(|üf5Ê¡~ QE=Íú|DI\§€Â6Ñpkœ¬<Œw×ÑÉiÐíÒEt9Ñ‘¡›‰híÁ$ÒÖ ñ 0 |J°jýÓA„ÅsdcÝйŸ ª!q 1{ qŸ}ÍÒkÝ%®9°¤7±#1 9¡2ÀÀ÷ÊPA Ö¯$\±e’óLK  <«{mðÙ1²‰;1» ÚðkPHþŠà˜zqÜ ±FIþ’ÆÆ©´ëðŽ–ÜÒ/5ÝgáåC pÆ8tA¡Á9QcÃËÛ‘};'ÏMq8…ÞfA><P7q6;ÐÍ’bû-O<Ûž-Ù~mÞ'åße=8dœ×0¡C1t:‘ÙWKÕAG½‘PßÐM­ç­¦rŽg±:! H®}ý™Ñ$¾—0¾Ò;~Ìý]±õ»s¹{!<¥…ä{AŒ,Éäpñ’œå ™ÕŠEå¸sKˆå[¡Y!\N”‘æZ¼¡€fn¯³W¾æGÄmçH:AÓt>ˆÅœ£“ç(:QáW~^‚ÕŸ8è ¡E²qWˆÞ{U6EÖèJ7p…èt•8bAçkÑ>iæmõR”å™cTbe¹fʼnS!¹]aŒZŸ^5 ‹c¸¡!U€kU´®>!‡BÉ‘—ë@™T#ÙTø ì<8[Š…caÆ^6;ÕShTCÕìFÕQñS*Á.2A;cmtk-3.3.1/Utilities/mxml/doc/logo.xcf.gz000066400000000000000000000331431276303427400202460ustar00rootroot00000000000000‹äš xTE¶ÇÏíNÒ IY „¤“@ÈB‡„}Ç EAGBÂ#áãŒÁuÔ§8"¢>QŸ#Œ¨£‚Š".#âs@QÑÙ•}‰„¾Ugþ§ª3“D?}‹ŸAÍ÷üNÕ½·»êÔ©SçvÕ”²3ýs'NöO.›^JDÞ¬˜¿xˆãà_"$òœ;ˆDð— Irˆr_K ?;˜X1cFiy….ŠP s¥lFÉ”ÒÀ”Ye“BÈÜʪyÓKýeåU¥³*K'V•U”Wæ5Ëe6\ÏŸUZ>nÔHùº %•¥Ie³¬Ëù§WÍBåÿÙq¦Í®¬*›<Ï?±TüX¾±¬¼4Pç8âú=šP170£bR©Ò¼ò’eCU ºWϼnäï¸ÈÈzÈ&ȧ ï ß9ÁHF¤@r!=!ƒ —C¦BæBn†,,‡¼y ² ‚ç#ð¼Ïûð¼Ïûð¼Ïûð¼ÏG&@ÖBÞ…l‡ÓÝg+‚“áûcGÁ‘ÎF G”=q8œçsÄŒk‰Z%J{§ž$jƒÏNëEÔv QúÅD¸ß?Ÿ(ó>¢,LƒìÕDížÚ·‚ ¯9³‰:´‡|H”{#Q^7È^¢ü»!Bp>îËß Ù Áw„Cà­x®÷|@Ôq(Q r5Q!¼¿ðîí4òäY¢âeD]`×nhw÷D½#êó4Q??QÿŽD`°ÇY’gO#:7è¼|¢ ž#Œø6Saè5DÃ@– _Ht ¾ëÒîD£Î!úU¢ÑLj~qƒÙ8c0özH Ñ•˜ÙWN„¤Þ¿»bX -Á׺ˈÙË?C˃G‹ÆœY›¹=»íà „ëô~ž3ÙÝ:È¡Rý·q[vkf‡SRQwqVw¤ R\Naµ:Ž2ÑOÍɬžŒ lÒ•Ü’Õ3áÔŽ”ó$'²ÚK9¤RŽr<«#Ù”KzT=‰òHEà8VëÊ'}#7g¥;RéŽPõr Þα¬óE{cX¿B…¤§ˆ6F´s9šÕ‰f¢fAÕOS'Ò ¢M-B´,*"ÜuÈ1Ô/S±à6 ¢¡» ÖÔ‹jâ†ÞºŽ·Ýy¶—:S0ïê·‡³»çŽÎh…›2w¾Ý]?:q{?äìî˜,=wó_EßÝÏíŸTÑËœÊêµµ%7á~ ùnuµ&5rg°z«µ"Õü1Îbut0\NuÙÎíØÕsøœ*©å¬îòˆ£ynÆp©‡Â0XÊ·’;B÷ˆ{E¿( *î q¯â^Ñâ^*ØWœÊ·N¥ö¤ˆ+y‡+©gø’^ þ3®¤Ká>µ‡nåŠÿÜMmö‹ÿ¸Ös_ÖGá>)\sdõõãûeµŠñÂ[ƒÍïäÅgqP¯ìîÀ·;±z:ž`µk5'´ïå$´»€Õ¢0jAhûBs'eé[.«µað4ôogÞQ:°‚þçÈ'S{V§Ó õX¨+7ÃP? .²1 †z¥ J,Ú0è—Ž¡^{g‰‰Z7 ˆô*íq2Οróc/¾÷ùc§ô©Ã»·oÞ°òöÙ-žÚ“> ‡>ýÉc•b1–i<ýRyFÒÏî–kÛš ª§zÐ0LœOÆzÐþìnœI”~¾ÒýÌg·¶ÒÀ ¯ùaèØP¨•*Ã`±Z ‰áZæÁ("Ü Ì†YÆb<»²ªÉG¬ÀØmŠ„Ýî0~+z°Ú†ÑíɺÌ@}‡îÅúfu$£Ü›õ4µÍƒ Ñ‡õË ‚ÇðÒˆpá²Xb±×Cð•+ÿOŽ£è–¹'/yWKlZŒ¸Ic_”èõ.Úê¶»ßeøA0Jœ1÷/Ðu=s=s´DáâºCjàºú*@_+árl¢ÇжVÒ÷K8]MÅÉy¬7ÂgôdÑ>–ÈëÛ#‘w ««E;,ZÑjat½ Z’|ˆA©ÅÙ™ @KŒR}i –Ÿ)Þ’F6èõ|¤kMÝ¢G¿ÑólN³|Ñ8™Ü!'A¹ö(V~¤‘¿9¯Þ/ELNzÎ O¾p/ü«~Ø­ðyÄäĵĘ9Ÿ! cÝïúæ¢ò9lj±â÷—µ]–ûãˆÆXëG°ª#:§~ŒU>æ=d,ñÎãÇY+{3–ù~®DeJÜ#«º³Rbó8îÇúÒÐňE›ÿój<üàÆþјÒÝØÝuM&ÂCvß¹8 Ñ£˜Ý.µÁÚ V‡cj"mAZ+AéÙXµo÷Ú¨}›c£ö=¡¨½Ök£ö®dL4Ì·6h?h‚¶:œlƒ÷omð>oƒ÷BužÝmìžkÑÙ†ðc^Â_@°nžß¢ÝùC+î\¾vãίž¿>¸óãž^T5ªGªƒì£Öi7yé{§%Hxnþà–øœ`äEì@úÜ~{ÿ04PËÈ‰ð†œ«õ2å9@ïhu2©„ÕÈÈ„? GDæý#²ÑTd`ïÂC‘C¤CŽÖ†ôUÊûzžFú"åÙ„ £-éi*ñ4§s:ég-fX(ç ¼ Ò¯ZüÁbºÅ%½€†3æ™ÿÇ‹”NCrËjƒÄ ‰Þu" ²Á×êH-X4P†!#D¾8Á@}‰\1AÖ3Úì LdÝËb™ú:ÙY’d« TMu –¬/´øƒÅg@2ëVHR“åšÁÕOZ|´b-‰Pd1Ô¢ÌâF‹‡-ÖY¼c±Õâ3‹½G,jÎù†ÄrƒWMç²3àµòÇ‘ú~ÖÔ¯“M-õm&©“ywnÑÅ"Ý"É"ÚÂkÐåg/ _á²dÆ›„˜E±…MØu¢E´…× íg/õ Ô8ëý¥I}[4}zæäþ¿”¼ÿö>-o¦æ×„ÉçZäX´¶ˆ³3hó³—úÊ7j“¶´ˆµ·p?¥ÿb¤¾iš}K.÷K’†ykSç’M-?¥\¶:¢áF\ØÞ}OêרÅMi}·TG´ýIåb­Ûô«ô÷ä3Ú{¦ç4áßhqS/ñß-Õ ãYS¯ºß»*7²î™¾2ÇÚÿdVÏêðÙMø§qù»EßBúÙ°Z)»¤™5¢vƒªç‰¶V*#þµé³€hÒp±ü¤ßêq é2å}ÛüŒ«º(.俤ï2Pq²Gz´r^æŽÜ‚T‡Ó\Àñ¤¯7PQ²óž@z0Рu1g‘HýH½ë¡¾¤xþ»üq8õ!G^Djw3êMÖs ÔáxêE^Ö¿7P5­©'…ɶ©@¹í©¢Œ^DÈÏEÝk€†f¹•ô t´©°-†z²œOxH »}V’ÄL×Kí8©M«‘ÚuŽÔ>$ê©©î¬eú·¢:ë¤zk„Ü1Lžë#µáÛä†%%¿[ãúÞR÷•Ô^&µ7Kíjù\•qJöOú¥úrC¥hÝå†÷ÃÌW¼.7¨.äkÔŸù¤>‰² çþ /g?>]ö Ó -Ö¨Zµ¥ ù-Ú ÊâIµÏ¡4òËqƒ1‹-6(ÝœÚP¦lìT[¬1PGíVº°ñ^vôJc?·ày-‹\a±Èâ ®4Ô}9APe±ÈÃì6Ôm9Qp™Åí€\R¾ZC݃“3-–-T„ ñCHÃÏ]Jí9õÆ­àž?ˆ|WôI$ý„ÙêQmOØ]èJþÙøÑ}€dÒØíŸ¤ƒfûGO´»@ž7í.P¡kvômv3(v—Ù Ò—Ú=!çy»”}ÊnýÎnE~l7ƒé¤³Û>©GíËú ö>§aÙTÝå J&é{ d#¾5g‘¾Ò@9¯r*g“Êr ·#ý{½ƒ[q{Ù}j……L?Íɀʨá–Èsô\‰­Æ_y7ĵT«ÃÖ §(ï—Uü¹#é; $¶Æq€ôåÊy‰›s!©œZŽåN¤¯3PQŸr I4‹ábÒs4 ÒŽcéï,©¶‰£ðö {]å¸@$ °»‘ž` —ÇN—k°]´°ÖÎÞûœ³Oÿs¿þaªtÞ­h#3>,=4QÑ Eoe'®Y°[¢ºìl¶÷ÇÔ2ž%õ“ÓulOéfsÌõ%uûð4“×Ý*ÞÞA™×IýŒ20i²¨bt`ŽS6³(@ÝÁkðÁµ¤%õ1©y·=ŸË'õ7|÷ç~’å¼½…î$5×øÎ Êú®à·î)h@Bl‹?##D²>(&B=|–š|–êªT³=æ‹P›L„ºû,uy„jŽó(÷ ´³%Ý¥âÚ‘)Dë¿Î7/„‘x1ùmq¡ŒÛ‹þœQ>ȸ÷Ât„u`ŸõÙÁ´Y|vˆ ?i­l°‘é‹û‰ü ¼eJŒÛß”ça±äÿNþœ"ÂŽ%¢ Þ:¯Á[ãö Æ‚í§Ø³v£¿vã@žc‰¢U™~\0 óhØœØò[Ðk £"†j¯fà>} kú"ƒßÈÈÜa]—™§+ñÃI>BëšÀnú/ÙÁÜÂó›`yøù9Dü ÅÜhGa×\;»eöW°\°ùÝ)ȉþ-Œš4ûÛn+}­{’'KÚÊq¥Ûa¹Sa"9uÚHþ×i9…‹h¥9´MÄÐ6‘ÃO÷ñjì‡g†î?ѶÚN¹nMší{¿ †vx?&I§ì®Æofï à'ëc!HÍ {“fLû_ÑŒé4cHŒt0Zâlà0oz![©É–FA™œÍZgÖáѦæ.¦s³=?1&*˜žú7èqÖù"M0¶‚-øWüÀP³šòaÑÇÒîK³_¬`w¨éGŸë£¦¿‰†‘"ØhÐ?û‚8`ÆNf ½¸sL1ºïEcµ…®3ˆ®ß9®ì¡V@ŽÇ·Á‡ŸX8K}Ûë㨜DÇñÑ6â8~ ?}t}ŠÇ¸>¨ã…ê1nê®®â~uOUWñéê*~J]Å#ÅUL»„N (hâ˨cW@·©æZˆ-ò,ÐÙäDšv…°m`é9N?Mù…!()¿³4å÷«MùÝâ¥ü^«)¿‡“4å÷ç@0±iªïoZhŽï=^Žožfõ¾èƒ}vÉzúvëI”Ó¨tº´èTÚk™Öu”^mÏO k1E‡Ñbó:œ@’ç1Ü·h<¬!õ©ÿ$5wÓhod“ºŠ"‡|$ïçí>¸ƒÔ´e¢Ä¡0€· eµ 9_å“l9w) cx7…/O%%¦ï‡¼+þ‡U¤^$5•ï|I²'ì)3B¬ž)ÚßÞL˜Gj¯Ž4üÕ|ùCR<¹—¥Q/÷W:{;HGÉØÄƒÝ¼AÛáìJáy f‘Ú££2‰»éë>y…ú p®• \†7L¯³ð3’é| ¡»Ÿ·ÅEò8©IM#õ©ŠÄ‡ùò“Ê\Æ«/ò¾LÓù¤~Iê‡òòOø¤ÛŠOæð껼ÿcRÕ¤–‘ú{R2’ò(+óâSùOÊ2µž÷eὓ”8e†:È›‚|0ƒ_çÅkH-!UIêRµ¤Úóå-¸Ò¹ ¼ØŸךNêV¶lIeŒ ·ˆìp² ü-§¢ÀŸ;ZÀ*»˜á’EyÉ¢üîeH«AÆŸ¶qŒBŽ´ñ¶KJƒ`· &Eq ÂÀ½±®¾nÝ[lK6ÛŸŒ% £ŸWJPŸáU î—k¿0 œ/y(²ÿ‰Fç;|©Ñùuêr­ÞńջX)ŽDÚdmé4 Ô©Ópºø#?á³ê £^¿Äƒêõ«R?_8²oª/o†zï2O©÷n½:èÆbGÉÐ41À(¥N_¸½„™s±¾Ãvä~˜u 8ËèF˸î8›ÇpwËC‚Ýå¶3ƒáý]àT¿c»pce»2rKµ+wlß&ÛÛø56ŸÛ~«ß vWØîL¸àC‰Þc{PG™m{R­É9­Çw}[l/jKãmoª[I‡È. ÚVìê¡Þèwa´Æs (´æø¹žËs„>~ê@˜BmY‘£Í Æþæ²ÌÂ]ŒŠgõŽàGŒ^½›O_˜yVúð¿E+z$­èa´¢·INÍ&fÌ„h-w§ í> Ü­–Peë>¤Ÿb³Ü$2ÆßÓ:Ÿ'óx}üïìœÉuýz†@bX;B£x”~Ã¥oú9}1Ôa­WÀÆ•’'P ó$ꉦÎhd@öa“Fß~Gjí¥Xŧüÿ.0í¨VäXçãwÌêÉæÚAuggŸ” %ŸI7“ªN†u^õ™,fòt°î@–ÉùšAåµ /—³Hs ‰$czCÑ3XRäD*H§Å~«ç0l=£Ûù]$ã) VweòìÃ=#Ã?™AAÌó­¬û‚‘D–)ÉÞI¤ªÙ‹½ËôLHöN<•£öÌücI Ù;Aú8ŠØ;°|÷ÇÀÆEïÄÐRícÜîX ]>µõ/S[ƒÚúá|µõ«ÕÖ?•.Ø-Sßù$AMþ©jò¿%Ø-?ÀŠåß_Lþ§hÕ7u¤É_E[??ΩԢx’Õù,E«>ªÇU‡Ê Sú"òìœ/ˆ¡ÐN ‰â—t4홊@ÄÀi.§Oe0BĪÎþ º*“ fDŒ&K¢Ók~Mmš¦©M'ÚkõÑ\õ¦çZç£×-ä8!ͽÐhÜ ÆÊÒˆÛFŸÆØÆiŒíPK §­Ä`«„ÎöDKy ÷.­²q:Wªl0TË#Þ2Ê{HLš570|“å.ÑÊ{c¤ò†{Ÿà8Ós¦@²ØL!Ç(<Ž‚)çÐkõWÔ‘ªé´ªrJbòÒŸÇɇ«M=x“¯PÜ*Aξ"vmP&_¡xW‚œ}Et°ÄqòqÎeòáþ?Çì+¢”ÉW(î– g_‘x\beöÑëäìÃÕ/3¼œº":_‚œ}!ú_beò‰æ’_ä¿ò‹¤Òÿ*™W=õä=¬¦^¹Ï)š«h¨æcÁ*”|¬7-U4^Q{ têÑÌÑrŸP4MQoïøþ MÜz^Ñ}Š®Ð4®„°¦qéÑ~l™‚®UÔ+ë=î¿FÑ-Š ¼Ãÿ_j¾×VE¥Š†kö %‰˜½­èE?V”©)a nÍ ƒ>!¨HuRcu/*š¯èš5–èh”o·¢Š&(ÊÊ¥#Á§MWÔGÓË¢Ni˜q›¢ŠFh²Y+Wã”{­T4QQŽ&¢ùa?·ca„=“|“ž'+Ž‘Ÿ2Mèg’:|F’‡²ë…„“…¿‰Þè¨ÓÜ»~Ë¢3ö1îÍ!>ð°ðõäo!?ù¾à£lópc¶òÃNG+?Ïøó…;°XGÙðóX’%¾*Ü ¿øÅ®‡m±—„ÍS”¹é=J½Ì/]2ƒ…7‹;“ìùZVäs¿fmÑD¹Š†y¢´ÊÈ[º·< h‚¢l-6rȧ¹G5š*r‡¢þš‰ÔØB3‘^Ñ=k‘¢1ŠR5=éCMÇ]¥hª¢žš³$þu†ä¨'$‡nÉ`rZ vßÑ]óAEåJV{N²›ÖiFñ, KÖø%küâ²ÆKc†ž_¶$¦ÎôÖJ‡ÿйÍ''H 鲋•ÌëB)MËŒ“"-Üæ$Éá‰Ý~^­æÕᤑú•¨n¢º½yu¯>Ê«EûkÌ0¥‰¯þÙ/±´¬B´–äˆD‘DÙbŲ*â*¤eÅã,´ªx&•œg9%çYÆëyØQ¶4qÇ·*‹Þùv,H¢½•£jû/‰îZÕÞ'éZ] ÔéOÞKOÃZMHº]Q_Mþê©Léyôׂ˜î&9PïÂ`ûæ+Å ,y _Çhh­Z*劮V”®É¡ûÅšqW4 -ûÑw^]Kwç5è›_]êª×Ι#ј,MaJƒóí9ð¿ÏÇÞiÏV—xÀ«Žþ(`—G_¸Ñ£»B^õÈŸ6`iüø+&ßpëM3oŸ=]žô{Ÿ•ô#ÿ¾­’¾ÿ·'Wãsqò¯Ù¸ ðàiÀ–óŸ™r%àyµ1·Ll¼xð1àKcnÅ︵5`¼1·AvÆí€R@•1³Ó7æ𠳟<xðàÀcîÄÿåÎLcî9p°vì¼88•ïž5e~ 00ð€<°¿÷Ÿ;ó³_Ö^¼øð) É˜ûÑÎ÷·t Œü-àc¡«í|mÌbôÏâv€|@1`$``ànÀbÀC´Ýâ­È/†übÈ/†|äË _ù2È—A¾ òe/ƒ|äË _ù2È—A¾ òe/ƒ|9äË!_ùrÈ—C¾òå/‡|9äË!_ùrÈ—C¾òå/‡|ä+ _ù ÈW@¾ò¯€|ä+ _ù ÈW@¾ò¯€|%ä+!_ ùJÈWB¾ò•¯„|%ä+!_ ùJÈWB¾ò•¯„üÈ/üÈ/üÈ/üÈW¡ªÐGUè£*ôQú¨ }T…>ZŠ>ZŠ>ZŠ>ZŠ>ZŠ>ZŠ>ZŠ>ZvŸ1+Ñ÷+Ñ÷+Ñ÷+Ñ÷+Ñ÷+—VÖ0vVbì¬ÄØY‰±ósŒ_ÔóðýƬÂ4^…ɹêã«Vžlìüð;À)cÃy siÍAÀIcž óôÃÆÔ`üÕàjð 5ø†|C ¾¡ßPƒo¨Á7¬K5f=Þ¿í±í±í±í±aó`æÁ̃ ˜06`lÀ<ØØ€öÙˆöݸø[kìG¿d]Vòc‰TVúcõ?V^eIÖɤ,°t"ѽ”ó†Î(nxé²Ðs§äÿØËÙ0‹yþ8lX¨ímžœîå‰ï¶– +ÝMŠeZKO)Þʘ–¶P*ã%ØT6ˆ³}MÐ2çf€¨UѶ–Ô ˜bƒ/}ê÷õ©zÁ`žÞ`M±qn;`}LÕ›Zo<6afÞˆ—m ƒ<…ÏØ Ã:­—ÙxÆsf· àŒª³-²ÉZm“«™Ùh“œ)©µ) Ç*l*u›1û Ût2N|5¡ŽÆÛ`Û1ƵBc,Ã÷Ú :a@ÓÉ0Nþv›MOÏäF £@›”°I¨Îvb¼äÇæÑYµÌv‘ÀÈ6 ŒŒ8l»12]"!îrÛƒ‘¤Íù(xÏöbÈãG' ƒ$0˜/!Ž{õðrhŽAú›$-/øœ-M«Õ¢¥÷hÑÒõZ´4tд¥fºzžg÷²[YH‚]Énd÷±ëØm©6_º+Éö–.b÷°kØ-ì¿b\kJã¶›‹ê¥& M0ýMSþu ××c…LSIËwž‚•ÖË4%Z°õ8ÆK¾iN¿ümŒÎ¦9~tÕnÛc š³ ƒ!Ë4G©>€aÐÁ4'Oz†#é&ÜfÊôyª ÇOÚd;³dè˜5Íèæ$ÈUB'âÖ͵èÒx.~¨*}ãe'º±… g/<‚D‡ Zó窭¶Ø†Í¤:LO÷[7Ïržli«_›o¥ÌgÉÝJÆM¹kQõ“›wÔím8|ìdc3“0ætrh܌ʵµ ;Íó×ì: ‘¹{Åœc·\P¾¿‰ÅïÙa¾‰;ÚÏœæÆ“Ç7ì­Û±ùÉêEwMWÒ-ê\{º±¡vmåŒq¡d3‰¥»Úð–)m ôð¦IñŠ 7¯c¸Ü‡UàaÚ…koŽ—âæáÆ‡Š¥œ¹³s24hŒÊ# ³¡Ê±áÕƒ Su·bx¾dK£ w“ŒÞ!Ƀ¤UbÑ¥qxIÞÈl!k)ØQØRiøštì(¬oÄñ·DÂH9**†]1̺È0S1Rº,¢(reèežU#Š#!ÓæŠQ"#Š$¡¶bB¤O¤HS[ªÝD¸ô_þò_6nˆ9=Ò èXÏ=Íi{¬ÁZ=Òóœé~,'y–ë ž{õÏ=¹ó#=¹SpBOî$½§'w6kzØrM›®ia#4-,û°`w›f…-Ól°$Œ51$+¬N³ÁªµšËd­ß’ߨÉ`Û5 l& ×$°¨½š¶BO“ŽÕƒ£ñ šV­¹_cô8h`Ÿæ€UhîW‰ÆkkõˆçL=͙ը`«5ók”Œë4l†&€µ>~n˜[¨ëg4ýk„ƶ_–,0w‚FÃ÷hØT œ×k Ømb? ™`h- Ê7hØ4è¿TÀ}Ø ¨Ý´šgþÖ8Ž@ñõ§ø¨ü=…âc<4à(½#¶´¥û½k­’¿L>ºÕÚ;ŸÐ¸Ê6Šß˜j[SI¢Ñ®´êª!¢2¶ÆÄNëB«Dº "](ÅMÅ…SJ.,Åà"n…º.„Æ?YT£4‹2 ¨P,¦*¥¨¨Ì¢ä^ßó<ç¼÷i¢ñOJ¦ò>oó}¦æÎ§÷Ü3¿çÜÏý‚Ý“ùE»7ô)zôúУ¸xo =ºÉzôÁУ;íBŽÅÅü¦Ð£¸ ÷³Gqa =ª‹;ÖPèQôçmÖŸÛãÅ^.7ú(è½y‡õæQ`Ýe½y{XèKE¡)"o;ûrØúòV  X½Ý`Ý¿£67f±iKc>ývqÔÇÞ.T|ìí§Ÿw[¼ÛæÝ–¿Ùê“n ›}Òíc‰A$Á* ’ R1(lÛo‚1ÂÚ~¾ÏÚ¾ð¶Ù°õã[ "ƒ@ƒ•?æZòü6dÅI“”AÔ #Oäy_[®øc(êþŠg÷žsv¯9ììÞgvûÄ#®eù£ŽìÙàÈÞ;Žì=íÈ^£Ï?úšÉpMÌ÷ºý¾×iƒ·L•æUçØ{Õ%jóŽ2g#(ÕÇýƒ¿º Váz—nöÏýÞÎp•Îï÷ýŽ»ˆ}Ì™½/<cŸ‡^|“áúž¿ààÞ¢Ýæ/·—Ÿu•ûŠÓ{ »ùKᎱ]mâôñôßÂÓ3Oú¸É»“¸×ü?y€Zé3H¶Ø=r¥%OöF»ãØ}B­MvR®v72}Ät]ow(å‚EÔ¾zí¦u]gw4­«Çîp|u?&½ç“Wwäý)‹”Ëçœx÷hÉ÷ÜîÝæ¹“S¾]púý9 ¥ÌOŸyb廆À/÷5…Ñc-œ„¯XØeþ–o猋ZÑ‚cf/÷]vëxÒ)ƒî y”æ~ß>r?y°xÛ¤oÇö)ÈÓ¦íáOÝQ*aÛYŸ¯;±½æÛIŸïó¡ô'};êÛ×>$¾ÕçןñmÚ·¯|b|Àf܃`µí°os¾]²iò|ćâ'}›öí”o¿ú€ù°Ñ?ëÛ”oú¶èCç›mâ>üð¶í Ûµ¤ÿÎ×h°{Ù`÷±ÁÔ\éÃ*l.5ÖŽ¤±v&¥†M*m¦´‘Ú›(mžöÆ©¶4-¢ê ¥Ö»'ëÔÿå îA"áü“æ…]?!Ê¡þ97ÆÆé Æü±mNöOùvÁܲ ¬Œï?e.t>æOÝ|×´¦?.Ñ·gÅ|ê l;çæÚ^{"gPŠö`ÎË}þ`ÎIßN¸å6dO ÂѶ܀ô‡uNúvÜ,×ÜàÏìÜëÛŒogíisá¿oð|Í·“|˜¼ÏŸßù¤oG}ûÚ}ó­îå=ãÛ´o_ù3ê¬>nõ°Õ9«—ÌjA±ç N£œBù5(°¢¨egqѳbéÉÊuUþÅðw. ägQ¦P>DÁ„øoá:‚±»¬Ø¨íMµÊjÅ7Ù–¼ÓߺޮÇ|;íÛE 0´@—ŠÕ «¬±úÕ« «MXƒ8ÛQª((ûQ¢¼‰2ƒò>Ê,Ê&(‰ŽP"MP2 ^¡ìÓµ’w·’Ç·’Øî¶û‰©×ØîC¦eê]¦ž¦|ÎÔûL=Qù¤òMS/Uþ*¼Öîñ¯óã÷»Ðòž”w¨‡J³t;7òmNhé‹Nµx¦pPé§–îêt‹ó:⩤—Z<ÚÃ-þ-Ü\z»¥ÓKß.0=áÒ!¦_ÜçY¦'[œeøÌtKÚi7¨éW—î5¼l:Û¥Ï=äOŽ:áV¸;ânÓ/§{N/½tÖá³Óu¼[òtèé×Ó½§—Og¿ôùáúó3~"ÀÏ:~Uù§_o¨­&vµõd{?¦}ØÞƒiÿ¥½—šê¹ÔMÍRõ› ÕÔdU¯¡Ïº‡pMB²7§£å9B4z¢‡¯t š¨¥¥Jƒv븛¯¥Kc6-MÛÒÂ…¡K{·4{aýÊ ŽÆ°lb˜Æ´KCö2ÍfZÏr¢£1m65MëÒ†¡M{›f·¼ïh…›1N›œ¦9-ôÒPßæ–yÏ=÷q² Œøvû*]«8¾µìÛU¿åê|Ûf‹¦MvÈ d3’7gÙbô˜§¢ï<œ…o³«¡>‰¾•`ŠðAá„ … ?Ž(<0Œ°EaŒ€f„7 wú( ÀðH ’r„O¥°#¼¨%Ká—@1dͦ dè&à  œ@:v óò ôpÑî"ëÿz?~‡ˆ H ž@$“€&qM›D9 vó$ôI”@(ñP¢DG ’Š+%fJè”*Tñ©ÄU ¯e%Ø*ΕØ+!X1±Dd ÌŠŸ%NK¸–¨­È[‚¸Är éŠÙ%ÂK —x¯h_¿D 5,Š˜P1cÇÄ #M'©ü0Ée‚ÌN5W³¬Ò"¯úç’V;¯ñþâ׸œë“nݸˆ #(fP ¡˜B1†bѪbÅ&¢MÅ,ŠaD›ŠmD›Šyÿˆ‰')f- ŽRL%Zœ%xK±—à0Ác‚ËD{‚Ó¯)v'xNphÏîñ¯ïã'ÿ@‚lI ’”ä*IY’¹$I“t&YM’›ä8Iu’ñ$ñIþS8(éP²¢$GÉ‘ +%eJæ”*yTᩤUÉ® e%ÙJÎUØ+)X2±$dÌ’Ÿ%MK¶V¨-É[r¸¤ré’Ù%ÁKž—t¯`_Á¿dI“&5L†˜D±cÇ$ P¾bÆÊÿÊÓÊöø½–L'™J2Žp›$3Hæ ˜<2u$a$Yðþ—<,‘Á{\òC²ïeÉ È É H H ¼O!!  v›ÜÞP+º?{g~vŸmá ‹æ^4£É}Mòk²_“þšügcÀÈ%(Q@  `ü€Ò”NÀ°e(Ë€ÑJ:Pòƒ”‹ œå&(GA¹ ÊYPî‚r”Ë œå6(ÇA¹ÊyPîƒr Æ™ ±Ëc"ªˆŒ€!‚gHÛ;ï¼3ð®À;ï¼p…ÂÙÇgggggâggóZþÃAV@U¹ŠP¬@ÌPð@L"ˆÙ1­ æÄDƒ˜qPÆ”Q1¡ L(C’d…$o¡ÓÚ´¶ÅþÚÛ W +8S-*%  ×ÈÑW`ÿaŸÇHóùØüq¦?Nùǹÿ˜³ʸ€2B ¦ ”Aeø@’HäÔzj²e1ÊB”e(›Ö ¬@éFéD¼҄ЂҀÐÒ|ÒzÐxÒvx‰¤åðRA¿A»A·A¯A«A§a&Ëþ>Þ]tâÿà'Î ÜÏî òª¢¡öqθqæ]3ð‰ç€<Çå9<¯YzÍÖkÖž£÷Ä×\¾æô9¶¯)~MõsÈ_3ÿÊ`$€” % (a@‰J P" ”X %(á@‰J@P"‚”˜ *„ÛŒðÂf˜+ÄëŒ9C¼Öý»‹µýOÅãdžéå£YøÿVHøã¯áRAâ eÕß—<»<”ç >tƒ<[í÷{_3Þžj³züÍøÌ¿ýuÖD®MKý$ͱ¹kÕ¸ÜBÞŽo®XéwIòK̃©eŸþéëß_Ø? ­¿ôe¿~à¯\øõWö;||jžÁ²cmtk-3.3.1/Utilities/mxml/doc/makedocs.sh000077500000000000000000000014221276303427400203050ustar00rootroot00000000000000#!/bin/sh # # "$Id: makedocs.sh 408 2010-09-19 05:26:46Z mike $" # # Script to make documentation... # # Copyright 2003-2010 by Michael R Sweet. # # These coded instructions, statements, and computer programs are the # property of Michael R Sweet and are protected by Federal copyright # law. Distribution and use rights are outlined in the file "COPYING" # which should have been included with this file. If this file is # missing or damaged, see the license at: # # http://www.minixml.org/ # htmldoc --verbose --path "hires;." --batch mxml.book -f mxml.pdf htmldoc --verbose --batch mxml.book --no-title -f mxml.html rm -rf mxml.d mkdir mxml.d htmldoc --verbose --batch mxml.book --no-title -t html -d mxml.d # # End of "$Id: makedocs.sh 408 2010-09-19 05:26:46Z mike $". # cmtk-3.3.1/Utilities/mxml/doc/mxml.book000066400000000000000000000016351276303427400200170ustar00rootroot00000000000000#HTMLDOC 1.8.27.1 -t pdf14 -f "mxml.pdf" --book --toclevels 3 --no-numbered --toctitle "Table of Contents" --title --titleimage "title.html" --linkstyle plain --size 4.25x6.875in --left 0.750in --right 0.50in --top 0.50in --bottom 0.50in --header .t. --header1 ... --footer h.1 --nup 1 --tocheader .t. --tocfooter ..i --duplex --portrait --color --no-pscommands --no-xrxcomments --compression=9 --jpeg=95 --fontsize 9.0 --fontspacing 1.2 --headingfont Helvetica --bodyfont Helvetica --headfootsize 8.0 --headfootfont Helvetica-Oblique --charset iso-8859-1 --links --embedfonts --pagemode outline --pagelayout single --firstpage c1 --pageeffect none --pageduration 10 --effectduration 1.0 --no-encryption --permissions all --owner-password "" --user-password "" --browserwidth 300 --no-strict --no-overflow intro.html install.html basics.html advanced.html mxmldoc.html license.html relnotes.html reference.html schema.html cmtk-3.3.1/Utilities/mxml/doc/mxml.html000066400000000000000000005612261276303427400200400ustar00rootroot00000000000000 Mini-XML Programmers Manual, Version 2.7

Table of Contents



Introduction Building, Installing, and Packaging Mini-XML Getting Started with Mini-XML More Mini-XML Programming Techniques Using the mxmldoc Utility Mini-XML License

Release Notes
    Library Reference XML Schema

      0Introduction

      This programmers manual describes Mini-XML version 2.7, a small XML parsing library that you can use to read and write XML data files in your C and C++ applications.

      Mini-XML was initially developed for the Gutenprint project to replace the rather large and unwieldy libxml2 library with something substantially smaller and easier-to-use. It all began one morning in June of 2003 when Robert posted the following sentence to the developer's list:

      It's bad enough that we require libxml2, but rolling our own XML parser is a bit more than we can handle.

      I then replied with:

      Given the limited scope of what you use in XML, it should be trivial to code a mini-XML API in a few hundred lines of code.

      I took my own challenge and coded furiously for two days to produced the initial public release of Mini-XML, total lines of code: 696. Robert promptly integrated Mini-XML into Gutenprint and removed libxml2.

      Thanks to lots of feedback and support from various developers, Mini-XML has evolved since then to provide a more complete XML implementation and now stands at a whopping 3,965 lines of code, compared to 103,893 lines of code for libxml2 version 2.6.9.

      Aside from Gutenprint, Mini-XML is used for the following projects/software applications:

      Please email me (mxml @ easysw . com) if you would like your project added or removed from this list, or if you have any comments/quotes you would like me to publish about your experiences with Mini-XML.

      Organization of This Document

      This manual is organized into the following chapters and appendices:

      Notation Conventions

      Various font and syntax conventions are used in this guide. Examples and their meanings and uses are explained below:

      mxmldoc
      mxmldoc(1)
      The names of commands; the first mention of a command or function in a chapter is followed by a manual page section number.

      /var
      /etc/hosts
      File and directory names.

      Request ID is Printer-123
      Screen output.

      lp -d printer filename ENTER
      Literal user input; special keys like ENTER are in ALL CAPS.

      12.3
      Numbers in the text are written using the period (.) to indicate the decimal point.

      Abbreviations

      The following abbreviations are used throughout this manual:

      Gb
      Gigabytes, or 1073741824 bytes

      kb
      Kilobytes, or 1024 bytes

      Mb
      Megabytes, or 1048576 bytes

      UTF-8, UTF-16
      Unicode Transformation Format, 8-bit or 16-bit

      W3C
      World Wide Web Consortium

      XML
      Extensible Markup Language

      Other References

      The Unicode Standard, Version 4.0, Addison-Wesley, ISBN 0-321-18578-1
      The definition of the Unicode character set which is used for XML.

      Extensible Markup Language (XML) 1.0 (Third Edition)
      The XML specification from the World Wide Web Consortium (W3C)

      Legal Stuff

      The Mini-XML library is copyright 2003-2011 by Michael Sweet. License terms are described in Appendix A - Mini-XML License .


      1Building, Installing, and Packaging Mini-XML

      This chapter describes how to build, install, and package Mini-XML on your system from the source archive. You will need an ANSI/ISO-C compatible compiler to build Mini-XML - GCC works, as do most vendors' C compilers. If you are building Mini-XML on Windows, we recommend using the Visual C++ environment with the supplied solution file. For other operating systems, you'll need a POSIX-compatible shell and make program in addition to the C compiler.

      Compiling Mini-XML

      Mini-XML comes with both an autoconf-based configure script and a Visual C++ solution that can be used to compile the library and associated tools.

      Compiling with Visual C++

      Open the mxml.sln solution in the vcnet folder. Choose the desired build configuration, "Debug" (the default) or "Release", and then choose Build Solution from the Build menu.

      Compiling with Command-Line Tools

      Type the following command to configure the Mini-XML source code for your system:

          ./configure ENTER
      

      The default install prefix is /usr/local, which can be overridden using the --prefix option:

          ./configure --prefix=/foo ENTER
      

      Other configure options can be found using the --help option:

          ./configure --help ENTER
      

      Once you have configured the software, use the make(1) program to do the build and run the test program to verify that things are working, as follows:

          make ENTER
      

      Installing Mini-XML

      If you are using Visual C++, copy the mxml.lib and and mxml.h files to the Visual C++ lib and include directories, respectively.

      Otherwise, use the make command with the install target to install Mini-XML in the configured directories:

          make install ENTER
      

      Creating Mini-XML Packages

      Mini-XML includes two files that can be used to create binary packages. The first file is mxml.spec which is used by the rpmbuild(8) software to create Red Hat Package Manager ("RPM") packages which are commonly used on Linux. Since rpmbuild wants to compile the software on its own, you can provide it with the Mini-XML tar file to build the package:

          rpmbuild -ta mxml-version.tar.gz ENTER
      

      The second file is mxml.list which is used by the epm(1) program to create software packages in a variety of formats. The epm program is available from the following URL:

          http://www.epmhome.org/
      

      Use the make command with the epm target to create portable and native packages for your system:

          make epm ENTER
      

      The packages are stored in a subdirectory named dist for your convenience. The portable packages utilize scripts and tar files to install the software on the target system. After extracting the package archive, use the mxml.install script to install the software.

      The native packages will be in the local OS's native format: RPM for Red Hat Linux, DPKG for Debian Linux, PKG for Solaris, and so forth. Use the corresponding commands to install the native packages.


      2Getting Started with Mini-XML

      This chapter describes how to write programs that use Mini-XML to access data in an XML file. Mini-XML provides the following functionality:

      • Functions for creating and managing XML documents in memory.
      • Reading of UTF-8 and UTF-16 encoded XML files and strings.
      • Writing of UTF-8 encoded XML files and strings.
      • Support for arbitrary element names, attributes, and attribute values with no preset limits, just available memory.
      • Support for integer, real, opaque ("CDATA"), and text data types in "leaf" nodes.
      • "Find", "index", and "walk" functions for easily accessing data in an XML document.

      Mini-XML doesn't do validation or other types of processing on the data based upon schema files or other sources of definition information, nor does it support character entities other than those required by the XML specification.

      The Basics

      Mini-XML provides a single header file which you include:

          #include <mxml.h>
      

      The Mini-XML library is included with your program using the -lmxml option:

          gcc -o myprogram myprogram.c -lmxml ENTER
      

      If you have the pkg-config(1) software installed, you can use it to determine the proper compiler and linker options for your installation:

          pkg-config --cflags mxml ENTER
          pkg-config --libs mxml ENTER
      

      Nodes

      Every piece of information in an XML file is stored in memory in "nodes". Nodes are defined by the mxml_node_t structure. Each node has a typed value, optional user data, a parent node, sibling nodes (previous and next), and potentially child nodes.

      For example, if you have an XML file like the following:

          <?xml version="1.0" encoding="utf-8"?>
          <data>
              <node>val1</node>
              <node>val2</node>
              <node>val3</node>
              <group>
                  <node>val4</node>
                  <node>val5</node>
                  <node>val6</node>
              </group>
              <node>val7</node>
              <node>val8</node>
          </data>
      

      the node tree for the file would look like the following in memory:

          ?xml version="1.0" encoding="utf-8"?
            |
          data
            |
          node - node - node - group - node - node
            |      |      |      |       |      |
          val1   val2   val3     |     val7   val8
                                 |
                               node - node - node
                                 |      |      |
                               val4   val5   val6
      

      where "-" is a pointer to the sibling node and "|" is a pointer to the first child or parent node.

      The mxmlGetType function gets the type of a node, one of MXML_CUSTOM, MXML_ELEMENT, MXML_INTEGER, MXML_OPAQUE, MXML_REAL, or MXML_TEXT. The parent and sibling nodes are accessed using the mxmlGetParent, mxmlGetNext , and mxmlGetPrevious functions. The mxmlGetUserData function gets any user data associated with the node.

      CDATA Nodes

      CDATA (MXML_ELEMENT) nodes are created using the mxmlNewCDATA function. The mxmlGetCDATA function retrieves the CDATA string pointer for a node.

      Note:

      CDATA nodes are currently stored in memory as special elements. This will be changed in a future major release of Mini-XML.

      Custom Nodes

      Custom (MXML_CUSTOM) nodes are created using the mxmlNewCustom function or using a custom load callback specified using the mxmlSetCustomHandlers function. The mxmlGetCustom function retrieves the custom value pointer for a node.

      Comment Nodes

      Comment (MXML_ELEMENT) nodes are created using the mxmlNewElement function. The mxmlGetElement function retrieves the comment string pointer for a node, including the surrounding "!--" and "--" characters.

      Note:

      Comment nodes are currently stored in memory as special elements. This will be changed in a future major release of Mini-XML.

      Element Nodes

      Element (MXML_ELEMENT) nodes are created using the mxmlNewElement function. The mxmlGetElement function retrieves the element name, the mxmlElementGetAttr function retrieves the value string for a named attribute associated with the element, and the mxmlGetFirstChild and mxmlGetLastChild functions retrieve the first and last child nodes for the element, respectively.

      Integer Nodes

      Integer (MXML_INTEGER) nodes are created using the mxmlNewInteger function. The mxmlGetInteger function retrieves the integer value for a node.

      Opaque Nodes

      Opaque (MXML_OPAQUE) nodes are created using the mxmlNewOpaque function. The mxmlGetOpaque function retrieves the opaque string pointer for a node. Opaque nodes are like string nodes but preserve all whitespace between nodes.

      Text Nodes

      Text (MXML_TEXT) nodes are created using the mxmlNewText and mxmlNewTextf functions. Each text node consists of a text string and (leading) whitespace value - the mxmlGetText function retrieves the text string pointer and whitespace value for a node.

      Processing Instruction Nodes

      Processing instruction (MXML_ELEMENT) nodes are created using the mxmlNewElement function. The mxmlGetElement function retrieves the processing instruction string for a node, including the surrounding "?" characters.

      Note:

      Processing instruction nodes are currently stored in memory as special elements. This will be changed in a future major release of Mini-XML.

      Real Number Nodes

      Real number (MXML_REAL) nodes are created using the mxmlNewReal function. The mxmlGetReal function retrieves the CDATA string pointer for a node.

      XML Declaration Nodes

      XML declaration (MXML_ELEMENT) nodes are created using the mxmlNewXML function. The mxmlGetElement function retrieves the XML declaration string for a node, including the surrounding "?" characters.

      Note:

      XML declaration nodes are currently stored in memory as special elements. This will be changed in a future major release of Mini-XML.

      Creating XML Documents

      You can create and update XML documents in memory using the various mxmlNew functions. The following code will create the XML document described in the previous section:

          mxml_node_t *xml;    /* <?xml ... ?> */
          mxml_node_t *data;   /* <data> */
          mxml_node_t *node;   /* <node> */
          mxml_node_t *group;  /* <group> */
      
          xml = mxmlNewXML("1.0");
      
          data = mxmlNewElement(xml, "data");
      
              node = mxmlNewElement(data, "node");
              mxmlNewText(node, 0, "val1");
              node = mxmlNewElement(data, "node");
              mxmlNewText(node, 0, "val2");
              node = mxmlNewElement(data, "node");
              mxmlNewText(node, 0, "val3");
      
              group = mxmlNewElement(data, "group");
      
                  node = mxmlNewElement(group, "node");
                  mxmlNewText(node, 0, "val4");
                  node = mxmlNewElement(group, "node");
                  mxmlNewText(node, 0, "val5");
                  node = mxmlNewElement(group, "node");
                  mxmlNewText(node, 0, "val6");
      
              node = mxmlNewElement(data, "node");
              mxmlNewText(node, 0, "val7");
              node = mxmlNewElement(data, "node");
              mxmlNewText(node, 0, "val8");
      

      We start by creating the declaration node common to all XML files using the mxmlNewXML function:

          xml = mxmlNewXML("1.0");
      

      We then create the <data> node used for this document using the mxmlNewElement function. The first argument specifies the parent node (xml) while the second specifies the element name (data):

          data = mxmlNewElement(xml, "data");
      

      Each <node>...</node> in the file is created using the mxmlNewElement and mxmlNewText functions. The first argument of mxmlNewText specifies the parent node (node). The second argument specifies whether whitespace appears before the text - 0 or false in this case. The last argument specifies the actual text to add:

          node = mxmlNewElement(data, "node");
          mxmlNewText(node, 0, "val1");
      

      The resulting in-memory XML document can then be saved or processed just like one loaded from disk or a string.

      Loading XML

      You load an XML file using the mxmlLoadFile function:

          FILE *fp;
          mxml_node_t *tree;
      
          fp = fopen("filename.xml", "r");
          tree = mxmlLoadFile(NULL, fp,
                              MXML_TEXT_CALLBACK);
          fclose(fp);
      

      The first argument specifies an existing XML parent node, if any. Normally you will pass NULL for this argument unless you are combining multiple XML sources. The XML file must contain a complete XML document including the ?xml element if the parent node is NULL.

      The second argument specifies the stdio file to read from, as opened by fopen() or popen(). You can also use stdin if you are implementing an XML filter program.

      The third argument specifies a callback function which returns the value type of the immediate children for a new element node: MXML_CUSTOM, MXML_IGNORE, MXML_INTEGER, MXML_OPAQUE, MXML_REAL, or MXML_TEXT. Load callbacks are described in detail in Chapter 3. The example code uses the MXML_TEXT_CALLBACK constant which specifies that all data nodes in the document contain whitespace-separated text values. Other standard callbacks include MXML_IGNORE_CALLBACK, MXML_INTEGER_CALLBACK, MXML_OPAQUE_CALLBACK, and MXML_REAL_CALLBACK.

      The mxmlLoadString function loads XML node trees from a string:

          char buffer[8192];
          mxml_node_t *tree;
      
          ...
          tree = mxmlLoadString(NULL, buffer,
                                MXML_TEXT_CALLBACK);
      

      The first and third arguments are the same as used for mxmlLoadFile(). The second argument specifies the string or character buffer to load and must be a complete XML document including the ?xml element if the parent node is NULL.

      Saving XML

      You save an XML file using the mxmlSaveFile function:

          FILE *fp;
          mxml_node_t *tree;
      
          fp = fopen("filename.xml", "w");
          mxmlSaveFile(tree, fp, MXML_NO_CALLBACK);
          fclose(fp);
      

      The first argument is the XML node tree to save. It should normally be a pointer to the top-level ?xml node in your XML document.

      The second argument is the stdio file to write to, as opened by fopen() or popen(). You can also use stdout if you are implementing an XML filter program.

      The third argument is the whitespace callback to use when saving the file. Whitespace callbacks are covered in detail in Chapter 3. The previous example code uses the MXML_NO_CALLBACK constant to specify that no special whitespace handling is required.

      The mxmlSaveAllocString, and mxmlSaveString functions save XML node trees to strings:

          char buffer[8192];
          char *ptr;
          mxml_node_t *tree;
      
          ...
          mxmlSaveString(tree, buffer, sizeof(buffer),
                         MXML_NO_CALLBACK);
      
          ...
          ptr = mxmlSaveAllocString(tree, MXML_NO_CALLBACK);
      

      The first and last arguments are the same as used for mxmlSaveFile(). The mxmlSaveString function takes pointer and size arguments for saving the XML document to a fixed-size buffer, while mxmlSaveAllocString() returns a string buffer that was allocated using malloc().

      Controlling Line Wrapping

      When saving XML documents, Mini-XML normally wraps output lines at column 75 so that the text is readable in terminal windows. The mxmlSetWrapMargin function overrides the default wrap margin:

          /* Set the margin to 132 columns */
          mxmlSetWrapMargin(132);
      
          /* Disable wrapping */
          mxmlSetWrapMargin(0);
      

      Memory Management

      Once you are done with the XML data, use the mxmlDelete function to recursively free the memory that is used for a particular node or the entire tree:

          mxmlDelete(tree);
      

      You can also use reference counting to manage memory usage. The mxmlRetain and mxmlRelease functions increment and decrement a node's use count, respectively. When the use count goes to 0, mxmlRelease will automatically call mxmlDelete to actually free the memory used by the node tree. New nodes automatically start with a use count of 1.

      Finding and Iterating Nodes

      The mxmlWalkPrev and mxmlWalkNextfunctions can be used to iterate through the XML node tree:

          mxml_node_t *node;
          
          node = mxmlWalkPrev(current, tree,
                              MXML_DESCEND);
      
          node = mxmlWalkNext(current, tree,
                              MXML_DESCEND);
      

      In addition, you can find a named element/node using the mxmlFindElement function:

          mxml_node_t *node;
          
          node = mxmlFindElement(tree, tree, "name",
                                 "attr", "value",
                                 MXML_DESCEND);
      

      The name, attr, and value arguments can be passed as NULL to act as wildcards, e.g.:

          /* Find the first "a" element */
          node = mxmlFindElement(tree, tree, "a",
                                 NULL, NULL,
                                 MXML_DESCEND);
      
          /* Find the first "a" element with "href"
             attribute */
          node = mxmlFindElement(tree, tree, "a",
                                 "href", NULL,
                                 MXML_DESCEND);
      
          /* Find the first "a" element with "href"
             to a URL */
          node = mxmlFindElement(tree, tree, "a",
                                 "href",
                                 "http://www.easysw.com/",
                                 MXML_DESCEND);
      
          /* Find the first element with a "src"
             attribute */
          node = mxmlFindElement(tree, tree, NULL,
                                 "src", NULL,
                                 MXML_DESCEND);
      
          /* Find the first element with a "src"
             = "foo.jpg" */
          node = mxmlFindElement(tree, tree, NULL,
                                 "src", "foo.jpg",
                                 MXML_DESCEND);
      

      You can also iterate with the same function:

          mxml_node_t *node;
      
          for (node = mxmlFindElement(tree, tree,
                                      "name",
                                      NULL, NULL,
                                      MXML_DESCEND);
               node != NULL;
               node = mxmlFindElement(node, tree,
                                      "name",
                                      NULL, NULL,
                                      MXML_DESCEND))
          {
            ... do something ...
          }
      

      The MXML_DESCEND argument can actually be one of three constants:

      • MXML_NO_DESCEND means to not to look at any child nodes in the element hierarchy, just look at siblings at the same level or parent nodes until the top node or top-of-tree is reached.

        The previous node from "group" would be the "node" element to the left, while the next node from "group" would be the "node" element to the right.

      • MXML_DESCEND_FIRST means that it is OK to descend to the first child of a node, but not to descend further when searching. You'll normally use this when iterating through direct children of a parent node, e.g. all of the "node" and "group" elements under the "?xml" parent node in the example above.

        This mode is only applicable to the search function; the walk functions treat this as MXML_DESCEND since every call is a first time.

      • MXML_DESCEND means to keep descending until you hit the bottom of the tree. The previous node from "group" would be the "val3" node and the next node would be the first node element under "group".

        If you were to walk from the root node "?xml" to the end of the tree with mxmlWalkNext(), the order would be:

        ?xml data node val1 node val2 node val3 group node val4 node val5 node val6 node val7 node val8

        If you started at "val8" and walked using mxmlWalkPrev(), the order would be reversed, ending at "?xml".

      Finding Specific Nodes

      You can find specific nodes in the tree using the mxmlFindPath, for example:

          mxml_node_t *value;
      
          value = mxmlFindPath(tree, "path/to/*/foo/bar");
      

      The second argument is a "path" to the parent node. Each component of the path is separated by a slash (/) and represents a named element in the document tree or a wildcard (*) path representing 0 or more intervening nodes.


      3More Mini-XML Programming Techniques

      This chapter shows additional ways to use the Mini-XML library in your programs.

      Load Callbacks

      Chapter 2 introduced the mxmlLoadFile() and mxmlLoadString() functions. The last argument to these functions is a callback function which is used to determine the value type of each data node in an XML document.

      Mini-XML defines several standard callbacks for simple XML data files:

      • MXML_INTEGER_CALLBACK - All data nodes contain whitespace-separated integers.
      • MXML_OPAQUE_CALLBACK - All data nodes contain opaque strings ("CDATA").
      • MXML_REAL_CALLBACK - All data nodes contain whitespace-separated floating-point numbers.
      • MXML_TEXT_CALLBACK - All data nodes contain whitespace-separated strings.

      You can provide your own callback functions for more complex XML documents. Your callback function will receive a pointer to the current element node and must return the value type of the immediate children for that element node: MXML_INTEGER, MXML_OPAQUE, MXML_REAL, or MXML_TEXT. The function is called after the element and its attributes have been read, so you can look at the element name, attributes, and attribute values to determine the proper value type to return.

      The following callback function looks for an attribute named "type" or the element name to determine the value type for its child nodes:

          mxml_type_t
          type_cb(mxml_node_t *node)
          {
            const char *type;
      
           /*
            * You can lookup attributes and/or use the
            * element name, hierarchy, etc...
            */
      
            type = mxmlElementGetAttr(node, "type");
            if (type == NULL)
      	type = mxmlGetElement(node);
      
            if (!strcmp(type, "integer"))
      	return (MXML_INTEGER);
            else if (!strcmp(type, "opaque"))
      	return (MXML_OPAQUE);
            else if (!strcmp(type, "real"))
      	return (MXML_REAL);
            else
      	return (MXML_TEXT);
          }
      

      To use this callback function, simply use the name when you call any of the load functions:

          FILE *fp;
          mxml_node_t *tree;
      
          fp = fopen("filename.xml", "r");
          tree = mxmlLoadFile(NULL, fp, type_cb);
          fclose(fp);
      

      Save Callbacks

      Chapter 2 also introduced the mxmlSaveFile(), mxmlSaveString(), and mxmlSaveAllocString() functions. The last argument to these functions is a callback function which is used to automatically insert whitespace in an XML document.

      Your callback function will be called up to four times for each element node with a pointer to the node and a "where" value of MXML_WS_BEFORE_OPEN, MXML_WS_AFTER_OPEN, MXML_WS_BEFORE_CLOSE, or MXML_WS_AFTER_CLOSE. The callback function should return NULL if no whitespace should be added and the string to insert (spaces, tabs, carriage returns, and newlines) otherwise.

      The following whitespace callback can be used to add whitespace to XHTML output to make it more readable in a standard text editor:

          const char *
          whitespace_cb(mxml_node_t *node,
                        int where)
          {
            const char *name;
      
           /*
            * We can conditionally break to a new line
            * before or after any element. These are
            * just common HTML elements...
            */
      
            name = mxmlGetElement(node);
      
            if (!strcmp(name, "html") ||
                !strcmp(name, "head") ||
                !strcmp(name, "body") ||
      	  !strcmp(name, "pre") ||
                !strcmp(name, "p") ||
      	  !strcmp(name, "h1") ||
                !strcmp(name, "h2") ||
                !strcmp(name, "h3") ||
      	  !strcmp(name, "h4") ||
                !strcmp(name, "h5") ||
                !strcmp(name, "h6"))
            {
             /*
      	* Newlines before open and after
              * close...
      	*/
      
      	if (where == MXML_WS_BEFORE_OPEN ||
                  where == MXML_WS_AFTER_CLOSE)
      	  return ("\n");
            }
            else if (!strcmp(name, "dl") ||
                     !strcmp(name, "ol") ||
                     !strcmp(name, "ul"))
            {
             /*
      	* Put a newline before and after list
              * elements...
      	*/
      
      	return ("\n");
            }
            else if (!strcmp(name, "dd") ||
                     !strcmp(name, "dt") ||
                     !strcmp(name, "li"))
            {
             /*
      	* Put a tab before <li>'s, * <dd>'s,
              * and <dt>'s, and a newline after them...
      	*/
      
      	if (where == MXML_WS_BEFORE_OPEN)
      	  return ("\t");
      	else if (where == MXML_WS_AFTER_CLOSE)
      	  return ("\n");
            }
      
           /*
            * Return NULL for no added whitespace...
            */
      
            return (NULL);
          }
      

      To use this callback function, simply use the name when you call any of the save functions:

          FILE *fp;
          mxml_node_t *tree;
      
          fp = fopen("filename.xml", "w");
          mxmlSaveFile(tree, fp, whitespace_cb);
          fclose(fp);
      

      Custom Data Types

      Mini-XML supports custom data types via global load and save callbacks. Only a single set of callbacks can be active at any time, however your callbacks can store additional information in order to support multiple custom data types as needed. The MXML_CUSTOM node type identifies custom data nodes.

      The load callback receives a pointer to the current data node and a string of opaque character data from the XML source with character entities converted to the corresponding UTF-8 characters. For example, if we wanted to support a custom date/time type whose value is encoded as "yyyy-mm-ddThh:mm:ssZ" (ISO format), the load callback would look like the following:

          typedef struct
          {
            unsigned      year,    /* Year */
                          month,   /* Month */
                          day,     /* Day */
                          hour,    /* Hour */
                          minute,  /* Minute */
                          second;  /* Second */
            time_t        unix;    /* UNIX time */
          } iso_date_time_t;
      
          int
          load_custom(mxml_node_t *node,
                      const char *data)
          {
            iso_date_time_t *dt;
            struct tm tmdata;
      
           /*
            * Allocate data structure...
            */
      
            dt = calloc(1, sizeof(iso_date_time_t));
      
           /*
            * Try reading 6 unsigned integers from the
            * data string...
            */
      
            if (sscanf(data, "%u-%u-%uT%u:%u:%uZ",
                       &(dt->year), &(dt->month),
                       &(dt->day), &(dt->hour),
                       &(dt->minute),
                       &(dt->second)) != 6)
            {
             /*
              * Unable to read numbers, free the data
              * structure and return an error...
              */
      
              free(dt);
      
              return (-1);
            }
      
           /*
            * Range check values...
            */
      
            if (dt->month <1 || dt->month > 12 ||
                dt->day  <1 || dt->day > 31 ||
                dt->hour  <0 || dt->hour > 23 ||
                dt->minute  <0 || dt->minute > 59 ||
                dt->second  <0 || dt->second > 59)
            {
             /*
              * Date information is out of range...
              */
      
              free(dt);
      
              return (-1);
            }
      
           /*
            * Convert ISO time to UNIX time in
            * seconds...
            */
      
            tmdata.tm_year = dt->year - 1900;
            tmdata.tm_mon  = dt->month - 1;
            tmdata.tm_day  = dt->day;
            tmdata.tm_hour = dt->hour;
            tmdata.tm_min  = dt->minute;
            tmdata.tm_sec  = dt->second;
      
            dt->unix = gmtime(&tmdata);
      
           /*
            * Assign custom node data and destroy
            * function pointers...
            */
      
            mxmlSetCustom(node, data, destroy);
      
           /*
            * Return with no errors...
            */
      
            return (0);
          }
      

      The function itself can return 0 on success or -1 if it is unable to decode the custom data or the data contains an error. Custom data nodes contain a void pointer to the allocated custom data for the node and a pointer to a destructor function which will free the custom data when the node is deleted.

      The save callback receives the node pointer and returns an allocated string containing the custom data value. The following save callback could be used for our ISO date/time type:

          char *
          save_custom(mxml_node_t *node)
          {
            char data[255];
            iso_date_time_t *dt;
      
      
            dt = (iso_date_time_t *)mxmlGetCustom(node);
      
            snprintf(data, sizeof(data),
                     "%04u-%02u-%02uT%02u:%02u:%02uZ",
                     dt->year, dt->month, dt->day,
                     dt->hour, dt->minute, dt->second);
      
            return (strdup(data));
          }
      

      You register the callback functions using the mxmlSetCustomHandlers() function:

          mxmlSetCustomHandlers(load_custom,
                                save_custom);
      

      Changing Node Values

      All of the examples so far have concentrated on creating and loading new XML data nodes. Many applications, however, need to manipulate or change the nodes during their operation, so Mini-XML provides functions to change node values safely and without leaking memory.

      Existing nodes can be changed using the mxmlSetElement(), mxmlSetInteger(), mxmlSetOpaque() , mxmlSetReal(), mxmlSetText(), and mxmlSetTextf() functions. For example, use the following function call to change a text node to contain the text "new" with leading whitespace:

          mxml_node_t *node;
      
          mxmlSetText(node, 1, "new");
      

      Formatted Text

      The mxmlNewTextf() and mxmlSetTextf() functions create and change text nodes, respectively, using printf-style format strings and arguments. For example, use the following function call to create a new text node containing a constructed filename:

          mxml_node_t *node;
      
          node = mxmlNewTextf(node, 1, "%s/%s",
                              path, filename);
      

      Indexing

      Mini-XML provides functions for managing indices of nodes. The current implementation provides the same functionality as mxmlFindElement(). The advantage of using an index is that searching and enumeration of elements is significantly faster. The only disadvantage is that each index is a static snapshot of the XML document, so indices are not well suited to XML data that is updated more often than it is searched. The overhead of creating an index is approximately equal to walking the XML document tree. Nodes in the index are sorted by element name and attribute value.

      Indices are stored in mxml_index_t structures. The mxmlIndexNew() function creates a new index:

          mxml_node_t *tree;
          mxml_index_t *ind;
      
          ind = mxmlIndexNew(tree, "element",
                             "attribute");
      

      The first argument is the XML node tree to index. Normally this will be a pointer to the ?xml element.

      The second argument contains the element to index; passing NULL indexes all element nodes alphabetically.

      The third argument contains the attribute to index; passing NULL causes only the element name to be indexed.

      Once the index is created, the mxmlIndexEnum(), mxmlIndexFind() , and mxmlIndexReset() functions are used to access the nodes in the index. The mxmlIndexReset() function resets the "current" node pointer in the index, allowing you to do new searches and enumerations on the same index. Typically you will call this function prior to your calls to mxmlIndexEnum() and mxmlIndexFind().

      The mxmlIndexEnum() function enumerates each of the nodes in the index and can be used in a loop as follows:

          mxml_node_t *node;
      
          mxmlIndexReset(ind);
      
          while ((node = mxmlIndexEnum(ind)) != NULL)
          {
            // do something with node
          }
      

      The mxmlIndexFind() function locates the next occurrence of the named element and attribute value in the index. It can be used to find all matching elements in an index, as follows:

          mxml_node_t *node;
      
          mxmlIndexReset(ind);
      
          while ((node = mxmlIndexFind(ind, "element",
                                       "attr-value"))
                      != NULL)
          {
            // do something with node
          }
      

      The second and third arguments represent the element name and attribute value, respectively. A NULL pointer is used to return all elements or attributes in the index. Passing NULL for both the element name and attribute value is equivalent to calling mxmlIndexEnum.

      When you are done using the index, delete it using the mxmlIndexDelete() function:

          mxmlIndexDelete(ind);
      

      SAX (Stream) Loading of Documents

      Mini-XML supports an implementation of the Simple API for XML (SAX) which allows you to load and process an XML document as a stream of nodes. Aside from allowing you to process XML documents of any size, the Mini-XML implementation also allows you to retain portions of the document in memory for later processing.

      The mxmlSAXLoadFd, mxmlSAXLoadFile, and mxmlSAXLoadString functions provide the SAX loading APIs. Each function works like the corresponding mxmlLoad function but uses a callback to process each node as it is read.

      The callback function receives the node, an event code, and a user data pointer you supply:

          void
          sax_cb(mxml_node_t *node,
                 mxml_sax_event_t event,
                 void *data)
          {
            ... do something ...
          }
      

      The event will be one of the following:

      • MXML_SAX_CDATA - CDATA was just read
      • MXML_SAX_COMMENT - A comment was just read
      • MXML_SAX_DATA - Data (custom, integer, opaque, real, or text) was just read
      • MXML_SAX_DIRECTIVE - A processing directive was just read
      • MXML_SAX_ELEMENT_CLOSE - A close element was just read ( </element>)
      • MXML_SAX_ELEMENT_OPEN - An open element was just read ( <element>)

      Elements are released after the close element is processed. All other nodes are released after they are processed. The SAX callback can retain the node using the mxmlRetain function. For example, the following SAX callback will retain all nodes, effectively simulating a normal in-memory load:

          void
          sax_cb(mxml_node_t *node,
                 mxml_sax_event_t event,
                 void *data)
          {
            if (event != MXML_SAX_ELEMENT_CLOSE)
              mxmlRetain(node);
          }
      

      More typically the SAX callback will only retain a small portion of the document that is needed for post-processing. For example, the following SAX callback will retain the title and headings in an XHTML file. It also retains the (parent) elements like <html>, <head>, and <body>, and processing directives like <?xml ... ?> and <!DOCTYPE ... >:

          void
          sax_cb(mxml_node_t *node,
                 mxml_sax_event_t event,
                 void *data)
          {
            if (event == MXML_SAX_ELEMENT_OPEN)
            {
             /*
              * Retain headings and titles...
              */
      
              char *name = mxmlGetElement(node);
      
              if (!strcmp(name, "html") ||
                  !strcmp(name, "head") ||
                  !strcmp(name, "title") ||
                  !strcmp(name, "body") ||
                  !strcmp(name, "h1") ||
                  !strcmp(name, "h2") ||
                  !strcmp(name, "h3") ||
                  !strcmp(name, "h4") ||
                  !strcmp(name, "h5") ||
                  !strcmp(name, "h6"))
                mxmlRetain(node);
            }
            else if (event == MXML_SAX_DIRECTIVE)
              mxmlRetain(node);
            else if (event == MXML_SAX_DATA)
            {
              if (mxmlGetRefCount(mxmlGetParent(node)) > 1)
              {
               /*
                * If the parent was retained, then retain
                * this data node as well.
                */
      
                mxmlRetain(node);
              }
            }
          }
      

      The resulting skeleton document tree can then be searched just like one loaded using the mxmlLoad functions. For example, a filter that reads an XHTML document from stdin and then shows the title and headings in the document would look like:

          mxml_node_t *doc, *title, *body, *heading;
      
          doc = mxmlSAXLoadFd(NULL, 0,
                              MXML_TEXT_CALLBACK,
                              sax_cb, NULL);
      
          title = mxmlFindElement(doc, doc, "title",
                                  NULL, NULL,
                                  MXML_DESCEND);
      
          if (title)
            print_children(title);
      
          body = mxmlFindElement(doc, doc, "body",
                                 NULL, NULL,
                                 MXML_DESCEND);
      
          if (body)
          {
            for (heading = mxmlGetFirstChild(body);
                 heading;
                 heading = mxmlGetNextSibling(heading))
              print_children(heading);
          }
      

      4Using the mxmldoc Utility

      This chapter describes how to use mxmldoc(1) program to automatically generate documentation from C and C++ source files.

      The Basics

      Originally developed to generate the Mini-XML and CUPS API documentation, mxmldoc is now a general-purpose utility which scans C and C++ source files to produce HTML and man page documentation along with an XML file representing the functions, types, and definitions in those source files. Unlike popular documentation generators like Doxygen or Javadoc, mxmldoc uses in-line comments rather than comment headers, allowing for more "natural" code documentation.

      By default, mxmldoc produces HTML documentation. For example, the following command will scan all of the C source and header files in the current directory and produce a HTML documentation file called filename.html:

          mxmldoc *.h *.c >filename.html ENTER
      

      You can also specify an XML file to create which contains all of the information from the source files. For example, the following command creates an XML file called filename.xml in addition to the HTML file:

          mxmldoc filename.xml *.h *.c >filename.html ENTER
      

      The --no-output option disables the normal HTML output:

          mxmldoc --no-output filename.xml *.h *.c ENTER
      

      You can then run mxmldoc again with the XML file alone to generate the HTML documentation:

          mxmldoc filename.xml >filename.html ENTER
      

      Creating Man Pages

      The --man filename option tells mxmldoc to create a man page instead of HTML documentation, for example:

          mxmldoc --man filename filename.xml \
              >filename.man ENTER
      
          mxmldoc --man filename *.h *.c \
              >filename.man ENTER
      

      Creating Xcode Documentation Sets

      The --docset directory.docset option tells mxmldoc to create an Xcode documentation set containing the HTML documentation, for example:

          mxmldoc --docset foo.docset *.h *.c foo.xml ENTER
      

      Xcode documentation sets can only be built on Mac OS X with Xcode 3.0 or higher installed.

      Commenting Your Code

      As noted previously, mxmldoc looks for in-line comments to describe the functions, types, and constants in your code. Mxmldoc will document all public names it finds in your source files - any names starting with the underscore character (_) or names that are documented with the @private@ directive are treated as private and are not documented.

      Comments appearing directly before a function or type definition are used to document that function or type. Comments appearing after argument, definition, return type, or variable declarations are used to document that argument, definition, return type, or variable. For example, the following code excerpt defines a key/value structure and a function that creates a new instance of that structure:

          /* A key/value pair. This is used with the
             dictionary structure. */
      
          struct keyval
          {
            char *key; /* Key string */
            char *val; /* Value string */
          };
      
          /* Create a new key/value pair. */
      
          struct keyval * /* New key/value pair */
          new_keyval(
              const char *key, /* Key string */
      	const char *val) /* Value string */
          {
            ...
          }
      

      Mxmldoc also knows to remove extra asterisks (*) from the comment string, so the comment string:

          /*
           * Compute the value of PI.
           *
           * The function connects to an Internet server
           * that streams audio of mathematical monks
           * chanting the first 100 digits of PI.
           */
      

      will be shown as:

          Compute the value of PI.
      
          The function connects to an Internet server
          that streams audio of mathematical monks
          chanting the first 100 digits of PI.
      

      Comments can also include the following special @name ...@ directive strings:

      • @deprecated@ - flags the item as deprecated to discourage its use
      • @private@ - flags the item as private so it will not be included in the documentation
      • @since ...@ - flags the item as new since a particular release. The text following the @since up to the closing @ is highlighted in the generated documentation, e.g. @since Mini-XML 2.7@.

      Titles, Sections, and Introductions

      Mxmldoc also provides options to set the title, section, and introduction text for the generated documentation. The --title text option specifies the title for the documentation. The title string is usually put in quotes:

          mxmldoc filename.xml \
              --title "My Famous Documentation" \
              >filename.html ENTER
      

      The --section name option specifies the section for the documentation. For HTML documentation, the name is placed in a HTML comment such as:

          <!-- SECTION: name -->
      

      For man pages, the section name is usually just a number ("3"), or a number followed by a vendor name ("3acme"). The section name is used in the .TH directive in the man page:

          .TH mylibrary 3acme "My Title" ...
      

      The default section name for man page output is "3". There is no default section name for HTML output.

      Finally, the --intro filename option specifies a file to embed after the title and section but before the generated documentation. For HTML documentation, the file must consist of valid HTML without the usual DOCTYPE, html, and body elements. For man page documentation, the file must consist of valid nroff(1) text.


      AMini-XML License

      The Mini-XML library and included programs are provided under the terms of the GNU Library General Public License version 2 (LGPL2) with the following exceptions:

      1. Static linking of applications to the Mini-XML library does not constitute a derivative work and does not require the author to provide source code for the application, use the shared Mini-XML libraries, or link their applications against a user-supplied version of Mini-XML.

      If you link the application to a modified version of Mini-XML, then the changes to Mini-XML must be provided under the terms of the LGPL2 in sections 1, 2, and 4.

      2. You do not have to provide a copy of the Mini-XML license with programs that are linked to the Mini-XML library, nor do you have to identify the Mini-XML license in your program or documentation as required by section 6 of the LGPL2.

       

      GNU LIBRARY GENERAL PUBLIC LICENSE

      Version 2, June 1991
      Copyright (C) 1991 Free Software Foundation, Inc.
      59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
      Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed.
      [This is the first released version of the library GPL. It is numbered 2 because it goes with version 2 of the ordinary GPL.]

      Preamble

      The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public Licenses are intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users.

      This license, the Library General Public License, applies to some specially designated Free Software Foundation software, and to any other libraries whose authors decide to use it. You can use it for your libraries, too.

      When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things.

      To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the library, or if you modify it.

      For example, if you distribute copies of the library, whether gratis or for a fee, you must give the recipients all the rights that we gave you. You must make sure that they, too, receive or can get the source code. If you link a program with the library, you must provide complete object files to the recipients so that they can relink them with the library, after making changes to the library and recompiling it. And you must show them these terms so they know their rights.

      Our method of protecting your rights has two steps: (1) copyright the library, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the library.

      Also, for each distributor's protection, we want to make certain that everyone understands that there is no warranty for this free library. If the library is modified by someone else and passed on, we want its recipients to know that what they have is not the original version, so that any problems introduced by others will not reflect on the original authors' reputations.

      Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that companies distributing free software will individually obtain patent licenses, thus in effect transforming the program into proprietary software. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all.

      Most GNU software, including some libraries, is covered by the ordinary GNU General Public License, which was designed for utility programs. This license, the GNU Library General Public License, applies to certain designated libraries. This license is quite different from the ordinary one; be sure to read it in full, and don't assume that anything in it is the same as in the ordinary license.

      The reason we have a separate public license for some libraries is that they blur the distinction we usually make between modifying or adding to a program and simply using it. Linking a program with a library, without changing the library, is in some sense simply using the library, and is analogous to running a utility program or application program. However, in a textual and legal sense, the linked executable is a combined work, a derivative of the original library, and the ordinary General Public License treats it as such.

      Because of this blurred distinction, using the ordinary General Public License for libraries did not effectively promote software sharing, because most developers did not use the libraries. We concluded that weaker conditions might promote sharing better.

      However, unrestricted linking of non-free programs would deprive the users of those programs of all benefit from the free status of the libraries themselves. This Library General Public License is intended to permit developers of non-free programs to use free libraries, while preserving your freedom as a user of such programs to change the free libraries that are incorporated in them. (We have not seen how to achieve this as regards changes in header files, but we have achieved it as regards changes in the actual functions of the Library.) The hope is that this will lead to faster development of free libraries.

      The precise terms and conditions for copying, distribution and modification follow. Pay close attention to the difference between a "work based on the libary" and a "work that uses the library". The former contains code derived from the library, while the latter only works together with the library.

      Note that it is possible for a library to be covered by the ordinary General Public License rather than by this special one.

      TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION

      0. This License Agreement applies to any software library which contains a notice placed by the copyright holder or other authorized party saying it may be distributed under the terms of this Library General Public License (also called "this License"). Each licensee is addressed as "you".

      A "library" means a collection of software functions and/or data prepared so as to be conveniently linked with application programs (which use some of those functions and data) to form executables.

      The "Library", below, refers to any such software library or work which has been distributed under these terms. A "work based on the Library" means either the Library or any derivative work under copyright law: that is to say, a work containing the Library or a portion of it, either verbatim or with modifications and/or translated straightforwardly into another language. (Hereinafter, translation is included without limitation in the term "modification".)

      "Source code" for a work means the preferred form of the work for making modifications to it. For a library, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the library.

      Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running a program using the Library is not restricted, and output from such a program is covered only if its contents constitute a work based on the Library (independent of the use of the Library in a tool for writing it). Whether that is true depends on what the Library does and what the program that uses the Library does.

      1. You may copy and distribute verbatim copies of the Library's complete source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and distribute a copy of this License along with the Library.

      You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee.

      2. You may modify your copy or copies of the Library or any portion of it, thus forming a work based on the Library, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions:

        a) The modified work must itself be a software library.

        b) You must cause the files modified to carry prominent notices stating that you changed the files and the date of any change.

        c) You must cause the whole of the work to be licensed at no charge to all third parties under the terms of this License.

        d) If a facility in the modified Library refers to a function or a table of data to be supplied by an application program that uses the facility, other than as an argument passed when the facility is invoked, then you must make a good faith effort to ensure that, in the event an application does not supply such function or table, the facility still operates, and performs whatever part of its purpose remains meaningful.

        (For example, a function in a library to compute square roots has a purpose that is entirely well-defined independent of the application. Therefore, Subsection 2d requires that any application-supplied function or table used by this function must be optional: if the application does not supply it, the square root function must still compute square roots.)

      These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Library, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Library, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it.

      Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Library.

      In addition, mere aggregation of another work not based on the Library with the Library (or with a work based on the Library) on a volume of a storage or distribution medium does not bring the other work under the scope of this License.

      3. You may opt to apply the terms of the ordinary GNU General Public License instead of this License to a given copy of the Library. To do this, you must alter all the notices that refer to this License, so that they refer to the ordinary GNU General Public License, version 2, instead of to this License. (If a newer version than version 2 of the ordinary GNU General Public License has appeared, then you can specify that version instead if you wish.) Do not make any other change in these notices.

      Once this change is made in a given copy, it is irreversible for that copy, so the ordinary GNU General Public License applies to all subsequent copies and derivative works made from that copy.

      This option is useful when you wish to copy part of the code of the Library into a program that is not a library.

      4. You may copy and distribute the Library (or a portion or derivative of it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange.

      If distribution of object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place satisfies the requirement to distribute the source code, even though third parties are not compelled to copy the source along with the object code.

      5. A program that contains no derivative of any portion of the Library, but is designed to work with the Library by being compiled or linked with it, is called a "work that uses the Library". Such a work, in isolation, is not a derivative work of the Library, and therefore falls outside the scope of this License.

      However, linking a "work that uses the Library" with the Library creates an executable that is a derivative of the Library (because it contains portions of the Library), rather than a "work that uses the library". The executable is therefore covered by this License. Section 6 states terms for distribution of such executables.

      When a "work that uses the Library" uses material from a header file that is part of the Library, the object code for the work may be a derivative work of the Library even though the source code is not. Whether this is true is especially significant if the work can be linked without the Library, or if the work is itself a library. The threshold for this to be true is not precisely defined by law.

      If such an object file uses only numerical parameters, data structure layouts and accessors, and small macros and small inline functions (ten lines or less in length), then the use of the object file is unrestricted, regardless of whether it is legally a derivative work. (Executables containing this object code plus portions of the Library will still fall under Section 6.)

      Otherwise, if the work is a derivative of the Library, you may distribute the object code for the work under the terms of Section 6. Any executables containing that work also fall under Section 6, whether or not they are linked directly with the Library itself.

      6. As an exception to the Sections above, you may also compile or link a "work that uses the Library" with the Library to produce a work containing portions of the Library, and distribute that work under terms of your choice, provided that the terms permit modification of the work for the customer's own use and reverse engineering for debugging such modifications.

      You must give prominent notice with each copy of the work that the Library is used in it and that the Library and its use are covered by this License. You must supply a copy of this License. If the work during execution displays copyright notices, you must include the copyright notice for the Library among them, as well as a reference directing the user to the copy of this License. Also, you must do one of these things:

        a) Accompany the work with the complete corresponding machine-readable source code for the Library including whatever changes were used in the work (which must be distributed under Sections 1 and 2 above); and, if the work is an executable linked with the Library, with the complete machine-readable "work that uses the Library", as object code and/or source code, so that the user can modify the Library and then relink to produce a modified executable containing the modified Library. (It is understood that the user who changes the contents of definitions files in the Library will not necessarily be able to recompile the application to use the modified definitions.)

        b) Accompany the work with a written offer, valid for at least three years, to give the same user the materials specified in Subsection 6a, above, for a charge no more than the cost of performing this distribution.

        c) If distribution of the work is made by offering access to copy from a designated place, offer equivalent access to copy the above specified materials from the same place.

        d) Verify that the user has already received a copy of these materials or that you have already sent this user a copy.

      For an executable, the required form of the "work that uses the Library" must include any data and utility programs needed for reproducing the executable from it. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable.

      It may happen that this requirement contradicts the license restrictions of other proprietary libraries that do not normally accompany the operating system. Such a contradiction means you cannot use both them and the Library together in an executable that you distribute.

      7. You may place library facilities that are a work based on the Library side-by-side in a single library together with other library facilities not covered by this License, and distribute such a combined library, provided that the separate distribution of the work based on the Library and of the other library facilities is otherwise permitted, and provided that you do these two things:

        a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities. This must be distributed under the terms of the Sections above.

        b) Give prominent notice with the combined library of the fact that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work.

      8. You may not copy, modify, sublicense, link with, or distribute the Library except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense, link with, or distribute the Library is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance.

      9. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Library or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Library (or any work based on the Library), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Library or works based on it.

      10. Each time you redistribute the Library (or any work based on the Library), the recipient automatically receives a license from the original licensor to copy, distribute, link with or modify the Library subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License.

      11. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Library at all. For example, if a patent license would not permit royalty-free redistribution of the Library by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Library.

      If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply, and the section as a whole is intended to apply in other circumstances.

      It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice.

      This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License.

      12. If the distribution and/or use of the Library is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Library under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License.

      13. The Free Software Foundation may publish revised and/or new versions of the Library General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns.

      Each version is given a distinguishing version number. If the Library specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Library does not specify a license version number, you may choose any version ever published by the Free Software Foundation.

      14. If you wish to incorporate parts of the Library into other free programs whose distribution conditions are incompatible with these, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally.

      NO WARRANTY

      15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.

      16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.

      END OF TERMS AND CONDITIONS

      How to Apply These Terms to Your New Libraries

      If you develop a new library, and you want it to be of the greatest possible use to the public, we recommend making it free software that everyone can redistribute and change. You can do so by permitting redistribution under these terms (or, alternatively, under the terms of the ordinary General Public License).

      To apply these terms, attach the following notices to the library. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found.

        one line to give the library's name and an idea of what it does.
        Copyright (C) year name of author

        This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version.

        This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.

        You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA

      Also add information on how to contact you by electronic and paper mail.

      You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the library, if necessary. Here is a sample; alter the names:

        Yoyodyne, Inc., hereby disclaims all copyright interest in the library `Frob' (a library for tweaking knobs) written by James Random Hacker.

        signature of Ty Coon, 1 April 1990 Ty Coon, President of Vice

      That's all there is to it!


      BRelease Notes

      Changes in Mini-XML 2.7

      • Added 64-bit configurations to the VC++ project files (STR #129)
      • Fixed conformance of mxmldoc's HTML and CSS output.
      • Added data accessor ("get") functions and made the mxml_node_t and mxml_index_t structures private but still available in the Mini-XML header to preserve source compatibility (STR #118)
      • Updated the source headers to reference the Mini-XML license and its exceptions to the LGPL2 (STR #108)
      • Added a new mxmlFindPath() function to find the value node of a named element (STR #110)
      • Building a static version of the library did not work on Windows (STR #112)
      • The shared library did not include a destructor for the thread- specific data key on UNIX-based operating systems (STR #103)
      • mxmlLoad* did not error out on XML with multiple root nodes (STR #101)
      • Fixed an issue with the _mxml_vstrdupf function (STR #107)
      • mxmlSave* no longer write all siblings of the passed node, just that node and its children (STR #109)

      Changes in Mini-XML 2.6

      • Documentation fixes (STR #91, STR #92)
      • The mxmldoc program did not handle typedef comments properly (STR #72)
      • Added support for "long long" printf formats.
      • The XML parser now ignores BOMs in UTF-8 XML files (STR #89)
      • The mxmldoc program now supports generating Xcode documentation sets.
      • mxmlSave*() did not output UTF-8 correctly on some platforms.
      • mxmlNewXML() now adds encoding="utf-8" in the ?xml directive to avoid problems with non-conformant XML parsers that assume something other than UTF-8 as the default encoding.
      • Wrapping was not disabled when mxmlSetWrapMargin(0) was called, and "<?xml ... ?>" was always followed by a newline (STR #76)
      • The mxml.pc.in file was broken (STR #79)
      • The mxmldoc program now handles "typedef enum name {} name" correctly (STR #72)

      Changes in Mini-XML 2.5

      • The mxmldoc program now makes greater use of CSS and supports a --css option to embed an alternate stylesheet.
      • The mxmldoc program now supports --header and --footer options to insert documentation content before and after the generated content.
      • The mxmldoc program now supports a --framed option to generate framed HTML output.
      • The mxmldoc program now creates a table of contents including any headings in the --intro file when generating HTML output.
      • The man pages and man page output from mxmldoc did not use "\-" for dashes (STR #68)
      • The debug version of the Mini-XML DLL could not be built (STR #65)
      • Processing instructions and directives did not work when not at the top level of a document (STR #67)
      • Spaces around the "=" in attributes were not supported (STR #67)

      Changes in Mini-XML 2.4

      • Fixed shared library build problems on HP-UX and Mac OS X.
      • The mxmldoc program did not output argument descriptions for functions properly.
      • All global settings (custom, error, and entity callbacks and the wrap margin) are now managed separately for each thread.
      • Added mxmlElementDeleteAttr() function (STR #59)
      • mxmlElementSetAttrf() did not work (STR #57)
      • mxmlLoad*() incorrectly treated declarations as parent elements (STR #56)
      • mxmlLoad*() incorrectly allowed attributes without values (STR #47)
      • Fixed Visual C++ build problems (STR #49)
      • mxmlLoad*() did not return NULL when an element contained an error (STR #46)
      • Added support for the apos character entity (STR #54)
      • Fixed whitespace detection with Unicode characters (STR #48)
      • mxmlWalkNext() and mxmlWalkPrev() did not work correctly when called with a node with no children as the top node (STR #53)

      Changes in Mini-XML 2.3

      • Added two exceptions to the LGPL to support static linking of applications against Mini-XML
      • The mxmldoc utility can now generate man pages, too.
      • Added a mxmlNewXML() function
      • Added a mxmlElementSetAttrf() function (STR #43)
      • Added a snprintf() emulation function for the test program (STR #32)
      • Added the _CRT_SECURE_NO_DEPRECATE definition when building on VC++ 2005 (STR #36)
      • mxmlLoad*() did not detect missing > characters in elements (STR #41)
      • mxmlLoad*() did not detect missing close tags at the end of an XML document (STR #45)
      • Added user_data and ref_count members to mxml_node_t structure
      • Added mxmlReleaseNode() and mxmlRetainNode() APIs for reference-counted nodes
      • Added mxmlSetWrapMargin() to control the wrapping of XML output
      • Added conditional check for EINTR error code for certain Windows compilers that do not define it (STR #33)
      • The mxmldoc program now generates correct HTML 4.0 output - previously it generated invalid XHTML
      • The mxmldoc program now supports "@deprecated@, "@private@", and "@since version@" comments
      • Fixed function and enumeration type bugs in mxmldoc
      • Fixed the XML schema for mxmldoc
      • The mxmldoc program now supports --intro, --section, and --title options
      • The mxmlLoad*() functions could leak a node on an error (STR #27)
      • The mxml_vsnprintf() function could get in an infinite loop on a buffer overflow (STR #25)
      • Added new mxmlNewCDATA() and mxmlSetCDATA() functions to create and set CDATA nodes, which are really just special element nodes
      • Added new MXML_IGNORE type and MXML_IGNORE_CB callback to ignore non-element nodes, e.g. whitespace
      • mxmlLoad*() did not treat custom data as opaque, so whitespace characters would be lost

      Changes in Mini-XML 2.2.2

      • mxmlLoad*() did not treat custom data as opaque, so whitespace characters would be lost.

      Changes in Mini-XML 2.2.1

      • mxmlLoadFd(), mxmlLoadFile(), and mxmlLoadString() now correctly return NULL on error (STR #21)
      • mxmlNewInteger(), mxmlNewOpaque(), mxmlNewReal(), mxmlNewText(), and mxmlNewTextf() incorrectly required a parent node (STR #22)
      • Fixed an XML output bug in mxmldoc.
      • The "make install" target now uses the install command to set the proper permissions on UNIX/Linux/OSX.
      • Fixed a MingW/Cygwin compilation problem (STR #18)

      Changes in Mini-XML 2.2

      • Added shared library support (STR #17)
      • mxmlLoad*() now returns an error when an XML stream contains illegal control characters (STR #10)
      • mxmlLoad*() now returns an error when an element contains two attributes with the same name in conformance with the XML spec (STR #16)
      • Added support for CDATA (STR #14, STR #15)
      • Updated comment and processing instruction handling - no entity support per XML specification.
      • Added checking for invalid comment termination ("--->" is not allowed)

      Changes in Mini-XML 2.1

      • Added support for custom data nodes (STR #6)
      • Now treat UTF-8 sequences which are longer than necessary as an error (STR #4)
      • Fixed entity number support (STR #8)
      • Fixed mxmlLoadString() bug with UTF-8 (STR #7)
      • Fixed entity lookup bug (STR #5)
      • Added mxmlLoadFd() and mxmlSaveFd() functions.
      • Fixed multi-word UTF-16 handling.

      Changes in Mini-XML 2.0

      • New programmers manual.
      • Added Visual C++ project files for Microsoft Windows users.
      • Added optimizations to mxmldoc, mxmlSaveFile(), and mxmlIndexNew() (STR #2)
      • mxmlEntityAddCallback() now returns an integer status (STR #2)
      • Added UTF-16 support (input only; all output is UTF-8)
      • Added index functions to build a searchable index of XML nodes.
      • Added character entity callback interface to support additional character entities beyond those defined in the XHTML specification.
      • Added support for XHTML character entities.
      • The mxmldoc utility now produces XML output which conforms to an updated XML schema, described in the file "doc/mxmldoc.xsd".
      • Changed the whitespace callback interface to return strings instead of a single character, allowing for greater control over the formatting of XML files written using Mini-XML. THIS CHANGE WILL REQUIRE CHANGES TO YOUR 1.x CODE IF YOU USE WHITESPACE CALLBACKS.
      • The mxmldoc utility now produces XML output which conforms to an updated XML schema, described in the file "doc/mxmldoc.xsd".
      • Changed the whitespace callback interface to return strings instead of a single character, allowing for greater control over the formatting of XML files written using Mini-XML. THIS CHANGE WILL REQUIRE CHANGES TO YOUR 1.x CODE IF YOU USE WHITESPACE CALLBACKS.
      • The mxmldoc utility is now capable of documenting C++ classes, functions, and structures, and correctly handles C++ comments.
      • Added new modular tests for mxmldoc.
      • Updated the mxmldoc output to be more compatible with embedding in manuals produced with HTMLDOC.
      • The makefile incorrectly included a "/" separator between the destination path and install path. This caused problems when building and installing with MingW.

      Changes in Mini-XML 1.3

      • Fixes for mxmldoc.
      • Added support for reading standard HTML entity names.
      • mxmlLoadString/File() did not decode character entities in element names, attribute names, or attribute values.
      • mxmlLoadString/File() would crash when loading non- conformant XML data under an existing parent (top) node.
      • Fixed several bugs in the mxmldoc utility.
      • Added new error callback function to catch a variety of errors and log them to someplace other than stderr.
      • The mxmlElementSetAttr() function now allows for NULL attribute values.
      • The load and save functions now properly handle quoted element and attribute name strings properly, e.g. for !DOCTYPE declarations.

      Changes in Mini-XML 1.2

      • Added new "set" methods to set the value of a node.
      • Added new formatted text methods mxmlNewTextf() and mxmlSetTextf() to create/set a text node value using printf-style formats.
      • Added new standard callbacks for use with the mxmlLoad functions.
      • Updated the HTML documentation to include examples of the walk and load function output.
      • Added --with/without-ansi configure option to control the strdup() function check.
      • Added --with/without-snprintf configure option to control the snprintf() and vsnprintf() function checks.

      Changes in Mini-XML 1.1.2

      • The mxml(3) man page wasn't updated for the string functions.
      • mxmlSaveString() returned the wrong number of characters.
      • mxml_add_char() updated the buffer pointer in the wrong place.

      Changes in Mini-XML 1.1.1

      • The private mxml_add_ch() function did not update the start-of-buffer pointer which could cause a crash when using mxmlSaveString().
      • The private mxml_write_ws() function called putc() instead of using the proper callback which could cause a crash when using mxmlSaveString().
      • Added a mxmlSaveAllocString() convenience function for saving an XML node tree to an allocated string.

      Changes in Mini-XML 1.1

      • The mxmlLoadFile() function now uses dynamically allocated string buffers for element names, attribute names, and attribute values. Previously they were capped at 16383, 255, and 255 bytes, respectively.
      • Added a new mxmlLoadString() function for loading an XML node tree from a string.
      • Added a new mxmlSaveString() function for saving an XML node tree to a string.
      • Add emulation of strdup() if the local platform does not provide the function.

      Changes in Mini-XML 1.0

      • The mxmldoc program now handles function arguments, structures, unions, enumerations, classes, and typedefs properly.
      • Documentation provided via mxmldoc and more in-line comments in the code.
      • Added man pages and packaging files.

      Changes in Mini-XML 0.93

      • New mxmldoc example program that is also used to create and update code documentation using XML and produce HTML reference pages.
      • Added mxmlAdd() and mxmlRemove() functions to add and remove nodes from a tree. This provides more flexibility over where the nodes are inserted and allows nodes to be moved within the tree as needed.
      • mxmlLoadFile() now correctly handles comments.
      • mxmlLoadFile() now supports the required "gt", "quot", and "nbsp" character entities.
      • mxmlSaveFile() now uses newlines as whitespace when valid to do so.
      • mxmlFindElement() now also takes attribute name and attribute value string arguments to limit the search to specific elements with attributes and/or values.
      • NULL pointers can be used as "wildcards".
      • Added uninstall target to makefile, and auto-reconfig if Makefile.in or configure.in are changed.
      • mxmlFindElement(), mxmlWalkNext(), and mxmlWalkPrev() now all provide "descend" arguments to control whether they descend into child nodes in the tree.
      • Fixed some whitespace issues in mxmlLoadFile().
      • Fixed Unicode output and whitespace issues in mxmlSaveFile().
      • mxmlSaveFile() now supports a whitespace callback to provide more human-readable XML output under program control.

      Changes in Mini-XML 0.92

      • mxmlSaveFile() didn't return a value on success.

      Changes in Mini-XML 0.91

      • mxmlWalkNext() would go into an infinite loop.

      Changes in Mini-XML 0.9

      • Initial public release.

      CLibrary Reference

      Contents

      Functions

      mxmlAdd

      Add a node to a tree.

      void mxmlAdd (
          mxml_node_t *parent,
          int where,
          mxml_node_t *child,
          mxml_node_t *node
      );

      Parameters

      parent
      Parent node
      where
      Where to add, MXML_ADD_BEFORE or MXML_ADD_AFTER
      child
      Child node for where or MXML_ADD_TO_PARENT
      node
      Node to add

      Discussion

      Adds the specified node to the parent. If the child argument is not NULL, puts the new node before or after the specified child depending on the value of the where argument. If the child argument is NULL, puts the new node at the beginning of the child list (MXML_ADD_BEFORE) or at the end of the child list (MXML_ADD_AFTER). The constant MXML_ADD_TO_PARENT can be used to specify a NULL child pointer.

      mxmlDelete

      Delete a node and all of its children.

      void mxmlDelete (
          mxml_node_t *node
      );

      Parameters

      node
      Node to delete

      Discussion

      If the specified node has a parent, this function first removes the node from its parent using the mxmlRemove() function.

       Mini-XML 2.4 mxmlElementDeleteAttr

      Delete an attribute.

      void mxmlElementDeleteAttr (
          mxml_node_t *node,
          const char *name
      );

      Parameters

      node
      Element
      name
      Attribute name

      mxmlElementGetAttr

      Get an attribute.

      const char *mxmlElementGetAttr (
          mxml_node_t *node,
          const char *name
      );

      Parameters

      node
      Element node
      name
      Name of attribute

      Return Value

      Attribute value or NULL

      Discussion

      This function returns NULL if the node is not an element or the named attribute does not exist.

      mxmlElementSetAttr

      Set an attribute.

      void mxmlElementSetAttr (
          mxml_node_t *node,
          const char *name,
          const char *value
      );

      Parameters

      node
      Element node
      name
      Name of attribute
      value
      Attribute value

      Discussion

      If the named attribute already exists, the value of the attribute is replaced by the new string value. The string value is copied into the element node. This function does nothing if the node is not an element.

       Mini-XML 2.3 mxmlElementSetAttrf

      Set an attribute with a formatted value.

      void mxmlElementSetAttrf (
          mxml_node_t *node,
          const char *name,
          const char *format,
          ...
      );

      Parameters

      node
      Element node
      name
      Name of attribute
      format
      Printf-style attribute value
      ...
      Additional arguments as needed

      Discussion

      If the named attribute already exists, the value of the attribute is replaced by the new formatted string. The formatted string value is copied into the element node. This function does nothing if the node is not an element.

      mxmlEntityAddCallback

      Add a callback to convert entities to Unicode.

      int mxmlEntityAddCallback (
          mxml_entity_cb_t cb
      );

      Parameters

      cb
      Callback function to add

      Return Value

      0 on success, -1 on failure

      mxmlEntityGetName

      Get the name that corresponds to the character value.

      const char *mxmlEntityGetName (
          int val
      );

      Parameters

      val
      Character value

      Return Value

      Entity name or NULL

      Discussion

      If val does not need to be represented by a named entity, NULL is returned.

      mxmlEntityGetValue

      Get the character corresponding to a named entity.

      int mxmlEntityGetValue (
          const char *name
      );

      Parameters

      name
      Entity name

      Return Value

      Character value or -1 on error

      Discussion

      The entity name can also be a numeric constant. -1 is returned if the name is not known.

      mxmlEntityRemoveCallback

      Remove a callback.

      void mxmlEntityRemoveCallback (
          mxml_entity_cb_t cb
      );

      Parameters

      cb
      Callback function to remove

      mxmlFindElement

      Find the named element.

      mxml_node_t *mxmlFindElement (
          mxml_node_t *node,
          mxml_node_t *top,
          const char *name,
          const char *attr,
          const char *value,
          int descend
      );

      Parameters

      node
      Current node
      top
      Top node
      name
      Element name or NULL for any
      attr
      Attribute name, or NULL for none
      value
      Attribute value, or NULL for any
      descend
      Descend into tree - MXML_DESCEND, MXML_NO_DESCEND, or MXML_DESCEND_FIRST

      Return Value

      Element node or NULL

      Discussion

      The search is constrained by the name, attribute name, and value; any NULL names or values are treated as wildcards, so different kinds of searches can be implemented by looking for all elements of a given name or all elements with a specific attribute. The descend argument determines whether the search descends into child nodes; normally you will use MXML_DESCEND_FIRST for the initial search and MXML_NO_DESCEND to find additional direct descendents of the node. The top node argument constrains the search to a particular node's children.

       Mini-XML 2.7 mxmlFindPath

      Find a node with the given path.

      mxml_node_t *mxmlFindPath (
          mxml_node_t *top,
          const char *path
      );

      Parameters

      top
      Top node
      path
      Path to element

      Return Value

      Found node or NULL

      Discussion

      The "path" is a slash-separated list of element names. The name "*" is considered a wildcard for one or more levels of elements. For example, "foo/one/two", "bar/two/one", "*/one", and so forth.

      The first child node of the found node is returned if the given node has children and the first child is a value node.

       Mini-XML 2.7 mxmlGetCDATA

      Get the value for a CDATA node.

      const char *mxmlGetCDATA (
          mxml_node_t *node
      );

      Parameters

      node
      Node to get

      Return Value

      CDATA value or NULL

      Discussion

      NULL is returned if the node is not a CDATA element.

       Mini-XML 2.7 mxmlGetCustom

      Get the value for a custom node.

      const void *mxmlGetCustom (
          mxml_node_t *node
      );

      Parameters

      node
      Node to get

      Return Value

      Custom value or NULL

      Discussion

      NULL is returned if the node (or its first child) is not a custom value node.

       Mini-XML 2.7 mxmlGetElement

      Get the name for an element node.

      const char *mxmlGetElement (
          mxml_node_t *node
      );

      Parameters

      node
      Node to get

      Return Value

      Element name or NULL

      Discussion

      NULL is returned if the node is not an element node.

       Mini-XML 2.7 mxmlGetFirstChild

      Get the first child of an element node.

      mxml_node_t *mxmlGetFirstChild (
          mxml_node_t *node
      );

      Parameters

      node
      Node to get

      Return Value

      First child or NULL

      Discussion

      NULL is returned if the node is not an element node or if the node has no children.

       Mini-XML 2.7 mxmlGetInteger

      Get the integer value from the specified node or its first child.

      int mxmlGetInteger (
          mxml_node_t *node
      );

      Parameters

      node
      Node to get

      Return Value

      Integer value or 0

      Discussion

      0 is returned if the node (or its first child) is not an integer value node.

       Mini-XML 2.7 mxmlGetLastChild

      Get the last child of an element node.

      mxml_node_t *mxmlGetLastChild (
          mxml_node_t *node
      );

      Parameters

      node
      Node to get

      Return Value

      Last child or NULL

      Discussion

      NULL is returned if the node is not an element node or if the node has no children.

      mxmlGetNextSibling

      Return the node type...

      mxml_node_t *mxmlGetNextSibling (
          mxml_node_t *node
      );

      Parameters

      node
      Node to get

      Return Value

      Get the next node for the current parent.

      NULL is returned if this is the last child for the current parent.

       Mini-XML 2.7 mxmlGetOpaque

      Get an opaque string value for a node or its first child.

      const char *mxmlGetOpaque (
          mxml_node_t *node
      );

      Parameters

      node
      Node to get

      Return Value

      Opaque string or NULL

      Discussion

      NULL is returned if the node (or its first child) is not an opaque value node.

       Mini-XML 2.7 mxmlGetParent

      Get the parent node.

      mxml_node_t *mxmlGetParent (
          mxml_node_t *node
      );

      Parameters

      node
      Node to get

      Return Value

      Parent node or NULL

      Discussion

      NULL is returned for a root node.

       Mini-XML 2.7 mxmlGetPrevSibling

      Get the previous node for the current parent.

      mxml_node_t *mxmlGetPrevSibling (
          mxml_node_t *node
      );

      Parameters

      node
      Node to get

      Return Value

      Previous node or NULL

      Discussion

      NULL is returned if this is the first child for the current parent.

       Mini-XML 2.7 mxmlGetReal

      Get the real value for a node or its first child.

      double mxmlGetReal (
          mxml_node_t *node
      );

      Parameters

      node
      Node to get

      Return Value

      Real value or 0.0

      Discussion

      0.0 is returned if the node (or its first child) is not a real value node.

       Mini-XML 2.7 mxmlGetRefCount

      Get the current reference (use) count for a node.

      int mxmlGetRefCount (
          mxml_node_t *node
      );

      Parameters

      node
      Node

      Return Value

      Reference count

      Discussion

      The initial reference count of new nodes is 1. Use the mxmlRetain and mxmlRelease functions to increment and decrement a node's reference count. .

       Mini-XML 2.7 mxmlGetText

      Get the text value for a node or its first child.

      const char *mxmlGetText (
          mxml_node_t *node,
          int *whitespace
      );

      Parameters

      node
      Node to get
      whitespace
      1 if string is preceded by whitespace, 0 otherwise

      Return Value

      Text string or NULL

      Discussion

      NULL is returned if the node (or its first child) is not a text node. The "whitespace" argument can be NULL.

       Mini-XML 2.7 mxmlGetType

      Get the node type.

      mxml_type_t mxmlGetType (
          mxml_node_t *node
      );

      Parameters

      node
      Node to get

      Return Value

      Type of node

      Discussion

      MXML_IGNORE is returned if "node" is NULL.

       Mini-XML 2.7 mxmlGetUserData

      Get the user data pointer for a node.

      void *mxmlGetUserData (
          mxml_node_t *node
      );

      Parameters

      node
      Node to get

      Return Value

      User data pointer

      mxmlIndexDelete

      Delete an index.

      void mxmlIndexDelete (
          mxml_index_t *ind
      );

      Parameters

      ind
      Index to delete

      mxmlIndexEnum

      Return the next node in the index.

      mxml_node_t *mxmlIndexEnum (
          mxml_index_t *ind
      );

      Parameters

      ind
      Index to enumerate

      Return Value

      Next node or NULL if there is none

      Discussion

      Nodes are returned in the sorted order of the index.

      mxmlIndexFind

      Find the next matching node.

      mxml_node_t *mxmlIndexFind (
          mxml_index_t *ind,
          const char *element,
          const char *value
      );

      Parameters

      ind
      Index to search
      element
      Element name to find, if any
      value
      Attribute value, if any

      Return Value

      Node or NULL if none found

      Discussion

      You should call mxmlIndexReset() prior to using this function for the first time with a particular set of "element" and "value" strings. Passing NULL for both "element" and "value" is equivalent to calling mxmlIndexEnum().

       Mini-XML 2.7 mxmlIndexGetCount

      Get the number of nodes in an index.

      int mxmlIndexGetCount (
          mxml_index_t *ind
      );

      Parameters

      ind
      Index of nodes

      Return Value

      Number of nodes in index

      mxmlIndexNew

      Create a new index.

      mxml_index_t *mxmlIndexNew (
          mxml_node_t *node,
          const char *element,
          const char *attr
      );

      Parameters

      node
      XML node tree
      element
      Element to index or NULL for all
      attr
      Attribute to index or NULL for none

      Return Value

      New index

      Discussion

      The index will contain all nodes that contain the named element and/or attribute. If both "element" and "attr" are NULL, then the index will contain a sorted list of the elements in the node tree. Nodes are sorted by element name and optionally by attribute value if the "attr" argument is not NULL.

      mxmlIndexReset

      Reset the enumeration/find pointer in the index and return the first node in the index.

      mxml_node_t *mxmlIndexReset (
          mxml_index_t *ind
      );

      Parameters

      ind
      Index to reset

      Return Value

      First node or NULL if there is none

      Discussion

      This function should be called prior to using mxmlIndexEnum() or mxmlIndexFind() for the first time.

      mxmlLoadFd

      Load a file descriptor into an XML node tree.

      mxml_node_t *mxmlLoadFd (
          mxml_node_t *top,
          int fd,
          mxml_load_cb_t cb
      );

      Parameters

      top
      Top node
      fd
      File descriptor to read from
      cb
      Callback function or MXML_NO_CALLBACK

      Return Value

      First node or NULL if the file could not be read.

      Discussion

      The nodes in the specified file are added to the specified top node. If no top node is provided, the XML file MUST be well-formed with a single parent node like <?xml> for the entire file. The callback function returns the value type that should be used for child nodes. If MXML_NO_CALLBACK is specified then all child nodes will be either MXML_ELEMENT or MXML_TEXT nodes.

      The constants MXML_INTEGER_CALLBACK, MXML_OPAQUE_CALLBACK, MXML_REAL_CALLBACK, and MXML_TEXT_CALLBACK are defined for loading child nodes of the specified type.

      mxmlLoadFile

      Load a file into an XML node tree.

      mxml_node_t *mxmlLoadFile (
          mxml_node_t *top,
          FILE *fp,
          mxml_load_cb_t cb
      );

      Parameters

      top
      Top node
      fp
      File to read from
      cb
      Callback function or MXML_NO_CALLBACK

      Return Value

      First node or NULL if the file could not be read.

      Discussion

      The nodes in the specified file are added to the specified top node. If no top node is provided, the XML file MUST be well-formed with a single parent node like <?xml> for the entire file. The callback function returns the value type that should be used for child nodes. If MXML_NO_CALLBACK is specified then all child nodes will be either MXML_ELEMENT or MXML_TEXT nodes.

      The constants MXML_INTEGER_CALLBACK, MXML_OPAQUE_CALLBACK, MXML_REAL_CALLBACK, and MXML_TEXT_CALLBACK are defined for loading child nodes of the specified type.

      mxmlLoadString

      Load a string into an XML node tree.

      mxml_node_t *mxmlLoadString (
          mxml_node_t *top,
          const char *s,
          mxml_load_cb_t cb
      );

      Parameters

      top
      Top node
      s
      String to load
      cb
      Callback function or MXML_NO_CALLBACK

      Return Value

      First node or NULL if the string has errors.

      Discussion

      The nodes in the specified string are added to the specified top node. If no top node is provided, the XML string MUST be well-formed with a single parent node like <?xml> for the entire string. The callback function returns the value type that should be used for child nodes. If MXML_NO_CALLBACK is specified then all child nodes will be either MXML_ELEMENT or MXML_TEXT nodes.

      The constants MXML_INTEGER_CALLBACK, MXML_OPAQUE_CALLBACK, MXML_REAL_CALLBACK, and MXML_TEXT_CALLBACK are defined for loading child nodes of the specified type.

       Mini-XML 2.3 mxmlNewCDATA

      Create a new CDATA node.

      mxml_node_t *mxmlNewCDATA (
          mxml_node_t *parent,
          const char *data
      );

      Parameters

      parent
      Parent node or MXML_NO_PARENT
      data
      Data string

      Return Value

      New node

      Discussion

      The new CDATA node is added to the end of the specified parent's child list. The constant MXML_NO_PARENT can be used to specify that the new CDATA node has no parent. The data string must be nul-terminated and is copied into the new node. CDATA nodes use the MXML_ELEMENT type.

       Mini-XML 2.1 mxmlNewCustom

      Create a new custom data node.

      mxml_node_t *mxmlNewCustom (
          mxml_node_t *parent,
          void *data,
          mxml_custom_destroy_cb_t destroy
      );

      Parameters

      parent
      Parent node or MXML_NO_PARENT
      data
      Pointer to data
      destroy
      Function to destroy data

      Return Value

      New node

      Discussion

      The new custom node is added to the end of the specified parent's child list. The constant MXML_NO_PARENT can be used to specify that the new element node has no parent. NULL can be passed when the data in the node is not dynamically allocated or is separately managed.

      mxmlNewElement

      Create a new element node.

      mxml_node_t *mxmlNewElement (
          mxml_node_t *parent,
          const char *name
      );

      Parameters

      parent
      Parent node or MXML_NO_PARENT
      name
      Name of element

      Return Value

      New node

      Discussion

      The new element node is added to the end of the specified parent's child list. The constant MXML_NO_PARENT can be used to specify that the new element node has no parent.

      mxmlNewInteger

      Create a new integer node.

      mxml_node_t *mxmlNewInteger (
          mxml_node_t *parent,
          int integer
      );

      Parameters

      parent
      Parent node or MXML_NO_PARENT
      integer
      Integer value

      Return Value

      New node

      Discussion

      The new integer node is added to the end of the specified parent's child list. The constant MXML_NO_PARENT can be used to specify that the new integer node has no parent.

      mxmlNewOpaque

      Create a new opaque string.

      mxml_node_t *mxmlNewOpaque (
          mxml_node_t *parent,
          const char *opaque
      );

      Parameters

      parent
      Parent node or MXML_NO_PARENT
      opaque
      Opaque string

      Return Value

      New node

      Discussion

      The new opaque node is added to the end of the specified parent's child list. The constant MXML_NO_PARENT can be used to specify that the new opaque node has no parent. The opaque string must be nul-terminated and is copied into the new node.

      mxmlNewReal

      Create a new real number node.

      mxml_node_t *mxmlNewReal (
          mxml_node_t *parent,
          double real
      );

      Parameters

      parent
      Parent node or MXML_NO_PARENT
      real
      Real number value

      Return Value

      New node

      Discussion

      The new real number node is added to the end of the specified parent's child list. The constant MXML_NO_PARENT can be used to specify that the new real number node has no parent.

      mxmlNewText

      Create a new text fragment node.

      mxml_node_t *mxmlNewText (
          mxml_node_t *parent,
          int whitespace,
          const char *string
      );

      Parameters

      parent
      Parent node or MXML_NO_PARENT
      whitespace
      1 = leading whitespace, 0 = no whitespace
      string
      String

      Return Value

      New node

      Discussion

      The new text node is added to the end of the specified parent's child list. The constant MXML_NO_PARENT can be used to specify that the new text node has no parent. The whitespace parameter is used to specify whether leading whitespace is present before the node. The text string must be nul-terminated and is copied into the new node.

      mxmlNewTextf

      Create a new formatted text fragment node.

      mxml_node_t *mxmlNewTextf (
          mxml_node_t *parent,
          int whitespace,
          const char *format,
          ...
      );

      Parameters

      parent
      Parent node or MXML_NO_PARENT
      whitespace
      1 = leading whitespace, 0 = no whitespace
      format
      Printf-style frmat string
      ...
      Additional args as needed

      Return Value

      New node

      Discussion

      The new text node is added to the end of the specified parent's child list. The constant MXML_NO_PARENT can be used to specify that the new text node has no parent. The whitespace parameter is used to specify whether leading whitespace is present before the node. The format string must be nul-terminated and is formatted into the new node.

       Mini-XML 2.3 mxmlNewXML

      Create a new XML document tree.

      mxml_node_t *mxmlNewXML (
          const char *version
      );

      Parameters

      version
      Version number to use

      Return Value

      New ?xml node

      Discussion

      The "version" argument specifies the version number to put in the ?xml element node. If NULL, version 1.0 is assumed.

       Mini-XML 2.3 mxmlRelease

      Release a node.

      int mxmlRelease (
          mxml_node_t *node
      );

      Parameters

      node
      Node

      Return Value

      New reference count

      Discussion

      When the reference count reaches zero, the node (and any children) is deleted via mxmlDelete().

      mxmlRemove

      Remove a node from its parent.

      void mxmlRemove (
          mxml_node_t *node
      );

      Parameters

      node
      Node to remove

      Discussion

      Does not free memory used by the node - use mxmlDelete() for that. This function does nothing if the node has no parent.

       Mini-XML 2.3 mxmlRetain

      Retain a node.

      int mxmlRetain (
          mxml_node_t *node
      );

      Parameters

      node
      Node

      Return Value

      New reference count

       Mini-XML 2.3 mxmlSAXLoadFd

      Load a file descriptor into an XML node tree using a SAX callback.

      mxml_node_t *mxmlSAXLoadFd (
          mxml_node_t *top,
          int fd,
          mxml_load_cb_t cb,
          mxml_sax_cb_t sax_cb,
          void *sax_data
      );

      Parameters

      top
      Top node
      fd
      File descriptor to read from
      cb
      Callback function or MXML_NO_CALLBACK
      sax_cb
      SAX callback or MXML_NO_CALLBACK
      sax_data
      SAX user data

      Return Value

      First node or NULL if the file could not be read.

      Discussion

      The nodes in the specified file are added to the specified top node. If no top node is provided, the XML file MUST be well-formed with a single parent node like <?xml> for the entire file. The callback function returns the value type that should be used for child nodes. If MXML_NO_CALLBACK is specified then all child nodes will be either MXML_ELEMENT or MXML_TEXT nodes.

      The constants MXML_INTEGER_CALLBACK, MXML_OPAQUE_CALLBACK, MXML_REAL_CALLBACK, and MXML_TEXT_CALLBACK are defined for loading child nodes of the specified type.

      The SAX callback must call mxmlRetain() for any nodes that need to be kept for later use. Otherwise, nodes are deleted when the parent node is closed or after each data, comment, CDATA, or directive node.

       Mini-XML 2.3 mxmlSAXLoadFile

      Load a file into an XML node tree using a SAX callback.

      mxml_node_t *mxmlSAXLoadFile (
          mxml_node_t *top,
          FILE *fp,
          mxml_load_cb_t cb,
          mxml_sax_cb_t sax_cb,
          void *sax_data
      );

      Parameters

      top
      Top node
      fp
      File to read from
      cb
      Callback function or MXML_NO_CALLBACK
      sax_cb
      SAX callback or MXML_NO_CALLBACK
      sax_data
      SAX user data

      Return Value

      First node or NULL if the file could not be read.

      Discussion

      The nodes in the specified file are added to the specified top node. If no top node is provided, the XML file MUST be well-formed with a single parent node like <?xml> for the entire file. The callback function returns the value type that should be used for child nodes. If MXML_NO_CALLBACK is specified then all child nodes will be either MXML_ELEMENT or MXML_TEXT nodes.

      The constants MXML_INTEGER_CALLBACK, MXML_OPAQUE_CALLBACK, MXML_REAL_CALLBACK, and MXML_TEXT_CALLBACK are defined for loading child nodes of the specified type.

      The SAX callback must call mxmlRetain() for any nodes that need to be kept for later use. Otherwise, nodes are deleted when the parent node is closed or after each data, comment, CDATA, or directive node.

       Mini-XML 2.3 mxmlSAXLoadString

      Load a string into an XML node tree using a SAX callback.

      mxml_node_t *mxmlSAXLoadString (
          mxml_node_t *top,
          const char *s,
          mxml_load_cb_t cb,
          mxml_sax_cb_t sax_cb,
          void *sax_data
      );

      Parameters

      top
      Top node
      s
      String to load
      cb
      Callback function or MXML_NO_CALLBACK
      sax_cb
      SAX callback or MXML_NO_CALLBACK
      sax_data
      SAX user data

      Return Value

      First node or NULL if the string has errors.

      Discussion

      The nodes in the specified string are added to the specified top node. If no top node is provided, the XML string MUST be well-formed with a single parent node like <?xml> for the entire string. The callback function returns the value type that should be used for child nodes. If MXML_NO_CALLBACK is specified then all child nodes will be either MXML_ELEMENT or MXML_TEXT nodes.

      The constants MXML_INTEGER_CALLBACK, MXML_OPAQUE_CALLBACK, MXML_REAL_CALLBACK, and MXML_TEXT_CALLBACK are defined for loading child nodes of the specified type.

      The SAX callback must call mxmlRetain() for any nodes that need to be kept for later use. Otherwise, nodes are deleted when the parent node is closed or after each data, comment, CDATA, or directive node.

      mxmlSaveAllocString

      Save an XML tree to an allocated string.

      char *mxmlSaveAllocString (
          mxml_node_t *node,
          mxml_save_cb_t cb
      );

      Parameters

      node
      Node to write
      cb
      Whitespace callback or MXML_NO_CALLBACK

      Return Value

      Allocated string or NULL

      Discussion

      This function returns a pointer to a string containing the textual representation of the XML node tree. The string should be freed using the free() function when you are done with it. NULL is returned if the node would produce an empty string or if the string cannot be allocated.

      The callback argument specifies a function that returns a whitespace string or NULL before and after each element. If MXML_NO_CALLBACK is specified, whitespace will only be added before MXML_TEXT nodes with leading whitespace and before attribute names inside opening element tags.

      mxmlSaveFd

      Save an XML tree to a file descriptor.

      int mxmlSaveFd (
          mxml_node_t *node,
          int fd,
          mxml_save_cb_t cb
      );

      Parameters

      node
      Node to write
      fd
      File descriptor to write to
      cb
      Whitespace callback or MXML_NO_CALLBACK

      Return Value

      0 on success, -1 on error.

      Discussion

      The callback argument specifies a function that returns a whitespace string or NULL before and after each element. If MXML_NO_CALLBACK is specified, whitespace will only be added before MXML_TEXT nodes with leading whitespace and before attribute names inside opening element tags.

      mxmlSaveFile

      Save an XML tree to a file.

      int mxmlSaveFile (
          mxml_node_t *node,
          FILE *fp,
          mxml_save_cb_t cb
      );

      Parameters

      node
      Node to write
      fp
      File to write to
      cb
      Whitespace callback or MXML_NO_CALLBACK

      Return Value

      0 on success, -1 on error.

      Discussion

      The callback argument specifies a function that returns a whitespace string or NULL before and after each element. If MXML_NO_CALLBACK is specified, whitespace will only be added before MXML_TEXT nodes with leading whitespace and before attribute names inside opening element tags.

      mxmlSaveString

      Save an XML node tree to a string.

      int mxmlSaveString (
          mxml_node_t *node,
          char *buffer,
          int bufsize,
          mxml_save_cb_t cb
      );

      Parameters

      node
      Node to write
      buffer
      String buffer
      bufsize
      Size of string buffer
      cb
      Whitespace callback or MXML_NO_CALLBACK

      Return Value

      Size of string

      Discussion

      This function returns the total number of bytes that would be required for the string but only copies (bufsize - 1) characters into the specified buffer.

      The callback argument specifies a function that returns a whitespace string or NULL before and after each element. If MXML_NO_CALLBACK is specified, whitespace will only be added before MXML_TEXT nodes with leading whitespace and before attribute names inside opening element tags.

       Mini-XML 2.3 mxmlSetCDATA

      Set the element name of a CDATA node.

      int mxmlSetCDATA (
          mxml_node_t *node,
          const char *data
      );

      Parameters

      node
      Node to set
      data
      New data string

      Return Value

      0 on success, -1 on failure

      Discussion

      The node is not changed if it (or its first child) is not a CDATA element node.

       Mini-XML 2.1 mxmlSetCustom

      Set the data and destructor of a custom data node.

      int mxmlSetCustom (
          mxml_node_t *node,
          void *data,
          mxml_custom_destroy_cb_t destroy
      );

      Parameters

      node
      Node to set
      data
      New data pointer
      destroy
      New destructor function

      Return Value

      0 on success, -1 on failure

      Discussion

      The node is not changed if it (or its first child) is not a custom node.

      mxmlSetCustomHandlers

      Set the handling functions for custom data.

      void mxmlSetCustomHandlers (
          mxml_custom_load_cb_t load,
          mxml_custom_save_cb_t save
      );

      Parameters

      load
      Load function
      save
      Save function

      Discussion

      The load function accepts a node pointer and a data string and must return 0 on success and non-zero on error.

      The save function accepts a node pointer and must return a malloc'd string on success and NULL on error.

      mxmlSetElement

      Set the name of an element node.

      int mxmlSetElement (
          mxml_node_t *node,
          const char *name
      );

      Parameters

      node
      Node to set
      name
      New name string

      Return Value

      0 on success, -1 on failure

      Discussion

      The node is not changed if it is not an element node.

      mxmlSetErrorCallback

      Set the error message callback.

      void mxmlSetErrorCallback (
          mxml_error_cb_t cb
      );

      Parameters

      cb
      Error callback function

      mxmlSetInteger

      Set the value of an integer node.

      int mxmlSetInteger (
          mxml_node_t *node,
          int integer
      );

      Parameters

      node
      Node to set
      integer
      Integer value

      Return Value

      0 on success, -1 on failure

      Discussion

      The node is not changed if it (or its first child) is not an integer node.

      mxmlSetOpaque

      Set the value of an opaque node.

      int mxmlSetOpaque (
          mxml_node_t *node,
          const char *opaque
      );

      Parameters

      node
      Node to set
      opaque
      Opaque string

      Return Value

      0 on success, -1 on failure

      Discussion

      The node is not changed if it (or its first child) is not an opaque node.

      mxmlSetReal

      Set the value of a real number node.

      int mxmlSetReal (
          mxml_node_t *node,
          double real
      );

      Parameters

      node
      Node to set
      real
      Real number value

      Return Value

      0 on success, -1 on failure

      Discussion

      The node is not changed if it (or its first child) is not a real number node.

      mxmlSetText

      Set the value of a text node.

      int mxmlSetText (
          mxml_node_t *node,
          int whitespace,
          const char *string
      );

      Parameters

      node
      Node to set
      whitespace
      1 = leading whitespace, 0 = no whitespace
      string
      String

      Return Value

      0 on success, -1 on failure

      Discussion

      The node is not changed if it (or its first child) is not a text node.

      mxmlSetTextf

      Set the value of a text node to a formatted string.

      int mxmlSetTextf (
          mxml_node_t *node,
          int whitespace,
          const char *format,
          ...
      );

      Parameters

      node
      Node to set
      whitespace
      1 = leading whitespace, 0 = no whitespace
      format
      Printf-style format string
      ...
      Additional arguments as needed

      Return Value

      0 on success, -1 on failure

      Discussion

      The node is not changed if it (or its first child) is not a text node.

       Mini-XML 2.7 mxmlSetUserData

      Set the user data pointer for a node.

      int mxmlSetUserData (
          mxml_node_t *node,
          void *data
      );

      Parameters

      node
      Node to set
      data
      User data pointer

      Return Value

      0 on success, -1 on failure

       Mini-XML 2.3 mxmlSetWrapMargin

      Set the wrap margin when saving XML data.

      void mxmlSetWrapMargin (
          int column
      );

      Parameters

      column
      Column for wrapping, 0 to disable wrapping

      Discussion

      Wrapping is disabled when "column" is 0.

      mxmlWalkNext

      Walk to the next logical node in the tree.

      mxml_node_t *mxmlWalkNext (
          mxml_node_t *node,
          mxml_node_t *top,
          int descend
      );

      Parameters

      node
      Current node
      top
      Top node
      descend
      Descend into tree - MXML_DESCEND, MXML_NO_DESCEND, or MXML_DESCEND_FIRST

      Return Value

      Next node or NULL

      Discussion

      The descend argument controls whether the first child is considered to be the next node. The top node argument constrains the walk to the node's children.

      mxmlWalkPrev

      Walk to the previous logical node in the tree.

      mxml_node_t *mxmlWalkPrev (
          mxml_node_t *node,
          mxml_node_t *top,
          int descend
      );

      Parameters

      node
      Current node
      top
      Top node
      descend
      Descend into tree - MXML_DESCEND, MXML_NO_DESCEND, or MXML_DESCEND_FIRST

      Return Value

      Previous node or NULL

      Discussion

      The descend argument controls whether the previous node's last child is considered to be the previous node. The top node argument constrains the walk to the node's children.

      Data Types

      mxml_custom_destroy_cb_t

      Custom data destructor

      typedef void (*mxml_custom_destroy_cb_t)(void *);

      mxml_custom_load_cb_t

      Custom data load callback function

      typedef int (*mxml_custom_load_cb_t)( mxml_node_t *, const char *);

      mxml_custom_save_cb_t

      Custom data save callback function

      typedef char *(*mxml_custom_save_cb_t)( mxml_node_t *);

      mxml_entity_cb_t

      Entity callback function

      typedef int (*mxml_entity_cb_t)(const char *);

      mxml_error_cb_t

      Error callback function

      typedef void (*mxml_error_cb_t)(const char *);

      mxml_index_t

      An XML node index.

      typedef struct mxml_index_s mxml_index_t;

      mxml_load_cb_t

      Load callback function

      typedef mxml_type_t (*mxml_load_cb_t)(mxml_node_t *);

      mxml_node_t

      An XML node.

      typedef struct mxml_node_s mxml_node_t;

      mxml_save_cb_t

      Save callback function

      typedef const char *(*mxml_save_cb_t)( mxml_node_t *, int);

      mxml_sax_cb_t

      SAX callback function

      typedef void (*mxml_sax_cb_t)( mxml_node_t *, mxml_sax_event_t, void *);

      mxml_sax_event_t

      SAX event type.

      typedef enum mxml_sax_event_e mxml_sax_event_t;

      mxml_type_t

      The XML node type.

      typedef enum mxml_type_e mxml_type_t;

      Constants

      mxml_sax_event_e

      SAX event type.

      Constants

      MXML_SAX_CDATA
      CDATA node
      MXML_SAX_COMMENT
      Comment node
      MXML_SAX_DATA
      Data node
      MXML_SAX_DIRECTIVE
      Processing directive node
      MXML_SAX_ELEMENT_CLOSE
      Element closed
      MXML_SAX_ELEMENT_OPEN
      Element opened

      mxml_type_e

      The XML node type.

      Constants

      MXML_CUSTOM  Mini-XML 2.1 
      Custom data
      MXML_ELEMENT
      XML element with attributes
      MXML_IGNORE  Mini-XML 2.3 
      Ignore/throw away node
      MXML_INTEGER
      Integer value
      MXML_OPAQUE
      Opaque string
      MXML_REAL
      Real value
      MXML_TEXT
      Text fragment

      DXML Schema

      This appendix provides the XML schema that is used for the XML files produced by mxmldoc. This schema is available on-line at:

          http://www.minixml.org/mxmldoc.xsd
      

      mxmldoc.xsd

      
      <?xml version="1.0"?>
      <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
        <xsd:annotation>
          <xsd:documentation xml:lang="en">
            Mini-XML 2.7 documentation schema for mxmldoc output.
            Copyright 2003-2011 by Michael Sweet.
          </xsd:documentation>
        </xsd:annotation>
      
        <!-- basic element definitions -->
        <xsd:element name="argument" type="argumentType"/>
        <xsd:element name="class" type="classType"/>
        <xsd:element name="constant" type="constantType"/>
        <xsd:element name="description" type="xsd:string"/>
        <xsd:element name="enumeration" type="enumerationType"/>
        <xsd:element name="function" type="functionType"/>
        <xsd:element name="mxmldoc" type="mxmldocType"/>
        <xsd:element name="namespace" type="namespaceType"/>
        <xsd:element name="returnvalue" type="returnvalueType"/>
        <xsd:element name="seealso" type="identifierList"/>
        <xsd:element name="struct" type="structType"/>
        <xsd:element name="typedef" type="typedefType"/>
        <xsd:element name="type" type="xsd:string"/>
        <xsd:element name="union" type="unionType"/>
        <xsd:element name="variable" type="variableType"/>
      
        <!-- descriptions of complex elements -->
        <xsd:complexType name="argumentType">
          <xsd:sequence>
            <xsd:element ref="type" minOccurs="1" maxOccurs="1"/>
            <xsd:element ref="description" minOccurs="0" maxOccurs="1"/>
          </xsd:sequence>
          <xsd:attribute name="default" type="xsd:string" use="optional"/>
          <xsd:attribute name="name" type="identifier" use="required"/>
          <xsd:attribute name="direction" type="direction" use="optional"
           default="I"/>
        </xsd:complexType>
      
        <xsd:complexType name="classType">
          <xsd:sequence>
            <xsd:element ref="description" minOccurs="0" maxOccurs="1"/>
            <xsd:choice minOccurs="0" maxOccurs="unbounded">
      	<xsd:element ref="class"/>
      	<xsd:element ref="enumeration"/>
      	<xsd:element ref="function"/>
      	<xsd:element ref="struct"/>
      	<xsd:element ref="typedef"/>
      	<xsd:element ref="union"/>
      	<xsd:element ref="variable"/>
            </xsd:choice>
          </xsd:sequence>
          <xsd:attribute name="name" type="identifier" use="required"/>
          <xsd:attribute name="parent" type="xsd:string" use="optional"/>
        </xsd:complexType>
      
        <xsd:complexType name="constantType">
          <xsd:sequence>
            <xsd:element ref="description" minOccurs="0" maxOccurs="1"/>
          </xsd:sequence>
          <xsd:attribute name="name" type="identifier" use="required"/>
        </xsd:complexType>
      
        <xsd:complexType name="enumerationType">
          <xsd:sequence>
            <xsd:element ref="description" minOccurs="0" maxOccurs="1"/>
            <xsd:element ref="constant" minOccurs="1" maxOccurs="unbounded"/>
          </xsd:sequence>
          <xsd:attribute name="name" type="identifier" use="required"/>
        </xsd:complexType>
      
        <xsd:complexType name="functionType">
          <xsd:sequence>
            <xsd:element ref="returnvalue" minOccurs="0" maxOccurs="1"/>
            <xsd:element ref="description" minOccurs="0" maxOccurs="1"/>
            <xsd:element ref="argument" minOccurs="1" maxOccurs="unbounded"/>
            <xsd:element ref="seealso" minOccurs="0" maxOccurs="1"/>
          </xsd:sequence>
          <xsd:attribute name="name" type="identifier" use="required"/>
          <xsd:attribute name="scope" type="scope" use="optional"/>
        </xsd:complexType>
      
        <xsd:complexType name="mxmldocType">
          <xsd:choice minOccurs="0" maxOccurs="unbounded">
            <xsd:element ref="class"/>
            <xsd:element ref="enumeration"/>
            <xsd:element ref="function"/>
            <xsd:element ref="namespace"/>
            <xsd:element ref="struct"/>
            <xsd:element ref="typedef"/>
            <xsd:element ref="union"/>
            <xsd:element ref="variable"/>
          </xsd:choice>
        </xsd:complexType>
      
        <xsd:complexType name="namespaceType">
          <xsd:sequence>
            <xsd:element ref="description" minOccurs="0" maxOccurs="1"/>
            <xsd:choice minOccurs="0" maxOccurs="unbounded">
      	<xsd:element ref="class"/>
      	<xsd:element ref="enumeration"/>
      	<xsd:element ref="function"/>
      	<xsd:element ref="struct"/>
      	<xsd:element ref="typedef"/>
      	<xsd:element ref="union"/>
      	<xsd:element ref="variable"/>
            </xsd:choice>
          </xsd:sequence>
          <xsd:attribute name="name" type="identifier" use="required"/>
        </xsd:complexType>
      
        <xsd:complexType name="returnvalueType">
          <xsd:sequence>
            <xsd:element ref="type" minOccurs="1" maxOccurs="1"/>
            <xsd:element ref="description" minOccurs="0" maxOccurs="1"/>
          </xsd:sequence>
        </xsd:complexType>
      
        <xsd:complexType name="structType">
          <xsd:sequence>
            <xsd:element ref="description" minOccurs="0" maxOccurs="1"/>
            <xsd:choice minOccurs="0" maxOccurs="unbounded">
      	<xsd:element ref="variable"/>
      	<xsd:element ref="function"/>
            </xsd:choice>
          </xsd:sequence>
          <xsd:attribute name="name" type="identifier" use="required"/>
        </xsd:complexType>
      
        <xsd:complexType name="typedefType">
          <xsd:sequence>
            <xsd:element ref="type" minOccurs="1" maxOccurs="1"/>
            <xsd:element ref="description" minOccurs="0" maxOccurs="1"/>
          </xsd:sequence>
          <xsd:attribute name="name" type="identifier" use="required"/>
        </xsd:complexType>
      
        <xsd:complexType name="unionType">
          <xsd:sequence>
            <xsd:element ref="description" minOccurs="0" maxOccurs="1"/>
            <xsd:element ref="variable" minOccurs="0" maxOccurs="unbounded"/>
          </xsd:sequence>
          <xsd:attribute name="name" type="identifier" use="required"/>
        </xsd:complexType>
      
        <xsd:complexType name="variableType">
          <xsd:sequence>
            <xsd:element ref="type" minOccurs="1" maxOccurs="1"/>
            <xsd:element ref="description" minOccurs="0" maxOccurs="1"/>
          </xsd:sequence>
          <xsd:attribute name="name" type="identifier" use="required"/>
        </xsd:complexType>
      
        <!-- data types -->
        <xsd:simpleType name="direction">
          <xsd:restriction base="xsd:string">
            <xsd:enumeration value="I"/>
            <xsd:enumeration value="O"/>
            <xsd:enumeration value="IO"/>
          </xsd:restriction>
        </xsd:simpleType>
      
        <xsd:simpleType name="identifier">
          <xsd:restriction base="xsd:string">
            <xsd:pattern value="[a-zA-Z_(.]([a-zA-Z_(.,)* 0-9])*"/>
          </xsd:restriction>
        </xsd:simpleType>
      
        <xsd:simpleType name="identifierList">
          <xsd:list itemType="identifier"/>
        </xsd:simpleType>
      
        <xsd:simpleType name="scope">
          <xsd:restriction base="xsd:string">
            <xsd:enumeration value=""/>
            <xsd:enumeration value="private"/>
            <xsd:enumeration value="protected"/>
            <xsd:enumeration value="public"/>
          </xsd:restriction>
        </xsd:simpleType>
      </xsd:schema>
      
      cmtk-3.3.1/Utilities/mxml/doc/mxmldoc.html000066400000000000000000000141021276303427400205100ustar00rootroot00000000000000

      4Using the mxmldoc Utility

      This chapter describes how to use mxmldoc(1) program to automatically generate documentation from C and C++ source files.

      The Basics

      Originally developed to generate the Mini-XML and CUPS API documentation, mxmldoc is now a general-purpose utility which scans C and C++ source files to produce HTML and man page documentation along with an XML file representing the functions, types, and definitions in those source files. Unlike popular documentation generators like Doxygen or Javadoc, mxmldoc uses in-line comments rather than comment headers, allowing for more "natural" code documentation.

      By default, mxmldoc produces HTML documentation. For example, the following command will scan all of the C source and header files in the current directory and produce a HTML documentation file called filename.html:

          mxmldoc *.h *.c >filename.html ENTER
      

      You can also specify an XML file to create which contains all of the information from the source files. For example, the following command creates an XML file called filename.xml in addition to the HTML file:

          mxmldoc filename.xml *.h *.c >filename.html ENTER
      

      The --no-output option disables the normal HTML output:

          mxmldoc --no-output filename.xml *.h *.c ENTER
      

      You can then run mxmldoc again with the XML file alone to generate the HTML documentation:

          mxmldoc filename.xml >filename.html ENTER
      

      Creating Man Pages

      The --man filename option tells mxmldoc to create a man page instead of HTML documentation, for example:

          mxmldoc --man filename filename.xml \
              >filename.man ENTER
      
          mxmldoc --man filename *.h *.c \
              >filename.man ENTER
      

      Creating Xcode Documentation Sets

      The --docset directory.docset option tells mxmldoc to create an Xcode documentation set containing the HTML documentation, for example:

          mxmldoc --docset foo.docset *.h *.c foo.xml ENTER
      

      Xcode documentation sets can only be built on Mac OS X with Xcode 3.0 or higher installed.

      Commenting Your Code

      As noted previously, mxmldoc looks for in-line comments to describe the functions, types, and constants in your code. Mxmldoc will document all public names it finds in your source files - any names starting with the underscore character (_) or names that are documented with the @private@ directive are treated as private and are not documented.

      Comments appearing directly before a function or type definition are used to document that function or type. Comments appearing after argument, definition, return type, or variable declarations are used to document that argument, definition, return type, or variable. For example, the following code excerpt defines a key/value structure and a function that creates a new instance of that structure:

          /* A key/value pair. This is used with the
             dictionary structure. */
      
          struct keyval
          {
            char *key; /* Key string */
            char *val; /* Value string */
          };
      
          /* Create a new key/value pair. */
      
          struct keyval * /* New key/value pair */
          new_keyval(
              const char *key, /* Key string */
      	const char *val) /* Value string */
          {
            ...
          }
      

      Mxmldoc also knows to remove extra asterisks (*) from the comment string, so the comment string:

          /*
           * Compute the value of PI.
           *
           * The function connects to an Internet server
           * that streams audio of mathematical monks
           * chanting the first 100 digits of PI.
           */
      

      will be shown as:

          Compute the value of PI.
      
          The function connects to an Internet server
          that streams audio of mathematical monks
          chanting the first 100 digits of PI.
      

      Comments can also include the following special @name ...@ directive strings:

      • @deprecated@ - flags the item as deprecated to discourage its use
      • @private@ - flags the item as private so it will not be included in the documentation
      • @since ...@ - flags the item as new since a particular release. The text following the @since up to the closing @ is highlighted in the generated documentation, e.g. @since Mini-XML 2.7@.

      Titles, Sections, and Introductions

      Mxmldoc also provides options to set the title, section, and introduction text for the generated documentation. The --title text option specifies the title for the documentation. The title string is usually put in quotes:

          mxmldoc filename.xml \
              --title "My Famous Documentation" \
              >filename.html ENTER
      

      The --section name option specifies the section for the documentation. For HTML documentation, the name is placed in a HTML comment such as:

          <!-- SECTION: name -->
      

      For man pages, the section name is usually just a number ("3"), or a number followed by a vendor name ("3acme"). The section name is used in the .TH directive in the man page:

          .TH mylibrary 3acme "My Title" ...
      

      The default section name for man page output is "3". There is no default section name for HTML output.

      Finally, the --intro filename option specifies a file to embed after the title and section but before the generated documentation. For HTML documentation, the file must consist of valid HTML without the usual DOCTYPE, html, and body elements. For man page documentation, the file must consist of valid nroff(1) text.

      cmtk-3.3.1/Utilities/mxml/doc/mxmldoc.man000066400000000000000000000102051276303427400203170ustar00rootroot00000000000000.\" .\" "$Id: mxmldoc.man 390 2009-05-05 13:38:00Z mike $" .\" .\" mxmldoc man page for mini-XML, a small XML-like file parsing library. .\" .\" Copyright 2003-2009 by Michael Sweet. .\" .\" This program is free software; you can redistribute it and/or .\" modify it under the terms of the GNU Library General Public .\" License as published by the Free Software Foundation; either .\" version 2, or (at your option) any later version. .\" .\" This program is distributed in the hope that it will be useful, .\" but WITHOUT ANY WARRANTY; without even the implied warranty of .\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the .\" GNU General Public License for more details. .\" .TH mxmldoc 1 "Mini-XML" "4 May 2009" "Michael Sweet" .SH NAME mxmldoc \- mini-xml documentation generator .SH SYNOPSIS .B mxmldoc \-\-no-output [ .I filename.xml ] .I source file(s) ] .br .B mxmldoc [ \-\-footer .I footerfile ] [ \-\-header .I headerfile ] [ \-\-intro .I introfile ] [ \-\-section .I section ] [ \-\-title .I title ] [ .I filename.xml ] [ .I source file(s) ] > .I filename.html .br .B mxmldoc \-\-docset .I directory.docset [ \-\-docversion .I version ] [ \-\-feedname .I name ] [ \-\-feedurl .I url ] [ \-\-footer .I footerfile ] [ \-\-header .I headerfile ] [ \-\-intro .I introfile ] [ \-\-section .I section ] [ \-\-title .I title ] [ .I filename.xml ] [ .I source file(s) ] .br .B mxmldoc \-\-tokens .I path [ .I filename.xml ] [ .I source file(s) ] > tokens.xml .br .B mxmldoc \-\-framed .I basename [ \-\-footer .I footerfile ] [ \-\-header .I headerfile ] [ \-\-intro .I introfile ] [ \-\-section .I section ] [ \-\-title .I title ] [ .I filename.xml ] [ .I source file(s) ] .br .B mxmldoc [ \-\-footer .I footerfile ] [ \-\-header .I headerfile ] [ \-\-intro .I introfile ] \-\-man .I manpage [ \-\-section .I section ] [ \-\-title .I title ] [ .I filename.xml ] [ .I source file(s) ] > .I filename.man .SH DESCRIPTION \fImxmldoc\fR scans the specified C and C++ source files to produce an XML representation of globally accessible classes, constants, enumerations, functions, structures, typedefs, unions, and variables - the XML file is updated as necessary. By default, a HTML representation of the XML file is written to the standard output. Use the \fI\-\-no-output\fR option to disable the HTML output. .PP Man page source can be generated using the \fI\-\-man\fR option. .PP If no source files are specified then the current XML file is converted to the standard output. .PP In general, any C or C++ source code is handled by \fImxmldoc\fR, however it was specifically written to handle code with documentation that is formatted according to the CUPS Developer Guide which is available at "http://www.cups.org/documentation.php". .SH OPTIONS .TP 5 \-\-docset directory.docset .br Creates an Xcode documentation set in the specified directory. .TP 5 \-\-docversion version .br Specifies the version number for the Xcode documentation set. .TP 5 \-\-feedname name .br Specifies the Xcode documentation set feed name, typically the project or company name. .TP 5 \-\-feedurl url .br Specifies the Xcode documentation set feed URL which must point to an ATOM file linking to updates. .TP 5 \-\-footer footerfile .br Inserts the specified file at the bottom of the output documentation. .TP 5 \-\-framed basename .br Creates HTML documentation using frames - one for the table-of-contents and one for the body. .TP 5 \-\-header headerfile .br Inserts the specified file at the top of the output documentation. .TP 5 \-\-intro introfile .br Inserts the specified file before the table of contents. .TP 5 \-\-man manpage .br Generated a man page instead of HTML documentation. .TP 5 \-\-no-output .br Disables generation of documentation on the standard output. .TP 5 \-\-section section .br Sets the section/keywords in the output documentation. .TP 5 \-\-title title .br Sets the title of the output documentation. .TP 5 \-\-tokens .br Generates a Tokens.xml file for use with the Xcode documentation tools. .SH SEE ALSO mxml(3), Mini-XML Programmers Manual, http://www.minixml.org/ .SH COPYRIGHT Copyright 2003-2009 by Michael Sweet. .\" .\" End of "$Id: mxmldoc.man 390 2009-05-05 13:38:00Z mike $". .\" cmtk-3.3.1/Utilities/mxml/doc/mxmldoc.xsd000066400000000000000000000155121276303427400203500ustar00rootroot00000000000000 Mini-XML 2.7 documentation schema for mxmldoc output. Copyright 2003-2011 by Michael Sweet. This program is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. cmtk-3.3.1/Utilities/mxml/doc/reference.heading000066400000000000000000000002161276303427400214370ustar00rootroot00000000000000

      CLibrary Reference

      cmtk-3.3.1/Utilities/mxml/doc/reference.html000066400000000000000000002220021276303427400210030ustar00rootroot00000000000000 Documentation

      CLibrary Reference

      Contents

      Functions

      mxmlAdd

      Add a node to a tree.

      void mxmlAdd (
          mxml_node_t *parent,
          int where,
          mxml_node_t *child,
          mxml_node_t *node
      );

      Parameters

      parent
      Parent node
      where
      Where to add, MXML_ADD_BEFORE or MXML_ADD_AFTER
      child
      Child node for where or MXML_ADD_TO_PARENT
      node
      Node to add

      Discussion

      Adds the specified node to the parent. If the child argument is not NULL, puts the new node before or after the specified child depending on the value of the where argument. If the child argument is NULL, puts the new node at the beginning of the child list (MXML_ADD_BEFORE) or at the end of the child list (MXML_ADD_AFTER). The constant MXML_ADD_TO_PARENT can be used to specify a NULL child pointer.

      mxmlDelete

      Delete a node and all of its children.

      void mxmlDelete (
          mxml_node_t *node
      );

      Parameters

      node
      Node to delete

      Discussion

      If the specified node has a parent, this function first removes the node from its parent using the mxmlRemove() function.

       Mini-XML 2.4 mxmlElementDeleteAttr

      Delete an attribute.

      void mxmlElementDeleteAttr (
          mxml_node_t *node,
          const char *name
      );

      Parameters

      node
      Element
      name
      Attribute name

      mxmlElementGetAttr

      Get an attribute.

      const char *mxmlElementGetAttr (
          mxml_node_t *node,
          const char *name
      );

      Parameters

      node
      Element node
      name
      Name of attribute

      Return Value

      Attribute value or NULL

      Discussion

      This function returns NULL if the node is not an element or the named attribute does not exist.

      mxmlElementSetAttr

      Set an attribute.

      void mxmlElementSetAttr (
          mxml_node_t *node,
          const char *name,
          const char *value
      );

      Parameters

      node
      Element node
      name
      Name of attribute
      value
      Attribute value

      Discussion

      If the named attribute already exists, the value of the attribute is replaced by the new string value. The string value is copied into the element node. This function does nothing if the node is not an element.

       Mini-XML 2.3 mxmlElementSetAttrf

      Set an attribute with a formatted value.

      void mxmlElementSetAttrf (
          mxml_node_t *node,
          const char *name,
          const char *format,
          ...
      );

      Parameters

      node
      Element node
      name
      Name of attribute
      format
      Printf-style attribute value
      ...
      Additional arguments as needed

      Discussion

      If the named attribute already exists, the value of the attribute is replaced by the new formatted string. The formatted string value is copied into the element node. This function does nothing if the node is not an element.

      mxmlEntityAddCallback

      Add a callback to convert entities to Unicode.

      int mxmlEntityAddCallback (
          mxml_entity_cb_t cb
      );

      Parameters

      cb
      Callback function to add

      Return Value

      0 on success, -1 on failure

      mxmlEntityGetName

      Get the name that corresponds to the character value.

      const char *mxmlEntityGetName (
          int val
      );

      Parameters

      val
      Character value

      Return Value

      Entity name or NULL

      Discussion

      If val does not need to be represented by a named entity, NULL is returned.

      mxmlEntityGetValue

      Get the character corresponding to a named entity.

      int mxmlEntityGetValue (
          const char *name
      );

      Parameters

      name
      Entity name

      Return Value

      Character value or -1 on error

      Discussion

      The entity name can also be a numeric constant. -1 is returned if the name is not known.

      mxmlEntityRemoveCallback

      Remove a callback.

      void mxmlEntityRemoveCallback (
          mxml_entity_cb_t cb
      );

      Parameters

      cb
      Callback function to remove

      mxmlFindElement

      Find the named element.

      mxml_node_t *mxmlFindElement (
          mxml_node_t *node,
          mxml_node_t *top,
          const char *name,
          const char *attr,
          const char *value,
          int descend
      );

      Parameters

      node
      Current node
      top
      Top node
      name
      Element name or NULL for any
      attr
      Attribute name, or NULL for none
      value
      Attribute value, or NULL for any
      descend
      Descend into tree - MXML_DESCEND, MXML_NO_DESCEND, or MXML_DESCEND_FIRST

      Return Value

      Element node or NULL

      Discussion

      The search is constrained by the name, attribute name, and value; any NULL names or values are treated as wildcards, so different kinds of searches can be implemented by looking for all elements of a given name or all elements with a specific attribute. The descend argument determines whether the search descends into child nodes; normally you will use MXML_DESCEND_FIRST for the initial search and MXML_NO_DESCEND to find additional direct descendents of the node. The top node argument constrains the search to a particular node's children.

       Mini-XML 2.7 mxmlFindPath

      Find a node with the given path.

      mxml_node_t *mxmlFindPath (
          mxml_node_t *top,
          const char *path
      );

      Parameters

      top
      Top node
      path
      Path to element

      Return Value

      Found node or NULL

      Discussion

      The "path" is a slash-separated list of element names. The name "*" is considered a wildcard for one or more levels of elements. For example, "foo/one/two", "bar/two/one", "*/one", and so forth.

      The first child node of the found node is returned if the given node has children and the first child is a value node.

       Mini-XML 2.7 mxmlGetCDATA

      Get the value for a CDATA node.

      const char *mxmlGetCDATA (
          mxml_node_t *node
      );

      Parameters

      node
      Node to get

      Return Value

      CDATA value or NULL

      Discussion

      NULL is returned if the node is not a CDATA element.

       Mini-XML 2.7 mxmlGetCustom

      Get the value for a custom node.

      const void *mxmlGetCustom (
          mxml_node_t *node
      );

      Parameters

      node
      Node to get

      Return Value

      Custom value or NULL

      Discussion

      NULL is returned if the node (or its first child) is not a custom value node.

       Mini-XML 2.7 mxmlGetElement

      Get the name for an element node.

      const char *mxmlGetElement (
          mxml_node_t *node
      );

      Parameters

      node
      Node to get

      Return Value

      Element name or NULL

      Discussion

      NULL is returned if the node is not an element node.

       Mini-XML 2.7 mxmlGetFirstChild

      Get the first child of an element node.

      mxml_node_t *mxmlGetFirstChild (
          mxml_node_t *node
      );

      Parameters

      node
      Node to get

      Return Value

      First child or NULL

      Discussion

      NULL is returned if the node is not an element node or if the node has no children.

       Mini-XML 2.7 mxmlGetInteger

      Get the integer value from the specified node or its first child.

      int mxmlGetInteger (
          mxml_node_t *node
      );

      Parameters

      node
      Node to get

      Return Value

      Integer value or 0

      Discussion

      0 is returned if the node (or its first child) is not an integer value node.

       Mini-XML 2.7 mxmlGetLastChild

      Get the last child of an element node.

      mxml_node_t *mxmlGetLastChild (
          mxml_node_t *node
      );

      Parameters

      node
      Node to get

      Return Value

      Last child or NULL

      Discussion

      NULL is returned if the node is not an element node or if the node has no children.

      mxmlGetNextSibling

      Return the node type...

      mxml_node_t *mxmlGetNextSibling (
          mxml_node_t *node
      );

      Parameters

      node
      Node to get

      Return Value

      Get the next node for the current parent.

      NULL is returned if this is the last child for the current parent.

       Mini-XML 2.7 mxmlGetOpaque

      Get an opaque string value for a node or its first child.

      const char *mxmlGetOpaque (
          mxml_node_t *node
      );

      Parameters

      node
      Node to get

      Return Value

      Opaque string or NULL

      Discussion

      NULL is returned if the node (or its first child) is not an opaque value node.

       Mini-XML 2.7 mxmlGetParent

      Get the parent node.

      mxml_node_t *mxmlGetParent (
          mxml_node_t *node
      );

      Parameters

      node
      Node to get

      Return Value

      Parent node or NULL

      Discussion

      NULL is returned for a root node.

       Mini-XML 2.7 mxmlGetPrevSibling

      Get the previous node for the current parent.

      mxml_node_t *mxmlGetPrevSibling (
          mxml_node_t *node
      );

      Parameters

      node
      Node to get

      Return Value

      Previous node or NULL

      Discussion

      NULL is returned if this is the first child for the current parent.

       Mini-XML 2.7 mxmlGetReal

      Get the real value for a node or its first child.

      double mxmlGetReal (
          mxml_node_t *node
      );

      Parameters

      node
      Node to get

      Return Value

      Real value or 0.0

      Discussion

      0.0 is returned if the node (or its first child) is not a real value node.

       Mini-XML 2.7 mxmlGetRefCount

      Get the current reference (use) count for a node.

      int mxmlGetRefCount (
          mxml_node_t *node
      );

      Parameters

      node
      Node

      Return Value

      Reference count

      Discussion

      The initial reference count of new nodes is 1. Use the mxmlRetain and mxmlRelease functions to increment and decrement a node's reference count. .

       Mini-XML 2.7 mxmlGetText

      Get the text value for a node or its first child.

      const char *mxmlGetText (
          mxml_node_t *node,
          int *whitespace
      );

      Parameters

      node
      Node to get
      whitespace
      1 if string is preceded by whitespace, 0 otherwise

      Return Value

      Text string or NULL

      Discussion

      NULL is returned if the node (or its first child) is not a text node. The "whitespace" argument can be NULL.

       Mini-XML 2.7 mxmlGetType

      Get the node type.

      mxml_type_t mxmlGetType (
          mxml_node_t *node
      );

      Parameters

      node
      Node to get

      Return Value

      Type of node

      Discussion

      MXML_IGNORE is returned if "node" is NULL.

       Mini-XML 2.7 mxmlGetUserData

      Get the user data pointer for a node.

      void *mxmlGetUserData (
          mxml_node_t *node
      );

      Parameters

      node
      Node to get

      Return Value

      User data pointer

      mxmlIndexDelete

      Delete an index.

      void mxmlIndexDelete (
          mxml_index_t *ind
      );

      Parameters

      ind
      Index to delete

      mxmlIndexEnum

      Return the next node in the index.

      mxml_node_t *mxmlIndexEnum (
          mxml_index_t *ind
      );

      Parameters

      ind
      Index to enumerate

      Return Value

      Next node or NULL if there is none

      Discussion

      Nodes are returned in the sorted order of the index.

      mxmlIndexFind

      Find the next matching node.

      mxml_node_t *mxmlIndexFind (
          mxml_index_t *ind,
          const char *element,
          const char *value
      );

      Parameters

      ind
      Index to search
      element
      Element name to find, if any
      value
      Attribute value, if any

      Return Value

      Node or NULL if none found

      Discussion

      You should call mxmlIndexReset() prior to using this function for the first time with a particular set of "element" and "value" strings. Passing NULL for both "element" and "value" is equivalent to calling mxmlIndexEnum().

       Mini-XML 2.7 mxmlIndexGetCount

      Get the number of nodes in an index.

      int mxmlIndexGetCount (
          mxml_index_t *ind
      );

      Parameters

      ind
      Index of nodes

      Return Value

      Number of nodes in index

      mxmlIndexNew

      Create a new index.

      mxml_index_t *mxmlIndexNew (
          mxml_node_t *node,
          const char *element,
          const char *attr
      );

      Parameters

      node
      XML node tree
      element
      Element to index or NULL for all
      attr
      Attribute to index or NULL for none

      Return Value

      New index

      Discussion

      The index will contain all nodes that contain the named element and/or attribute. If both "element" and "attr" are NULL, then the index will contain a sorted list of the elements in the node tree. Nodes are sorted by element name and optionally by attribute value if the "attr" argument is not NULL.

      mxmlIndexReset

      Reset the enumeration/find pointer in the index and return the first node in the index.

      mxml_node_t *mxmlIndexReset (
          mxml_index_t *ind
      );

      Parameters

      ind
      Index to reset

      Return Value

      First node or NULL if there is none

      Discussion

      This function should be called prior to using mxmlIndexEnum() or mxmlIndexFind() for the first time.

      mxmlLoadFd

      Load a file descriptor into an XML node tree.

      mxml_node_t *mxmlLoadFd (
          mxml_node_t *top,
          int fd,
          mxml_load_cb_t cb
      );

      Parameters

      top
      Top node
      fd
      File descriptor to read from
      cb
      Callback function or MXML_NO_CALLBACK

      Return Value

      First node or NULL if the file could not be read.

      Discussion

      The nodes in the specified file are added to the specified top node. If no top node is provided, the XML file MUST be well-formed with a single parent node like <?xml> for the entire file. The callback function returns the value type that should be used for child nodes. If MXML_NO_CALLBACK is specified then all child nodes will be either MXML_ELEMENT or MXML_TEXT nodes.

      The constants MXML_INTEGER_CALLBACK, MXML_OPAQUE_CALLBACK, MXML_REAL_CALLBACK, and MXML_TEXT_CALLBACK are defined for loading child nodes of the specified type.

      mxmlLoadFile

      Load a file into an XML node tree.

      mxml_node_t *mxmlLoadFile (
          mxml_node_t *top,
          FILE *fp,
          mxml_load_cb_t cb
      );

      Parameters

      top
      Top node
      fp
      File to read from
      cb
      Callback function or MXML_NO_CALLBACK

      Return Value

      First node or NULL if the file could not be read.

      Discussion

      The nodes in the specified file are added to the specified top node. If no top node is provided, the XML file MUST be well-formed with a single parent node like <?xml> for the entire file. The callback function returns the value type that should be used for child nodes. If MXML_NO_CALLBACK is specified then all child nodes will be either MXML_ELEMENT or MXML_TEXT nodes.

      The constants MXML_INTEGER_CALLBACK, MXML_OPAQUE_CALLBACK, MXML_REAL_CALLBACK, and MXML_TEXT_CALLBACK are defined for loading child nodes of the specified type.

      mxmlLoadString

      Load a string into an XML node tree.

      mxml_node_t *mxmlLoadString (
          mxml_node_t *top,
          const char *s,
          mxml_load_cb_t cb
      );

      Parameters

      top
      Top node
      s
      String to load
      cb
      Callback function or MXML_NO_CALLBACK

      Return Value

      First node or NULL if the string has errors.

      Discussion

      The nodes in the specified string are added to the specified top node. If no top node is provided, the XML string MUST be well-formed with a single parent node like <?xml> for the entire string. The callback function returns the value type that should be used for child nodes. If MXML_NO_CALLBACK is specified then all child nodes will be either MXML_ELEMENT or MXML_TEXT nodes.

      The constants MXML_INTEGER_CALLBACK, MXML_OPAQUE_CALLBACK, MXML_REAL_CALLBACK, and MXML_TEXT_CALLBACK are defined for loading child nodes of the specified type.

       Mini-XML 2.3 mxmlNewCDATA

      Create a new CDATA node.

      mxml_node_t *mxmlNewCDATA (
          mxml_node_t *parent,
          const char *data
      );

      Parameters

      parent
      Parent node or MXML_NO_PARENT
      data
      Data string

      Return Value

      New node

      Discussion

      The new CDATA node is added to the end of the specified parent's child list. The constant MXML_NO_PARENT can be used to specify that the new CDATA node has no parent. The data string must be nul-terminated and is copied into the new node. CDATA nodes use the MXML_ELEMENT type.

       Mini-XML 2.1 mxmlNewCustom

      Create a new custom data node.

      mxml_node_t *mxmlNewCustom (
          mxml_node_t *parent,
          void *data,
          mxml_custom_destroy_cb_t destroy
      );

      Parameters

      parent
      Parent node or MXML_NO_PARENT
      data
      Pointer to data
      destroy
      Function to destroy data

      Return Value

      New node

      Discussion

      The new custom node is added to the end of the specified parent's child list. The constant MXML_NO_PARENT can be used to specify that the new element node has no parent. NULL can be passed when the data in the node is not dynamically allocated or is separately managed.

      mxmlNewElement

      Create a new element node.

      mxml_node_t *mxmlNewElement (
          mxml_node_t *parent,
          const char *name
      );

      Parameters

      parent
      Parent node or MXML_NO_PARENT
      name
      Name of element

      Return Value

      New node

      Discussion

      The new element node is added to the end of the specified parent's child list. The constant MXML_NO_PARENT can be used to specify that the new element node has no parent.

      mxmlNewInteger

      Create a new integer node.

      mxml_node_t *mxmlNewInteger (
          mxml_node_t *parent,
          int integer
      );

      Parameters

      parent
      Parent node or MXML_NO_PARENT
      integer
      Integer value

      Return Value

      New node

      Discussion

      The new integer node is added to the end of the specified parent's child list. The constant MXML_NO_PARENT can be used to specify that the new integer node has no parent.

      mxmlNewOpaque

      Create a new opaque string.

      mxml_node_t *mxmlNewOpaque (
          mxml_node_t *parent,
          const char *opaque
      );

      Parameters

      parent
      Parent node or MXML_NO_PARENT
      opaque
      Opaque string

      Return Value

      New node

      Discussion

      The new opaque node is added to the end of the specified parent's child list. The constant MXML_NO_PARENT can be used to specify that the new opaque node has no parent. The opaque string must be nul-terminated and is copied into the new node.

      mxmlNewReal

      Create a new real number node.

      mxml_node_t *mxmlNewReal (
          mxml_node_t *parent,
          double real
      );

      Parameters

      parent
      Parent node or MXML_NO_PARENT
      real
      Real number value

      Return Value

      New node

      Discussion

      The new real number node is added to the end of the specified parent's child list. The constant MXML_NO_PARENT can be used to specify that the new real number node has no parent.

      mxmlNewText

      Create a new text fragment node.

      mxml_node_t *mxmlNewText (
          mxml_node_t *parent,
          int whitespace,
          const char *string
      );

      Parameters

      parent
      Parent node or MXML_NO_PARENT
      whitespace
      1 = leading whitespace, 0 = no whitespace
      string
      String

      Return Value

      New node

      Discussion

      The new text node is added to the end of the specified parent's child list. The constant MXML_NO_PARENT can be used to specify that the new text node has no parent. The whitespace parameter is used to specify whether leading whitespace is present before the node. The text string must be nul-terminated and is copied into the new node.

      mxmlNewTextf

      Create a new formatted text fragment node.

      mxml_node_t *mxmlNewTextf (
          mxml_node_t *parent,
          int whitespace,
          const char *format,
          ...
      );

      Parameters

      parent
      Parent node or MXML_NO_PARENT
      whitespace
      1 = leading whitespace, 0 = no whitespace
      format
      Printf-style frmat string
      ...
      Additional args as needed

      Return Value

      New node

      Discussion

      The new text node is added to the end of the specified parent's child list. The constant MXML_NO_PARENT can be used to specify that the new text node has no parent. The whitespace parameter is used to specify whether leading whitespace is present before the node. The format string must be nul-terminated and is formatted into the new node.

       Mini-XML 2.3 mxmlNewXML

      Create a new XML document tree.

      mxml_node_t *mxmlNewXML (
          const char *version
      );

      Parameters

      version
      Version number to use

      Return Value

      New ?xml node

      Discussion

      The "version" argument specifies the version number to put in the ?xml element node. If NULL, version 1.0 is assumed.

       Mini-XML 2.3 mxmlRelease

      Release a node.

      int mxmlRelease (
          mxml_node_t *node
      );

      Parameters

      node
      Node

      Return Value

      New reference count

      Discussion

      When the reference count reaches zero, the node (and any children) is deleted via mxmlDelete().

      mxmlRemove

      Remove a node from its parent.

      void mxmlRemove (
          mxml_node_t *node
      );

      Parameters

      node
      Node to remove

      Discussion

      Does not free memory used by the node - use mxmlDelete() for that. This function does nothing if the node has no parent.

       Mini-XML 2.3 mxmlRetain

      Retain a node.

      int mxmlRetain (
          mxml_node_t *node
      );

      Parameters

      node
      Node

      Return Value

      New reference count

       Mini-XML 2.3 mxmlSAXLoadFd

      Load a file descriptor into an XML node tree using a SAX callback.

      mxml_node_t *mxmlSAXLoadFd (
          mxml_node_t *top,
          int fd,
          mxml_load_cb_t cb,
          mxml_sax_cb_t sax_cb,
          void *sax_data
      );

      Parameters

      top
      Top node
      fd
      File descriptor to read from
      cb
      Callback function or MXML_NO_CALLBACK
      sax_cb
      SAX callback or MXML_NO_CALLBACK
      sax_data
      SAX user data

      Return Value

      First node or NULL if the file could not be read.

      Discussion

      The nodes in the specified file are added to the specified top node. If no top node is provided, the XML file MUST be well-formed with a single parent node like <?xml> for the entire file. The callback function returns the value type that should be used for child nodes. If MXML_NO_CALLBACK is specified then all child nodes will be either MXML_ELEMENT or MXML_TEXT nodes.

      The constants MXML_INTEGER_CALLBACK, MXML_OPAQUE_CALLBACK, MXML_REAL_CALLBACK, and MXML_TEXT_CALLBACK are defined for loading child nodes of the specified type.

      The SAX callback must call mxmlRetain() for any nodes that need to be kept for later use. Otherwise, nodes are deleted when the parent node is closed or after each data, comment, CDATA, or directive node.

       Mini-XML 2.3 mxmlSAXLoadFile

      Load a file into an XML node tree using a SAX callback.

      mxml_node_t *mxmlSAXLoadFile (
          mxml_node_t *top,
          FILE *fp,
          mxml_load_cb_t cb,
          mxml_sax_cb_t sax_cb,
          void *sax_data
      );

      Parameters

      top
      Top node
      fp
      File to read from
      cb
      Callback function or MXML_NO_CALLBACK
      sax_cb
      SAX callback or MXML_NO_CALLBACK
      sax_data
      SAX user data

      Return Value

      First node or NULL if the file could not be read.

      Discussion

      The nodes in the specified file are added to the specified top node. If no top node is provided, the XML file MUST be well-formed with a single parent node like <?xml> for the entire file. The callback function returns the value type that should be used for child nodes. If MXML_NO_CALLBACK is specified then all child nodes will be either MXML_ELEMENT or MXML_TEXT nodes.

      The constants MXML_INTEGER_CALLBACK, MXML_OPAQUE_CALLBACK, MXML_REAL_CALLBACK, and MXML_TEXT_CALLBACK are defined for loading child nodes of the specified type.

      The SAX callback must call mxmlRetain() for any nodes that need to be kept for later use. Otherwise, nodes are deleted when the parent node is closed or after each data, comment, CDATA, or directive node.

       Mini-XML 2.3 mxmlSAXLoadString

      Load a string into an XML node tree using a SAX callback.

      mxml_node_t *mxmlSAXLoadString (
          mxml_node_t *top,
          const char *s,
          mxml_load_cb_t cb,
          mxml_sax_cb_t sax_cb,
          void *sax_data
      );

      Parameters

      top
      Top node
      s
      String to load
      cb
      Callback function or MXML_NO_CALLBACK
      sax_cb
      SAX callback or MXML_NO_CALLBACK
      sax_data
      SAX user data

      Return Value

      First node or NULL if the string has errors.

      Discussion

      The nodes in the specified string are added to the specified top node. If no top node is provided, the XML string MUST be well-formed with a single parent node like <?xml> for the entire string. The callback function returns the value type that should be used for child nodes. If MXML_NO_CALLBACK is specified then all child nodes will be either MXML_ELEMENT or MXML_TEXT nodes.

      The constants MXML_INTEGER_CALLBACK, MXML_OPAQUE_CALLBACK, MXML_REAL_CALLBACK, and MXML_TEXT_CALLBACK are defined for loading child nodes of the specified type.

      The SAX callback must call mxmlRetain() for any nodes that need to be kept for later use. Otherwise, nodes are deleted when the parent node is closed or after each data, comment, CDATA, or directive node.

      mxmlSaveAllocString

      Save an XML tree to an allocated string.

      char *mxmlSaveAllocString (
          mxml_node_t *node,
          mxml_save_cb_t cb
      );

      Parameters

      node
      Node to write
      cb
      Whitespace callback or MXML_NO_CALLBACK

      Return Value

      Allocated string or NULL

      Discussion

      This function returns a pointer to a string containing the textual representation of the XML node tree. The string should be freed using the free() function when you are done with it. NULL is returned if the node would produce an empty string or if the string cannot be allocated.

      The callback argument specifies a function that returns a whitespace string or NULL before and after each element. If MXML_NO_CALLBACK is specified, whitespace will only be added before MXML_TEXT nodes with leading whitespace and before attribute names inside opening element tags.

      mxmlSaveFd

      Save an XML tree to a file descriptor.

      int mxmlSaveFd (
          mxml_node_t *node,
          int fd,
          mxml_save_cb_t cb
      );

      Parameters

      node
      Node to write
      fd
      File descriptor to write to
      cb
      Whitespace callback or MXML_NO_CALLBACK

      Return Value

      0 on success, -1 on error.

      Discussion

      The callback argument specifies a function that returns a whitespace string or NULL before and after each element. If MXML_NO_CALLBACK is specified, whitespace will only be added before MXML_TEXT nodes with leading whitespace and before attribute names inside opening element tags.

      mxmlSaveFile

      Save an XML tree to a file.

      int mxmlSaveFile (
          mxml_node_t *node,
          FILE *fp,
          mxml_save_cb_t cb
      );

      Parameters

      node
      Node to write
      fp
      File to write to
      cb
      Whitespace callback or MXML_NO_CALLBACK

      Return Value

      0 on success, -1 on error.

      Discussion

      The callback argument specifies a function that returns a whitespace string or NULL before and after each element. If MXML_NO_CALLBACK is specified, whitespace will only be added before MXML_TEXT nodes with leading whitespace and before attribute names inside opening element tags.

      mxmlSaveString

      Save an XML node tree to a string.

      int mxmlSaveString (
          mxml_node_t *node,
          char *buffer,
          int bufsize,
          mxml_save_cb_t cb
      );

      Parameters

      node
      Node to write
      buffer
      String buffer
      bufsize
      Size of string buffer
      cb
      Whitespace callback or MXML_NO_CALLBACK

      Return Value

      Size of string

      Discussion

      This function returns the total number of bytes that would be required for the string but only copies (bufsize - 1) characters into the specified buffer.

      The callback argument specifies a function that returns a whitespace string or NULL before and after each element. If MXML_NO_CALLBACK is specified, whitespace will only be added before MXML_TEXT nodes with leading whitespace and before attribute names inside opening element tags.

       Mini-XML 2.3 mxmlSetCDATA

      Set the element name of a CDATA node.

      int mxmlSetCDATA (
          mxml_node_t *node,
          const char *data
      );

      Parameters

      node
      Node to set
      data
      New data string

      Return Value

      0 on success, -1 on failure

      Discussion

      The node is not changed if it (or its first child) is not a CDATA element node.

       Mini-XML 2.1 mxmlSetCustom

      Set the data and destructor of a custom data node.

      int mxmlSetCustom (
          mxml_node_t *node,
          void *data,
          mxml_custom_destroy_cb_t destroy
      );

      Parameters

      node
      Node to set
      data
      New data pointer
      destroy
      New destructor function

      Return Value

      0 on success, -1 on failure

      Discussion

      The node is not changed if it (or its first child) is not a custom node.

      mxmlSetCustomHandlers

      Set the handling functions for custom data.

      void mxmlSetCustomHandlers (
          mxml_custom_load_cb_t load,
          mxml_custom_save_cb_t save
      );

      Parameters

      load
      Load function
      save
      Save function

      Discussion

      The load function accepts a node pointer and a data string and must return 0 on success and non-zero on error.

      The save function accepts a node pointer and must return a malloc'd string on success and NULL on error.

      mxmlSetElement

      Set the name of an element node.

      int mxmlSetElement (
          mxml_node_t *node,
          const char *name
      );

      Parameters

      node
      Node to set
      name
      New name string

      Return Value

      0 on success, -1 on failure

      Discussion

      The node is not changed if it is not an element node.

      mxmlSetErrorCallback

      Set the error message callback.

      void mxmlSetErrorCallback (
          mxml_error_cb_t cb
      );

      Parameters

      cb
      Error callback function

      mxmlSetInteger

      Set the value of an integer node.

      int mxmlSetInteger (
          mxml_node_t *node,
          int integer
      );

      Parameters

      node
      Node to set
      integer
      Integer value

      Return Value

      0 on success, -1 on failure

      Discussion

      The node is not changed if it (or its first child) is not an integer node.

      mxmlSetOpaque

      Set the value of an opaque node.

      int mxmlSetOpaque (
          mxml_node_t *node,
          const char *opaque
      );

      Parameters

      node
      Node to set
      opaque
      Opaque string

      Return Value

      0 on success, -1 on failure

      Discussion

      The node is not changed if it (or its first child) is not an opaque node.

      mxmlSetReal

      Set the value of a real number node.

      int mxmlSetReal (
          mxml_node_t *node,
          double real
      );

      Parameters

      node
      Node to set
      real
      Real number value

      Return Value

      0 on success, -1 on failure

      Discussion

      The node is not changed if it (or its first child) is not a real number node.

      mxmlSetText

      Set the value of a text node.

      int mxmlSetText (
          mxml_node_t *node,
          int whitespace,
          const char *string
      );

      Parameters

      node
      Node to set
      whitespace
      1 = leading whitespace, 0 = no whitespace
      string
      String

      Return Value

      0 on success, -1 on failure

      Discussion

      The node is not changed if it (or its first child) is not a text node.

      mxmlSetTextf

      Set the value of a text node to a formatted string.

      int mxmlSetTextf (
          mxml_node_t *node,
          int whitespace,
          const char *format,
          ...
      );

      Parameters

      node
      Node to set
      whitespace
      1 = leading whitespace, 0 = no whitespace
      format
      Printf-style format string
      ...
      Additional arguments as needed

      Return Value

      0 on success, -1 on failure

      Discussion

      The node is not changed if it (or its first child) is not a text node.

       Mini-XML 2.7 mxmlSetUserData

      Set the user data pointer for a node.

      int mxmlSetUserData (
          mxml_node_t *node,
          void *data
      );

      Parameters

      node
      Node to set
      data
      User data pointer

      Return Value

      0 on success, -1 on failure

       Mini-XML 2.3 mxmlSetWrapMargin

      Set the wrap margin when saving XML data.

      void mxmlSetWrapMargin (
          int column
      );

      Parameters

      column
      Column for wrapping, 0 to disable wrapping

      Discussion

      Wrapping is disabled when "column" is 0.

      mxmlWalkNext

      Walk to the next logical node in the tree.

      mxml_node_t *mxmlWalkNext (
          mxml_node_t *node,
          mxml_node_t *top,
          int descend
      );

      Parameters

      node
      Current node
      top
      Top node
      descend
      Descend into tree - MXML_DESCEND, MXML_NO_DESCEND, or MXML_DESCEND_FIRST

      Return Value

      Next node or NULL

      Discussion

      The descend argument controls whether the first child is considered to be the next node. The top node argument constrains the walk to the node's children.

      mxmlWalkPrev

      Walk to the previous logical node in the tree.

      mxml_node_t *mxmlWalkPrev (
          mxml_node_t *node,
          mxml_node_t *top,
          int descend
      );

      Parameters

      node
      Current node
      top
      Top node
      descend
      Descend into tree - MXML_DESCEND, MXML_NO_DESCEND, or MXML_DESCEND_FIRST

      Return Value

      Previous node or NULL

      Discussion

      The descend argument controls whether the previous node's last child is considered to be the previous node. The top node argument constrains the walk to the node's children.

      Data Types

      mxml_custom_destroy_cb_t

      Custom data destructor

      typedef void (*mxml_custom_destroy_cb_t)(void *);

      mxml_custom_load_cb_t

      Custom data load callback function

      typedef int (*mxml_custom_load_cb_t)(mxml_node_t *, const char *);

      mxml_custom_save_cb_t

      Custom data save callback function

      typedef char *(*mxml_custom_save_cb_t)(mxml_node_t *);

      mxml_entity_cb_t

      Entity callback function

      typedef int (*mxml_entity_cb_t)(const char *);

      mxml_error_cb_t

      Error callback function

      typedef void (*mxml_error_cb_t)(const char *);

      mxml_index_t

      An XML node index.

      typedef struct mxml_index_s mxml_index_t;

      mxml_load_cb_t

      Load callback function

      typedef mxml_type_t (*mxml_load_cb_t)(mxml_node_t *);

      mxml_node_t

      An XML node.

      typedef struct mxml_node_s mxml_node_t;

      mxml_save_cb_t

      Save callback function

      typedef const char *(*mxml_save_cb_t)(mxml_node_t *, int);

      mxml_sax_cb_t

      SAX callback function

      typedef void (*mxml_sax_cb_t)(mxml_node_t *, mxml_sax_event_t, void *);

      mxml_sax_event_t

      SAX event type.

      typedef enum mxml_sax_event_e mxml_sax_event_t;

      mxml_type_t

      The XML node type.

      typedef enum mxml_type_e mxml_type_t;

      Constants

      mxml_sax_event_e

      SAX event type.

      Constants

      MXML_SAX_CDATA
      CDATA node
      MXML_SAX_COMMENT
      Comment node
      MXML_SAX_DATA
      Data node
      MXML_SAX_DIRECTIVE
      Processing directive node
      MXML_SAX_ELEMENT_CLOSE
      Element closed
      MXML_SAX_ELEMENT_OPEN
      Element opened

      mxml_type_e

      The XML node type.

      Constants

      MXML_CUSTOM  Mini-XML 2.1 
      Custom data
      MXML_ELEMENT
      XML element with attributes
      MXML_IGNORE  Mini-XML 2.3 
      Ignore/throw away node
      MXML_INTEGER
      Integer value
      MXML_OPAQUE
      Opaque string
      MXML_REAL
      Real value
      MXML_TEXT
      Text fragment
      cmtk-3.3.1/Utilities/mxml/doc/relnotes.html000066400000000000000000000350221276303427400207040ustar00rootroot00000000000000

      BRelease Notes

      Changes in Mini-XML 2.7

      • Added 64-bit configurations to the VC++ project files (STR #129)
      • Fixed conformance of mxmldoc's HTML and CSS output.
      • Added data accessor ("get") functions and made the mxml_node_t and mxml_index_t structures private but still available in the Mini-XML header to preserve source compatibility (STR #118)
      • Updated the source headers to reference the Mini-XML license and its exceptions to the LGPL2 (STR #108)
      • Added a new mxmlFindPath() function to find the value node of a named element (STR #110)
      • Building a static version of the library did not work on Windows (STR #112)
      • The shared library did not include a destructor for the thread- specific data key on UNIX-based operating systems (STR #103)
      • mxmlLoad* did not error out on XML with multiple root nodes (STR #101)
      • Fixed an issue with the _mxml_vstrdupf function (STR #107)
      • mxmlSave* no longer write all siblings of the passed node, just that node and its children (STR #109)

      Changes in Mini-XML 2.6

      • Documentation fixes (STR #91, STR #92)
      • The mxmldoc program did not handle typedef comments properly (STR #72)
      • Added support for "long long" printf formats.
      • The XML parser now ignores BOMs in UTF-8 XML files (STR #89)
      • The mxmldoc program now supports generating Xcode documentation sets.
      • mxmlSave*() did not output UTF-8 correctly on some platforms.
      • mxmlNewXML() now adds encoding="utf-8" in the ?xml directive to avoid problems with non-conformant XML parsers that assume something other than UTF-8 as the default encoding.
      • Wrapping was not disabled when mxmlSetWrapMargin(0) was called, and "<?xml ... ?>" was always followed by a newline (STR #76)
      • The mxml.pc.in file was broken (STR #79)
      • The mxmldoc program now handles "typedef enum name {} name" correctly (STR #72)

      Changes in Mini-XML 2.5

      • The mxmldoc program now makes greater use of CSS and supports a --css option to embed an alternate stylesheet.
      • The mxmldoc program now supports --header and --footer options to insert documentation content before and after the generated content.
      • The mxmldoc program now supports a --framed option to generate framed HTML output.
      • The mxmldoc program now creates a table of contents including any headings in the --intro file when generating HTML output.
      • The man pages and man page output from mxmldoc did not use "\-" for dashes (STR #68)
      • The debug version of the Mini-XML DLL could not be built (STR #65)
      • Processing instructions and directives did not work when not at the top level of a document (STR #67)
      • Spaces around the "=" in attributes were not supported (STR #67)

      Changes in Mini-XML 2.4

      • Fixed shared library build problems on HP-UX and Mac OS X.
      • The mxmldoc program did not output argument descriptions for functions properly.
      • All global settings (custom, error, and entity callbacks and the wrap margin) are now managed separately for each thread.
      • Added mxmlElementDeleteAttr() function (STR #59)
      • mxmlElementSetAttrf() did not work (STR #57)
      • mxmlLoad*() incorrectly treated declarations as parent elements (STR #56)
      • mxmlLoad*() incorrectly allowed attributes without values (STR #47)
      • Fixed Visual C++ build problems (STR #49)
      • mxmlLoad*() did not return NULL when an element contained an error (STR #46)
      • Added support for the apos character entity (STR #54)
      • Fixed whitespace detection with Unicode characters (STR #48)
      • mxmlWalkNext() and mxmlWalkPrev() did not work correctly when called with a node with no children as the top node (STR #53)

      Changes in Mini-XML 2.3

      • Added two exceptions to the LGPL to support static linking of applications against Mini-XML
      • The mxmldoc utility can now generate man pages, too.
      • Added a mxmlNewXML() function
      • Added a mxmlElementSetAttrf() function (STR #43)
      • Added a snprintf() emulation function for the test program (STR #32)
      • Added the _CRT_SECURE_NO_DEPRECATE definition when building on VC++ 2005 (STR #36)
      • mxmlLoad*() did not detect missing > characters in elements (STR #41)
      • mxmlLoad*() did not detect missing close tags at the end of an XML document (STR #45)
      • Added user_data and ref_count members to mxml_node_t structure
      • Added mxmlReleaseNode() and mxmlRetainNode() APIs for reference-counted nodes
      • Added mxmlSetWrapMargin() to control the wrapping of XML output
      • Added conditional check for EINTR error code for certain Windows compilers that do not define it (STR #33)
      • The mxmldoc program now generates correct HTML 4.0 output - previously it generated invalid XHTML
      • The mxmldoc program now supports "@deprecated@, "@private@", and "@since version@" comments
      • Fixed function and enumeration type bugs in mxmldoc
      • Fixed the XML schema for mxmldoc
      • The mxmldoc program now supports --intro, --section, and --title options
      • The mxmlLoad*() functions could leak a node on an error (STR #27)
      • The mxml_vsnprintf() function could get in an infinite loop on a buffer overflow (STR #25)
      • Added new mxmlNewCDATA() and mxmlSetCDATA() functions to create and set CDATA nodes, which are really just special element nodes
      • Added new MXML_IGNORE type and MXML_IGNORE_CB callback to ignore non-element nodes, e.g. whitespace
      • mxmlLoad*() did not treat custom data as opaque, so whitespace characters would be lost

      Changes in Mini-XML 2.2.2

      • mxmlLoad*() did not treat custom data as opaque, so whitespace characters would be lost.

      Changes in Mini-XML 2.2.1

      • mxmlLoadFd(), mxmlLoadFile(), and mxmlLoadString() now correctly return NULL on error (STR #21)
      • mxmlNewInteger(), mxmlNewOpaque(), mxmlNewReal(), mxmlNewText(), and mxmlNewTextf() incorrectly required a parent node (STR #22)
      • Fixed an XML output bug in mxmldoc.
      • The "make install" target now uses the install command to set the proper permissions on UNIX/Linux/OSX.
      • Fixed a MingW/Cygwin compilation problem (STR #18)

      Changes in Mini-XML 2.2

      • Added shared library support (STR #17)
      • mxmlLoad*() now returns an error when an XML stream contains illegal control characters (STR #10)
      • mxmlLoad*() now returns an error when an element contains two attributes with the same name in conformance with the XML spec (STR #16)
      • Added support for CDATA (STR #14, STR #15)
      • Updated comment and processing instruction handling - no entity support per XML specification.
      • Added checking for invalid comment termination ("--->" is not allowed)

      Changes in Mini-XML 2.1

      • Added support for custom data nodes (STR #6)
      • Now treat UTF-8 sequences which are longer than necessary as an error (STR #4)
      • Fixed entity number support (STR #8)
      • Fixed mxmlLoadString() bug with UTF-8 (STR #7)
      • Fixed entity lookup bug (STR #5)
      • Added mxmlLoadFd() and mxmlSaveFd() functions.
      • Fixed multi-word UTF-16 handling.

      Changes in Mini-XML 2.0

      • New programmers manual.
      • Added Visual C++ project files for Microsoft Windows users.
      • Added optimizations to mxmldoc, mxmlSaveFile(), and mxmlIndexNew() (STR #2)
      • mxmlEntityAddCallback() now returns an integer status (STR #2)
      • Added UTF-16 support (input only; all output is UTF-8)
      • Added index functions to build a searchable index of XML nodes.
      • Added character entity callback interface to support additional character entities beyond those defined in the XHTML specification.
      • Added support for XHTML character entities.
      • The mxmldoc utility now produces XML output which conforms to an updated XML schema, described in the file "doc/mxmldoc.xsd".
      • Changed the whitespace callback interface to return strings instead of a single character, allowing for greater control over the formatting of XML files written using Mini-XML. THIS CHANGE WILL REQUIRE CHANGES TO YOUR 1.x CODE IF YOU USE WHITESPACE CALLBACKS.
      • The mxmldoc utility now produces XML output which conforms to an updated XML schema, described in the file "doc/mxmldoc.xsd".
      • Changed the whitespace callback interface to return strings instead of a single character, allowing for greater control over the formatting of XML files written using Mini-XML. THIS CHANGE WILL REQUIRE CHANGES TO YOUR 1.x CODE IF YOU USE WHITESPACE CALLBACKS.
      • The mxmldoc utility is now capable of documenting C++ classes, functions, and structures, and correctly handles C++ comments.
      • Added new modular tests for mxmldoc.
      • Updated the mxmldoc output to be more compatible with embedding in manuals produced with HTMLDOC.
      • The makefile incorrectly included a "/" separator between the destination path and install path. This caused problems when building and installing with MingW.

      Changes in Mini-XML 1.3

      • Fixes for mxmldoc.
      • Added support for reading standard HTML entity names.
      • mxmlLoadString/File() did not decode character entities in element names, attribute names, or attribute values.
      • mxmlLoadString/File() would crash when loading non- conformant XML data under an existing parent (top) node.
      • Fixed several bugs in the mxmldoc utility.
      • Added new error callback function to catch a variety of errors and log them to someplace other than stderr.
      • The mxmlElementSetAttr() function now allows for NULL attribute values.
      • The load and save functions now properly handle quoted element and attribute name strings properly, e.g. for !DOCTYPE declarations.

      Changes in Mini-XML 1.2

      • Added new "set" methods to set the value of a node.
      • Added new formatted text methods mxmlNewTextf() and mxmlSetTextf() to create/set a text node value using printf-style formats.
      • Added new standard callbacks for use with the mxmlLoad functions.
      • Updated the HTML documentation to include examples of the walk and load function output.
      • Added --with/without-ansi configure option to control the strdup() function check.
      • Added --with/without-snprintf configure option to control the snprintf() and vsnprintf() function checks.

      Changes in Mini-XML 1.1.2

      • The mxml(3) man page wasn't updated for the string functions.
      • mxmlSaveString() returned the wrong number of characters.
      • mxml_add_char() updated the buffer pointer in the wrong place.

      Changes in Mini-XML 1.1.1

      • The private mxml_add_ch() function did not update the start-of-buffer pointer which could cause a crash when using mxmlSaveString().
      • The private mxml_write_ws() function called putc() instead of using the proper callback which could cause a crash when using mxmlSaveString().
      • Added a mxmlSaveAllocString() convenience function for saving an XML node tree to an allocated string.

      Changes in Mini-XML 1.1

      • The mxmlLoadFile() function now uses dynamically allocated string buffers for element names, attribute names, and attribute values. Previously they were capped at 16383, 255, and 255 bytes, respectively.
      • Added a new mxmlLoadString() function for loading an XML node tree from a string.
      • Added a new mxmlSaveString() function for saving an XML node tree to a string.
      • Add emulation of strdup() if the local platform does not provide the function.

      Changes in Mini-XML 1.0

      • The mxmldoc program now handles function arguments, structures, unions, enumerations, classes, and typedefs properly.
      • Documentation provided via mxmldoc and more in-line comments in the code.
      • Added man pages and packaging files.

      Changes in Mini-XML 0.93

      • New mxmldoc example program that is also used to create and update code documentation using XML and produce HTML reference pages.
      • Added mxmlAdd() and mxmlRemove() functions to add and remove nodes from a tree. This provides more flexibility over where the nodes are inserted and allows nodes to be moved within the tree as needed.
      • mxmlLoadFile() now correctly handles comments.
      • mxmlLoadFile() now supports the required "gt", "quot", and "nbsp" character entities.
      • mxmlSaveFile() now uses newlines as whitespace when valid to do so.
      • mxmlFindElement() now also takes attribute name and attribute value string arguments to limit the search to specific elements with attributes and/or values.
      • NULL pointers can be used as "wildcards".
      • Added uninstall target to makefile, and auto-reconfig if Makefile.in or configure.in are changed.
      • mxmlFindElement(), mxmlWalkNext(), and mxmlWalkPrev() now all provide "descend" arguments to control whether they descend into child nodes in the tree.
      • Fixed some whitespace issues in mxmlLoadFile().
      • Fixed Unicode output and whitespace issues in mxmlSaveFile().
      • mxmlSaveFile() now supports a whitespace callback to provide more human-readable XML output under program control.

      Changes in Mini-XML 0.92

      • mxmlSaveFile() didn't return a value on success.

      Changes in Mini-XML 0.91

      • mxmlWalkNext() would go into an infinite loop.

      Changes in Mini-XML 0.9

      • Initial public release.
      cmtk-3.3.1/Utilities/mxml/doc/schema.html000066400000000000000000000163221276303427400203130ustar00rootroot00000000000000

      DXML Schema

      This appendix provides the XML schema that is used for the XML files produced by mxmldoc. This schema is available on-line at:

          http://www.minixml.org/mxmldoc.xsd
      

      mxmldoc.xsd

      
      <?xml version="1.0"?>
      <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
        <xsd:annotation>
          <xsd:documentation xml:lang="en">
            Mini-XML 2.7 documentation schema for mxmldoc output.
            Copyright 2003-2011 by Michael Sweet.
          </xsd:documentation>
        </xsd:annotation>
      
        <!-- basic element definitions -->
        <xsd:element name="argument" type="argumentType"/>
        <xsd:element name="class" type="classType"/>
        <xsd:element name="constant" type="constantType"/>
        <xsd:element name="description" type="xsd:string"/>
        <xsd:element name="enumeration" type="enumerationType"/>
        <xsd:element name="function" type="functionType"/>
        <xsd:element name="mxmldoc" type="mxmldocType"/>
        <xsd:element name="namespace" type="namespaceType"/>
        <xsd:element name="returnvalue" type="returnvalueType"/>
        <xsd:element name="seealso" type="identifierList"/>
        <xsd:element name="struct" type="structType"/>
        <xsd:element name="typedef" type="typedefType"/>
        <xsd:element name="type" type="xsd:string"/>
        <xsd:element name="union" type="unionType"/>
        <xsd:element name="variable" type="variableType"/>
      
        <!-- descriptions of complex elements -->
        <xsd:complexType name="argumentType">
          <xsd:sequence>
            <xsd:element ref="type" minOccurs="1" maxOccurs="1"/>
            <xsd:element ref="description" minOccurs="0" maxOccurs="1"/>
          </xsd:sequence>
          <xsd:attribute name="default" type="xsd:string" use="optional"/>
          <xsd:attribute name="name" type="identifier" use="required"/>
          <xsd:attribute name="direction" type="direction" use="optional"
           default="I"/>
        </xsd:complexType>
      
        <xsd:complexType name="classType">
          <xsd:sequence>
            <xsd:element ref="description" minOccurs="0" maxOccurs="1"/>
            <xsd:choice minOccurs="0" maxOccurs="unbounded">
      	<xsd:element ref="class"/>
      	<xsd:element ref="enumeration"/>
      	<xsd:element ref="function"/>
      	<xsd:element ref="struct"/>
      	<xsd:element ref="typedef"/>
      	<xsd:element ref="union"/>
      	<xsd:element ref="variable"/>
            </xsd:choice>
          </xsd:sequence>
          <xsd:attribute name="name" type="identifier" use="required"/>
          <xsd:attribute name="parent" type="xsd:string" use="optional"/>
        </xsd:complexType>
      
        <xsd:complexType name="constantType">
          <xsd:sequence>
            <xsd:element ref="description" minOccurs="0" maxOccurs="1"/>
          </xsd:sequence>
          <xsd:attribute name="name" type="identifier" use="required"/>
        </xsd:complexType>
      
        <xsd:complexType name="enumerationType">
          <xsd:sequence>
            <xsd:element ref="description" minOccurs="0" maxOccurs="1"/>
            <xsd:element ref="constant" minOccurs="1" maxOccurs="unbounded"/>
          </xsd:sequence>
          <xsd:attribute name="name" type="identifier" use="required"/>
        </xsd:complexType>
      
        <xsd:complexType name="functionType">
          <xsd:sequence>
            <xsd:element ref="returnvalue" minOccurs="0" maxOccurs="1"/>
            <xsd:element ref="description" minOccurs="0" maxOccurs="1"/>
            <xsd:element ref="argument" minOccurs="1" maxOccurs="unbounded"/>
            <xsd:element ref="seealso" minOccurs="0" maxOccurs="1"/>
          </xsd:sequence>
          <xsd:attribute name="name" type="identifier" use="required"/>
          <xsd:attribute name="scope" type="scope" use="optional"/>
        </xsd:complexType>
      
        <xsd:complexType name="mxmldocType">
          <xsd:choice minOccurs="0" maxOccurs="unbounded">
            <xsd:element ref="class"/>
            <xsd:element ref="enumeration"/>
            <xsd:element ref="function"/>
            <xsd:element ref="namespace"/>
            <xsd:element ref="struct"/>
            <xsd:element ref="typedef"/>
            <xsd:element ref="union"/>
            <xsd:element ref="variable"/>
          </xsd:choice>
        </xsd:complexType>
      
        <xsd:complexType name="namespaceType">
          <xsd:sequence>
            <xsd:element ref="description" minOccurs="0" maxOccurs="1"/>
            <xsd:choice minOccurs="0" maxOccurs="unbounded">
      	<xsd:element ref="class"/>
      	<xsd:element ref="enumeration"/>
      	<xsd:element ref="function"/>
      	<xsd:element ref="struct"/>
      	<xsd:element ref="typedef"/>
      	<xsd:element ref="union"/>
      	<xsd:element ref="variable"/>
            </xsd:choice>
          </xsd:sequence>
          <xsd:attribute name="name" type="identifier" use="required"/>
        </xsd:complexType>
      
        <xsd:complexType name="returnvalueType">
          <xsd:sequence>
            <xsd:element ref="type" minOccurs="1" maxOccurs="1"/>
            <xsd:element ref="description" minOccurs="0" maxOccurs="1"/>
          </xsd:sequence>
        </xsd:complexType>
      
        <xsd:complexType name="structType">
          <xsd:sequence>
            <xsd:element ref="description" minOccurs="0" maxOccurs="1"/>
            <xsd:choice minOccurs="0" maxOccurs="unbounded">
      	<xsd:element ref="variable"/>
      	<xsd:element ref="function"/>
            </xsd:choice>
          </xsd:sequence>
          <xsd:attribute name="name" type="identifier" use="required"/>
        </xsd:complexType>
      
        <xsd:complexType name="typedefType">
          <xsd:sequence>
            <xsd:element ref="type" minOccurs="1" maxOccurs="1"/>
            <xsd:element ref="description" minOccurs="0" maxOccurs="1"/>
          </xsd:sequence>
          <xsd:attribute name="name" type="identifier" use="required"/>
        </xsd:complexType>
      
        <xsd:complexType name="unionType">
          <xsd:sequence>
            <xsd:element ref="description" minOccurs="0" maxOccurs="1"/>
            <xsd:element ref="variable" minOccurs="0" maxOccurs="unbounded"/>
          </xsd:sequence>
          <xsd:attribute name="name" type="identifier" use="required"/>
        </xsd:complexType>
      
        <xsd:complexType name="variableType">
          <xsd:sequence>
            <xsd:element ref="type" minOccurs="1" maxOccurs="1"/>
            <xsd:element ref="description" minOccurs="0" maxOccurs="1"/>
          </xsd:sequence>
          <xsd:attribute name="name" type="identifier" use="required"/>
        </xsd:complexType>
      
        <!-- data types -->
        <xsd:simpleType name="direction">
          <xsd:restriction base="xsd:string">
            <xsd:enumeration value="I"/>
            <xsd:enumeration value="O"/>
            <xsd:enumeration value="IO"/>
          </xsd:restriction>
        </xsd:simpleType>
      
        <xsd:simpleType name="identifier">
          <xsd:restriction base="xsd:string">
            <xsd:pattern value="[a-zA-Z_(.]([a-zA-Z_(.,)* 0-9])*"/>
          </xsd:restriction>
        </xsd:simpleType>
      
        <xsd:simpleType name="identifierList">
          <xsd:list itemType="identifier"/>
        </xsd:simpleType>
      
        <xsd:simpleType name="scope">
          <xsd:restriction base="xsd:string">
            <xsd:enumeration value=""/>
            <xsd:enumeration value="private"/>
            <xsd:enumeration value="protected"/>
            <xsd:enumeration value="public"/>
          </xsd:restriction>
        </xsd:simpleType>
      </xsd:schema>
      
      cmtk-3.3.1/Utilities/mxml/doc/title.html000066400000000000000000000016421276303427400201730ustar00rootroot00000000000000 Mini-XML Programmers Manual

      Mini-XML Programmers Manual
      Version 2.7

      MICHAEL R. SWEET

      Mini-XML Programmers Manual, Version 2.7

      Copyright © 2003-2011 by Michael R. Sweet

      Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Library General Public License, Version 2. A copy of this license is included in Appendix A - Mini-XML License.

      cmtk-3.3.1/Utilities/mxml/install-sh000077500000000000000000000126711276303427400174270ustar00rootroot00000000000000#!/bin/sh # # install - install a program, script, or datafile # This comes from X11R5 (mit/util/scripts/install.sh). # # Copyright 1991 by the Massachusetts Institute of Technology # # Permission to use, copy, modify, distribute, and sell this software and its # documentation for any purpose is hereby granted without fee, provided that # the above copyright notice appear in all copies and that both that # copyright notice and this permission notice appear in supporting # documentation, and that the name of M.I.T. not be used in advertising or # publicity pertaining to distribution of the software without specific, # written prior permission. M.I.T. makes no representations about the # suitability of this software for any purpose. It is provided "as is" # without express or implied warranty. # # Calling this script install-sh is preferred over install.sh, to prevent # `make' implicit rules from creating a file called install from it # when there is no Makefile. # # This script is compatible with the BSD install script, but was written # from scratch. It can only install one file at a time, a restriction # shared with many OS's install programs. # set DOITPROG to echo to test this script # Don't use :- since 4.3BSD and earlier shells don't like it. doit="${DOITPROG-}" # put in absolute paths if you don't have them in your path; or use env. vars. mvprog="${MVPROG-mv}" cpprog="${CPPROG-cp}" chmodprog="${CHMODPROG-chmod}" chownprog="${CHOWNPROG-chown}" chgrpprog="${CHGRPPROG-chgrp}" stripprog="${STRIPPROG-strip}" rmprog="${RMPROG-rm}" mkdirprog="${MKDIRPROG-mkdir}" transformbasename="" transform_arg="" instcmd="$mvprog" chmodcmd="$chmodprog 0755" chowncmd="" chgrpcmd="" stripcmd="" rmcmd="$rmprog -f" mvcmd="$mvprog" src="" dst="" dir_arg="" while [ x"$1" != x ]; do case $1 in -c) instcmd="$cpprog" shift continue;; -d) dir_arg=true shift continue;; -m) chmodcmd="$chmodprog $2" shift shift continue;; -o) chowncmd="$chownprog $2" shift shift continue;; -g) chgrpcmd="$chgrpprog $2" shift shift continue;; -s) stripcmd="$stripprog" shift continue;; -t=*) transformarg=`echo $1 | sed 's/-t=//'` shift continue;; -b=*) transformbasename=`echo $1 | sed 's/-b=//'` shift continue;; *) if [ x"$src" = x ] then src=$1 else # this colon is to work around a 386BSD /bin/sh bug : dst=$1 fi shift continue;; esac done if [ x"$src" = x ] then echo "install: no input file specified" exit 1 else : fi if [ x"$dir_arg" != x ]; then dst=$src src="" if [ -d $dst ]; then instcmd=: chmodcmd="" else instcmd=$mkdirprog fi else # Waiting for this to be detected by the "$instcmd $src $dsttmp" command # might cause directories to be created, which would be especially bad # if $src (and thus $dsttmp) contains '*'. if [ -f $src -o -d $src ] then : else echo "install: $src does not exist" exit 1 fi if [ x"$dst" = x ] then echo "install: no destination specified" exit 1 else : fi # If destination is a directory, append the input filename; if your system # does not like double slashes in filenames, you may need to add some logic if [ -d $dst ] then dst="$dst"/`basename $src` else : fi fi ## this sed command emulates the dirname command dstdir=`echo $dst | sed -e 's,[^/]*$,,;s,/$,,;s,^$,.,'` # Make sure that the destination directory exists. # this part is taken from Noah Friedman's mkinstalldirs script # Skip lots of stat calls in the usual case. if [ ! -d "$dstdir" ]; then defaultIFS=' ' IFS="${IFS-${defaultIFS}}" oIFS="${IFS}" # Some sh's can't handle IFS=/ for some reason. IFS='%' set - `echo ${dstdir} | sed -e 's@/@%@g' -e 's@^%@/@'` IFS="${oIFS}" pathcomp='' while [ $# -ne 0 ] ; do pathcomp="${pathcomp}${1}" shift if [ ! -d "${pathcomp}" ] ; then $mkdirprog "${pathcomp}" else : fi pathcomp="${pathcomp}/" done fi if [ x"$dir_arg" != x ] then $doit $instcmd $dst && if [ x"$chowncmd" != x ]; then $doit $chowncmd $dst; else : ; fi && if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dst; else : ; fi && if [ x"$stripcmd" != x ]; then $doit $stripcmd $dst; else : ; fi && if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dst; else : ; fi else # If we're going to rename the final executable, determine the name now. if [ x"$transformarg" = x ] then dstfile=`basename $dst` else dstfile=`basename $dst $transformbasename | sed $transformarg`$transformbasename fi # don't allow the sed command to completely eliminate the filename if [ x"$dstfile" = x ] then dstfile=`basename $dst` else : fi # Make a temp file name in the proper directory. dsttmp=$dstdir/#inst.$$# # Move or copy the file name to the temp name $doit $instcmd $src $dsttmp && trap "rm -f ${dsttmp}" 0 && # and set any options; do chmod last to preserve setuid bits # If any of these fail, we abort the whole thing. If we want to # ignore errors from any of these, just make sure not to ignore # errors from the above "$doit $instcmd $src $dsttmp" command. if [ x"$chowncmd" != x ]; then $doit $chowncmd $dsttmp; else :;fi && if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dsttmp; else :;fi && if [ x"$stripcmd" != x ]; then $doit $stripcmd $dsttmp; else :;fi && if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dsttmp; else :;fi && # Now rename the file to the real destination. $doit $rmcmd -f $dstdir/$dstfile && $doit $mvcmd $dsttmp $dstdir/$dstfile fi && exit 0 cmtk-3.3.1/Utilities/mxml/mxml-attr.c000066400000000000000000000160001276303427400175020ustar00rootroot00000000000000/* * "$Id: mxml-attr.c 408 2010-09-19 05:26:46Z mike $" * * Attribute support code for Mini-XML, a small XML-like file parsing library. * * Copyright 2003-2010 by Michael R Sweet. * * These coded instructions, statements, and computer programs are the * property of Michael R Sweet and are protected by Federal copyright * law. Distribution and use rights are outlined in the file "COPYING" * which should have been included with this file. If this file is * missing or damaged, see the license at: * * http://www.minixml.org/ * * Contents: * * mxmlElementDeleteAttr() - Delete an attribute. * mxmlElementGetAttr() - Get an attribute. * mxmlElementSetAttr() - Set an attribute. * mxmlElementSetAttrf() - Set an attribute with a formatted value. * mxml_set_attr() - Set or add an attribute name/value pair. */ /* * Include necessary headers... */ #include "config.h" #include "mxml.h" /* * Local functions... */ static int mxml_set_attr(mxml_node_t *node, const char *name, char *value); /* * 'mxmlElementDeleteAttr()' - Delete an attribute. * * @since Mini-XML 2.4@ */ void mxmlElementDeleteAttr(mxml_node_t *node,/* I - Element */ const char *name)/* I - Attribute name */ { int i; /* Looping var */ mxml_attr_t *attr; /* Cirrent attribute */ #ifdef DEBUG fprintf(stderr, "mxmlElementDeleteAttr(node=%p, name=\"%s\")\n", node, name ? name : "(null)"); #endif /* DEBUG */ /* * Range check input... */ if (!node || node->type != MXML_ELEMENT || !name) return; /* * Look for the attribute... */ for (i = node->value.element.num_attrs, attr = node->value.element.attrs; i > 0; i --, attr ++) { #ifdef DEBUG printf(" %s=\"%s\"\n", attr->name, attr->value); #endif /* DEBUG */ if (!strcmp(attr->name, name)) { /* * Delete this attribute... */ free(attr->name); free(attr->value); i --; if (i > 0) memmove(attr, attr + 1, i * sizeof(mxml_attr_t)); node->value.element.num_attrs --; return; } } } /* * 'mxmlElementGetAttr()' - Get an attribute. * * This function returns NULL if the node is not an element or the * named attribute does not exist. */ const char * /* O - Attribute value or NULL */ mxmlElementGetAttr(mxml_node_t *node, /* I - Element node */ const char *name) /* I - Name of attribute */ { int i; /* Looping var */ mxml_attr_t *attr; /* Cirrent attribute */ #ifdef DEBUG fprintf(stderr, "mxmlElementGetAttr(node=%p, name=\"%s\")\n", node, name ? name : "(null)"); #endif /* DEBUG */ /* * Range check input... */ if (!node || node->type != MXML_ELEMENT || !name) return (NULL); /* * Look for the attribute... */ for (i = node->value.element.num_attrs, attr = node->value.element.attrs; i > 0; i --, attr ++) { #ifdef DEBUG printf(" %s=\"%s\"\n", attr->name, attr->value); #endif /* DEBUG */ if (!strcmp(attr->name, name)) { #ifdef DEBUG printf(" Returning \"%s\"!\n", attr->value); #endif /* DEBUG */ return (attr->value); } } /* * Didn't find attribute, so return NULL... */ #ifdef DEBUG puts(" Returning NULL!\n"); #endif /* DEBUG */ return (NULL); } /* * 'mxmlElementSetAttr()' - Set an attribute. * * If the named attribute already exists, the value of the attribute * is replaced by the new string value. The string value is copied * into the element node. This function does nothing if the node is * not an element. */ void mxmlElementSetAttr(mxml_node_t *node, /* I - Element node */ const char *name, /* I - Name of attribute */ const char *value) /* I - Attribute value */ { char *valuec; /* Copy of value */ #ifdef DEBUG fprintf(stderr, "mxmlElementSetAttr(node=%p, name=\"%s\", value=\"%s\")\n", node, name ? name : "(null)", value ? value : "(null)"); #endif /* DEBUG */ /* * Range check input... */ if (!node || node->type != MXML_ELEMENT || !name) return; if (value) valuec = strdup(value); else valuec = NULL; if (mxml_set_attr(node, name, valuec)) free(valuec); } /* * 'mxmlElementSetAttrf()' - Set an attribute with a formatted value. * * If the named attribute already exists, the value of the attribute * is replaced by the new formatted string. The formatted string value is * copied into the element node. This function does nothing if the node * is not an element. * * @since Mini-XML 2.3@ */ void mxmlElementSetAttrf(mxml_node_t *node, /* I - Element node */ const char *name, /* I - Name of attribute */ const char *format,/* I - Printf-style attribute value */ ...) /* I - Additional arguments as needed */ { va_list ap; /* Argument pointer */ char *value; /* Value */ #ifdef DEBUG fprintf(stderr, "mxmlElementSetAttrf(node=%p, name=\"%s\", format=\"%s\", ...)\n", node, name ? name : "(null)", format ? format : "(null)"); #endif /* DEBUG */ /* * Range check input... */ if (!node || node->type != MXML_ELEMENT || !name || !format) return; /* * Format the value... */ va_start(ap, format); value = _mxml_vstrdupf(format, ap); va_end(ap); if (!value) mxml_error("Unable to allocate memory for attribute '%s' in element %s!", name, node->value.element.name); else if (mxml_set_attr(node, name, value)) free(value); } /* * 'mxml_set_attr()' - Set or add an attribute name/value pair. */ static int /* O - 0 on success, -1 on failure */ mxml_set_attr(mxml_node_t *node, /* I - Element node */ const char *name, /* I - Attribute name */ char *value) /* I - Attribute value */ { int i; /* Looping var */ mxml_attr_t *attr; /* New attribute */ /* * Look for the attribute... */ for (i = node->value.element.num_attrs, attr = node->value.element.attrs; i > 0; i --, attr ++) if (!strcmp(attr->name, name)) { /* * Free the old value as needed... */ if (attr->value) free(attr->value); attr->value = value; return (0); } /* * Add a new attribute... */ if (node->value.element.num_attrs == 0) attr = malloc(sizeof(mxml_attr_t)); else attr = realloc(node->value.element.attrs, (node->value.element.num_attrs + 1) * sizeof(mxml_attr_t)); if (!attr) { mxml_error("Unable to allocate memory for attribute '%s' in element %s!", name, node->value.element.name); return (-1); } node->value.element.attrs = attr; attr += node->value.element.num_attrs; if ((attr->name = strdup(name)) == NULL) { mxml_error("Unable to allocate memory for attribute '%s' in element %s!", name, node->value.element.name); return (-1); } attr->value = value; node->value.element.num_attrs ++; return (0); } /* * End of "$Id: mxml-attr.c 408 2010-09-19 05:26:46Z mike $". */ cmtk-3.3.1/Utilities/mxml/mxml-entity.c000066400000000000000000000244321276303427400200540ustar00rootroot00000000000000/* * "$Id: mxml-entity.c 408 2010-09-19 05:26:46Z mike $" * * Character entity support code for Mini-XML, a small XML-like * file parsing library. * * Copyright 2003-2010 by Michael R Sweet. * * These coded instructions, statements, and computer programs are the * property of Michael R Sweet and are protected by Federal copyright * law. Distribution and use rights are outlined in the file "COPYING" * which should have been included with this file. If this file is * missing or damaged, see the license at: * * http://www.minixml.org/ * * Contents: * * mxmlEntityAddCallback() - Add a callback to convert entities to * Unicode. * mxmlEntityGetName() - Get the name that corresponds to the * character value. * mxmlEntityGetValue() - Get the character corresponding to a named * entity. * mxmlEntityRemoveCallback() - Remove a callback. * _mxml_entity_cb() - Lookup standard (X)HTML entities. */ /* * Include necessary headers... */ #include "mxml-private.h" /* * 'mxmlEntityAddCallback()' - Add a callback to convert entities to Unicode. */ int /* O - 0 on success, -1 on failure */ mxmlEntityAddCallback( mxml_entity_cb_t cb) /* I - Callback function to add */ { _mxml_global_t *global = _mxml_global(); /* Global data */ if (global->num_entity_cbs < (int)(sizeof(global->entity_cbs) / sizeof(global->entity_cbs[0]))) { global->entity_cbs[global->num_entity_cbs] = cb; global->num_entity_cbs ++; return (0); } else { mxml_error("Unable to add entity callback!"); return (-1); } } /* * 'mxmlEntityGetName()' - Get the name that corresponds to the character value. * * If val does not need to be represented by a named entity, NULL is returned. */ const char * /* O - Entity name or NULL */ mxmlEntityGetName(int val) /* I - Character value */ { switch (val) { case '&' : return ("amp"); case '<' : return ("lt"); case '>' : return ("gt"); case '\"' : return ("quot"); default : return (NULL); } } /* * 'mxmlEntityGetValue()' - Get the character corresponding to a named entity. * * The entity name can also be a numeric constant. -1 is returned if the * name is not known. */ int /* O - Character value or -1 on error */ mxmlEntityGetValue(const char *name) /* I - Entity name */ { int i; /* Looping var */ int ch; /* Character value */ _mxml_global_t *global = _mxml_global(); /* Global data */ for (i = 0; i < global->num_entity_cbs; i ++) if ((ch = (global->entity_cbs[i])(name)) >= 0) return (ch); return (-1); } /* * 'mxmlEntityRemoveCallback()' - Remove a callback. */ void mxmlEntityRemoveCallback( mxml_entity_cb_t cb) /* I - Callback function to remove */ { int i; /* Looping var */ _mxml_global_t *global = _mxml_global(); /* Global data */ for (i = 0; i < global->num_entity_cbs; i ++) if (cb == global->entity_cbs[i]) { /* * Remove the callback... */ global->num_entity_cbs --; if (i < global->num_entity_cbs) memmove(global->entity_cbs + i, global->entity_cbs + i + 1, (global->num_entity_cbs - i) * sizeof(global->entity_cbs[0])); return; } } /* * '_mxml_entity_cb()' - Lookup standard (X)HTML entities. */ int /* O - Unicode value or -1 */ _mxml_entity_cb(const char *name) /* I - Entity name */ { int diff, /* Difference between names */ current, /* Current entity in search */ first, /* First entity in search */ last; /* Last entity in search */ static const struct { const char *name; /* Entity name */ int val; /* Character value */ } entities[] = { { "AElig", 198 }, { "Aacute", 193 }, { "Acirc", 194 }, { "Agrave", 192 }, { "Alpha", 913 }, { "Aring", 197 }, { "Atilde", 195 }, { "Auml", 196 }, { "Beta", 914 }, { "Ccedil", 199 }, { "Chi", 935 }, { "Dagger", 8225 }, { "Delta", 916 }, { "Dstrok", 208 }, { "ETH", 208 }, { "Eacute", 201 }, { "Ecirc", 202 }, { "Egrave", 200 }, { "Epsilon", 917 }, { "Eta", 919 }, { "Euml", 203 }, { "Gamma", 915 }, { "Iacute", 205 }, { "Icirc", 206 }, { "Igrave", 204 }, { "Iota", 921 }, { "Iuml", 207 }, { "Kappa", 922 }, { "Lambda", 923 }, { "Mu", 924 }, { "Ntilde", 209 }, { "Nu", 925 }, { "OElig", 338 }, { "Oacute", 211 }, { "Ocirc", 212 }, { "Ograve", 210 }, { "Omega", 937 }, { "Omicron", 927 }, { "Oslash", 216 }, { "Otilde", 213 }, { "Ouml", 214 }, { "Phi", 934 }, { "Pi", 928 }, { "Prime", 8243 }, { "Psi", 936 }, { "Rho", 929 }, { "Scaron", 352 }, { "Sigma", 931 }, { "THORN", 222 }, { "Tau", 932 }, { "Theta", 920 }, { "Uacute", 218 }, { "Ucirc", 219 }, { "Ugrave", 217 }, { "Upsilon", 933 }, { "Uuml", 220 }, { "Xi", 926 }, { "Yacute", 221 }, { "Yuml", 376 }, { "Zeta", 918 }, { "aacute", 225 }, { "acirc", 226 }, { "acute", 180 }, { "aelig", 230 }, { "agrave", 224 }, { "alefsym", 8501 }, { "alpha", 945 }, { "amp", '&' }, { "and", 8743 }, { "ang", 8736 }, { "apos", '\'' }, { "aring", 229 }, { "asymp", 8776 }, { "atilde", 227 }, { "auml", 228 }, { "bdquo", 8222 }, { "beta", 946 }, { "brkbar", 166 }, { "brvbar", 166 }, { "bull", 8226 }, { "cap", 8745 }, { "ccedil", 231 }, { "cedil", 184 }, { "cent", 162 }, { "chi", 967 }, { "circ", 710 }, { "clubs", 9827 }, { "cong", 8773 }, { "copy", 169 }, { "crarr", 8629 }, { "cup", 8746 }, { "curren", 164 }, { "dArr", 8659 }, { "dagger", 8224 }, { "darr", 8595 }, { "deg", 176 }, { "delta", 948 }, { "diams", 9830 }, { "die", 168 }, { "divide", 247 }, { "eacute", 233 }, { "ecirc", 234 }, { "egrave", 232 }, { "empty", 8709 }, { "emsp", 8195 }, { "ensp", 8194 }, { "epsilon", 949 }, { "equiv", 8801 }, { "eta", 951 }, { "eth", 240 }, { "euml", 235 }, { "euro", 8364 }, { "exist", 8707 }, { "fnof", 402 }, { "forall", 8704 }, { "frac12", 189 }, { "frac14", 188 }, { "frac34", 190 }, { "frasl", 8260 }, { "gamma", 947 }, { "ge", 8805 }, { "gt", '>' }, { "hArr", 8660 }, { "harr", 8596 }, { "hearts", 9829 }, { "hellip", 8230 }, { "hibar", 175 }, { "iacute", 237 }, { "icirc", 238 }, { "iexcl", 161 }, { "igrave", 236 }, { "image", 8465 }, { "infin", 8734 }, { "int", 8747 }, { "iota", 953 }, { "iquest", 191 }, { "isin", 8712 }, { "iuml", 239 }, { "kappa", 954 }, { "lArr", 8656 }, { "lambda", 955 }, { "lang", 9001 }, { "laquo", 171 }, { "larr", 8592 }, { "lceil", 8968 }, { "ldquo", 8220 }, { "le", 8804 }, { "lfloor", 8970 }, { "lowast", 8727 }, { "loz", 9674 }, { "lrm", 8206 }, { "lsaquo", 8249 }, { "lsquo", 8216 }, { "lt", '<' }, { "macr", 175 }, { "mdash", 8212 }, { "micro", 181 }, { "middot", 183 }, { "minus", 8722 }, { "mu", 956 }, { "nabla", 8711 }, { "nbsp", 160 }, { "ndash", 8211 }, { "ne", 8800 }, { "ni", 8715 }, { "not", 172 }, { "notin", 8713 }, { "nsub", 8836 }, { "ntilde", 241 }, { "nu", 957 }, { "oacute", 243 }, { "ocirc", 244 }, { "oelig", 339 }, { "ograve", 242 }, { "oline", 8254 }, { "omega", 969 }, { "omicron", 959 }, { "oplus", 8853 }, { "or", 8744 }, { "ordf", 170 }, { "ordm", 186 }, { "oslash", 248 }, { "otilde", 245 }, { "otimes", 8855 }, { "ouml", 246 }, { "para", 182 }, { "part", 8706 }, { "permil", 8240 }, { "perp", 8869 }, { "phi", 966 }, { "pi", 960 }, { "piv", 982 }, { "plusmn", 177 }, { "pound", 163 }, { "prime", 8242 }, { "prod", 8719 }, { "prop", 8733 }, { "psi", 968 }, { "quot", '\"' }, { "rArr", 8658 }, { "radic", 8730 }, { "rang", 9002 }, { "raquo", 187 }, { "rarr", 8594 }, { "rceil", 8969 }, { "rdquo", 8221 }, { "real", 8476 }, { "reg", 174 }, { "rfloor", 8971 }, { "rho", 961 }, { "rlm", 8207 }, { "rsaquo", 8250 }, { "rsquo", 8217 }, { "sbquo", 8218 }, { "scaron", 353 }, { "sdot", 8901 }, { "sect", 167 }, { "shy", 173 }, { "sigma", 963 }, { "sigmaf", 962 }, { "sim", 8764 }, { "spades", 9824 }, { "sub", 8834 }, { "sube", 8838 }, { "sum", 8721 }, { "sup", 8835 }, { "sup1", 185 }, { "sup2", 178 }, { "sup3", 179 }, { "supe", 8839 }, { "szlig", 223 }, { "tau", 964 }, { "there4", 8756 }, { "theta", 952 }, { "thetasym", 977 }, { "thinsp", 8201 }, { "thorn", 254 }, { "tilde", 732 }, { "times", 215 }, { "trade", 8482 }, { "uArr", 8657 }, { "uacute", 250 }, { "uarr", 8593 }, { "ucirc", 251 }, { "ugrave", 249 }, { "uml", 168 }, { "upsih", 978 }, { "upsilon", 965 }, { "uuml", 252 }, { "weierp", 8472 }, { "xi", 958 }, { "yacute", 253 }, { "yen", 165 }, { "yuml", 255 }, { "zeta", 950 }, { "zwj", 8205 }, { "zwnj", 8204 } }; /* * Do a binary search for the named entity... */ first = 0; last = (int)(sizeof(entities) / sizeof(entities[0]) - 1); while ((last - first) > 1) { current = (first + last) / 2; if ((diff = strcmp(name, entities[current].name)) == 0) return (entities[current].val); else if (diff < 0) last = current; else first = current; } /* * If we get here, there is a small chance that there is still * a match; check first and last... */ if (!strcmp(name, entities[first].name)) return (entities[first].val); else if (!strcmp(name, entities[last].name)) return (entities[last].val); else return (-1); } /* * End of "$Id: mxml-entity.c 408 2010-09-19 05:26:46Z mike $". */ cmtk-3.3.1/Utilities/mxml/mxml-file.c000066400000000000000000002027051276303427400174600ustar00rootroot00000000000000/* * "$Id: mxml-file.c 438 2011-03-24 05:47:51Z mike $" * * File loading code for Mini-XML, a small XML-like file parsing library. * * Copyright 2003-2011 by Michael R Sweet. * * These coded instructions, statements, and computer programs are the * property of Michael R Sweet and are protected by Federal copyright * law. Distribution and use rights are outlined in the file "COPYING" * which should have been included with this file. If this file is * missing or damaged, see the license at: * * http://www.minixml.org/ * * Contents: * * mxmlLoadFd() - Load a file descriptor into an XML node tree. * mxmlLoadFile() - Load a file into an XML node tree. * mxmlLoadString() - Load a string into an XML node tree. * mxmlSaveAllocString() - Save an XML tree to an allocated string. * mxmlSaveFd() - Save an XML tree to a file descriptor. * mxmlSaveFile() - Save an XML tree to a file. * mxmlSaveString() - Save an XML node tree to a string. * mxmlSAXLoadFd() - Load a file descriptor into an XML node tree * using a SAX callback. * mxmlSAXLoadFile() - Load a file into an XML node tree * using a SAX callback. * mxmlSAXLoadString() - Load a string into an XML node tree * using a SAX callback. * mxmlSetCustomHandlers() - Set the handling functions for custom data. * mxmlSetErrorCallback() - Set the error message callback. * mxmlSetWrapMargin() - Set the wrap margin when saving XML data. * mxml_add_char() - Add a character to a buffer, expanding as needed. * mxml_fd_getc() - Read a character from a file descriptor. * mxml_fd_putc() - Write a character to a file descriptor. * mxml_fd_read() - Read a buffer of data from a file descriptor. * mxml_fd_write() - Write a buffer of data to a file descriptor. * mxml_file_getc() - Get a character from a file. * mxml_file_putc() - Write a character to a file. * mxml_get_entity() - Get the character corresponding to an entity... * mxml_load_data() - Load data into an XML node tree. * mxml_parse_element() - Parse an element for any attributes... * mxml_string_getc() - Get a character from a string. * mxml_string_putc() - Write a character to a string. * mxml_write_name() - Write a name string. * mxml_write_node() - Save an XML node to a file. * mxml_write_string() - Write a string, escaping & and < as needed. * mxml_write_ws() - Do whitespace callback... */ /* * Include necessary headers... */ #ifndef WIN32 # include #endif /* !WIN32 */ #include "mxml-private.h" /* * Character encoding... */ #define ENCODE_UTF8 0 /* UTF-8 */ #define ENCODE_UTF16BE 1 /* UTF-16 Big-Endian */ #define ENCODE_UTF16LE 2 /* UTF-16 Little-Endian */ /* * Macro to test for a bad XML character... */ #define mxml_bad_char(ch) ((ch) < ' ' && (ch) != '\n' && (ch) != '\r' && (ch) != '\t') /* * Types and structures... */ typedef int (*_mxml_getc_cb_t)(void *, int *); typedef int (*_mxml_putc_cb_t)(int, void *); typedef struct _mxml_fdbuf_s /**** File descriptor buffer ****/ { int fd; /* File descriptor */ unsigned char *current, /* Current position in buffer */ *end, /* End of buffer */ buffer[8192]; /* Character buffer */ } _mxml_fdbuf_t; /* * Local functions... */ static int mxml_add_char(int ch, char **ptr, char **buffer, int *bufsize); static int mxml_fd_getc(void *p, int *encoding); static int mxml_fd_putc(int ch, void *p); static int mxml_fd_read(_mxml_fdbuf_t *buf); static int mxml_fd_write(_mxml_fdbuf_t *buf); static int mxml_file_getc(void *p, int *encoding); static int mxml_file_putc(int ch, void *p); static int mxml_get_entity(mxml_node_t *parent, void *p, int *encoding, _mxml_getc_cb_t getc_cb); static inline int mxml_isspace(int ch) { return (ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n'); } static mxml_node_t *mxml_load_data(mxml_node_t *top, void *p, mxml_load_cb_t cb, _mxml_getc_cb_t getc_cb, mxml_sax_cb_t sax_cb, void *sax_data); static int mxml_parse_element(mxml_node_t *node, void *p, int *encoding, _mxml_getc_cb_t getc_cb); static int mxml_string_getc(void *p, int *encoding); static int mxml_string_putc(int ch, void *p); static int mxml_write_name(const char *s, void *p, _mxml_putc_cb_t putc_cb); static int mxml_write_node(mxml_node_t *node, void *p, mxml_save_cb_t cb, int col, _mxml_putc_cb_t putc_cb, _mxml_global_t *global); static int mxml_write_string(const char *s, void *p, _mxml_putc_cb_t putc_cb); static int mxml_write_ws(mxml_node_t *node, void *p, mxml_save_cb_t cb, int ws, int col, _mxml_putc_cb_t putc_cb); /* * 'mxmlLoadFd()' - Load a file descriptor into an XML node tree. * * The nodes in the specified file are added to the specified top node. * If no top node is provided, the XML file MUST be well-formed with a * single parent node like for the entire file. The callback * function returns the value type that should be used for child nodes. * If MXML_NO_CALLBACK is specified then all child nodes will be either * MXML_ELEMENT or MXML_TEXT nodes. * * The constants MXML_INTEGER_CALLBACK, MXML_OPAQUE_CALLBACK, * MXML_REAL_CALLBACK, and MXML_TEXT_CALLBACK are defined for loading * child nodes of the specified type. */ mxml_node_t * /* O - First node or NULL if the file could not be read. */ mxmlLoadFd(mxml_node_t *top, /* I - Top node */ int fd, /* I - File descriptor to read from */ mxml_load_cb_t cb) /* I - Callback function or MXML_NO_CALLBACK */ { _mxml_fdbuf_t buf; /* File descriptor buffer */ /* * Initialize the file descriptor buffer... */ buf.fd = fd; buf.current = buf.buffer; buf.end = buf.buffer; /* * Read the XML data... */ return (mxml_load_data(top, &buf, cb, mxml_fd_getc, MXML_NO_CALLBACK, NULL)); } /* * 'mxmlLoadFile()' - Load a file into an XML node tree. * * The nodes in the specified file are added to the specified top node. * If no top node is provided, the XML file MUST be well-formed with a * single parent node like for the entire file. The callback * function returns the value type that should be used for child nodes. * If MXML_NO_CALLBACK is specified then all child nodes will be either * MXML_ELEMENT or MXML_TEXT nodes. * * The constants MXML_INTEGER_CALLBACK, MXML_OPAQUE_CALLBACK, * MXML_REAL_CALLBACK, and MXML_TEXT_CALLBACK are defined for loading * child nodes of the specified type. */ mxml_node_t * /* O - First node or NULL if the file could not be read. */ mxmlLoadFile(mxml_node_t *top, /* I - Top node */ FILE *fp, /* I - File to read from */ mxml_load_cb_t cb) /* I - Callback function or MXML_NO_CALLBACK */ { /* * Read the XML data... */ return (mxml_load_data(top, fp, cb, mxml_file_getc, MXML_NO_CALLBACK, NULL)); } /* * 'mxmlLoadString()' - Load a string into an XML node tree. * * The nodes in the specified string are added to the specified top node. * If no top node is provided, the XML string MUST be well-formed with a * single parent node like for the entire string. The callback * function returns the value type that should be used for child nodes. * If MXML_NO_CALLBACK is specified then all child nodes will be either * MXML_ELEMENT or MXML_TEXT nodes. * * The constants MXML_INTEGER_CALLBACK, MXML_OPAQUE_CALLBACK, * MXML_REAL_CALLBACK, and MXML_TEXT_CALLBACK are defined for loading * child nodes of the specified type. */ mxml_node_t * /* O - First node or NULL if the string has errors. */ mxmlLoadString(mxml_node_t *top, /* I - Top node */ const char *s, /* I - String to load */ mxml_load_cb_t cb) /* I - Callback function or MXML_NO_CALLBACK */ { /* * Read the XML data... */ return (mxml_load_data(top, (void *)&s, cb, mxml_string_getc, MXML_NO_CALLBACK, NULL)); } /* * 'mxmlSaveAllocString()' - Save an XML tree to an allocated string. * * This function returns a pointer to a string containing the textual * representation of the XML node tree. The string should be freed * using the free() function when you are done with it. NULL is returned * if the node would produce an empty string or if the string cannot be * allocated. * * The callback argument specifies a function that returns a whitespace * string or NULL before and after each element. If MXML_NO_CALLBACK * is specified, whitespace will only be added before MXML_TEXT nodes * with leading whitespace and before attribute names inside opening * element tags. */ char * /* O - Allocated string or NULL */ mxmlSaveAllocString( mxml_node_t *node, /* I - Node to write */ mxml_save_cb_t cb) /* I - Whitespace callback or MXML_NO_CALLBACK */ { int bytes; /* Required bytes */ char buffer[8192]; /* Temporary buffer */ char *s; /* Allocated string */ /* * Write the node to the temporary buffer... */ bytes = mxmlSaveString(node, buffer, sizeof(buffer), cb); if (bytes <= 0) return (NULL); if (bytes < (int)(sizeof(buffer) - 1)) { /* * Node fit inside the buffer, so just duplicate that string and * return... */ return (strdup(buffer)); } /* * Allocate a buffer of the required size and save the node to the * new buffer... */ if ((s = malloc(bytes + 1)) == NULL) return (NULL); mxmlSaveString(node, s, bytes + 1, cb); /* * Return the allocated string... */ return (s); } /* * 'mxmlSaveFd()' - Save an XML tree to a file descriptor. * * The callback argument specifies a function that returns a whitespace * string or NULL before and after each element. If MXML_NO_CALLBACK * is specified, whitespace will only be added before MXML_TEXT nodes * with leading whitespace and before attribute names inside opening * element tags. */ int /* O - 0 on success, -1 on error. */ mxmlSaveFd(mxml_node_t *node, /* I - Node to write */ int fd, /* I - File descriptor to write to */ mxml_save_cb_t cb) /* I - Whitespace callback or MXML_NO_CALLBACK */ { int col; /* Final column */ _mxml_fdbuf_t buf; /* File descriptor buffer */ _mxml_global_t *global = _mxml_global(); /* Global data */ /* * Initialize the file descriptor buffer... */ buf.fd = fd; buf.current = buf.buffer; buf.end = buf.buffer + sizeof(buf.buffer); /* * Write the node... */ if ((col = mxml_write_node(node, &buf, cb, 0, mxml_fd_putc, global)) < 0) return (-1); if (col > 0) if (mxml_fd_putc('\n', &buf) < 0) return (-1); /* * Flush and return... */ return (mxml_fd_write(&buf)); } /* * 'mxmlSaveFile()' - Save an XML tree to a file. * * The callback argument specifies a function that returns a whitespace * string or NULL before and after each element. If MXML_NO_CALLBACK * is specified, whitespace will only be added before MXML_TEXT nodes * with leading whitespace and before attribute names inside opening * element tags. */ int /* O - 0 on success, -1 on error. */ mxmlSaveFile(mxml_node_t *node, /* I - Node to write */ FILE *fp, /* I - File to write to */ mxml_save_cb_t cb) /* I - Whitespace callback or MXML_NO_CALLBACK */ { int col; /* Final column */ _mxml_global_t *global = _mxml_global(); /* Global data */ /* * Write the node... */ if ((col = mxml_write_node(node, fp, cb, 0, mxml_file_putc, global)) < 0) return (-1); if (col > 0) if (putc('\n', fp) < 0) return (-1); /* * Return 0 (success)... */ return (0); } /* * 'mxmlSaveString()' - Save an XML node tree to a string. * * This function returns the total number of bytes that would be * required for the string but only copies (bufsize - 1) characters * into the specified buffer. * * The callback argument specifies a function that returns a whitespace * string or NULL before and after each element. If MXML_NO_CALLBACK * is specified, whitespace will only be added before MXML_TEXT nodes * with leading whitespace and before attribute names inside opening * element tags. */ int /* O - Size of string */ mxmlSaveString(mxml_node_t *node, /* I - Node to write */ char *buffer, /* I - String buffer */ int bufsize, /* I - Size of string buffer */ mxml_save_cb_t cb) /* I - Whitespace callback or MXML_NO_CALLBACK */ { int col; /* Final column */ char *ptr[2]; /* Pointers for putc_cb */ _mxml_global_t *global = _mxml_global(); /* Global data */ /* * Write the node... */ ptr[0] = buffer; ptr[1] = buffer + bufsize; if ((col = mxml_write_node(node, ptr, cb, 0, mxml_string_putc, global)) < 0) return (-1); if (col > 0) mxml_string_putc('\n', ptr); /* * Nul-terminate the buffer... */ if (ptr[0] >= ptr[1]) buffer[bufsize - 1] = '\0'; else ptr[0][0] = '\0'; /* * Return the number of characters... */ return (ptr[0] - buffer); } /* * 'mxmlSAXLoadFd()' - Load a file descriptor into an XML node tree * using a SAX callback. * * The nodes in the specified file are added to the specified top node. * If no top node is provided, the XML file MUST be well-formed with a * single parent node like for the entire file. The callback * function returns the value type that should be used for child nodes. * If MXML_NO_CALLBACK is specified then all child nodes will be either * MXML_ELEMENT or MXML_TEXT nodes. * * The constants MXML_INTEGER_CALLBACK, MXML_OPAQUE_CALLBACK, * MXML_REAL_CALLBACK, and MXML_TEXT_CALLBACK are defined for loading * child nodes of the specified type. * * The SAX callback must call mxmlRetain() for any nodes that need to * be kept for later use. Otherwise, nodes are deleted when the parent * node is closed or after each data, comment, CDATA, or directive node. * * @since Mini-XML 2.3@ */ mxml_node_t * /* O - First node or NULL if the file could not be read. */ mxmlSAXLoadFd(mxml_node_t *top, /* I - Top node */ int fd, /* I - File descriptor to read from */ mxml_load_cb_t cb, /* I - Callback function or MXML_NO_CALLBACK */ mxml_sax_cb_t sax_cb, /* I - SAX callback or MXML_NO_CALLBACK */ void *sax_data) /* I - SAX user data */ { _mxml_fdbuf_t buf; /* File descriptor buffer */ /* * Initialize the file descriptor buffer... */ buf.fd = fd; buf.current = buf.buffer; buf.end = buf.buffer; /* * Read the XML data... */ return (mxml_load_data(top, &buf, cb, mxml_fd_getc, sax_cb, sax_data)); } /* * 'mxmlSAXLoadFile()' - Load a file into an XML node tree * using a SAX callback. * * The nodes in the specified file are added to the specified top node. * If no top node is provided, the XML file MUST be well-formed with a * single parent node like for the entire file. The callback * function returns the value type that should be used for child nodes. * If MXML_NO_CALLBACK is specified then all child nodes will be either * MXML_ELEMENT or MXML_TEXT nodes. * * The constants MXML_INTEGER_CALLBACK, MXML_OPAQUE_CALLBACK, * MXML_REAL_CALLBACK, and MXML_TEXT_CALLBACK are defined for loading * child nodes of the specified type. * * The SAX callback must call mxmlRetain() for any nodes that need to * be kept for later use. Otherwise, nodes are deleted when the parent * node is closed or after each data, comment, CDATA, or directive node. * * @since Mini-XML 2.3@ */ mxml_node_t * /* O - First node or NULL if the file could not be read. */ mxmlSAXLoadFile( mxml_node_t *top, /* I - Top node */ FILE *fp, /* I - File to read from */ mxml_load_cb_t cb, /* I - Callback function or MXML_NO_CALLBACK */ mxml_sax_cb_t sax_cb, /* I - SAX callback or MXML_NO_CALLBACK */ void *sax_data) /* I - SAX user data */ { /* * Read the XML data... */ return (mxml_load_data(top, fp, cb, mxml_file_getc, sax_cb, sax_data)); } /* * 'mxmlSAXLoadString()' - Load a string into an XML node tree * using a SAX callback. * * The nodes in the specified string are added to the specified top node. * If no top node is provided, the XML string MUST be well-formed with a * single parent node like for the entire string. The callback * function returns the value type that should be used for child nodes. * If MXML_NO_CALLBACK is specified then all child nodes will be either * MXML_ELEMENT or MXML_TEXT nodes. * * The constants MXML_INTEGER_CALLBACK, MXML_OPAQUE_CALLBACK, * MXML_REAL_CALLBACK, and MXML_TEXT_CALLBACK are defined for loading * child nodes of the specified type. * * The SAX callback must call mxmlRetain() for any nodes that need to * be kept for later use. Otherwise, nodes are deleted when the parent * node is closed or after each data, comment, CDATA, or directive node. * * @since Mini-XML 2.3@ */ mxml_node_t * /* O - First node or NULL if the string has errors. */ mxmlSAXLoadString( mxml_node_t *top, /* I - Top node */ const char *s, /* I - String to load */ mxml_load_cb_t cb, /* I - Callback function or MXML_NO_CALLBACK */ mxml_sax_cb_t sax_cb, /* I - SAX callback or MXML_NO_CALLBACK */ void *sax_data) /* I - SAX user data */ { /* * Read the XML data... */ return (mxml_load_data(top, (void *)&s, cb, mxml_string_getc, sax_cb, sax_data)); } /* * 'mxmlSetCustomHandlers()' - Set the handling functions for custom data. * * The load function accepts a node pointer and a data string and must * return 0 on success and non-zero on error. * * The save function accepts a node pointer and must return a malloc'd * string on success and NULL on error. * */ void mxmlSetCustomHandlers( mxml_custom_load_cb_t load, /* I - Load function */ mxml_custom_save_cb_t save) /* I - Save function */ { _mxml_global_t *global = _mxml_global(); /* Global data */ global->custom_load_cb = load; global->custom_save_cb = save; } /* * 'mxmlSetErrorCallback()' - Set the error message callback. */ void mxmlSetErrorCallback(mxml_error_cb_t cb)/* I - Error callback function */ { _mxml_global_t *global = _mxml_global(); /* Global data */ global->error_cb = cb; } /* * 'mxmlSetWrapMargin()' - Set the wrap margin when saving XML data. * * Wrapping is disabled when "column" is 0. * * @since Mini-XML 2.3@ */ void mxmlSetWrapMargin(int column) /* I - Column for wrapping, 0 to disable wrapping */ { _mxml_global_t *global = _mxml_global(); /* Global data */ global->wrap = column; } /* * 'mxml_add_char()' - Add a character to a buffer, expanding as needed. */ static int /* O - 0 on success, -1 on error */ mxml_add_char(int ch, /* I - Character to add */ char **bufptr, /* IO - Current position in buffer */ char **buffer, /* IO - Current buffer */ int *bufsize) /* IO - Current buffer size */ { char *newbuffer; /* New buffer value */ if (*bufptr >= (*buffer + *bufsize - 4)) { /* * Increase the size of the buffer... */ if (*bufsize < 1024) (*bufsize) *= 2; else (*bufsize) += 1024; if ((newbuffer = realloc(*buffer, *bufsize)) == NULL) { free(*buffer); mxml_error("Unable to expand string buffer to %d bytes!", *bufsize); return (-1); } *bufptr = newbuffer + (*bufptr - *buffer); *buffer = newbuffer; } if (ch < 0x80) { /* * Single byte ASCII... */ *(*bufptr)++ = ch; } else if (ch < 0x800) { /* * Two-byte UTF-8... */ *(*bufptr)++ = 0xc0 | (ch >> 6); *(*bufptr)++ = 0x80 | (ch & 0x3f); } else if (ch < 0x10000) { /* * Three-byte UTF-8... */ *(*bufptr)++ = 0xe0 | (ch >> 12); *(*bufptr)++ = 0x80 | ((ch >> 6) & 0x3f); *(*bufptr)++ = 0x80 | (ch & 0x3f); } else { /* * Four-byte UTF-8... */ *(*bufptr)++ = 0xf0 | (ch >> 18); *(*bufptr)++ = 0x80 | ((ch >> 12) & 0x3f); *(*bufptr)++ = 0x80 | ((ch >> 6) & 0x3f); *(*bufptr)++ = 0x80 | (ch & 0x3f); } return (0); } /* * 'mxml_fd_getc()' - Read a character from a file descriptor. */ static int /* O - Character or EOF */ mxml_fd_getc(void *p, /* I - File descriptor buffer */ int *encoding) /* IO - Encoding */ { _mxml_fdbuf_t *buf; /* File descriptor buffer */ int ch, /* Current character */ temp; /* Temporary character */ /* * Grab the next character in the buffer... */ buf = (_mxml_fdbuf_t *)p; if (buf->current >= buf->end) if (mxml_fd_read(buf) < 0) return (EOF); ch = *(buf->current)++; switch (*encoding) { case ENCODE_UTF8 : /* * Got a UTF-8 character; convert UTF-8 to Unicode and return... */ if (!(ch & 0x80)) { #if DEBUG > 1 printf("mxml_fd_getc: %c (0x%04x)\n", ch < ' ' ? '.' : ch, ch); #endif /* DEBUG > 1 */ if (mxml_bad_char(ch)) { mxml_error("Bad control character 0x%02x not allowed by XML standard!", ch); return (EOF); } return (ch); } else if (ch == 0xfe) { /* * UTF-16 big-endian BOM? */ if (buf->current >= buf->end) if (mxml_fd_read(buf) < 0) return (EOF); ch = *(buf->current)++; if (ch != 0xff) return (EOF); *encoding = ENCODE_UTF16BE; return (mxml_fd_getc(p, encoding)); } else if (ch == 0xff) { /* * UTF-16 little-endian BOM? */ if (buf->current >= buf->end) if (mxml_fd_read(buf) < 0) return (EOF); ch = *(buf->current)++; if (ch != 0xfe) return (EOF); *encoding = ENCODE_UTF16LE; return (mxml_fd_getc(p, encoding)); } else if ((ch & 0xe0) == 0xc0) { /* * Two-byte value... */ if (buf->current >= buf->end) if (mxml_fd_read(buf) < 0) return (EOF); temp = *(buf->current)++; if ((temp & 0xc0) != 0x80) return (EOF); ch = ((ch & 0x1f) << 6) | (temp & 0x3f); if (ch < 0x80) { mxml_error("Invalid UTF-8 sequence for character 0x%04x!", ch); return (EOF); } } else if ((ch & 0xf0) == 0xe0) { /* * Three-byte value... */ if (buf->current >= buf->end) if (mxml_fd_read(buf) < 0) return (EOF); temp = *(buf->current)++; if ((temp & 0xc0) != 0x80) return (EOF); ch = ((ch & 0x0f) << 6) | (temp & 0x3f); if (buf->current >= buf->end) if (mxml_fd_read(buf) < 0) return (EOF); temp = *(buf->current)++; if ((temp & 0xc0) != 0x80) return (EOF); ch = (ch << 6) | (temp & 0x3f); if (ch < 0x800) { mxml_error("Invalid UTF-8 sequence for character 0x%04x!", ch); return (EOF); } /* * Ignore (strip) Byte Order Mark (BOM)... */ if (ch == 0xfeff) return (mxml_fd_getc(p, encoding)); } else if ((ch & 0xf8) == 0xf0) { /* * Four-byte value... */ if (buf->current >= buf->end) if (mxml_fd_read(buf) < 0) return (EOF); temp = *(buf->current)++; if ((temp & 0xc0) != 0x80) return (EOF); ch = ((ch & 0x07) << 6) | (temp & 0x3f); if (buf->current >= buf->end) if (mxml_fd_read(buf) < 0) return (EOF); temp = *(buf->current)++; if ((temp & 0xc0) != 0x80) return (EOF); ch = (ch << 6) | (temp & 0x3f); if (buf->current >= buf->end) if (mxml_fd_read(buf) < 0) return (EOF); temp = *(buf->current)++; if ((temp & 0xc0) != 0x80) return (EOF); ch = (ch << 6) | (temp & 0x3f); if (ch < 0x10000) { mxml_error("Invalid UTF-8 sequence for character 0x%04x!", ch); return (EOF); } } else return (EOF); break; case ENCODE_UTF16BE : /* * Read UTF-16 big-endian char... */ if (buf->current >= buf->end) if (mxml_fd_read(buf) < 0) return (EOF); temp = *(buf->current)++; ch = (ch << 8) | temp; if (mxml_bad_char(ch)) { mxml_error("Bad control character 0x%02x not allowed by XML standard!", ch); return (EOF); } else if (ch >= 0xd800 && ch <= 0xdbff) { /* * Multi-word UTF-16 char... */ int lch; if (buf->current >= buf->end) if (mxml_fd_read(buf) < 0) return (EOF); lch = *(buf->current)++; if (buf->current >= buf->end) if (mxml_fd_read(buf) < 0) return (EOF); temp = *(buf->current)++; lch = (lch << 8) | temp; if (lch < 0xdc00 || lch >= 0xdfff) return (EOF); ch = (((ch & 0x3ff) << 10) | (lch & 0x3ff)) + 0x10000; } break; case ENCODE_UTF16LE : /* * Read UTF-16 little-endian char... */ if (buf->current >= buf->end) if (mxml_fd_read(buf) < 0) return (EOF); temp = *(buf->current)++; ch |= (temp << 8); if (mxml_bad_char(ch)) { mxml_error("Bad control character 0x%02x not allowed by XML standard!", ch); return (EOF); } else if (ch >= 0xd800 && ch <= 0xdbff) { /* * Multi-word UTF-16 char... */ int lch; if (buf->current >= buf->end) if (mxml_fd_read(buf) < 0) return (EOF); lch = *(buf->current)++; if (buf->current >= buf->end) if (mxml_fd_read(buf) < 0) return (EOF); temp = *(buf->current)++; lch |= (temp << 8); if (lch < 0xdc00 || lch >= 0xdfff) return (EOF); ch = (((ch & 0x3ff) << 10) | (lch & 0x3ff)) + 0x10000; } break; } #if DEBUG > 1 printf("mxml_fd_getc: %c (0x%04x)\n", ch < ' ' ? '.' : ch, ch); #endif /* DEBUG > 1 */ return (ch); } /* * 'mxml_fd_putc()' - Write a character to a file descriptor. */ static int /* O - 0 on success, -1 on error */ mxml_fd_putc(int ch, /* I - Character */ void *p) /* I - File descriptor buffer */ { _mxml_fdbuf_t *buf; /* File descriptor buffer */ /* * Flush the write buffer as needed... */ buf = (_mxml_fdbuf_t *)p; if (buf->current >= buf->end) if (mxml_fd_write(buf) < 0) return (-1); *(buf->current)++ = ch; /* * Return successfully... */ return (0); } /* * 'mxml_fd_read()' - Read a buffer of data from a file descriptor. */ static int /* O - 0 on success, -1 on error */ mxml_fd_read(_mxml_fdbuf_t *buf) /* I - File descriptor buffer */ { int bytes; /* Bytes read... */ /* * Range check input... */ if (!buf) return (-1); /* * Read from the file descriptor... */ while ((bytes = read(buf->fd, buf->buffer, sizeof(buf->buffer))) < 0) #ifdef EINTR if (errno != EAGAIN && errno != EINTR) #else if (errno != EAGAIN) #endif /* EINTR */ return (-1); if (bytes == 0) return (-1); /* * Update the pointers and return success... */ buf->current = buf->buffer; buf->end = buf->buffer + bytes; return (0); } /* * 'mxml_fd_write()' - Write a buffer of data to a file descriptor. */ static int /* O - 0 on success, -1 on error */ mxml_fd_write(_mxml_fdbuf_t *buf) /* I - File descriptor buffer */ { int bytes; /* Bytes written */ unsigned char *ptr; /* Pointer into buffer */ /* * Range check... */ if (!buf) return (-1); /* * Return 0 if there is nothing to write... */ if (buf->current == buf->buffer) return (0); /* * Loop until we have written everything... */ for (ptr = buf->buffer; ptr < buf->current; ptr += bytes) if ((bytes = write(buf->fd, ptr, buf->current - ptr)) < 0) return (-1); /* * All done, reset pointers and return success... */ buf->current = buf->buffer; return (0); } /* * 'mxml_file_getc()' - Get a character from a file. */ static int /* O - Character or EOF */ mxml_file_getc(void *p, /* I - Pointer to file */ int *encoding) /* IO - Encoding */ { int ch, /* Character from file */ temp; /* Temporary character */ FILE *fp; /* Pointer to file */ /* * Read a character from the file and see if it is EOF or ASCII... */ fp = (FILE *)p; ch = getc(fp); if (ch == EOF) return (EOF); switch (*encoding) { case ENCODE_UTF8 : /* * Got a UTF-8 character; convert UTF-8 to Unicode and return... */ if (!(ch & 0x80)) { if (mxml_bad_char(ch)) { mxml_error("Bad control character 0x%02x not allowed by XML standard!", ch); return (EOF); } #if DEBUG > 1 printf("mxml_file_getc: %c (0x%04x)\n", ch < ' ' ? '.' : ch, ch); #endif /* DEBUG > 1 */ return (ch); } else if (ch == 0xfe) { /* * UTF-16 big-endian BOM? */ ch = getc(fp); if (ch != 0xff) return (EOF); *encoding = ENCODE_UTF16BE; return (mxml_file_getc(p, encoding)); } else if (ch == 0xff) { /* * UTF-16 little-endian BOM? */ ch = getc(fp); if (ch != 0xfe) return (EOF); *encoding = ENCODE_UTF16LE; return (mxml_file_getc(p, encoding)); } else if ((ch & 0xe0) == 0xc0) { /* * Two-byte value... */ if ((temp = getc(fp)) == EOF || (temp & 0xc0) != 0x80) return (EOF); ch = ((ch & 0x1f) << 6) | (temp & 0x3f); if (ch < 0x80) { mxml_error("Invalid UTF-8 sequence for character 0x%04x!", ch); return (EOF); } } else if ((ch & 0xf0) == 0xe0) { /* * Three-byte value... */ if ((temp = getc(fp)) == EOF || (temp & 0xc0) != 0x80) return (EOF); ch = ((ch & 0x0f) << 6) | (temp & 0x3f); if ((temp = getc(fp)) == EOF || (temp & 0xc0) != 0x80) return (EOF); ch = (ch << 6) | (temp & 0x3f); if (ch < 0x800) { mxml_error("Invalid UTF-8 sequence for character 0x%04x!", ch); return (EOF); } /* * Ignore (strip) Byte Order Mark (BOM)... */ if (ch == 0xfeff) return (mxml_file_getc(p, encoding)); } else if ((ch & 0xf8) == 0xf0) { /* * Four-byte value... */ if ((temp = getc(fp)) == EOF || (temp & 0xc0) != 0x80) return (EOF); ch = ((ch & 0x07) << 6) | (temp & 0x3f); if ((temp = getc(fp)) == EOF || (temp & 0xc0) != 0x80) return (EOF); ch = (ch << 6) | (temp & 0x3f); if ((temp = getc(fp)) == EOF || (temp & 0xc0) != 0x80) return (EOF); ch = (ch << 6) | (temp & 0x3f); if (ch < 0x10000) { mxml_error("Invalid UTF-8 sequence for character 0x%04x!", ch); return (EOF); } } else return (EOF); break; case ENCODE_UTF16BE : /* * Read UTF-16 big-endian char... */ ch = (ch << 8) | getc(fp); if (mxml_bad_char(ch)) { mxml_error("Bad control character 0x%02x not allowed by XML standard!", ch); return (EOF); } else if (ch >= 0xd800 && ch <= 0xdbff) { /* * Multi-word UTF-16 char... */ int lch = (getc(fp) << 8) | getc(fp); if (lch < 0xdc00 || lch >= 0xdfff) return (EOF); ch = (((ch & 0x3ff) << 10) | (lch & 0x3ff)) + 0x10000; } break; case ENCODE_UTF16LE : /* * Read UTF-16 little-endian char... */ ch |= (getc(fp) << 8); if (mxml_bad_char(ch)) { mxml_error("Bad control character 0x%02x not allowed by XML standard!", ch); return (EOF); } else if (ch >= 0xd800 && ch <= 0xdbff) { /* * Multi-word UTF-16 char... */ int lch = getc(fp) | (getc(fp) << 8); if (lch < 0xdc00 || lch >= 0xdfff) return (EOF); ch = (((ch & 0x3ff) << 10) | (lch & 0x3ff)) + 0x10000; } break; } #if DEBUG > 1 printf("mxml_file_getc: %c (0x%04x)\n", ch < ' ' ? '.' : ch, ch); #endif /* DEBUG > 1 */ return (ch); } /* * 'mxml_file_putc()' - Write a character to a file. */ static int /* O - 0 on success, -1 on failure */ mxml_file_putc(int ch, /* I - Character to write */ void *p) /* I - Pointer to file */ { return (putc(ch, (FILE *)p) == EOF ? -1 : 0); } /* * 'mxml_get_entity()' - Get the character corresponding to an entity... */ static int /* O - Character value or EOF on error */ mxml_get_entity(mxml_node_t *parent, /* I - Parent node */ void *p, /* I - Pointer to source */ int *encoding, /* IO - Character encoding */ int (*getc_cb)(void *, int *)) /* I - Get character function */ { int ch; /* Current character */ char entity[64], /* Entity string */ *entptr; /* Pointer into entity */ entptr = entity; while ((ch = (*getc_cb)(p, encoding)) != EOF) if (ch > 126 || (!isalnum(ch) && ch != '#')) break; else if (entptr < (entity + sizeof(entity) - 1)) *entptr++ = ch; else { mxml_error("Entity name too long under parent <%s>!", parent ? parent->value.element.name : "null"); break; } *entptr = '\0'; if (ch != ';') { mxml_error("Character entity \"%s\" not terminated under parent <%s>!", entity, parent ? parent->value.element.name : "null"); return (EOF); } if (entity[0] == '#') { if (entity[1] == 'x') ch = strtol(entity + 2, NULL, 16); else ch = strtol(entity + 1, NULL, 10); } else if ((ch = mxmlEntityGetValue(entity)) < 0) mxml_error("Entity name \"%s;\" not supported under parent <%s>!", entity, parent ? parent->value.element.name : "null"); if (mxml_bad_char(ch)) { mxml_error("Bad control character 0x%02x under parent <%s> not allowed by XML standard!", ch, parent ? parent->value.element.name : "null"); return (EOF); } return (ch); } /* * 'mxml_load_data()' - Load data into an XML node tree. */ static mxml_node_t * /* O - First node or NULL if the file could not be read. */ mxml_load_data( mxml_node_t *top, /* I - Top node */ void *p, /* I - Pointer to data */ mxml_load_cb_t cb, /* I - Callback function or MXML_NO_CALLBACK */ _mxml_getc_cb_t getc_cb, /* I - Read function */ mxml_sax_cb_t sax_cb, /* I - SAX callback or MXML_NO_CALLBACK */ void *sax_data) /* I - SAX user data */ { mxml_node_t *node, /* Current node */ *first, /* First node added */ *parent; /* Current parent node */ int ch, /* Character from file */ whitespace; /* Non-zero if whitespace seen */ char *buffer, /* String buffer */ *bufptr; /* Pointer into buffer */ int bufsize; /* Size of buffer */ mxml_type_t type; /* Current node type */ int encoding; /* Character encoding */ _mxml_global_t *global = _mxml_global(); /* Global data */ static const char * const types[] = /* Type strings... */ { "MXML_ELEMENT", /* XML element with attributes */ "MXML_INTEGER", /* Integer value */ "MXML_OPAQUE", /* Opaque string */ "MXML_REAL", /* Real value */ "MXML_TEXT", /* Text fragment */ "MXML_CUSTOM" /* Custom data */ }; /* * Read elements and other nodes from the file... */ if ((buffer = malloc(64)) == NULL) { mxml_error("Unable to allocate string buffer!"); return (NULL); } bufsize = 64; bufptr = buffer; parent = top; first = NULL; whitespace = 0; encoding = ENCODE_UTF8; if (cb && parent) type = (*cb)(parent); else type = MXML_TEXT; while ((ch = (*getc_cb)(p, &encoding)) != EOF) { if ((ch == '<' || (mxml_isspace(ch) && type != MXML_OPAQUE && type != MXML_CUSTOM)) && bufptr > buffer) { /* * Add a new value node... */ *bufptr = '\0'; switch (type) { case MXML_INTEGER : node = mxmlNewInteger(parent, strtol(buffer, &bufptr, 0)); break; case MXML_OPAQUE : node = mxmlNewOpaque(parent, buffer); break; case MXML_REAL : node = mxmlNewReal(parent, strtod(buffer, &bufptr)); break; case MXML_TEXT : node = mxmlNewText(parent, whitespace, buffer); break; case MXML_CUSTOM : if (global->custom_load_cb) { /* * Use the callback to fill in the custom data... */ node = mxmlNewCustom(parent, NULL, NULL); if ((*global->custom_load_cb)(node, buffer)) { mxml_error("Bad custom value '%s' in parent <%s>!", buffer, parent ? parent->value.element.name : "null"); mxmlDelete(node); node = NULL; } break; } default : /* Ignore... */ node = NULL; break; } if (*bufptr) { /* * Bad integer/real number value... */ mxml_error("Bad %s value '%s' in parent <%s>!", type == MXML_INTEGER ? "integer" : "real", buffer, parent ? parent->value.element.name : "null"); break; } bufptr = buffer; whitespace = mxml_isspace(ch) && type == MXML_TEXT; if (!node && type != MXML_IGNORE) { /* * Print error and return... */ mxml_error("Unable to add value node of type %s to parent <%s>!", types[type], parent ? parent->value.element.name : "null"); goto error; } if (sax_cb) { (*sax_cb)(node, MXML_SAX_DATA, sax_data); if (!mxmlRelease(node)) node = NULL; } if (!first && node) first = node; } else if (mxml_isspace(ch) && type == MXML_TEXT) whitespace = 1; /* * Add lone whitespace node if we have an element and existing * whitespace... */ if (ch == '<' && whitespace && type == MXML_TEXT) { if (parent) { node = mxmlNewText(parent, whitespace, ""); if (sax_cb) { (*sax_cb)(node, MXML_SAX_DATA, sax_data); if (!mxmlRelease(node)) node = NULL; } if (!first && node) first = node; } whitespace = 0; } if (ch == '<') { /* * Start of open/close tag... */ bufptr = buffer; while ((ch = (*getc_cb)(p, &encoding)) != EOF) if (mxml_isspace(ch) || ch == '>' || (ch == '/' && bufptr > buffer)) break; else if (ch == '<') { mxml_error("Bare < in element!"); goto error; } else if (ch == '&') { if ((ch = mxml_get_entity(parent, p, &encoding, getc_cb)) == EOF) goto error; if (mxml_add_char(ch, &bufptr, &buffer, &bufsize)) goto error; } else if (mxml_add_char(ch, &bufptr, &buffer, &bufsize)) goto error; else if (((bufptr - buffer) == 1 && buffer[0] == '?') || ((bufptr - buffer) == 3 && !strncmp(buffer, "!--", 3)) || ((bufptr - buffer) == 8 && !strncmp(buffer, "![CDATA[", 8))) break; *bufptr = '\0'; if (!strcmp(buffer, "!--")) { /* * Gather rest of comment... */ while ((ch = (*getc_cb)(p, &encoding)) != EOF) { if (ch == '>' && bufptr > (buffer + 4) && bufptr[-3] != '-' && bufptr[-2] == '-' && bufptr[-1] == '-') break; else if (mxml_add_char(ch, &bufptr, &buffer, &bufsize)) goto error; } /* * Error out if we didn't get the whole comment... */ if (ch != '>') { /* * Print error and return... */ mxml_error("Early EOF in comment node!"); goto error; } /* * Otherwise add this as an element under the current parent... */ *bufptr = '\0'; if (!parent && first) { /* * There can only be one root element! */ mxml_error("<%s> cannot be a second root node after <%s>", buffer, first->value.element.name); goto error; } if ((node = mxmlNewElement(parent, buffer)) == NULL) { /* * Just print error for now... */ mxml_error("Unable to add comment node to parent <%s>!", parent ? parent->value.element.name : "null"); break; } if (sax_cb) { (*sax_cb)(node, MXML_SAX_COMMENT, sax_data); if (!mxmlRelease(node)) node = NULL; } if (node && !first) first = node; } else if (!strcmp(buffer, "![CDATA[")) { /* * Gather CDATA section... */ while ((ch = (*getc_cb)(p, &encoding)) != EOF) { if (ch == '>' && !strncmp(bufptr - 2, "]]", 2)) break; else if (mxml_add_char(ch, &bufptr, &buffer, &bufsize)) goto error; } /* * Error out if we didn't get the whole comment... */ if (ch != '>') { /* * Print error and return... */ mxml_error("Early EOF in CDATA node!"); goto error; } /* * Otherwise add this as an element under the current parent... */ *bufptr = '\0'; if (!parent && first) { /* * There can only be one root element! */ mxml_error("<%s> cannot be a second root node after <%s>", buffer, first->value.element.name); goto error; } if ((node = mxmlNewElement(parent, buffer)) == NULL) { /* * Print error and return... */ mxml_error("Unable to add CDATA node to parent <%s>!", parent ? parent->value.element.name : "null"); goto error; } if (sax_cb) { (*sax_cb)(node, MXML_SAX_CDATA, sax_data); if (!mxmlRelease(node)) node = NULL; } if (node && !first) first = node; } else if (buffer[0] == '?') { /* * Gather rest of processing instruction... */ while ((ch = (*getc_cb)(p, &encoding)) != EOF) { if (ch == '>' && bufptr > buffer && bufptr[-1] == '?') break; else if (mxml_add_char(ch, &bufptr, &buffer, &bufsize)) goto error; } /* * Error out if we didn't get the whole processing instruction... */ if (ch != '>') { /* * Print error and return... */ mxml_error("Early EOF in processing instruction node!"); goto error; } /* * Otherwise add this as an element under the current parent... */ *bufptr = '\0'; if (!parent && first) { /* * There can only be one root element! */ mxml_error("<%s> cannot be a second root node after <%s>", buffer, first->value.element.name); goto error; } if ((node = mxmlNewElement(parent, buffer)) == NULL) { /* * Print error and return... */ mxml_error("Unable to add processing instruction node to parent <%s>!", parent ? parent->value.element.name : "null"); goto error; } if (sax_cb) { (*sax_cb)(node, MXML_SAX_DIRECTIVE, sax_data); if (!mxmlRelease(node)) node = NULL; } if (node) { if (!first) first = node; if (!parent) { parent = node; if (cb) type = (*cb)(parent); } } } else if (buffer[0] == '!') { /* * Gather rest of declaration... */ do { if (ch == '>') break; else { if (ch == '&') if ((ch = mxml_get_entity(parent, p, &encoding, getc_cb)) == EOF) goto error; if (mxml_add_char(ch, &bufptr, &buffer, &bufsize)) goto error; } } while ((ch = (*getc_cb)(p, &encoding)) != EOF); /* * Error out if we didn't get the whole declaration... */ if (ch != '>') { /* * Print error and return... */ mxml_error("Early EOF in declaration node!"); goto error; } /* * Otherwise add this as an element under the current parent... */ *bufptr = '\0'; if (!parent && first) { /* * There can only be one root element! */ mxml_error("<%s> cannot be a second root node after <%s>", buffer, first->value.element.name); goto error; } if ((node = mxmlNewElement(parent, buffer)) == NULL) { /* * Print error and return... */ mxml_error("Unable to add declaration node to parent <%s>!", parent ? parent->value.element.name : "null"); goto error; } if (sax_cb) { (*sax_cb)(node, MXML_SAX_DIRECTIVE, sax_data); if (!mxmlRelease(node)) node = NULL; } if (node) { if (!first) first = node; if (!parent) { parent = node; if (cb) type = (*cb)(parent); } } } else if (buffer[0] == '/') { /* * Handle close tag... */ if (!parent || strcmp(buffer + 1, parent->value.element.name)) { /* * Close tag doesn't match tree; print an error for now... */ mxml_error("Mismatched close tag <%s> under parent <%s>!", buffer, parent ? parent->value.element.name : "(null)"); goto error; } /* * Keep reading until we see >... */ while (ch != '>' && ch != EOF) ch = (*getc_cb)(p, &encoding); node = parent; parent = parent->parent; if (sax_cb) { (*sax_cb)(node, MXML_SAX_ELEMENT_CLOSE, sax_data); if (!mxmlRelease(node) && first == node) first = NULL; } /* * Ascend into the parent and set the value type as needed... */ if (cb && parent) type = (*cb)(parent); } else { /* * Handle open tag... */ if (!parent && first) { /* * There can only be one root element! */ mxml_error("<%s> cannot be a second root node after <%s>", buffer, first->value.element.name); goto error; } if ((node = mxmlNewElement(parent, buffer)) == NULL) { /* * Just print error for now... */ mxml_error("Unable to add element node to parent <%s>!", parent ? parent->value.element.name : "null"); goto error; } if (mxml_isspace(ch)) { if ((ch = mxml_parse_element(node, p, &encoding, getc_cb)) == EOF) goto error; } else if (ch == '/') { if ((ch = (*getc_cb)(p, &encoding)) != '>') { mxml_error("Expected > but got '%c' instead for element <%s/>!", ch, buffer); mxmlDelete(node); goto error; } ch = '/'; } if (sax_cb) (*sax_cb)(node, MXML_SAX_ELEMENT_OPEN, sax_data); if (!first) first = node; if (ch == EOF) break; if (ch != '/') { /* * Descend into this node, setting the value type as needed... */ parent = node; if (cb && parent) type = (*cb)(parent); } else if (sax_cb) { (*sax_cb)(node, MXML_SAX_ELEMENT_CLOSE, sax_data); if (!mxmlRelease(node) && first == node) first = NULL; } } bufptr = buffer; } else if (ch == '&') { /* * Add character entity to current buffer... */ if ((ch = mxml_get_entity(parent, p, &encoding, getc_cb)) == EOF) goto error; if (mxml_add_char(ch, &bufptr, &buffer, &bufsize)) goto error; } else if (type == MXML_OPAQUE || type == MXML_CUSTOM || !mxml_isspace(ch)) { /* * Add character to current buffer... */ if (mxml_add_char(ch, &bufptr, &buffer, &bufsize)) goto error; } } /* * Free the string buffer - we don't need it anymore... */ free(buffer); /* * Find the top element and return it... */ if (parent) { node = parent; while (parent->parent != top && parent->parent) parent = parent->parent; if (node != parent) { mxml_error("Missing close tag under parent <%s>!", node->value.element.name, node->parent ? node->parent->value.element.name : "(null)"); mxmlDelete(first); return (NULL); } } if (parent) return (parent); else return (first); /* * Common error return... */ error: mxmlDelete(first); free(buffer); return (NULL); } /* * 'mxml_parse_element()' - Parse an element for any attributes... */ static int /* O - Terminating character */ mxml_parse_element( mxml_node_t *node, /* I - Element node */ void *p, /* I - Data to read from */ int *encoding, /* IO - Encoding */ _mxml_getc_cb_t getc_cb) /* I - Data callback */ { int ch, /* Current character in file */ quote; /* Quoting character */ char *name, /* Attribute name */ *value, /* Attribute value */ *ptr; /* Pointer into name/value */ int namesize, /* Size of name string */ valsize; /* Size of value string */ /* * Initialize the name and value buffers... */ if ((name = malloc(64)) == NULL) { mxml_error("Unable to allocate memory for name!"); return (EOF); } namesize = 64; if ((value = malloc(64)) == NULL) { free(name); mxml_error("Unable to allocate memory for value!"); return (EOF); } valsize = 64; /* * Loop until we hit a >, /, ?, or EOF... */ while ((ch = (*getc_cb)(p, encoding)) != EOF) { #if DEBUG > 1 fprintf(stderr, "parse_element: ch='%c'\n", ch); #endif /* DEBUG > 1 */ /* * Skip leading whitespace... */ if (mxml_isspace(ch)) continue; /* * Stop at /, ?, or >... */ if (ch == '/' || ch == '?') { /* * Grab the > character and print an error if it isn't there... */ quote = (*getc_cb)(p, encoding); if (quote != '>') { mxml_error("Expected '>' after '%c' for element %s, but got '%c'!", ch, node->value.element.name, quote); goto error; } break; } else if (ch == '<') { mxml_error("Bare < in element %s!", node->value.element.name); goto error; } else if (ch == '>') break; /* * Read the attribute name... */ name[0] = ch; ptr = name + 1; if (ch == '\"' || ch == '\'') { /* * Name is in quotes, so get a quoted string... */ quote = ch; while ((ch = (*getc_cb)(p, encoding)) != EOF) { if (ch == '&') if ((ch = mxml_get_entity(node, p, encoding, getc_cb)) == EOF) goto error; if (mxml_add_char(ch, &ptr, &name, &namesize)) goto error; if (ch == quote) break; } } else { /* * Grab an normal, non-quoted name... */ while ((ch = (*getc_cb)(p, encoding)) != EOF) if (mxml_isspace(ch) || ch == '=' || ch == '/' || ch == '>' || ch == '?') break; else { if (ch == '&') if ((ch = mxml_get_entity(node, p, encoding, getc_cb)) == EOF) goto error; if (mxml_add_char(ch, &ptr, &name, &namesize)) goto error; } } *ptr = '\0'; if (mxmlElementGetAttr(node, name)) goto error; while (ch != EOF && mxml_isspace(ch)) ch = (*getc_cb)(p, encoding); if (ch == '=') { /* * Read the attribute value... */ while ((ch = (*getc_cb)(p, encoding)) != EOF && mxml_isspace(ch)); if (ch == EOF) { mxml_error("Missing value for attribute '%s' in element %s!", name, node->value.element.name); goto error; } if (ch == '\'' || ch == '\"') { /* * Read quoted value... */ quote = ch; ptr = value; while ((ch = (*getc_cb)(p, encoding)) != EOF) if (ch == quote) break; else { if (ch == '&') if ((ch = mxml_get_entity(node, p, encoding, getc_cb)) == EOF) goto error; if (mxml_add_char(ch, &ptr, &value, &valsize)) goto error; } *ptr = '\0'; } else { /* * Read unquoted value... */ value[0] = ch; ptr = value + 1; while ((ch = (*getc_cb)(p, encoding)) != EOF) if (mxml_isspace(ch) || ch == '=' || ch == '/' || ch == '>') break; else { if (ch == '&') if ((ch = mxml_get_entity(node, p, encoding, getc_cb)) == EOF) goto error; if (mxml_add_char(ch, &ptr, &value, &valsize)) goto error; } *ptr = '\0'; } /* * Set the attribute with the given string value... */ mxmlElementSetAttr(node, name, value); } else { mxml_error("Missing value for attribute '%s' in element %s!", name, node->value.element.name); goto error; } /* * Check the end character... */ if (ch == '/' || ch == '?') { /* * Grab the > character and print an error if it isn't there... */ quote = (*getc_cb)(p, encoding); if (quote != '>') { mxml_error("Expected '>' after '%c' for element %s, but got '%c'!", ch, node->value.element.name, quote); ch = EOF; } break; } else if (ch == '>') break; } /* * Free the name and value buffers and return... */ free(name); free(value); return (ch); /* * Common error return point... */ error: free(name); free(value); return (EOF); } /* * 'mxml_string_getc()' - Get a character from a string. */ static int /* O - Character or EOF */ mxml_string_getc(void *p, /* I - Pointer to file */ int *encoding) /* IO - Encoding */ { int ch; /* Character */ const char **s; /* Pointer to string pointer */ s = (const char **)p; if ((ch = (*s)[0] & 255) != 0 || *encoding == ENCODE_UTF16LE) { /* * Got character; convert UTF-8 to integer and return... */ (*s)++; switch (*encoding) { case ENCODE_UTF8 : if (!(ch & 0x80)) { #if DEBUG > 1 printf("mxml_string_getc: %c (0x%04x)\n", ch < ' ' ? '.' : ch, ch); #endif /* DEBUG > 1 */ if (mxml_bad_char(ch)) { mxml_error("Bad control character 0x%02x not allowed by XML standard!", ch); return (EOF); } return (ch); } else if (ch == 0xfe) { /* * UTF-16 big-endian BOM? */ if (((*s)[0] & 255) != 0xff) return (EOF); *encoding = ENCODE_UTF16BE; (*s)++; return (mxml_string_getc(p, encoding)); } else if (ch == 0xff) { /* * UTF-16 little-endian BOM? */ if (((*s)[0] & 255) != 0xfe) return (EOF); *encoding = ENCODE_UTF16LE; (*s)++; return (mxml_string_getc(p, encoding)); } else if ((ch & 0xe0) == 0xc0) { /* * Two-byte value... */ if (((*s)[0] & 0xc0) != 0x80) return (EOF); ch = ((ch & 0x1f) << 6) | ((*s)[0] & 0x3f); (*s)++; if (ch < 0x80) { mxml_error("Invalid UTF-8 sequence for character 0x%04x!", ch); return (EOF); } #if DEBUG > 1 printf("mxml_string_getc: %c (0x%04x)\n", ch < ' ' ? '.' : ch, ch); #endif /* DEBUG > 1 */ return (ch); } else if ((ch & 0xf0) == 0xe0) { /* * Three-byte value... */ if (((*s)[0] & 0xc0) != 0x80 || ((*s)[1] & 0xc0) != 0x80) return (EOF); ch = ((((ch & 0x0f) << 6) | ((*s)[0] & 0x3f)) << 6) | ((*s)[1] & 0x3f); (*s) += 2; if (ch < 0x800) { mxml_error("Invalid UTF-8 sequence for character 0x%04x!", ch); return (EOF); } /* * Ignore (strip) Byte Order Mark (BOM)... */ if (ch == 0xfeff) return (mxml_string_getc(p, encoding)); #if DEBUG > 1 printf("mxml_string_getc: %c (0x%04x)\n", ch < ' ' ? '.' : ch, ch); #endif /* DEBUG > 1 */ return (ch); } else if ((ch & 0xf8) == 0xf0) { /* * Four-byte value... */ if (((*s)[0] & 0xc0) != 0x80 || ((*s)[1] & 0xc0) != 0x80 || ((*s)[2] & 0xc0) != 0x80) return (EOF); ch = ((((((ch & 0x07) << 6) | ((*s)[0] & 0x3f)) << 6) | ((*s)[1] & 0x3f)) << 6) | ((*s)[2] & 0x3f); (*s) += 3; if (ch < 0x10000) { mxml_error("Invalid UTF-8 sequence for character 0x%04x!", ch); return (EOF); } #if DEBUG > 1 printf("mxml_string_getc: %c (0x%04x)\n", ch < ' ' ? '.' : ch, ch); #endif /* DEBUG > 1 */ return (ch); } else return (EOF); case ENCODE_UTF16BE : /* * Read UTF-16 big-endian char... */ ch = (ch << 8) | ((*s)[0] & 255); (*s) ++; if (mxml_bad_char(ch)) { mxml_error("Bad control character 0x%02x not allowed by XML standard!", ch); return (EOF); } else if (ch >= 0xd800 && ch <= 0xdbff) { /* * Multi-word UTF-16 char... */ int lch; /* Lower word */ if (!(*s)[0]) return (EOF); lch = (((*s)[0] & 255) << 8) | ((*s)[1] & 255); (*s) += 2; if (lch < 0xdc00 || lch >= 0xdfff) return (EOF); ch = (((ch & 0x3ff) << 10) | (lch & 0x3ff)) + 0x10000; } #if DEBUG > 1 printf("mxml_string_getc: %c (0x%04x)\n", ch < ' ' ? '.' : ch, ch); #endif /* DEBUG > 1 */ return (ch); case ENCODE_UTF16LE : /* * Read UTF-16 little-endian char... */ ch = ch | (((*s)[0] & 255) << 8); if (!ch) { (*s) --; return (EOF); } (*s) ++; if (mxml_bad_char(ch)) { mxml_error("Bad control character 0x%02x not allowed by XML standard!", ch); return (EOF); } else if (ch >= 0xd800 && ch <= 0xdbff) { /* * Multi-word UTF-16 char... */ int lch; /* Lower word */ if (!(*s)[1]) return (EOF); lch = (((*s)[1] & 255) << 8) | ((*s)[0] & 255); (*s) += 2; if (lch < 0xdc00 || lch >= 0xdfff) return (EOF); ch = (((ch & 0x3ff) << 10) | (lch & 0x3ff)) + 0x10000; } #if DEBUG > 1 printf("mxml_string_getc: %c (0x%04x)\n", ch < ' ' ? '.' : ch, ch); #endif /* DEBUG > 1 */ return (ch); } } return (EOF); } /* * 'mxml_string_putc()' - Write a character to a string. */ static int /* O - 0 on success, -1 on failure */ mxml_string_putc(int ch, /* I - Character to write */ void *p) /* I - Pointer to string pointers */ { char **pp; /* Pointer to string pointers */ pp = (char **)p; if (pp[0] < pp[1]) pp[0][0] = ch; pp[0] ++; return (0); } /* * 'mxml_write_name()' - Write a name string. */ static int /* O - 0 on success, -1 on failure */ mxml_write_name(const char *s, /* I - Name to write */ void *p, /* I - Write pointer */ int (*putc_cb)(int, void *)) /* I - Write callback */ { char quote; /* Quote character */ const char *name; /* Entity name */ if (*s == '\"' || *s == '\'') { /* * Write a quoted name string... */ if ((*putc_cb)(*s, p) < 0) return (-1); quote = *s++; while (*s && *s != quote) { if ((name = mxmlEntityGetName(*s)) != NULL) { if ((*putc_cb)('&', p) < 0) return (-1); while (*name) { if ((*putc_cb)(*name, p) < 0) return (-1); name ++; } if ((*putc_cb)(';', p) < 0) return (-1); } else if ((*putc_cb)(*s, p) < 0) return (-1); s ++; } /* * Write the end quote... */ if ((*putc_cb)(quote, p) < 0) return (-1); } else { /* * Write a non-quoted name string... */ while (*s) { if ((*putc_cb)(*s, p) < 0) return (-1); s ++; } } return (0); } /* * 'mxml_write_node()' - Save an XML node to a file. */ static int /* O - Column or -1 on error */ mxml_write_node(mxml_node_t *node, /* I - Node to write */ void *p, /* I - File to write to */ mxml_save_cb_t cb, /* I - Whitespace callback */ int col, /* I - Current column */ _mxml_putc_cb_t putc_cb,/* I - Output callback */ _mxml_global_t *global)/* I - Global data */ { int i, /* Looping var */ width; /* Width of attr + value */ mxml_attr_t *attr; /* Current attribute */ char s[255]; /* Temporary string */ /* * Print the node value... */ switch (node->type) { case MXML_ELEMENT : col = mxml_write_ws(node, p, cb, MXML_WS_BEFORE_OPEN, col, putc_cb); if ((*putc_cb)('<', p) < 0) return (-1); if (node->value.element.name[0] == '?' || !strncmp(node->value.element.name, "!--", 3) || !strncmp(node->value.element.name, "![CDATA[", 8)) { /* * Comments, CDATA, and processing instructions do not * use character entities. */ const char *ptr; /* Pointer into name */ for (ptr = node->value.element.name; *ptr; ptr ++) if ((*putc_cb)(*ptr, p) < 0) return (-1); } else if (mxml_write_name(node->value.element.name, p, putc_cb) < 0) return (-1); col += strlen(node->value.element.name) + 1; for (i = node->value.element.num_attrs, attr = node->value.element.attrs; i > 0; i --, attr ++) { width = strlen(attr->name); if (attr->value) width += strlen(attr->value) + 3; if (global->wrap > 0 && (col + width) > global->wrap) { if ((*putc_cb)('\n', p) < 0) return (-1); col = 0; } else { if ((*putc_cb)(' ', p) < 0) return (-1); col ++; } if (mxml_write_name(attr->name, p, putc_cb) < 0) return (-1); if (attr->value) { if ((*putc_cb)('=', p) < 0) return (-1); if ((*putc_cb)('\"', p) < 0) return (-1); if (mxml_write_string(attr->value, p, putc_cb) < 0) return (-1); if ((*putc_cb)('\"', p) < 0) return (-1); } col += width; } if (node->child) { /* * Write children... */ mxml_node_t *child; /* Current child */ if ((*putc_cb)('>', p) < 0) return (-1); else col ++; col = mxml_write_ws(node, p, cb, MXML_WS_AFTER_OPEN, col, putc_cb); for (child = node->child; child; child = child->next) { if ((col = mxml_write_node(child, p, cb, col, putc_cb, global)) < 0) return (-1); } /* * The ? and ! elements are special-cases and have no end tags... */ if (node->value.element.name[0] != '!' && node->value.element.name[0] != '?') { col = mxml_write_ws(node, p, cb, MXML_WS_BEFORE_CLOSE, col, putc_cb); if ((*putc_cb)('<', p) < 0) return (-1); if ((*putc_cb)('/', p) < 0) return (-1); if (mxml_write_string(node->value.element.name, p, putc_cb) < 0) return (-1); if ((*putc_cb)('>', p) < 0) return (-1); col += strlen(node->value.element.name) + 3; col = mxml_write_ws(node, p, cb, MXML_WS_AFTER_CLOSE, col, putc_cb); } } else if (node->value.element.name[0] == '!' || node->value.element.name[0] == '?') { /* * The ? and ! elements are special-cases... */ if ((*putc_cb)('>', p) < 0) return (-1); else col ++; col = mxml_write_ws(node, p, cb, MXML_WS_AFTER_OPEN, col, putc_cb); } else { if ((*putc_cb)(' ', p) < 0) return (-1); if ((*putc_cb)('/', p) < 0) return (-1); if ((*putc_cb)('>', p) < 0) return (-1); col += 3; col = mxml_write_ws(node, p, cb, MXML_WS_AFTER_OPEN, col, putc_cb); } break; case MXML_INTEGER : if (node->prev) { if (global->wrap > 0 && col > global->wrap) { if ((*putc_cb)('\n', p) < 0) return (-1); col = 0; } else if ((*putc_cb)(' ', p) < 0) return (-1); else col ++; } sprintf(s, "%d", node->value.integer); if (mxml_write_string(s, p, putc_cb) < 0) return (-1); col += strlen(s); break; case MXML_OPAQUE : if (mxml_write_string(node->value.opaque, p, putc_cb) < 0) return (-1); col += strlen(node->value.opaque); break; case MXML_REAL : if (node->prev) { if (global->wrap > 0 && col > global->wrap) { if ((*putc_cb)('\n', p) < 0) return (-1); col = 0; } else if ((*putc_cb)(' ', p) < 0) return (-1); else col ++; } sprintf(s, "%f", node->value.real); if (mxml_write_string(s, p, putc_cb) < 0) return (-1); col += strlen(s); break; case MXML_TEXT : if (node->value.text.whitespace && col > 0) { if (global->wrap > 0 && col > global->wrap) { if ((*putc_cb)('\n', p) < 0) return (-1); col = 0; } else if ((*putc_cb)(' ', p) < 0) return (-1); else col ++; } if (mxml_write_string(node->value.text.string, p, putc_cb) < 0) return (-1); col += strlen(node->value.text.string); break; case MXML_CUSTOM : if (global->custom_save_cb) { char *data; /* Custom data string */ const char *newline; /* Last newline in string */ if ((data = (*global->custom_save_cb)(node)) == NULL) return (-1); if (mxml_write_string(data, p, putc_cb) < 0) return (-1); if ((newline = strrchr(data, '\n')) == NULL) col += strlen(data); else col = strlen(newline); free(data); break; } default : /* Should never happen */ return (-1); } return (col); } /* * 'mxml_write_string()' - Write a string, escaping & and < as needed. */ static int /* O - 0 on success, -1 on failure */ mxml_write_string( const char *s, /* I - String to write */ void *p, /* I - Write pointer */ _mxml_putc_cb_t putc_cb) /* I - Write callback */ { const char *name; /* Entity name, if any */ while (*s) { if ((name = mxmlEntityGetName(*s)) != NULL) { if ((*putc_cb)('&', p) < 0) return (-1); while (*name) { if ((*putc_cb)(*name, p) < 0) return (-1); name ++; } if ((*putc_cb)(';', p) < 0) return (-1); } else if ((*putc_cb)(*s, p) < 0) return (-1); s ++; } return (0); } /* * 'mxml_write_ws()' - Do whitespace callback... */ static int /* O - New column */ mxml_write_ws(mxml_node_t *node, /* I - Current node */ void *p, /* I - Write pointer */ mxml_save_cb_t cb, /* I - Callback function */ int ws, /* I - Where value */ int col, /* I - Current column */ _mxml_putc_cb_t putc_cb) /* I - Write callback */ { const char *s; /* Whitespace string */ if (cb && (s = (*cb)(node, ws)) != NULL) { while (*s) { if ((*putc_cb)(*s, p) < 0) return (-1); else if (*s == '\n') col = 0; else if (*s == '\t') { col += MXML_TAB; col = col - (col % MXML_TAB); } else col ++; s ++; } } return (col); } /* * End of "$Id: mxml-file.c 438 2011-03-24 05:47:51Z mike $". */ cmtk-3.3.1/Utilities/mxml/mxml-get.c000066400000000000000000000220121276303427400173070ustar00rootroot00000000000000/* * "$Id: mxml-get.c 427 2011-01-03 02:03:29Z mike $" * * Node get functions for Mini-XML, a small XML-like file parsing library. * * Copyright 2011 by Michael R Sweet. * * These coded instructions, statements, and computer programs are the * property of Michael R Sweet and are protected by Federal copyright * law. Distribution and use rights are outlined in the file "COPYING" * which should have been included with this file. If this file is * missing or damaged, see the license at: * * http://www.minixml.org/ * * Contents: * * mxmlGetCDATA() - Get the value for a CDATA node. * mxmlGetCustom() - Get the value for a custom node. * mxmlGetElement() - Get the name for an element node. * mxmlGetFirstChild() - Get the first child of an element node. * mxmlGetInteger() - Get the integer value from the specified node or its * first child. * mxmlGetLastChild() - Get the last child of an element node. * mxmlGetNextSibling() - Get the next node for the current parent. * mxmlGetOpaque() - Get an opaque string value for a node or its first * child. * mxmlGetParent() - Get the parent node. * mxmlGetPrevSibling() - Get the previous node for the current parent. * mxmlGetReal() - Get the real value for a node or its first child. * mxmlGetText() - Get the text value for a node or its first child. * mxmlGetType() - Get the node type. * mxmlGetUserData() - Get the user data pointer for a node. */ /* * Include necessary headers... */ #include "config.h" #include "mxml.h" /* * 'mxmlGetCDATA()' - Get the value for a CDATA node. * * @code NULL@ is returned if the node is not a CDATA element. * * @since Mini-XML 2.7@ */ const char * /* O - CDATA value or NULL */ mxmlGetCDATA(mxml_node_t *node) /* I - Node to get */ { /* * Range check input... */ if (!node || node->type != MXML_ELEMENT || strncmp(node->value.element.name, "![CDATA[", 8)) return (NULL); /* * Return the text following the CDATA declaration... */ return (node->value.element.name + 8); } /* * 'mxmlGetCustom()' - Get the value for a custom node. * * @code NULL@ is returned if the node (or its first child) is not a custom * value node. * * @since Mini-XML 2.7@ */ const void * /* O - Custom value or NULL */ mxmlGetCustom(mxml_node_t *node) /* I - Node to get */ { /* * Range check input... */ if (!node) return (NULL); /* * Return the integer value... */ if (node->type == MXML_CUSTOM) return (node->value.custom.data); else if (node->type == MXML_ELEMENT && node->child && node->child->type == MXML_CUSTOM) return (node->child->value.custom.data); else return (NULL); } /* * 'mxmlGetElement()' - Get the name for an element node. * * @code NULL@ is returned if the node is not an element node. * * @since Mini-XML 2.7@ */ const char * /* O - Element name or NULL */ mxmlGetElement(mxml_node_t *node) /* I - Node to get */ { /* * Range check input... */ if (!node || node->type != MXML_ELEMENT) return (NULL); /* * Return the element name... */ return (node->value.element.name); } /* * 'mxmlGetFirstChild()' - Get the first child of an element node. * * @code NULL@ is returned if the node is not an element node or if the node * has no children. * * @since Mini-XML 2.7@ */ mxml_node_t * /* O - First child or NULL */ mxmlGetFirstChild(mxml_node_t *node) /* I - Node to get */ { /* * Range check input... */ if (!node || node->type != MXML_ELEMENT) return (NULL); /* * Return the first child node... */ return (node->child); } /* * 'mxmlGetInteger()' - Get the integer value from the specified node or its * first child. * * 0 is returned if the node (or its first child) is not an integer value node. * * @since Mini-XML 2.7@ */ int /* O - Integer value or 0 */ mxmlGetInteger(mxml_node_t *node) /* I - Node to get */ { /* * Range check input... */ if (!node) return (0); /* * Return the integer value... */ if (node->type == MXML_INTEGER) return (node->value.integer); else if (node->type == MXML_ELEMENT && node->child && node->child->type == MXML_INTEGER) return (node->child->value.integer); else return (0); } /* * 'mxmlGetLastChild()' - Get the last child of an element node. * * @code NULL@ is returned if the node is not an element node or if the node * has no children. * * @since Mini-XML 2.7@ */ mxml_node_t * /* O - Last child or NULL */ mxmlGetLastChild(mxml_node_t *node) /* I - Node to get */ { /* * Range check input... */ if (!node || node->type != MXML_ELEMENT) return (NULL); /* * Return the node type... */ return (node->last_child); } /* * 'mxmlGetNextSibling()' - Get the next node for the current parent. * * @code NULL@ is returned if this is the last child for the current parent. * * @since Mini-XML 2.7@ */ mxml_node_t * mxmlGetNextSibling(mxml_node_t *node) /* I - Node to get */ { /* * Range check input... */ if (!node) return (NULL); /* * Return the node type... */ return (node->next); } /* * 'mxmlGetOpaque()' - Get an opaque string value for a node or its first child. * * @code NULL@ is returned if the node (or its first child) is not an opaque * value node. * * @since Mini-XML 2.7@ */ const char * /* O - Opaque string or NULL */ mxmlGetOpaque(mxml_node_t *node) /* I - Node to get */ { /* * Range check input... */ if (!node) return (NULL); /* * Return the integer value... */ if (node->type == MXML_OPAQUE) return (node->value.opaque); else if (node->type == MXML_ELEMENT && node->child && node->child->type == MXML_OPAQUE) return (node->child->value.opaque); else return (NULL); } /* * 'mxmlGetParent()' - Get the parent node. * * @code NULL@ is returned for a root node. * * @since Mini-XML 2.7@ */ mxml_node_t * /* O - Parent node or NULL */ mxmlGetParent(mxml_node_t *node) /* I - Node to get */ { /* * Range check input... */ if (!node) return (NULL); /* * Return the node type... */ return (node->parent); } /* * 'mxmlGetPrevSibling()' - Get the previous node for the current parent. * * @code NULL@ is returned if this is the first child for the current parent. * * @since Mini-XML 2.7@ */ mxml_node_t * /* O - Previous node or NULL */ mxmlGetPrevSibling(mxml_node_t *node) /* I - Node to get */ { /* * Range check input... */ if (!node) return (NULL); /* * Return the node type... */ return (node->prev); } /* * 'mxmlGetReal()' - Get the real value for a node or its first child. * * 0.0 is returned if the node (or its first child) is not a real value node. * * @since Mini-XML 2.7@ */ double /* O - Real value or 0.0 */ mxmlGetReal(mxml_node_t *node) /* I - Node to get */ { /* * Range check input... */ if (!node) return (0.0); /* * Return the integer value... */ if (node->type == MXML_REAL) return (node->value.real); else if (node->type == MXML_ELEMENT && node->child && node->child->type == MXML_REAL) return (node->child->value.real); else return (0.0); } /* * 'mxmlGetText()' - Get the text value for a node or its first child. * * @code NULL@ is returned if the node (or its first child) is not a text node. * The "whitespace" argument can be NULL. * * @since Mini-XML 2.7@ */ const char * /* O - Text string or NULL */ mxmlGetText(mxml_node_t *node, /* I - Node to get */ int *whitespace) /* O - 1 if string is preceded by whitespace, 0 otherwise */ { /* * Range check input... */ if (!node) { if (whitespace) *whitespace = 0; return (NULL); } /* * Return the integer value... */ if (node->type == MXML_TEXT) { if (whitespace) *whitespace = node->value.text.whitespace; return (node->value.text.string); } else if (node->type == MXML_ELEMENT && node->child && node->child->type == MXML_TEXT) { if (whitespace) *whitespace = node->child->value.text.whitespace; return (node->child->value.text.string); } else { if (whitespace) *whitespace = 0; return (NULL); } } /* * 'mxmlGetType()' - Get the node type. * * @code MXML_IGNORE@ is returned if "node" is @code NULL@. * * @since Mini-XML 2.7@ */ mxml_type_t /* O - Type of node */ mxmlGetType(mxml_node_t *node) /* I - Node to get */ { /* * Range check input... */ if (!node) return (MXML_IGNORE); /* * Return the node type... */ return (node->type); } /* * 'mxmlGetUserData()' - Get the user data pointer for a node. * * @since Mini-XML 2.7@ */ void * /* O - User data pointer */ mxmlGetUserData(mxml_node_t *node) /* I - Node to get */ { /* * Range check input... */ if (!node) return (NULL); /* * Return the user data pointer... */ return (node->user_data); } /* * End of "$Id: mxml-get.c 427 2011-01-03 02:03:29Z mike $". */ cmtk-3.3.1/Utilities/mxml/mxml-index.c000066400000000000000000000326461276303427400176550ustar00rootroot00000000000000/* * "$Id: mxml-index.c 426 2011-01-01 23:42:17Z mike $" * * Index support code for Mini-XML, a small XML-like file parsing library. * * Copyright 2003-2011 by Michael R Sweet. * * These coded instructions, statements, and computer programs are the * property of Michael R Sweet and are protected by Federal copyright * law. Distribution and use rights are outlined in the file "COPYING" * which should have been included with this file. If this file is * missing or damaged, see the license at: * * http://www.minixml.org/ * * Contents: * */ /* * Include necessary headers... */ #include "config.h" #include "mxml.h" /* * Sort functions... */ static int index_compare(mxml_index_t *ind, mxml_node_t *first, mxml_node_t *second); static int index_find(mxml_index_t *ind, const char *element, const char *value, mxml_node_t *node); static void index_sort(mxml_index_t *ind, int left, int right); /* * 'mxmlIndexDelete()' - Delete an index. */ void mxmlIndexDelete(mxml_index_t *ind) /* I - Index to delete */ { /* * Range check input.. */ if (!ind) return; /* * Free memory... */ if (ind->attr) free(ind->attr); if (ind->alloc_nodes) free(ind->nodes); free(ind); } /* * 'mxmlIndexEnum()' - Return the next node in the index. * * Nodes are returned in the sorted order of the index. */ mxml_node_t * /* O - Next node or NULL if there is none */ mxmlIndexEnum(mxml_index_t *ind) /* I - Index to enumerate */ { /* * Range check input... */ if (!ind) return (NULL); /* * Return the next node... */ if (ind->cur_node < ind->num_nodes) return (ind->nodes[ind->cur_node ++]); else return (NULL); } /* * 'mxmlIndexFind()' - Find the next matching node. * * You should call mxmlIndexReset() prior to using this function for * the first time with a particular set of "element" and "value" * strings. Passing NULL for both "element" and "value" is equivalent * to calling mxmlIndexEnum(). */ mxml_node_t * /* O - Node or NULL if none found */ mxmlIndexFind(mxml_index_t *ind, /* I - Index to search */ const char *element, /* I - Element name to find, if any */ const char *value) /* I - Attribute value, if any */ { int diff, /* Difference between names */ current, /* Current entity in search */ first, /* First entity in search */ last; /* Last entity in search */ #ifdef DEBUG printf("mxmlIndexFind(ind=%p, element=\"%s\", value=\"%s\")\n", ind, element ? element : "(null)", value ? value : "(null)"); #endif /* DEBUG */ /* * Range check input... */ if (!ind || (!ind->attr && value)) { #ifdef DEBUG puts(" returning NULL..."); printf(" ind->attr=\"%s\"\n", ind->attr ? ind->attr : "(null)"); #endif /* DEBUG */ return (NULL); } /* * If both element and value are NULL, just enumerate the nodes in the * index... */ if (!element && !value) return (mxmlIndexEnum(ind)); /* * If there are no nodes in the index, return NULL... */ if (!ind->num_nodes) { #ifdef DEBUG puts(" returning NULL..."); puts(" no nodes!"); #endif /* DEBUG */ return (NULL); } /* * If cur_node == 0, then find the first matching node... */ if (ind->cur_node == 0) { /* * Find the first node using a modified binary search algorithm... */ first = 0; last = ind->num_nodes - 1; #ifdef DEBUG printf(" find first time, num_nodes=%d...\n", ind->num_nodes); #endif /* DEBUG */ while ((last - first) > 1) { current = (first + last) / 2; #ifdef DEBUG printf(" first=%d, last=%d, current=%d\n", first, last, current); #endif /* DEBUG */ if ((diff = index_find(ind, element, value, ind->nodes[current])) == 0) { /* * Found a match, move back to find the first... */ #ifdef DEBUG puts(" match!"); #endif /* DEBUG */ while (current > 0 && !index_find(ind, element, value, ind->nodes[current - 1])) current --; #ifdef DEBUG printf(" returning first match=%d\n", current); #endif /* DEBUG */ /* * Return the first match and save the index to the next... */ ind->cur_node = current + 1; return (ind->nodes[current]); } else if (diff < 0) last = current; else first = current; #ifdef DEBUG printf(" diff=%d\n", diff); #endif /* DEBUG */ } /* * If we get this far, then we found exactly 0 or 1 matches... */ for (current = first; current <= last; current ++) if (!index_find(ind, element, value, ind->nodes[current])) { /* * Found exactly one (or possibly two) match... */ #ifdef DEBUG printf(" returning only match %d...\n", current); #endif /* DEBUG */ ind->cur_node = current + 1; return (ind->nodes[current]); } /* * No matches... */ ind->cur_node = ind->num_nodes; #ifdef DEBUG puts(" returning NULL..."); #endif /* DEBUG */ return (NULL); } else if (ind->cur_node < ind->num_nodes && !index_find(ind, element, value, ind->nodes[ind->cur_node])) { /* * Return the next matching node... */ #ifdef DEBUG printf(" returning next match %d...\n", ind->cur_node); #endif /* DEBUG */ return (ind->nodes[ind->cur_node ++]); } /* * If we get this far, then we have no matches... */ ind->cur_node = ind->num_nodes; #ifdef DEBUG puts(" returning NULL..."); #endif /* DEBUG */ return (NULL); } /* * 'mxmlIndexGetCount()' - Get the number of nodes in an index. * * @since Mini-XML 2.7@ */ int /* I - Number of nodes in index */ mxmlIndexGetCount(mxml_index_t *ind) /* I - Index of nodes */ { /* * Range check input... */ if (!ind) return (0); /* * Return the number of nodes in the index... */ return (ind->num_nodes); } /* * 'mxmlIndexNew()' - Create a new index. * * The index will contain all nodes that contain the named element and/or * attribute. If both "element" and "attr" are NULL, then the index will * contain a sorted list of the elements in the node tree. Nodes are * sorted by element name and optionally by attribute value if the "attr" * argument is not NULL. */ mxml_index_t * /* O - New index */ mxmlIndexNew(mxml_node_t *node, /* I - XML node tree */ const char *element, /* I - Element to index or NULL for all */ const char *attr) /* I - Attribute to index or NULL for none */ { mxml_index_t *ind; /* New index */ mxml_node_t *current, /* Current node in index */ **temp; /* Temporary node pointer array */ /* * Range check input... */ #ifdef DEBUG printf("mxmlIndexNew(node=%p, element=\"%s\", attr=\"%s\")\n", node, element ? element : "(null)", attr ? attr : "(null)"); #endif /* DEBUG */ if (!node) return (NULL); /* * Create a new index... */ if ((ind = calloc(1, sizeof(mxml_index_t))) == NULL) { mxml_error("Unable to allocate %d bytes for index - %s", sizeof(mxml_index_t), strerror(errno)); return (NULL); } if (attr) ind->attr = strdup(attr); if (!element && !attr) current = node; else current = mxmlFindElement(node, node, element, attr, NULL, MXML_DESCEND); while (current) { if (ind->num_nodes >= ind->alloc_nodes) { if (!ind->alloc_nodes) temp = malloc(64 * sizeof(mxml_node_t *)); else temp = realloc(ind->nodes, (ind->alloc_nodes + 64) * sizeof(mxml_node_t *)); if (!temp) { /* * Unable to allocate memory for the index, so abort... */ mxml_error("Unable to allocate %d bytes for index: %s", (ind->alloc_nodes + 64) * sizeof(mxml_node_t *), strerror(errno)); mxmlIndexDelete(ind); return (NULL); } ind->nodes = temp; ind->alloc_nodes += 64; } ind->nodes[ind->num_nodes ++] = current; current = mxmlFindElement(current, node, element, attr, NULL, MXML_DESCEND); } /* * Sort nodes based upon the search criteria... */ #ifdef DEBUG { int i; /* Looping var */ printf("%d node(s) in index.\n\n", ind->num_nodes); if (attr) { printf("Node Address Element %s\n", attr); puts("-------- -------- -------------- ------------------------------"); for (i = 0; i < ind->num_nodes; i ++) printf("%8d %-8p %-14.14s %s\n", i, ind->nodes[i], ind->nodes[i]->value.element.name, mxmlElementGetAttr(ind->nodes[i], attr)); } else { puts("Node Address Element"); puts("-------- -------- --------------"); for (i = 0; i < ind->num_nodes; i ++) printf("%8d %-8p %s\n", i, ind->nodes[i], ind->nodes[i]->value.element.name); } putchar('\n'); } #endif /* DEBUG */ if (ind->num_nodes > 1) index_sort(ind, 0, ind->num_nodes - 1); #ifdef DEBUG { int i; /* Looping var */ puts("After sorting:\n"); if (attr) { printf("Node Address Element %s\n", attr); puts("-------- -------- -------------- ------------------------------"); for (i = 0; i < ind->num_nodes; i ++) printf("%8d %-8p %-14.14s %s\n", i, ind->nodes[i], ind->nodes[i]->value.element.name, mxmlElementGetAttr(ind->nodes[i], attr)); } else { puts("Node Address Element"); puts("-------- -------- --------------"); for (i = 0; i < ind->num_nodes; i ++) printf("%8d %-8p %s\n", i, ind->nodes[i], ind->nodes[i]->value.element.name); } putchar('\n'); } #endif /* DEBUG */ /* * Return the new index... */ return (ind); } /* * 'mxmlIndexReset()' - Reset the enumeration/find pointer in the index and * return the first node in the index. * * This function should be called prior to using mxmlIndexEnum() or * mxmlIndexFind() for the first time. */ mxml_node_t * /* O - First node or NULL if there is none */ mxmlIndexReset(mxml_index_t *ind) /* I - Index to reset */ { #ifdef DEBUG printf("mxmlIndexReset(ind=%p)\n", ind); #endif /* DEBUG */ /* * Range check input... */ if (!ind) return (NULL); /* * Set the index to the first element... */ ind->cur_node = 0; /* * Return the first node... */ if (ind->num_nodes) return (ind->nodes[0]); else return (NULL); } /* * 'index_compare()' - Compare two nodes. */ static int /* O - Result of comparison */ index_compare(mxml_index_t *ind, /* I - Index */ mxml_node_t *first, /* I - First node */ mxml_node_t *second) /* I - Second node */ { int diff; /* Difference */ /* * Check the element name... */ if ((diff = strcmp(first->value.element.name, second->value.element.name)) != 0) return (diff); /* * Check the attribute value... */ if (ind->attr) { if ((diff = strcmp(mxmlElementGetAttr(first, ind->attr), mxmlElementGetAttr(second, ind->attr))) != 0) return (diff); } /* * No difference, return 0... */ return (0); } /* * 'index_find()' - Compare a node with index values. */ static int /* O - Result of comparison */ index_find(mxml_index_t *ind, /* I - Index */ const char *element, /* I - Element name or NULL */ const char *value, /* I - Attribute value or NULL */ mxml_node_t *node) /* I - Node */ { int diff; /* Difference */ /* * Check the element name... */ if (element) { if ((diff = strcmp(element, node->value.element.name)) != 0) return (diff); } /* * Check the attribute value... */ if (value) { if ((diff = strcmp(value, mxmlElementGetAttr(node, ind->attr))) != 0) return (diff); } /* * No difference, return 0... */ return (0); } /* * 'index_sort()' - Sort the nodes in the index... * * This function implements the classic quicksort algorithm... */ static void index_sort(mxml_index_t *ind, /* I - Index to sort */ int left, /* I - Left node in partition */ int right) /* I - Right node in partition */ { mxml_node_t *pivot, /* Pivot node */ *temp; /* Swap node */ int templ, /* Temporary left node */ tempr; /* Temporary right node */ /* * Loop until we have sorted all the way to the right... */ do { /* * Sort the pivot in the current partition... */ pivot = ind->nodes[left]; for (templ = left, tempr = right; templ < tempr;) { /* * Move left while left node <= pivot node... */ while ((templ < right) && index_compare(ind, ind->nodes[templ], pivot) <= 0) templ ++; /* * Move right while right node > pivot node... */ while ((tempr > left) && index_compare(ind, ind->nodes[tempr], pivot) > 0) tempr --; /* * Swap nodes if needed... */ if (templ < tempr) { temp = ind->nodes[templ]; ind->nodes[templ] = ind->nodes[tempr]; ind->nodes[tempr] = temp; } } /* * When we get here, the right (tempr) node is the new position for the * pivot node... */ if (index_compare(ind, pivot, ind->nodes[tempr]) > 0) { ind->nodes[left] = ind->nodes[tempr]; ind->nodes[tempr] = pivot; } /* * Recursively sort the left partition as needed... */ if (left < (tempr - 1)) index_sort(ind, left, tempr - 1); } while (right > (left = tempr + 1)); } /* * End of "$Id: mxml-index.c 426 2011-01-01 23:42:17Z mike $". */ cmtk-3.3.1/Utilities/mxml/mxml-node.c000066400000000000000000000444731276303427400174740ustar00rootroot00000000000000/* * "$Id: mxml-node.c 436 2011-01-22 01:02:05Z mike $" * * Node support code for Mini-XML, a small XML-like file parsing library. * * Copyright 2003-2011 by Michael R Sweet. * * These coded instructions, statements, and computer programs are the * property of Michael R Sweet and are protected by Federal copyright * law. Distribution and use rights are outlined in the file "COPYING" * which should have been included with this file. If this file is * missing or damaged, see the license at: * * http://www.minixml.org/ * * Contents: * * mxmlAdd() - Add a node to a tree. * mxmlDelete() - Delete a node and all of its children. * mxmlGetRefCount() - Get the current reference (use) count for a node. * mxmlNewCDATA() - Create a new CDATA node. * mxmlNewCustom() - Create a new custom data node. * mxmlNewElement() - Create a new element node. * mxmlNewInteger() - Create a new integer node. * mxmlNewOpaque() - Create a new opaque string. * mxmlNewReal() - Create a new real number node. * mxmlNewText() - Create a new text fragment node. * mxmlNewTextf() - Create a new formatted text fragment node. * mxmlRemove() - Remove a node from its parent. * mxmlNewXML() - Create a new XML document tree. * mxmlRelease() - Release a node. * mxmlRetain() - Retain a node. * mxml_new() - Create a new node. */ /* * Include necessary headers... */ #include "config.h" #include "mxml.h" /* * Local functions... */ static mxml_node_t *mxml_new(mxml_node_t *parent, mxml_type_t type); /* * 'mxmlAdd()' - Add a node to a tree. * * Adds the specified node to the parent. If the child argument is not * NULL, puts the new node before or after the specified child depending * on the value of the where argument. If the child argument is NULL, * puts the new node at the beginning of the child list (MXML_ADD_BEFORE) * or at the end of the child list (MXML_ADD_AFTER). The constant * MXML_ADD_TO_PARENT can be used to specify a NULL child pointer. */ void mxmlAdd(mxml_node_t *parent, /* I - Parent node */ int where, /* I - Where to add, MXML_ADD_BEFORE or MXML_ADD_AFTER */ mxml_node_t *child, /* I - Child node for where or MXML_ADD_TO_PARENT */ mxml_node_t *node) /* I - Node to add */ { #ifdef DEBUG fprintf(stderr, "mxmlAdd(parent=%p, where=%d, child=%p, node=%p)\n", parent, where, child, node); #endif /* DEBUG */ /* * Range check input... */ if (!parent || !node) return; #if DEBUG > 1 fprintf(stderr, " BEFORE: node->parent=%p\n", node->parent); if (parent) { fprintf(stderr, " BEFORE: parent->child=%p\n", parent->child); fprintf(stderr, " BEFORE: parent->last_child=%p\n", parent->last_child); fprintf(stderr, " BEFORE: parent->prev=%p\n", parent->prev); fprintf(stderr, " BEFORE: parent->next=%p\n", parent->next); } #endif /* DEBUG > 1 */ /* * Remove the node from any existing parent... */ if (node->parent) mxmlRemove(node); /* * Reset pointers... */ node->parent = parent; switch (where) { case MXML_ADD_BEFORE : if (!child || child == parent->child || child->parent != parent) { /* * Insert as first node under parent... */ node->next = parent->child; if (parent->child) parent->child->prev = node; else parent->last_child = node; parent->child = node; } else { /* * Insert node before this child... */ node->next = child; node->prev = child->prev; if (child->prev) child->prev->next = node; else parent->child = node; child->prev = node; } break; case MXML_ADD_AFTER : if (!child || child == parent->last_child || child->parent != parent) { /* * Insert as last node under parent... */ node->parent = parent; node->prev = parent->last_child; if (parent->last_child) parent->last_child->next = node; else parent->child = node; parent->last_child = node; } else { /* * Insert node after this child... */ node->prev = child; node->next = child->next; if (child->next) child->next->prev = node; else parent->last_child = node; child->next = node; } break; } #if DEBUG > 1 fprintf(stderr, " AFTER: node->parent=%p\n", node->parent); if (parent) { fprintf(stderr, " AFTER: parent->child=%p\n", parent->child); fprintf(stderr, " AFTER: parent->last_child=%p\n", parent->last_child); fprintf(stderr, " AFTER: parent->prev=%p\n", parent->prev); fprintf(stderr, " AFTER: parent->next=%p\n", parent->next); } #endif /* DEBUG > 1 */ } /* * 'mxmlDelete()' - Delete a node and all of its children. * * If the specified node has a parent, this function first removes the * node from its parent using the mxmlRemove() function. */ void mxmlDelete(mxml_node_t *node) /* I - Node to delete */ { int i; /* Looping var */ #ifdef DEBUG fprintf(stderr, "mxmlDelete(node=%p)\n", node); #endif /* DEBUG */ /* * Range check input... */ if (!node) return; /* * Remove the node from its parent, if any... */ mxmlRemove(node); /* * Delete children... */ while (node->child) mxmlDelete(node->child); /* * Now delete any node data... */ switch (node->type) { case MXML_ELEMENT : if (node->value.element.name) free(node->value.element.name); if (node->value.element.num_attrs) { for (i = 0; i < node->value.element.num_attrs; i ++) { if (node->value.element.attrs[i].name) free(node->value.element.attrs[i].name); if (node->value.element.attrs[i].value) free(node->value.element.attrs[i].value); } free(node->value.element.attrs); } break; case MXML_INTEGER : /* Nothing to do */ break; case MXML_OPAQUE : if (node->value.opaque) free(node->value.opaque); break; case MXML_REAL : /* Nothing to do */ break; case MXML_TEXT : if (node->value.text.string) free(node->value.text.string); break; case MXML_CUSTOM : if (node->value.custom.data && node->value.custom.destroy) (*(node->value.custom.destroy))(node->value.custom.data); break; default : break; } /* * Free this node... */ free(node); } /* * 'mxmlGetRefCount()' - Get the current reference (use) count for a node. * * The initial reference count of new nodes is 1. Use the @link mxmlRetain@ * and @link mxmlRelease@ functions to increment and decrement a node's * reference count. * * @since Mini-XML 2.7@. */ int /* O - Reference count */ mxmlGetRefCount(mxml_node_t *node) /* I - Node */ { /* * Range check input... */ if (!node) return (0); /* * Return the reference count... */ return (node->ref_count); } /* * 'mxmlNewCDATA()' - Create a new CDATA node. * * The new CDATA node is added to the end of the specified parent's child * list. The constant MXML_NO_PARENT can be used to specify that the new * CDATA node has no parent. The data string must be nul-terminated and * is copied into the new node. CDATA nodes use the MXML_ELEMENT type. * * @since Mini-XML 2.3@ */ mxml_node_t * /* O - New node */ mxmlNewCDATA(mxml_node_t *parent, /* I - Parent node or MXML_NO_PARENT */ const char *data) /* I - Data string */ { mxml_node_t *node; /* New node */ #ifdef DEBUG fprintf(stderr, "mxmlNewCDATA(parent=%p, data=\"%s\")\n", parent, data ? data : "(null)"); #endif /* DEBUG */ /* * Range check input... */ if (!data) return (NULL); /* * Create the node and set the name value... */ if ((node = mxml_new(parent, MXML_ELEMENT)) != NULL) node->value.element.name = _mxml_strdupf("![CDATA[%s]]", data); return (node); } /* * 'mxmlNewCustom()' - Create a new custom data node. * * The new custom node is added to the end of the specified parent's child * list. The constant MXML_NO_PARENT can be used to specify that the new * element node has no parent. NULL can be passed when the data in the * node is not dynamically allocated or is separately managed. * * @since Mini-XML 2.1@ */ mxml_node_t * /* O - New node */ mxmlNewCustom( mxml_node_t *parent, /* I - Parent node or MXML_NO_PARENT */ void *data, /* I - Pointer to data */ mxml_custom_destroy_cb_t destroy) /* I - Function to destroy data */ { mxml_node_t *node; /* New node */ #ifdef DEBUG fprintf(stderr, "mxmlNewCustom(parent=%p, data=%p, destroy=%p)\n", parent, data, destroy); #endif /* DEBUG */ /* * Create the node and set the value... */ if ((node = mxml_new(parent, MXML_CUSTOM)) != NULL) { node->value.custom.data = data; node->value.custom.destroy = destroy; } return (node); } /* * 'mxmlNewElement()' - Create a new element node. * * The new element node is added to the end of the specified parent's child * list. The constant MXML_NO_PARENT can be used to specify that the new * element node has no parent. */ mxml_node_t * /* O - New node */ mxmlNewElement(mxml_node_t *parent, /* I - Parent node or MXML_NO_PARENT */ const char *name) /* I - Name of element */ { mxml_node_t *node; /* New node */ #ifdef DEBUG fprintf(stderr, "mxmlNewElement(parent=%p, name=\"%s\")\n", parent, name ? name : "(null)"); #endif /* DEBUG */ /* * Range check input... */ if (!name) return (NULL); /* * Create the node and set the element name... */ if ((node = mxml_new(parent, MXML_ELEMENT)) != NULL) node->value.element.name = strdup(name); return (node); } /* * 'mxmlNewInteger()' - Create a new integer node. * * The new integer node is added to the end of the specified parent's child * list. The constant MXML_NO_PARENT can be used to specify that the new * integer node has no parent. */ mxml_node_t * /* O - New node */ mxmlNewInteger(mxml_node_t *parent, /* I - Parent node or MXML_NO_PARENT */ int integer) /* I - Integer value */ { mxml_node_t *node; /* New node */ #ifdef DEBUG fprintf(stderr, "mxmlNewInteger(parent=%p, integer=%d)\n", parent, integer); #endif /* DEBUG */ /* * Create the node and set the element name... */ if ((node = mxml_new(parent, MXML_INTEGER)) != NULL) node->value.integer = integer; return (node); } /* * 'mxmlNewOpaque()' - Create a new opaque string. * * The new opaque node is added to the end of the specified parent's child * list. The constant MXML_NO_PARENT can be used to specify that the new * opaque node has no parent. The opaque string must be nul-terminated and * is copied into the new node. */ mxml_node_t * /* O - New node */ mxmlNewOpaque(mxml_node_t *parent, /* I - Parent node or MXML_NO_PARENT */ const char *opaque) /* I - Opaque string */ { mxml_node_t *node; /* New node */ #ifdef DEBUG fprintf(stderr, "mxmlNewOpaque(parent=%p, opaque=\"%s\")\n", parent, opaque ? opaque : "(null)"); #endif /* DEBUG */ /* * Range check input... */ if (!opaque) return (NULL); /* * Create the node and set the element name... */ if ((node = mxml_new(parent, MXML_OPAQUE)) != NULL) node->value.opaque = strdup(opaque); return (node); } /* * 'mxmlNewReal()' - Create a new real number node. * * The new real number node is added to the end of the specified parent's * child list. The constant MXML_NO_PARENT can be used to specify that * the new real number node has no parent. */ mxml_node_t * /* O - New node */ mxmlNewReal(mxml_node_t *parent, /* I - Parent node or MXML_NO_PARENT */ double real) /* I - Real number value */ { mxml_node_t *node; /* New node */ #ifdef DEBUG fprintf(stderr, "mxmlNewReal(parent=%p, real=%g)\n", parent, real); #endif /* DEBUG */ /* * Create the node and set the element name... */ if ((node = mxml_new(parent, MXML_REAL)) != NULL) node->value.real = real; return (node); } /* * 'mxmlNewText()' - Create a new text fragment node. * * The new text node is added to the end of the specified parent's child * list. The constant MXML_NO_PARENT can be used to specify that the new * text node has no parent. The whitespace parameter is used to specify * whether leading whitespace is present before the node. The text * string must be nul-terminated and is copied into the new node. */ mxml_node_t * /* O - New node */ mxmlNewText(mxml_node_t *parent, /* I - Parent node or MXML_NO_PARENT */ int whitespace, /* I - 1 = leading whitespace, 0 = no whitespace */ const char *string) /* I - String */ { mxml_node_t *node; /* New node */ #ifdef DEBUG fprintf(stderr, "mxmlNewText(parent=%p, whitespace=%d, string=\"%s\")\n", parent, whitespace, string ? string : "(null)"); #endif /* DEBUG */ /* * Range check input... */ if (!string) return (NULL); /* * Create the node and set the text value... */ if ((node = mxml_new(parent, MXML_TEXT)) != NULL) { node->value.text.whitespace = whitespace; node->value.text.string = strdup(string); } return (node); } /* * 'mxmlNewTextf()' - Create a new formatted text fragment node. * * The new text node is added to the end of the specified parent's child * list. The constant MXML_NO_PARENT can be used to specify that the new * text node has no parent. The whitespace parameter is used to specify * whether leading whitespace is present before the node. The format * string must be nul-terminated and is formatted into the new node. */ mxml_node_t * /* O - New node */ mxmlNewTextf(mxml_node_t *parent, /* I - Parent node or MXML_NO_PARENT */ int whitespace, /* I - 1 = leading whitespace, 0 = no whitespace */ const char *format, /* I - Printf-style frmat string */ ...) /* I - Additional args as needed */ { mxml_node_t *node; /* New node */ va_list ap; /* Pointer to arguments */ #ifdef DEBUG fprintf(stderr, "mxmlNewTextf(parent=%p, whitespace=%d, format=\"%s\", ...)\n", parent, whitespace, format ? format : "(null)"); #endif /* DEBUG */ /* * Range check input... */ if (!format) return (NULL); /* * Create the node and set the text value... */ if ((node = mxml_new(parent, MXML_TEXT)) != NULL) { va_start(ap, format); node->value.text.whitespace = whitespace; node->value.text.string = _mxml_vstrdupf(format, ap); va_end(ap); } return (node); } /* * 'mxmlRemove()' - Remove a node from its parent. * * Does not free memory used by the node - use mxmlDelete() for that. * This function does nothing if the node has no parent. */ void mxmlRemove(mxml_node_t *node) /* I - Node to remove */ { #ifdef DEBUG fprintf(stderr, "mxmlRemove(node=%p)\n", node); #endif /* DEBUG */ /* * Range check input... */ if (!node || !node->parent) return; /* * Remove from parent... */ #if DEBUG > 1 fprintf(stderr, " BEFORE: node->parent=%p\n", node->parent); if (node->parent) { fprintf(stderr, " BEFORE: node->parent->child=%p\n", node->parent->child); fprintf(stderr, " BEFORE: node->parent->last_child=%p\n", node->parent->last_child); } fprintf(stderr, " BEFORE: node->child=%p\n", node->child); fprintf(stderr, " BEFORE: node->last_child=%p\n", node->last_child); fprintf(stderr, " BEFORE: node->prev=%p\n", node->prev); fprintf(stderr, " BEFORE: node->next=%p\n", node->next); #endif /* DEBUG > 1 */ if (node->prev) node->prev->next = node->next; else node->parent->child = node->next; if (node->next) node->next->prev = node->prev; else node->parent->last_child = node->prev; node->parent = NULL; node->prev = NULL; node->next = NULL; #if DEBUG > 1 fprintf(stderr, " AFTER: node->parent=%p\n", node->parent); if (node->parent) { fprintf(stderr, " AFTER: node->parent->child=%p\n", node->parent->child); fprintf(stderr, " AFTER: node->parent->last_child=%p\n", node->parent->last_child); } fprintf(stderr, " AFTER: node->child=%p\n", node->child); fprintf(stderr, " AFTER: node->last_child=%p\n", node->last_child); fprintf(stderr, " AFTER: node->prev=%p\n", node->prev); fprintf(stderr, " AFTER: node->next=%p\n", node->next); #endif /* DEBUG > 1 */ } /* * 'mxmlNewXML()' - Create a new XML document tree. * * The "version" argument specifies the version number to put in the * ?xml element node. If NULL, version 1.0 is assumed. * * @since Mini-XML 2.3@ */ mxml_node_t * /* O - New ?xml node */ mxmlNewXML(const char *version) /* I - Version number to use */ { char element[1024]; /* Element text */ snprintf(element, sizeof(element), "?xml version=\"%s\" encoding=\"utf-8\"?", version ? version : "1.0"); return (mxmlNewElement(NULL, element)); } /* * 'mxmlRelease()' - Release a node. * * When the reference count reaches zero, the node (and any children) * is deleted via mxmlDelete(). * * @since Mini-XML 2.3@ */ int /* O - New reference count */ mxmlRelease(mxml_node_t *node) /* I - Node */ { if (node) { if ((-- node->ref_count) <= 0) { mxmlDelete(node); return (0); } else return (node->ref_count); } else return (-1); } /* * 'mxmlRetain()' - Retain a node. * * @since Mini-XML 2.3@ */ int /* O - New reference count */ mxmlRetain(mxml_node_t *node) /* I - Node */ { if (node) return (++ node->ref_count); else return (-1); } /* * 'mxml_new()' - Create a new node. */ static mxml_node_t * /* O - New node */ mxml_new(mxml_node_t *parent, /* I - Parent node */ mxml_type_t type) /* I - Node type */ { mxml_node_t *node; /* New node */ #if DEBUG > 1 fprintf(stderr, "mxml_new(parent=%p, type=%d)\n", parent, type); #endif /* DEBUG > 1 */ /* * Allocate memory for the node... */ if ((node = calloc(1, sizeof(mxml_node_t))) == NULL) { #if DEBUG > 1 fputs(" returning NULL\n", stderr); #endif /* DEBUG > 1 */ return (NULL); } #if DEBUG > 1 fprintf(stderr, " returning %p\n", node); #endif /* DEBUG > 1 */ /* * Set the node type... */ node->type = type; node->ref_count = 1; /* * Add to the parent if present... */ if (parent) mxmlAdd(parent, MXML_ADD_AFTER, MXML_ADD_TO_PARENT, node); /* * Return the new node... */ return (node); } /* * End of "$Id: mxml-node.c 436 2011-01-22 01:02:05Z mike $". */ cmtk-3.3.1/Utilities/mxml/mxml-private.c000066400000000000000000000156141276303427400202140ustar00rootroot00000000000000/* * "$Id: mxml-private.c 422 2010-11-07 22:55:11Z mike $" * * Private functions for Mini-XML, a small XML-like file parsing library. * * Copyright 2003-2010 by Michael R Sweet. * * These coded instructions, statements, and computer programs are the * property of Michael R Sweet and are protected by Federal copyright * law. Distribution and use rights are outlined in the file "COPYING" * which should have been included with this file. If this file is * missing or damaged, see the license at: * * http://www.minixml.org/ * * Contents: * * mxml_error() - Display an error message. * mxml_integer_cb() - Default callback for integer values. * mxml_opaque_cb() - Default callback for opaque values. * mxml_real_cb() - Default callback for real number values. * _mxml_global() - Get global data. */ /* * Include necessary headers... */ #include "mxml-private.h" /* * Some crazy people think that unloading a shared object is a good or safe * thing to do. Unfortunately, most objects are simply *not* safe to unload * and bad things *will* happen. * * The following mess of conditional code allows us to provide a destructor * function in Mini-XML for our thread-global storage so that it can possibly * be unloaded safely, although since there is no standard way to do so I * can't even provide any guarantees that you can do it safely on all platforms. * * This code currently supports AIX, HP-UX, Linux, Mac OS X, Solaris, and * Windows. It might work on the BSDs and IRIX, but I haven't tested that. */ #if defined(__sun) || defined(_AIX) # pragma fini(_mxml_fini) # define _MXML_FINI _mxml_fini #elif defined(__hpux) # pragma FINI _mxml_fini # define _MXML_FINI _mxml_fini #elif defined(__GNUC__) /* Linux and Mac OS X */ # define _MXML_FINI __attribute((destructor)) _mxml_fini #else # define _MXML_FINI _fini #endif /* __sun */ /* * 'mxml_error()' - Display an error message. */ void mxml_error(const char *format, /* I - Printf-style format string */ ...) /* I - Additional arguments as needed */ { va_list ap; /* Pointer to arguments */ char s[1024]; /* Message string */ _mxml_global_t *global = _mxml_global(); /* Global data */ /* * Range check input... */ if (!format) return; /* * Format the error message string... */ va_start(ap, format); vsnprintf(s, sizeof(s), format, ap); va_end(ap); /* * And then display the error message... */ if (global->error_cb) (*global->error_cb)(s); else fprintf(stderr, "mxml: %s\n", s); } /* * 'mxml_ignore_cb()' - Default callback for ignored values. */ mxml_type_t /* O - Node type */ mxml_ignore_cb(mxml_node_t *node) /* I - Current node */ { (void)node; return (MXML_IGNORE); } /* * 'mxml_integer_cb()' - Default callback for integer values. */ mxml_type_t /* O - Node type */ mxml_integer_cb(mxml_node_t *node) /* I - Current node */ { (void)node; return (MXML_INTEGER); } /* * 'mxml_opaque_cb()' - Default callback for opaque values. */ mxml_type_t /* O - Node type */ mxml_opaque_cb(mxml_node_t *node) /* I - Current node */ { (void)node; return (MXML_OPAQUE); } /* * 'mxml_real_cb()' - Default callback for real number values. */ mxml_type_t /* O - Node type */ mxml_real_cb(mxml_node_t *node) /* I - Current node */ { (void)node; return (MXML_REAL); } #ifdef HAVE_PTHREAD_H /**** POSIX threading ****/ # include static pthread_key_t _mxml_key = -1; /* Thread local storage key */ static pthread_once_t _mxml_key_once = PTHREAD_ONCE_INIT; /* One-time initialization object */ static void _mxml_init(void); static void _mxml_destructor(void *g); /* * '_mxml_destructor()' - Free memory used for globals... */ static void _mxml_destructor(void *g) /* I - Global data */ { free(g); } /* * '_mxml_fini()' - Clean up when unloaded. */ static void _MXML_FINI(void) { _mxml_global_t *global; /* Global data */ if (_mxml_key != -1) { if ((global = (_mxml_global_t *)pthread_getspecific(_mxml_key)) != NULL) _mxml_destructor(global); pthread_key_delete(_mxml_key); _mxml_key = -1; } } /* * '_mxml_global()' - Get global data. */ _mxml_global_t * /* O - Global data */ _mxml_global(void) { _mxml_global_t *global; /* Global data */ pthread_once(&_mxml_key_once, _mxml_init); if ((global = (_mxml_global_t *)pthread_getspecific(_mxml_key)) == NULL) { global = (_mxml_global_t *)calloc(1, sizeof(_mxml_global_t)); pthread_setspecific(_mxml_key, global); global->num_entity_cbs = 1; global->entity_cbs[0] = _mxml_entity_cb; global->wrap = 72; } return (global); } /* * '_mxml_init()' - Initialize global data... */ static void _mxml_init(void) { pthread_key_create(&_mxml_key, _mxml_destructor); } #elif defined(WIN32) && defined(MXML1_EXPORTS) /**** WIN32 threading ****/ # include static DWORD _mxml_tls_index; /* Index for global storage */ /* * 'DllMain()' - Main entry for library. */ BOOL WINAPI /* O - Success/failure */ DllMain(HINSTANCE hinst, /* I - DLL module handle */ DWORD reason, /* I - Reason */ LPVOID reserved) /* I - Unused */ { _mxml_global_t *global; /* Global data */ (void)hinst; (void)reserved; switch (reason) { case DLL_PROCESS_ATTACH : /* Called on library initialization */ if ((_mxml_tls_index = TlsAlloc()) == TLS_OUT_OF_INDEXES) return (FALSE); break; case DLL_THREAD_DETACH : /* Called when a thread terminates */ if ((global = (_mxml_global_t *)TlsGetValue(_mxml_tls_index)) != NULL) free(global); break; case DLL_PROCESS_DETACH : /* Called when library is unloaded */ if ((global = (_mxml_global_t *)TlsGetValue(_mxml_tls_index)) != NULL) free(global); TlsFree(_mxml_tls_index); break; default: break; } return (TRUE); } /* * '_mxml_global()' - Get global data. */ _mxml_global_t * /* O - Global data */ _mxml_global(void) { _mxml_global_t *global; /* Global data */ if ((global = (_mxml_global_t *)TlsGetValue(_mxml_tls_index)) == NULL) { global = (_mxml_global_t *)calloc(1, sizeof(_mxml_global_t)); global->num_entity_cbs = 1; global->entity_cbs[0] = _mxml_entity_cb; global->wrap = 72; TlsSetValue(_mxml_tls_index, (LPVOID)global); } return (global); } #else /**** No threading ****/ /* * '_mxml_global()' - Get global data. */ _mxml_global_t * /* O - Global data */ _mxml_global(void) { static _mxml_global_t global = /* Global data */ { NULL, /* error_cb */ 1, /* num_entity_cbs */ { _mxml_entity_cb }, /* entity_cbs */ 72, /* wrap */ NULL, /* custom_load_cb */ NULL /* custom_save_cb */ }; return (&global); } #endif /* HAVE_PTHREAD_H */ /* * End of "$Id: mxml-private.c 422 2010-11-07 22:55:11Z mike $". */ cmtk-3.3.1/Utilities/mxml/mxml-private.h000066400000000000000000000021151276303427400202110ustar00rootroot00000000000000/* * "$Id: mxml-private.h 408 2010-09-19 05:26:46Z mike $" * * Private definitions for Mini-XML, a small XML-like file parsing library. * * Copyright 2003-2010 by Michael R Sweet. * * These coded instructions, statements, and computer programs are the * property of Michael R Sweet and are protected by Federal copyright * law. Distribution and use rights are outlined in the file "COPYING" * which should have been included with this file. If this file is * missing or damaged, see the license at: * * http://www.minixml.org/ */ /* * Include necessary headers... */ #include "config.h" #include "mxml.h" /* * Global, per-thread data... */ typedef struct _mxml_global_s { void (*error_cb)(const char *); int num_entity_cbs; int (*entity_cbs[100])(const char *name); int wrap; mxml_custom_load_cb_t custom_load_cb; mxml_custom_save_cb_t custom_save_cb; } _mxml_global_t; /* * Functions... */ extern _mxml_global_t *_mxml_global(void); extern int _mxml_entity_cb(const char *name); /* * End of "$Id: mxml-private.h 408 2010-09-19 05:26:46Z mike $". */ cmtk-3.3.1/Utilities/mxml/mxml-search.c000066400000000000000000000156041276303427400200060ustar00rootroot00000000000000/* * "$Id: mxml-search.c 427 2011-01-03 02:03:29Z mike $" * * Search/navigation functions for Mini-XML, a small XML-like file * parsing library. * * Copyright 2003-2010 by Michael R Sweet. * * These coded instructions, statements, and computer programs are the * property of Michael R Sweet and are protected by Federal copyright * law. Distribution and use rights are outlined in the file "COPYING" * which should have been included with this file. If this file is * missing or damaged, see the license at: * * http://www.minixml.org/ * * Contents: * * mxmlFindElement() - Find the named element. * mxmlFindValue() - Find a value with the given path. * mxmlWalkNext() - Walk to the next logical node in the tree. * mxmlWalkPrev() - Walk to the previous logical node in the tree. */ /* * Include necessary headers... */ #include "config.h" #include "mxml.h" /* * 'mxmlFindElement()' - Find the named element. * * The search is constrained by the name, attribute name, and value; any * NULL names or values are treated as wildcards, so different kinds of * searches can be implemented by looking for all elements of a given name * or all elements with a specific attribute. The descend argument determines * whether the search descends into child nodes; normally you will use * MXML_DESCEND_FIRST for the initial search and MXML_NO_DESCEND to find * additional direct descendents of the node. The top node argument * constrains the search to a particular node's children. */ mxml_node_t * /* O - Element node or NULL */ mxmlFindElement(mxml_node_t *node, /* I - Current node */ mxml_node_t *top, /* I - Top node */ const char *name, /* I - Element name or NULL for any */ const char *attr, /* I - Attribute name, or NULL for none */ const char *value, /* I - Attribute value, or NULL for any */ int descend) /* I - Descend into tree - MXML_DESCEND, MXML_NO_DESCEND, or MXML_DESCEND_FIRST */ { const char *temp; /* Current attribute value */ /* * Range check input... */ if (!node || !top || (!attr && value)) return (NULL); /* * Start with the next node... */ node = mxmlWalkNext(node, top, descend); /* * Loop until we find a matching element... */ while (node != NULL) { /* * See if this node matches... */ if (node->type == MXML_ELEMENT && node->value.element.name && (!name || !strcmp(node->value.element.name, name))) { /* * See if we need to check for an attribute... */ if (!attr) return (node); /* No attribute search, return it... */ /* * Check for the attribute... */ if ((temp = mxmlElementGetAttr(node, attr)) != NULL) { /* * OK, we have the attribute, does it match? */ if (!value || !strcmp(value, temp)) return (node); /* Yes, return it... */ } } /* * No match, move on to the next node... */ if (descend == MXML_DESCEND) node = mxmlWalkNext(node, top, MXML_DESCEND); else node = node->next; } return (NULL); } /* * 'mxmlFindPath()' - Find a node with the given path. * * The "path" is a slash-separated list of element names. The name "*" is * considered a wildcard for one or more levels of elements. For example, * "foo/one/two", "bar/two/one", "*\/one", and so forth. * * The first child node of the found node is returned if the given node has * children and the first child is a value node. * * @since Mini-XML 2.7@ */ mxml_node_t * /* O - Found node or NULL */ mxmlFindPath(mxml_node_t *top, /* I - Top node */ const char *path) /* I - Path to element */ { mxml_node_t *node; /* Current node */ char element[256]; /* Current element name */ const char *pathsep; /* Separator in path */ int descend; /* mxmlFindElement option */ /* * Range check input... */ if (!top || !path || !*path) return (NULL); /* * Search each element in the path... */ node = top; while (*path) { /* * Handle wildcards... */ if (!strncmp(path, "*/", 2)) { path += 2; descend = MXML_DESCEND; } else descend = MXML_DESCEND_FIRST; /* * Get the next element in the path... */ if ((pathsep = strchr(path, '/')) == NULL) pathsep = path + strlen(path); if (pathsep == path || (pathsep - path) >= sizeof(element)) return (NULL); memcpy(element, path, pathsep - path); element[pathsep - path] = '\0'; if (*pathsep) path = pathsep + 1; else path = pathsep; /* * Search for the element... */ if ((node = mxmlFindElement(node, node, element, NULL, NULL, descend)) == NULL) return (NULL); } /* * If we get this far, return the node or its first child... */ if (node->child && node->child->type != MXML_ELEMENT) return (node->child); else return (node); } /* * 'mxmlWalkNext()' - Walk to the next logical node in the tree. * * The descend argument controls whether the first child is considered * to be the next node. The top node argument constrains the walk to * the node's children. */ mxml_node_t * /* O - Next node or NULL */ mxmlWalkNext(mxml_node_t *node, /* I - Current node */ mxml_node_t *top, /* I - Top node */ int descend) /* I - Descend into tree - MXML_DESCEND, MXML_NO_DESCEND, or MXML_DESCEND_FIRST */ { if (!node) return (NULL); else if (node->child && descend) return (node->child); else if (node == top) return (NULL); else if (node->next) return (node->next); else if (node->parent && node->parent != top) { node = node->parent; while (!node->next) if (node->parent == top || !node->parent) return (NULL); else node = node->parent; return (node->next); } else return (NULL); } /* * 'mxmlWalkPrev()' - Walk to the previous logical node in the tree. * * The descend argument controls whether the previous node's last child * is considered to be the previous node. The top node argument constrains * the walk to the node's children. */ mxml_node_t * /* O - Previous node or NULL */ mxmlWalkPrev(mxml_node_t *node, /* I - Current node */ mxml_node_t *top, /* I - Top node */ int descend) /* I - Descend into tree - MXML_DESCEND, MXML_NO_DESCEND, or MXML_DESCEND_FIRST */ { if (!node || node == top) return (NULL); else if (node->prev) { if (node->prev->last_child && descend) { /* * Find the last child under the previous node... */ node = node->prev->last_child; while (node->last_child) node = node->last_child; return (node); } else return (node->prev); } else if (node->parent != top) return (node->parent); else return (NULL); } /* * End of "$Id: mxml-search.c 427 2011-01-03 02:03:29Z mike $". */ cmtk-3.3.1/Utilities/mxml/mxml-set.c000066400000000000000000000176101276303427400173330ustar00rootroot00000000000000/* * "$Id: mxml-set.c 441 2011-12-09 23:49:00Z mike $" * * Node set functions for Mini-XML, a small XML-like file parsing library. * * Copyright 2003-2011 by Michael R Sweet. * * These coded instructions, statements, and computer programs are the * property of Michael R Sweet and are protected by Federal copyright * law. Distribution and use rights are outlined in the file "COPYING" * which should have been included with this file. If this file is * missing or damaged, see the license at: * * http://www.minixml.org/ * * Contents: * * mxmlSetCDATA() - Set the element name of a CDATA node. * mxmlSetCustom() - Set the data and destructor of a custom data node. * mxmlSetElement() - Set the name of an element node. * mxmlSetInteger() - Set the value of an integer node. * mxmlSetOpaque() - Set the value of an opaque node. * mxmlSetReal() - Set the value of a real number node. * mxmlSetText() - Set the value of a text node. * mxmlSetTextf() - Set the value of a text node to a formatted string. * mxmlSetUserData() - Set the user data pointer for a node. */ /* * Include necessary headers... */ #include "config.h" #include "mxml.h" /* * 'mxmlSetCDATA()' - Set the element name of a CDATA node. * * The node is not changed if it (or its first child) is not a CDATA element node. * * @since Mini-XML 2.3@ */ int /* O - 0 on success, -1 on failure */ mxmlSetCDATA(mxml_node_t *node, /* I - Node to set */ const char *data) /* I - New data string */ { /* * Range check input... */ if (node && node->type == MXML_ELEMENT && strncmp(node->value.element.name, "![CDATA[", 8) && node->child && node->child->type == MXML_ELEMENT && !strncmp(node->child->value.element.name, "![CDATA[", 8)) node = node->child; if (!node || node->type != MXML_ELEMENT || !data || strncmp(node->value.element.name, "![CDATA[", 8)) return (-1); /* * Free any old element value and set the new value... */ if (node->value.element.name) free(node->value.element.name); node->value.element.name = _mxml_strdupf("![CDATA[%s]]", data); return (0); } /* * 'mxmlSetCustom()' - Set the data and destructor of a custom data node. * * The node is not changed if it (or its first child) is not a custom node. * * @since Mini-XML 2.1@ */ int /* O - 0 on success, -1 on failure */ mxmlSetCustom( mxml_node_t *node, /* I - Node to set */ void *data, /* I - New data pointer */ mxml_custom_destroy_cb_t destroy) /* I - New destructor function */ { /* * Range check input... */ if (node && node->type == MXML_ELEMENT && node->child && node->child->type == MXML_CUSTOM) node = node->child; if (!node || node->type != MXML_CUSTOM) return (-1); /* * Free any old element value and set the new value... */ if (node->value.custom.data && node->value.custom.destroy) (*(node->value.custom.destroy))(node->value.custom.data); node->value.custom.data = data; node->value.custom.destroy = destroy; return (0); } /* * 'mxmlSetElement()' - Set the name of an element node. * * The node is not changed if it is not an element node. */ int /* O - 0 on success, -1 on failure */ mxmlSetElement(mxml_node_t *node, /* I - Node to set */ const char *name) /* I - New name string */ { /* * Range check input... */ if (!node || node->type != MXML_ELEMENT || !name) return (-1); /* * Free any old element value and set the new value... */ if (node->value.element.name) free(node->value.element.name); node->value.element.name = strdup(name); return (0); } /* * 'mxmlSetInteger()' - Set the value of an integer node. * * The node is not changed if it (or its first child) is not an integer node. */ int /* O - 0 on success, -1 on failure */ mxmlSetInteger(mxml_node_t *node, /* I - Node to set */ int integer) /* I - Integer value */ { /* * Range check input... */ if (node && node->type == MXML_ELEMENT && node->child && node->child->type == MXML_INTEGER) node = node->child; if (!node || node->type != MXML_INTEGER) return (-1); /* * Set the new value and return... */ node->value.integer = integer; return (0); } /* * 'mxmlSetOpaque()' - Set the value of an opaque node. * * The node is not changed if it (or its first child) is not an opaque node. */ int /* O - 0 on success, -1 on failure */ mxmlSetOpaque(mxml_node_t *node, /* I - Node to set */ const char *opaque) /* I - Opaque string */ { /* * Range check input... */ if (node && node->type == MXML_ELEMENT && node->child && node->child->type == MXML_OPAQUE) node = node->child; if (!node || node->type != MXML_OPAQUE || !opaque) return (-1); /* * Free any old opaque value and set the new value... */ if (node->value.opaque) free(node->value.opaque); node->value.opaque = strdup(opaque); return (0); } /* * 'mxmlSetReal()' - Set the value of a real number node. * * The node is not changed if it (or its first child) is not a real number node. */ int /* O - 0 on success, -1 on failure */ mxmlSetReal(mxml_node_t *node, /* I - Node to set */ double real) /* I - Real number value */ { /* * Range check input... */ if (node && node->type == MXML_ELEMENT && node->child && node->child->type == MXML_REAL) node = node->child; if (!node || node->type != MXML_REAL) return (-1); /* * Set the new value and return... */ node->value.real = real; return (0); } /* * 'mxmlSetText()' - Set the value of a text node. * * The node is not changed if it (or its first child) is not a text node. */ int /* O - 0 on success, -1 on failure */ mxmlSetText(mxml_node_t *node, /* I - Node to set */ int whitespace, /* I - 1 = leading whitespace, 0 = no whitespace */ const char *string) /* I - String */ { /* * Range check input... */ if (node && node->type == MXML_ELEMENT && node->child && node->child->type == MXML_TEXT) node = node->child; if (!node || node->type != MXML_TEXT || !string) return (-1); /* * Free any old string value and set the new value... */ if (node->value.text.string) free(node->value.text.string); node->value.text.whitespace = whitespace; node->value.text.string = strdup(string); return (0); } /* * 'mxmlSetTextf()' - Set the value of a text node to a formatted string. * * The node is not changed if it (or its first child) is not a text node. */ int /* O - 0 on success, -1 on failure */ mxmlSetTextf(mxml_node_t *node, /* I - Node to set */ int whitespace, /* I - 1 = leading whitespace, 0 = no whitespace */ const char *format, /* I - Printf-style format string */ ...) /* I - Additional arguments as needed */ { va_list ap; /* Pointer to arguments */ /* * Range check input... */ if (node && node->type == MXML_ELEMENT && node->child && node->child->type == MXML_TEXT) node = node->child; if (!node || node->type != MXML_TEXT || !format) return (-1); /* * Free any old string value and set the new value... */ if (node->value.text.string) free(node->value.text.string); va_start(ap, format); node->value.text.whitespace = whitespace; node->value.text.string = _mxml_strdupf(format, ap); va_end(ap); return (0); } /* * 'mxmlSetUserData()' - Set the user data pointer for a node. * * @since Mini-XML 2.7@ */ int /* O - 0 on success, -1 on failure */ mxmlSetUserData(mxml_node_t *node, /* I - Node to set */ void *data) /* I - User data pointer */ { /* * Range check input... */ if (!node) return (-1); /* * Set the user data pointer and return... */ node->user_data = data; return (0); } /* * End of "$Id: mxml-set.c 441 2011-12-09 23:49:00Z mike $". */ cmtk-3.3.1/Utilities/mxml/mxml-string.c000066400000000000000000000232141276303427400200430ustar00rootroot00000000000000/* * "$Id: mxml-string.c 424 2010-12-25 16:21:50Z mike $" * * String functions for Mini-XML, a small XML-like file parsing library. * * Copyright 2003-2010 by Michael R Sweet. * * These coded instructions, statements, and computer programs are the * property of Michael R Sweet and are protected by Federal copyright * law. Distribution and use rights are outlined in the file "COPYING" * which should have been included with this file. If this file is * missing or damaged, see the license at: * * http://www.minixml.org/ * * Contents: * * _mxml_snprintf() - Format a string. * _mxml_strdup() - Duplicate a string. * _mxml_strdupf() - Format and duplicate a string. * _mxml_vsnprintf() - Format a string into a fixed size buffer. * _mxml_vstrdupf() - Format and duplicate a string. */ /* * Include necessary headers... */ #include "config.h" /* * The va_copy macro is part of C99, but many compilers don't implement it. * Provide a "direct assignment" implmentation when va_copy isn't defined... */ #ifndef va_copy # ifdef __va_copy # define va_copy(dst,src) __va_copy(dst,src) # else # define va_copy(dst,src) memcpy(&dst, &src, sizeof(va_list)) # endif /* __va_copy */ #endif /* va_copy */ #ifndef HAVE_SNPRINTF /* * '_mxml_snprintf()' - Format a string. */ int /* O - Number of bytes formatted */ _mxml_snprintf(char *buffer, /* I - Output buffer */ size_t bufsize, /* I - Size of output buffer */ const char *format, /* I - Printf-style format string */ ...) /* I - Additional arguments as needed */ { va_list ap; /* Argument list */ int bytes; /* Number of bytes formatted */ va_start(ap, format); bytes = vsnprintf(buffer, bufsize, format, ap); va_end(ap); return (bytes); } #endif /* !HAVE_SNPRINTF */ /* * '_mxml_strdup()' - Duplicate a string. */ #ifndef HAVE_STRDUP char * /* O - New string pointer */ _mxml_strdup(const char *s) /* I - String to duplicate */ { char *t; /* New string pointer */ if (s == NULL) return (NULL); if ((t = malloc(strlen(s) + 1)) == NULL) return (NULL); return (strcpy(t, s)); } #endif /* !HAVE_STRDUP */ /* * '_mxml_strdupf()' - Format and duplicate a string. */ char * /* O - New string pointer */ _mxml_strdupf(const char *format, /* I - Printf-style format string */ ...) /* I - Additional arguments as needed */ { va_list ap; /* Pointer to additional arguments */ char *s; /* Pointer to formatted string */ /* * Get a pointer to the additional arguments, format the string, * and return it... */ va_start(ap, format); s = _mxml_vstrdupf(format, ap); va_end(ap); return (s); } #ifndef HAVE_VSNPRINTF /* * '_mxml_vsnprintf()' - Format a string into a fixed size buffer. */ int /* O - Number of bytes formatted */ _mxml_vsnprintf(char *buffer, /* O - Output buffer */ size_t bufsize, /* O - Size of output buffer */ const char *format, /* I - Printf-style format string */ va_list ap) /* I - Pointer to additional arguments */ { char *bufptr, /* Pointer to position in buffer */ *bufend, /* Pointer to end of buffer */ sign, /* Sign of format width */ size, /* Size character (h, l, L) */ type; /* Format type character */ int width, /* Width of field */ prec; /* Number of characters of precision */ char tformat[100], /* Temporary format string for sprintf() */ *tptr, /* Pointer into temporary format */ temp[1024]; /* Buffer for formatted numbers */ char *s; /* Pointer to string */ int slen; /* Length of string */ int bytes; /* Total number of bytes needed */ /* * Loop through the format string, formatting as needed... */ bufptr = buffer; bufend = buffer + bufsize - 1; bytes = 0; while (*format) { if (*format == '%') { tptr = tformat; *tptr++ = *format++; if (*format == '%') { if (bufptr && bufptr < bufend) *bufptr++ = *format; bytes ++; format ++; continue; } else if (strchr(" -+#\'", *format)) { *tptr++ = *format; sign = *format++; } else sign = 0; if (*format == '*') { /* * Get width from argument... */ format ++; width = va_arg(ap, int); snprintf(tptr, sizeof(tformat) - (tptr - tformat), "%d", width); tptr += strlen(tptr); } else { width = 0; while (isdigit(*format & 255)) { if (tptr < (tformat + sizeof(tformat) - 1)) *tptr++ = *format; width = width * 10 + *format++ - '0'; } } if (*format == '.') { if (tptr < (tformat + sizeof(tformat) - 1)) *tptr++ = *format; format ++; if (*format == '*') { /* * Get precision from argument... */ format ++; prec = va_arg(ap, int); snprintf(tptr, sizeof(tformat) - (tptr - tformat), "%d", prec); tptr += strlen(tptr); } else { prec = 0; while (isdigit(*format & 255)) { if (tptr < (tformat + sizeof(tformat) - 1)) *tptr++ = *format; prec = prec * 10 + *format++ - '0'; } } } else prec = -1; if (*format == 'l' && format[1] == 'l') { size = 'L'; if (tptr < (tformat + sizeof(tformat) - 2)) { *tptr++ = 'l'; *tptr++ = 'l'; } format += 2; } else if (*format == 'h' || *format == 'l' || *format == 'L') { if (tptr < (tformat + sizeof(tformat) - 1)) *tptr++ = *format; size = *format++; } if (!*format) break; if (tptr < (tformat + sizeof(tformat) - 1)) *tptr++ = *format; type = *format++; *tptr = '\0'; switch (type) { case 'E' : /* Floating point formats */ case 'G' : case 'e' : case 'f' : case 'g' : if ((width + 2) > sizeof(temp)) break; sprintf(temp, tformat, va_arg(ap, double)); bytes += strlen(temp); if (bufptr) { if ((bufptr + strlen(temp)) > bufend) { strncpy(bufptr, temp, (size_t)(bufend - bufptr)); bufptr = bufend; } else { strcpy(bufptr, temp); bufptr += strlen(temp); } } break; case 'B' : /* Integer formats */ case 'X' : case 'b' : case 'd' : case 'i' : case 'o' : case 'u' : case 'x' : if ((width + 2) > sizeof(temp)) break; #ifdef HAVE_LONG_LONG if (size == 'L') sprintf(temp, tformat, va_arg(ap, long long)); else #endif /* HAVE_LONG_LONG */ sprintf(temp, tformat, va_arg(ap, int)); bytes += strlen(temp); if (bufptr) { if ((bufptr + strlen(temp)) > bufend) { strncpy(bufptr, temp, (size_t)(bufend - bufptr)); bufptr = bufend; } else { strcpy(bufptr, temp); bufptr += strlen(temp); } } break; case 'p' : /* Pointer value */ if ((width + 2) > sizeof(temp)) break; sprintf(temp, tformat, va_arg(ap, void *)); bytes += strlen(temp); if (bufptr) { if ((bufptr + strlen(temp)) > bufend) { strncpy(bufptr, temp, (size_t)(bufend - bufptr)); bufptr = bufend; } else { strcpy(bufptr, temp); bufptr += strlen(temp); } } break; case 'c' : /* Character or character array */ bytes += width; if (bufptr) { if (width <= 1) *bufptr++ = va_arg(ap, int); else { if ((bufptr + width) > bufend) width = bufend - bufptr; memcpy(bufptr, va_arg(ap, char *), (size_t)width); bufptr += width; } } break; case 's' : /* String */ if ((s = va_arg(ap, char *)) == NULL) s = "(null)"; slen = strlen(s); if (slen > width && prec != width) width = slen; bytes += width; if (bufptr) { if ((bufptr + width) > bufend) width = bufend - bufptr; if (slen > width) slen = width; if (sign == '-') { strncpy(bufptr, s, (size_t)slen); memset(bufptr + slen, ' ', (size_t)(width - slen)); } else { memset(bufptr, ' ', (size_t)(width - slen)); strncpy(bufptr + width - slen, s, (size_t)slen); } bufptr += width; } break; case 'n' : /* Output number of chars so far */ *(va_arg(ap, int *)) = bytes; break; } } else { bytes ++; if (bufptr && bufptr < bufend) *bufptr++ = *format; format ++; } } /* * Nul-terminate the string and return the number of characters needed. */ *bufptr = '\0'; return (bytes); } #endif /* !HAVE_VSNPRINTF */ /* * '_mxml_vstrdupf()' - Format and duplicate a string. */ char * /* O - New string pointer */ _mxml_vstrdupf(const char *format, /* I - Printf-style format string */ va_list ap) /* I - Pointer to additional arguments */ { int bytes; /* Number of bytes required */ char *buffer, /* String buffer */ temp[256]; /* Small buffer for first vsnprintf */ va_list apcopy; /* Copy of argument list */ /* * First format with a tiny buffer; this will tell us how many bytes are * needed... */ va_copy(apcopy, ap); bytes = vsnprintf(temp, sizeof(temp), format, apcopy); if (bytes < sizeof(temp)) { /* * Hey, the formatted string fits in the tiny buffer, so just dup that... */ return (strdup(temp)); } /* * Allocate memory for the whole thing and reformat to the new, larger * buffer... */ if ((buffer = calloc(1, bytes + 1)) != NULL) vsnprintf(buffer, bytes + 1, format, ap); /* * Return the new string... */ return (buffer); } /* * End of "$Id: mxml-string.c 424 2010-12-25 16:21:50Z mike $". */ cmtk-3.3.1/Utilities/mxml/mxml.h000066400000000000000000000276161276303427400165560ustar00rootroot00000000000000/* * "$Id: mxml.h 427 2011-01-03 02:03:29Z mike $" * * Header file for Mini-XML, a small XML-like file parsing library. * * Copyright 2003-2011 by Michael R Sweet. * * These coded instructions, statements, and computer programs are the * property of Michael R Sweet and are protected by Federal copyright * law. Distribution and use rights are outlined in the file "COPYING" * which should have been included with this file. If this file is * missing or damaged, see the license at: * * http://www.minixml.org/ */ /* * Prevent multiple inclusion... */ #ifndef _mxml_h_ # define _mxml_h_ /* * add "cmtk_" prefix to exported symbols - we are building this library bundled with CMTK */ #include /* * Include necessary headers... */ # include # include # include # include # include /* * Constants... */ # define MXML_TAB 8 /* Tabs every N columns */ # define MXML_NO_CALLBACK 0 /* Don't use a type callback */ # define MXML_INTEGER_CALLBACK mxml_integer_cb /* Treat all data as integers */ # define MXML_OPAQUE_CALLBACK mxml_opaque_cb /* Treat all data as opaque */ # define MXML_REAL_CALLBACK mxml_real_cb /* Treat all data as real numbers */ # define MXML_TEXT_CALLBACK 0 /* Treat all data as text */ # define MXML_IGNORE_CALLBACK mxml_ignore_cb /* Ignore all non-element content */ # define MXML_NO_PARENT 0 /* No parent for the node */ # define MXML_DESCEND 1 /* Descend when finding/walking */ # define MXML_NO_DESCEND 0 /* Don't descend when finding/walking */ # define MXML_DESCEND_FIRST -1 /* Descend for first find */ # define MXML_WS_BEFORE_OPEN 0 /* Callback for before open tag */ # define MXML_WS_AFTER_OPEN 1 /* Callback for after open tag */ # define MXML_WS_BEFORE_CLOSE 2 /* Callback for before close tag */ # define MXML_WS_AFTER_CLOSE 3 /* Callback for after close tag */ # define MXML_ADD_BEFORE 0 /* Add node before specified node */ # define MXML_ADD_AFTER 1 /* Add node after specified node */ # define MXML_ADD_TO_PARENT NULL /* Add node relative to parent */ /* * Data types... */ typedef enum mxml_sax_event_e /**** SAX event type. ****/ { MXML_SAX_CDATA, /* CDATA node */ MXML_SAX_COMMENT, /* Comment node */ MXML_SAX_DATA, /* Data node */ MXML_SAX_DIRECTIVE, /* Processing directive node */ MXML_SAX_ELEMENT_CLOSE, /* Element closed */ MXML_SAX_ELEMENT_OPEN /* Element opened */ } mxml_sax_event_t; typedef enum mxml_type_e /**** The XML node type. ****/ { MXML_IGNORE = -1, /* Ignore/throw away node @since Mini-XML 2.3@ */ MXML_ELEMENT, /* XML element with attributes */ MXML_INTEGER, /* Integer value */ MXML_OPAQUE, /* Opaque string */ MXML_REAL, /* Real value */ MXML_TEXT, /* Text fragment */ MXML_CUSTOM /* Custom data @since Mini-XML 2.1@ */ } mxml_type_t; typedef void (*mxml_custom_destroy_cb_t)(void *); /**** Custom data destructor ****/ typedef void (*mxml_error_cb_t)(const char *); /**** Error callback function ****/ typedef struct mxml_attr_s /**** An XML element attribute value. @private@ ****/ { char *name; /* Attribute name */ char *value; /* Attribute value */ } mxml_attr_t; typedef struct mxml_element_s /**** An XML element value. @private@ ****/ { char *name; /* Name of element */ int num_attrs; /* Number of attributes */ mxml_attr_t *attrs; /* Attributes */ } mxml_element_t; typedef struct mxml_text_s /**** An XML text value. @private@ ****/ { int whitespace; /* Leading whitespace? */ char *string; /* Fragment string */ } mxml_text_t; typedef struct mxml_custom_s /**** An XML custom value. @private@ ****/ { void *data; /* Pointer to (allocated) custom data */ mxml_custom_destroy_cb_t destroy; /* Pointer to destructor function */ } mxml_custom_t; typedef union mxml_value_u /**** An XML node value. @private@ ****/ { mxml_element_t element; /* Element */ int integer; /* Integer number */ char *opaque; /* Opaque string */ double real; /* Real number */ mxml_text_t text; /* Text fragment */ mxml_custom_t custom; /* Custom data @since Mini-XML 2.1@ */ } mxml_value_t; struct mxml_node_s /**** An XML node. @private@ ****/ { mxml_type_t type; /* Node type */ struct mxml_node_s *next; /* Next node under same parent */ struct mxml_node_s *prev; /* Previous node under same parent */ struct mxml_node_s *parent; /* Parent node */ struct mxml_node_s *child; /* First child node */ struct mxml_node_s *last_child; /* Last child node */ mxml_value_t value; /* Node value */ int ref_count; /* Use count */ void *user_data; /* User data */ }; typedef struct mxml_node_s mxml_node_t; /**** An XML node. ****/ struct mxml_index_s /**** An XML node index. @private@ ****/ { char *attr; /* Attribute used for indexing or NULL */ int num_nodes; /* Number of nodes in index */ int alloc_nodes; /* Allocated nodes in index */ int cur_node; /* Current node */ mxml_node_t **nodes; /* Node array */ }; typedef struct mxml_index_s mxml_index_t; /**** An XML node index. ****/ typedef int (*mxml_custom_load_cb_t)(mxml_node_t *, const char *); /**** Custom data load callback function ****/ typedef char *(*mxml_custom_save_cb_t)(mxml_node_t *); /**** Custom data save callback function ****/ typedef int (*mxml_entity_cb_t)(const char *); /**** Entity callback function */ typedef mxml_type_t (*mxml_load_cb_t)(mxml_node_t *); /**** Load callback function ****/ typedef const char *(*mxml_save_cb_t)(mxml_node_t *, int); /**** Save callback function ****/ typedef void (*mxml_sax_cb_t)(mxml_node_t *, mxml_sax_event_t, void *); /**** SAX callback function ****/ /* * C++ support... */ # ifdef __cplusplus extern "C" { # endif /* __cplusplus */ /* * Prototypes... */ extern void mxmlAdd(mxml_node_t *parent, int where, mxml_node_t *child, mxml_node_t *node); extern void mxmlDelete(mxml_node_t *node); extern void mxmlElementDeleteAttr(mxml_node_t *node, const char *name); extern const char *mxmlElementGetAttr(mxml_node_t *node, const char *name); extern void mxmlElementSetAttr(mxml_node_t *node, const char *name, const char *value); extern void mxmlElementSetAttrf(mxml_node_t *node, const char *name, const char *format, ...) # ifdef __GNUC__ __attribute__ ((__format__ (__printf__, 3, 4))) # endif /* __GNUC__ */ ; extern int mxmlEntityAddCallback(mxml_entity_cb_t cb); extern const char *mxmlEntityGetName(int val); extern int mxmlEntityGetValue(const char *name); extern void mxmlEntityRemoveCallback(mxml_entity_cb_t cb); extern mxml_node_t *mxmlFindElement(mxml_node_t *node, mxml_node_t *top, const char *name, const char *attr, const char *value, int descend); extern mxml_node_t *mxmlFindPath(mxml_node_t *node, const char *path); extern const char *mxmlGetCDATA(mxml_node_t *node); extern const void *mxmlGetCustom(mxml_node_t *node); extern const char *mxmlGetElement(mxml_node_t *node); extern mxml_node_t *mxmlGetFirstChild(mxml_node_t *node); extern int mxmlGetInteger(mxml_node_t *node); extern mxml_node_t *mxmlGetLastChild(mxml_node_t *node); extern mxml_node_t *mxmlGetNextSibling(mxml_node_t *node); extern const char *mxmlGetOpaque(mxml_node_t *node); extern mxml_node_t *mxmlGetParent(mxml_node_t *node); extern mxml_node_t *mxmlGetPrevSibling(mxml_node_t *node); extern double mxmlGetReal(mxml_node_t *node); extern int mxmlGetRefCount(mxml_node_t *node); extern const char *mxmlGetText(mxml_node_t *node, int *whitespace); extern mxml_type_t mxmlGetType(mxml_node_t *node); extern void *mxmlGetUserData(mxml_node_t *node); extern void mxmlIndexDelete(mxml_index_t *ind); extern mxml_node_t *mxmlIndexEnum(mxml_index_t *ind); extern mxml_node_t *mxmlIndexFind(mxml_index_t *ind, const char *element, const char *value); extern int mxmlIndexGetCount(mxml_index_t *ind); extern mxml_index_t *mxmlIndexNew(mxml_node_t *node, const char *element, const char *attr); extern mxml_node_t *mxmlIndexReset(mxml_index_t *ind); extern mxml_node_t *mxmlLoadFd(mxml_node_t *top, int fd, mxml_type_t (*cb)(mxml_node_t *)); extern mxml_node_t *mxmlLoadFile(mxml_node_t *top, FILE *fp, mxml_type_t (*cb)(mxml_node_t *)); extern mxml_node_t *mxmlLoadString(mxml_node_t *top, const char *s, mxml_type_t (*cb)(mxml_node_t *)); extern mxml_node_t *mxmlNewCDATA(mxml_node_t *parent, const char *string); extern mxml_node_t *mxmlNewCustom(mxml_node_t *parent, void *data, mxml_custom_destroy_cb_t destroy); extern mxml_node_t *mxmlNewElement(mxml_node_t *parent, const char *name); extern mxml_node_t *mxmlNewInteger(mxml_node_t *parent, int integer); extern mxml_node_t *mxmlNewOpaque(mxml_node_t *parent, const char *opaque); extern mxml_node_t *mxmlNewReal(mxml_node_t *parent, double real); extern mxml_node_t *mxmlNewText(mxml_node_t *parent, int whitespace, const char *string); extern mxml_node_t *mxmlNewTextf(mxml_node_t *parent, int whitespace, const char *format, ...) # ifdef __GNUC__ __attribute__ ((__format__ (__printf__, 3, 4))) # endif /* __GNUC__ */ ; extern mxml_node_t *mxmlNewXML(const char *version); extern int mxmlRelease(mxml_node_t *node); extern void mxmlRemove(mxml_node_t *node); extern int mxmlRetain(mxml_node_t *node); extern char *mxmlSaveAllocString(mxml_node_t *node, mxml_save_cb_t cb); extern int mxmlSaveFd(mxml_node_t *node, int fd, mxml_save_cb_t cb); extern int mxmlSaveFile(mxml_node_t *node, FILE *fp, mxml_save_cb_t cb); extern int mxmlSaveString(mxml_node_t *node, char *buffer, int bufsize, mxml_save_cb_t cb); extern mxml_node_t *mxmlSAXLoadFd(mxml_node_t *top, int fd, mxml_type_t (*cb)(mxml_node_t *), mxml_sax_cb_t sax, void *sax_data); extern mxml_node_t *mxmlSAXLoadFile(mxml_node_t *top, FILE *fp, mxml_type_t (*cb)(mxml_node_t *), mxml_sax_cb_t sax, void *sax_data); extern mxml_node_t *mxmlSAXLoadString(mxml_node_t *top, const char *s, mxml_type_t (*cb)(mxml_node_t *), mxml_sax_cb_t sax, void *sax_data); extern int mxmlSetCDATA(mxml_node_t *node, const char *data); extern int mxmlSetCustom(mxml_node_t *node, void *data, mxml_custom_destroy_cb_t destroy); extern void mxmlSetCustomHandlers(mxml_custom_load_cb_t load, mxml_custom_save_cb_t save); extern int mxmlSetElement(mxml_node_t *node, const char *name); extern void mxmlSetErrorCallback(mxml_error_cb_t cb); extern int mxmlSetInteger(mxml_node_t *node, int integer); extern int mxmlSetOpaque(mxml_node_t *node, const char *opaque); extern int mxmlSetReal(mxml_node_t *node, double real); extern int mxmlSetText(mxml_node_t *node, int whitespace, const char *string); extern int mxmlSetTextf(mxml_node_t *node, int whitespace, const char *format, ...) # ifdef __GNUC__ __attribute__ ((__format__ (__printf__, 3, 4))) # endif /* __GNUC__ */ ; extern int mxmlSetUserData(mxml_node_t *node, void *data); extern void mxmlSetWrapMargin(int column); extern mxml_node_t *mxmlWalkNext(mxml_node_t *node, mxml_node_t *top, int descend); extern mxml_node_t *mxmlWalkPrev(mxml_node_t *node, mxml_node_t *top, int descend); /* * Semi-private functions... */ extern void mxml_error(const char *format, ...); extern mxml_type_t mxml_ignore_cb(mxml_node_t *node); extern mxml_type_t mxml_integer_cb(mxml_node_t *node); extern mxml_type_t mxml_opaque_cb(mxml_node_t *node); extern mxml_type_t mxml_real_cb(mxml_node_t *node); /* * C++ support... */ # ifdef __cplusplus } # endif /* __cplusplus */ #endif /* !_mxml_h_ */ /* * End of "$Id: mxml.h 427 2011-01-03 02:03:29Z mike $". */ cmtk-3.3.1/Utilities/mxml/mxml.list.in000066400000000000000000000063161276303427400177010ustar00rootroot00000000000000# # "$Id: mxml.list.in 399 2009-05-17 17:20:51Z mike $" # # EPM software list file for Mini-XML, a small XML library. # # Copyright 2003-2009 by Michael Sweet. # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU Library General Public # License as published by the Free Software Foundation; either # version 2, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # Directories... $prefix=@prefix@ $exec_prefix=@exec_prefix@ $bindir=@bindir@ $datarootdir=@datarootdir@ $docdir=@docdir@ $includedir=@includedir@ $libdir=@libdir@ $mandir=@mandir@ $srcdir=@srcdir@ $PICFLAG=@PICFLAG@ # Product information %product mxml %copyright 2003-2009 by Michael Sweet %vendor Michael Sweet %license ${srcdir}/COPYING %readme ${srcdir}/README %version @VERSION@ %description < Vendor: Michael Sweet # Use buildroot so as not to disturb the version already installed BuildRoot: /var/tmp/%{name}-root %description Mini-XML is a small XML parsing library that you can use to read XML and XML-like data files in your application without requiring large non-standard libraries. Mini-XML provides the following functionality: - Reading of UTF-8 and UTF-16 and writing of UTF-8 encoded XML files and strings. - Data is stored in a linked-list tree structure, preserving the XML data hierarchy. - Supports arbitrary element names, attributes, and attribute values with no preset limits, just available memory. - Supports integer, real, opaque ("cdata"), and text data types in "leaf" nodes. - Functions for creating and managing trees of data. - "Find" and "walk" functions for easily locating and navigating trees of data. Mini-XML doesn't do validation or other types of processing on the data based upon schema files or other sources of definition information, nor does it support character entities other than those required by the XML specification. %prep %setup %build CFLAGS="$RPM_OPT_FLAGS" CXXFLAGS="$RPM_OPT_FLAGS" LDFLAGS="$RPM_OPT_FLAGS" ./configure --enable-shared --prefix=/usr # If we got this far, all prerequisite libraries must be here. make %install # Make sure the RPM_BUILD_ROOT directory exists. rm -rf $RPM_BUILD_ROOT make BUILDROOT=$RPM_BUILD_ROOT install %clean rm -rf $RPM_BUILD_ROOT %files %defattr(-,root,root) %dir /usr/bin /usr/bin/* %dir /usr/include /usr/include/mxml.h %dir /usr/lib /usr/lib/* %dir /usr/lib/pkgconfig /usr/lib/pkgconfig/mxml.pc %dir /usr/share/doc/mxml /usr/share/doc/mxml/* %dir /usr/share/man/man1 /usr/share/man/man1/* %dir /usr/share/man/man3 /usr/share/man/man3/* # # End of "$Id: mxml.spec 399 2009-05-17 17:20:51Z mike $". # cmtk-3.3.1/Utilities/mxml/mxml.xml000066400000000000000000001621471276303427400171260ustar00rootroot00000000000000 Add a node to a tree. Adds the specified node to the parent. If the child argument is not NULL, puts the new node before or after the specified child depending on the value of the where argument. If the child argument is NULL, puts the new node at the beginning of the child list (MXML_ADD_BEFORE) or at the end of the child list (MXML_ADD_AFTER). The constant MXML_ADD_TO_PARENT can be used to specify a NULL child pointer. mxml_node_t * Parent node int Where to add, MXML_ADD_BEFORE or MXML_ADD_AFTER mxml_node_t * Child node for where or MXML_ADD_TO_PARENT mxml_node_t * Node to add Delete a node and all of its children. If the specified node has a parent, this function first removes the node from its parent using the mxmlRemove() function. mxml_node_t * Node to delete Delete an attribute. @since Mini-XML 2.4@ mxml_node_t * Element const char * Attribute name const char * Attribute value or NULL Get an attribute. This function returns NULL if the node is not an element or the named attribute does not exist. mxml_node_t * Element node const char * Name of attribute Set an attribute. If the named attribute already exists, the value of the attribute is replaced by the new string value. The string value is copied into the element node. This function does nothing if the node is not an element. mxml_node_t * Element node const char * Name of attribute const char * Attribute value Set an attribute with a formatted value. If the named attribute already exists, the value of the attribute is replaced by the new formatted string. The formatted string value is copied into the element node. This function does nothing if the node is not an element. @since Mini-XML 2.3@ mxml_node_t * Element node const char * Name of attribute const char * Printf-style attribute value Additional arguments as needed int 0 on success, -1 on failure Add a callback to convert entities to Unicode. mxml_entity_cb_t Callback function to add const char * Entity name or NULL Get the name that corresponds to the character value. If val does not need to be represented by a named entity, NULL is returned. int Character value int Character value or -1 on error Get the character corresponding to a named entity. The entity name can also be a numeric constant. -1 is returned if the name is not known. const char * Entity name Remove a callback. mxml_entity_cb_t Callback function to remove mxml_node_t * Element node or NULL Find the named element. The search is constrained by the name, attribute name, and value; any NULL names or values are treated as wildcards, so different kinds of searches can be implemented by looking for all elements of a given name or all elements with a specific attribute. The descend argument determines whether the search descends into child nodes; normally you will use MXML_DESCEND_FIRST for the initial search and MXML_NO_DESCEND to find additional direct descendents of the node. The top node argument constrains the search to a particular node's children. mxml_node_t * Current node mxml_node_t * Top node const char * Element name or NULL for any const char * Attribute name, or NULL for none const char * Attribute value, or NULL for any int Descend into tree - MXML_DESCEND, MXML_NO_DESCEND, or MXML_DESCEND_FIRST mxml_node_t * Found node or NULL Find a node with the given path. The "path" is a slash-separated list of element names. The name "*" is considered a wildcard for one or more levels of elements. For example, "foo/one/two", "bar/two/one", "*/one", and so forth. The first child node of the found node is returned if the given node has children and the first child is a value node. @since Mini-XML 2.7@ mxml_node_t * Top node const char * Path to element const char * CDATA value or NULL Get the value for a CDATA node. @code NULL@ is returned if the node is not a CDATA element. @since Mini-XML 2.7@ mxml_node_t * Node to get const void * Custom value or NULL Get the value for a custom node. @code NULL@ is returned if the node (or its first child) is not a custom value node. @since Mini-XML 2.7@ mxml_node_t * Node to get const char * Element name or NULL Get the name for an element node. @code NULL@ is returned if the node is not an element node. @since Mini-XML 2.7@ mxml_node_t * Node to get mxml_node_t * First child or NULL Get the first child of an element node. @code NULL@ is returned if the node is not an element node or if the node has no children. @since Mini-XML 2.7@ mxml_node_t * Node to get int Integer value or 0 Get the integer value from the specified node or its first child. 0 is returned if the node (or its first child) is not an integer value node. @since Mini-XML 2.7@ mxml_node_t * Node to get mxml_node_t * Last child or NULL Get the last child of an element node. @code NULL@ is returned if the node is not an element node or if the node has no children. @since Mini-XML 2.7@ mxml_node_t * Node to get mxml_node_t * Get the next node for the current parent. @code NULL@ is returned if this is the last child for the current parent. @since Mini-XML 2.7@ Return the node type... mxml_node_t * Node to get const char * Opaque string or NULL Get an opaque string value for a node or its first child. @code NULL@ is returned if the node (or its first child) is not an opaque value node. @since Mini-XML 2.7@ mxml_node_t * Node to get mxml_node_t * Parent node or NULL Get the parent node. @code NULL@ is returned for a root node. @since Mini-XML 2.7@ mxml_node_t * Node to get mxml_node_t * Previous node or NULL Get the previous node for the current parent. @code NULL@ is returned if this is the first child for the current parent. @since Mini-XML 2.7@ mxml_node_t * Node to get double Real value or 0.0 Get the real value for a node or its first child. 0.0 is returned if the node (or its first child) is not a real value node. @since Mini-XML 2.7@ mxml_node_t * Node to get int Reference count Get the current reference (use) count for a node. The initial reference count of new nodes is 1. Use the @link mxmlRetain@ and @link mxmlRelease@ functions to increment and decrement a node's reference count. @since Mini-XML 2.7@. mxml_node_t * Node const char * Text string or NULL Get the text value for a node or its first child. @code NULL@ is returned if the node (or its first child) is not a text node. The "whitespace" argument can be NULL. @since Mini-XML 2.7@ mxml_node_t * Node to get int * 1 if string is preceded by whitespace, 0 otherwise mxml_type_t Type of node Get the node type. @code MXML_IGNORE@ is returned if "node" is @code NULL@. @since Mini-XML 2.7@ mxml_node_t * Node to get void * User data pointer Get the user data pointer for a node. @since Mini-XML 2.7@ mxml_node_t * Node to get Delete an index. mxml_index_t * Index to delete mxml_node_t * Next node or NULL if there is none Return the next node in the index. Nodes are returned in the sorted order of the index. mxml_index_t * Index to enumerate mxml_node_t * Node or NULL if none found Find the next matching node. You should call mxmlIndexReset() prior to using this function for the first time with a particular set of "element" and "value" strings. Passing NULL for both "element" and "value" is equivalent to calling mxmlIndexEnum(). mxml_index_t * Index to search const char * Element name to find, if any const char * Attribute value, if any int Number of nodes in index Get the number of nodes in an index. @since Mini-XML 2.7@ mxml_index_t * Index of nodes mxml_index_t * New index Create a new index. The index will contain all nodes that contain the named element and/or attribute. If both "element" and "attr" are NULL, then the index will contain a sorted list of the elements in the node tree. Nodes are sorted by element name and optionally by attribute value if the "attr" argument is not NULL. mxml_node_t * XML node tree const char * Element to index or NULL for all const char * Attribute to index or NULL for none mxml_node_t * First node or NULL if there is none Reset the enumeration/find pointer in the index and return the first node in the index. This function should be called prior to using mxmlIndexEnum() or mxmlIndexFind() for the first time. mxml_index_t * Index to reset mxml_node_t * First node or NULL if the file could not be read. Load a file descriptor into an XML node tree. The nodes in the specified file are added to the specified top node. If no top node is provided, the XML file MUST be well-formed with a single parent node like <?xml> for the entire file. The callback function returns the value type that should be used for child nodes. If MXML_NO_CALLBACK is specified then all child nodes will be either MXML_ELEMENT or MXML_TEXT nodes. The constants MXML_INTEGER_CALLBACK, MXML_OPAQUE_CALLBACK, MXML_REAL_CALLBACK, and MXML_TEXT_CALLBACK are defined for loading child nodes of the specified type. mxml_node_t * Top node int File descriptor to read from mxml_load_cb_t Callback function or MXML_NO_CALLBACK mxml_node_t * First node or NULL if the file could not be read. Load a file into an XML node tree. The nodes in the specified file are added to the specified top node. If no top node is provided, the XML file MUST be well-formed with a single parent node like <?xml> for the entire file. The callback function returns the value type that should be used for child nodes. If MXML_NO_CALLBACK is specified then all child nodes will be either MXML_ELEMENT or MXML_TEXT nodes. The constants MXML_INTEGER_CALLBACK, MXML_OPAQUE_CALLBACK, MXML_REAL_CALLBACK, and MXML_TEXT_CALLBACK are defined for loading child nodes of the specified type. mxml_node_t * Top node FILE * File to read from mxml_load_cb_t Callback function or MXML_NO_CALLBACK mxml_node_t * First node or NULL if the string has errors. Load a string into an XML node tree. The nodes in the specified string are added to the specified top node. If no top node is provided, the XML string MUST be well-formed with a single parent node like <?xml> for the entire string. The callback function returns the value type that should be used for child nodes. If MXML_NO_CALLBACK is specified then all child nodes will be either MXML_ELEMENT or MXML_TEXT nodes. The constants MXML_INTEGER_CALLBACK, MXML_OPAQUE_CALLBACK, MXML_REAL_CALLBACK, and MXML_TEXT_CALLBACK are defined for loading child nodes of the specified type. mxml_node_t * Top node const char * String to load mxml_load_cb_t Callback function or MXML_NO_CALLBACK mxml_node_t * New node Create a new CDATA node. The new CDATA node is added to the end of the specified parent's child list. The constant MXML_NO_PARENT can be used to specify that the new CDATA node has no parent. The data string must be nul-terminated and is copied into the new node. CDATA nodes use the MXML_ELEMENT type. @since Mini-XML 2.3@ mxml_node_t * Parent node or MXML_NO_PARENT const char * Data string mxml_node_t * New node Create a new custom data node. The new custom node is added to the end of the specified parent's child list. The constant MXML_NO_PARENT can be used to specify that the new element node has no parent. NULL can be passed when the data in the node is not dynamically allocated or is separately managed. @since Mini-XML 2.1@ mxml_node_t * Parent node or MXML_NO_PARENT void * Pointer to data mxml_custom_destroy_cb_t Function to destroy data mxml_node_t * New node Create a new element node. The new element node is added to the end of the specified parent's child list. The constant MXML_NO_PARENT can be used to specify that the new element node has no parent. mxml_node_t * Parent node or MXML_NO_PARENT const char * Name of element mxml_node_t * New node Create a new integer node. The new integer node is added to the end of the specified parent's child list. The constant MXML_NO_PARENT can be used to specify that the new integer node has no parent. mxml_node_t * Parent node or MXML_NO_PARENT int Integer value mxml_node_t * New node Create a new opaque string. The new opaque node is added to the end of the specified parent's child list. The constant MXML_NO_PARENT can be used to specify that the new opaque node has no parent. The opaque string must be nul-terminated and is copied into the new node. mxml_node_t * Parent node or MXML_NO_PARENT const char * Opaque string mxml_node_t * New node Create a new real number node. The new real number node is added to the end of the specified parent's child list. The constant MXML_NO_PARENT can be used to specify that the new real number node has no parent. mxml_node_t * Parent node or MXML_NO_PARENT double Real number value mxml_node_t * New node Create a new text fragment node. The new text node is added to the end of the specified parent's child list. The constant MXML_NO_PARENT can be used to specify that the new text node has no parent. The whitespace parameter is used to specify whether leading whitespace is present before the node. The text string must be nul-terminated and is copied into the new node. mxml_node_t * Parent node or MXML_NO_PARENT int 1 = leading whitespace, 0 = no whitespace const char * String mxml_node_t * New node Create a new formatted text fragment node. The new text node is added to the end of the specified parent's child list. The constant MXML_NO_PARENT can be used to specify that the new text node has no parent. The whitespace parameter is used to specify whether leading whitespace is present before the node. The format string must be nul-terminated and is formatted into the new node. mxml_node_t * Parent node or MXML_NO_PARENT int 1 = leading whitespace, 0 = no whitespace const char * Printf-style frmat string Additional args as needed mxml_node_t * New ?xml node Create a new XML document tree. The "version" argument specifies the version number to put in the ?xml element node. If NULL, version 1.0 is assumed. @since Mini-XML 2.3@ const char * Version number to use int New reference count Release a node. When the reference count reaches zero, the node (and any children) is deleted via mxmlDelete(). @since Mini-XML 2.3@ mxml_node_t * Node Remove a node from its parent. Does not free memory used by the node - use mxmlDelete() for that. This function does nothing if the node has no parent. mxml_node_t * Node to remove int New reference count Retain a node. @since Mini-XML 2.3@ mxml_node_t * Node mxml_node_t * First node or NULL if the file could not be read. Load a file descriptor into an XML node tree using a SAX callback. The nodes in the specified file are added to the specified top node. If no top node is provided, the XML file MUST be well-formed with a single parent node like <?xml> for the entire file. The callback function returns the value type that should be used for child nodes. If MXML_NO_CALLBACK is specified then all child nodes will be either MXML_ELEMENT or MXML_TEXT nodes. The constants MXML_INTEGER_CALLBACK, MXML_OPAQUE_CALLBACK, MXML_REAL_CALLBACK, and MXML_TEXT_CALLBACK are defined for loading child nodes of the specified type. The SAX callback must call mxmlRetain() for any nodes that need to be kept for later use. Otherwise, nodes are deleted when the parent node is closed or after each data, comment, CDATA, or directive node. @since Mini-XML 2.3@ mxml_node_t * Top node int File descriptor to read from mxml_load_cb_t Callback function or MXML_NO_CALLBACK mxml_sax_cb_t SAX callback or MXML_NO_CALLBACK void * SAX user data mxml_node_t * First node or NULL if the file could not be read. Load a file into an XML node tree using a SAX callback. The nodes in the specified file are added to the specified top node. If no top node is provided, the XML file MUST be well-formed with a single parent node like <?xml> for the entire file. The callback function returns the value type that should be used for child nodes. If MXML_NO_CALLBACK is specified then all child nodes will be either MXML_ELEMENT or MXML_TEXT nodes. The constants MXML_INTEGER_CALLBACK, MXML_OPAQUE_CALLBACK, MXML_REAL_CALLBACK, and MXML_TEXT_CALLBACK are defined for loading child nodes of the specified type. The SAX callback must call mxmlRetain() for any nodes that need to be kept for later use. Otherwise, nodes are deleted when the parent node is closed or after each data, comment, CDATA, or directive node. @since Mini-XML 2.3@ mxml_node_t * Top node FILE * File to read from mxml_load_cb_t Callback function or MXML_NO_CALLBACK mxml_sax_cb_t SAX callback or MXML_NO_CALLBACK void * SAX user data mxml_node_t * First node or NULL if the string has errors. Load a string into an XML node tree using a SAX callback. The nodes in the specified string are added to the specified top node. If no top node is provided, the XML string MUST be well-formed with a single parent node like <?xml> for the entire string. The callback function returns the value type that should be used for child nodes. If MXML_NO_CALLBACK is specified then all child nodes will be either MXML_ELEMENT or MXML_TEXT nodes. The constants MXML_INTEGER_CALLBACK, MXML_OPAQUE_CALLBACK, MXML_REAL_CALLBACK, and MXML_TEXT_CALLBACK are defined for loading child nodes of the specified type. The SAX callback must call mxmlRetain() for any nodes that need to be kept for later use. Otherwise, nodes are deleted when the parent node is closed or after each data, comment, CDATA, or directive node. @since Mini-XML 2.3@ mxml_node_t * Top node const char * String to load mxml_load_cb_t Callback function or MXML_NO_CALLBACK mxml_sax_cb_t SAX callback or MXML_NO_CALLBACK void * SAX user data char * Allocated string or NULL Save an XML tree to an allocated string. This function returns a pointer to a string containing the textual representation of the XML node tree. The string should be freed using the free() function when you are done with it. NULL is returned if the node would produce an empty string or if the string cannot be allocated. The callback argument specifies a function that returns a whitespace string or NULL before and after each element. If MXML_NO_CALLBACK is specified, whitespace will only be added before MXML_TEXT nodes with leading whitespace and before attribute names inside opening element tags. mxml_node_t * Node to write mxml_save_cb_t Whitespace callback or MXML_NO_CALLBACK int 0 on success, -1 on error. Save an XML tree to a file descriptor. The callback argument specifies a function that returns a whitespace string or NULL before and after each element. If MXML_NO_CALLBACK is specified, whitespace will only be added before MXML_TEXT nodes with leading whitespace and before attribute names inside opening element tags. mxml_node_t * Node to write int File descriptor to write to mxml_save_cb_t Whitespace callback or MXML_NO_CALLBACK int 0 on success, -1 on error. Save an XML tree to a file. The callback argument specifies a function that returns a whitespace string or NULL before and after each element. If MXML_NO_CALLBACK is specified, whitespace will only be added before MXML_TEXT nodes with leading whitespace and before attribute names inside opening element tags. mxml_node_t * Node to write FILE * File to write to mxml_save_cb_t Whitespace callback or MXML_NO_CALLBACK int Size of string Save an XML node tree to a string. This function returns the total number of bytes that would be required for the string but only copies (bufsize - 1) characters into the specified buffer. The callback argument specifies a function that returns a whitespace string or NULL before and after each element. If MXML_NO_CALLBACK is specified, whitespace will only be added before MXML_TEXT nodes with leading whitespace and before attribute names inside opening element tags. mxml_node_t * Node to write char * String buffer int Size of string buffer mxml_save_cb_t Whitespace callback or MXML_NO_CALLBACK int 0 on success, -1 on failure Set the element name of a CDATA node. The node is not changed if it (or its first child) is not a CDATA element node. @since Mini-XML 2.3@ mxml_node_t * Node to set const char * New data string int 0 on success, -1 on failure Set the data and destructor of a custom data node. The node is not changed if it (or its first child) is not a custom node. @since Mini-XML 2.1@ mxml_node_t * Node to set void * New data pointer mxml_custom_destroy_cb_t New destructor function Set the handling functions for custom data. The load function accepts a node pointer and a data string and must return 0 on success and non-zero on error. The save function accepts a node pointer and must return a malloc'd string on success and NULL on error. mxml_custom_load_cb_t Load function mxml_custom_save_cb_t Save function int 0 on success, -1 on failure Set the name of an element node. The node is not changed if it is not an element node. mxml_node_t * Node to set const char * New name string Set the error message callback. mxml_error_cb_t Error callback function int 0 on success, -1 on failure Set the value of an integer node. The node is not changed if it (or its first child) is not an integer node. mxml_node_t * Node to set int Integer value int 0 on success, -1 on failure Set the value of an opaque node. The node is not changed if it (or its first child) is not an opaque node. mxml_node_t * Node to set const char * Opaque string int 0 on success, -1 on failure Set the value of a real number node. The node is not changed if it (or its first child) is not a real number node. mxml_node_t * Node to set double Real number value int 0 on success, -1 on failure Set the value of a text node. The node is not changed if it (or its first child) is not a text node. mxml_node_t * Node to set int 1 = leading whitespace, 0 = no whitespace const char * String int 0 on success, -1 on failure Set the value of a text node to a formatted string. The node is not changed if it (or its first child) is not a text node. mxml_node_t * Node to set int 1 = leading whitespace, 0 = no whitespace const char * Printf-style format string Additional arguments as needed int 0 on success, -1 on failure Set the user data pointer for a node. @since Mini-XML 2.7@ mxml_node_t * Node to set void * User data pointer Set the wrap margin when saving XML data. Wrapping is disabled when "column" is 0. @since Mini-XML 2.3@ int Column for wrapping, 0 to disable wrapping mxml_node_t * Next node or NULL Walk to the next logical node in the tree. The descend argument controls whether the first child is considered to be the next node. The top node argument constrains the walk to the node's children. mxml_node_t * Current node mxml_node_t * Top node int Descend into tree - MXML_DESCEND, MXML_NO_DESCEND, or MXML_DESCEND_FIRST mxml_node_t * Previous node or NULL Walk to the previous logical node in the tree. The descend argument controls whether the previous node's last child is considered to be the previous node. The top node argument constrains the walk to the node's children. mxml_node_t * Current node mxml_node_t * Top node int Descend into tree - MXML_DESCEND, MXML_NO_DESCEND, or MXML_DESCEND_FIRST An XML element attribute value. @private@ char * Attribute name char * Attribute value struct mxml_attr_s An XML element attribute value. @private@ void(*)(void *) Custom data destructor int(*)(mxml_node_t *, const char *) Custom data load callback function An XML custom value. @private@ void * Pointer to (allocated) custom data mxml_custom_destroy_cb_t Pointer to destructor function char *(*)(mxml_node_t *) Custom data save callback function struct mxml_custom_s An XML custom value. @private@ An XML element value. @private@ mxml_attr_t * Attributes char * Name of element int Number of attributes struct mxml_element_s An XML element value. @private@ int(*)(const char *) Entity callback function void(*)(const char *) Error callback function An XML node index. @private@ int Allocated nodes in index char * Attribute used for indexing or NULL int Current node mxml_node_t ** Node array int Number of nodes in index struct mxml_index_s An XML node index. mxml_type_t(*)(mxml_node_t *) Load callback function An XML node. @private@ struct mxml_node_s * First child node struct mxml_node_s * Last child node struct mxml_node_s * Next node under same parent struct mxml_node_s * Parent node struct mxml_node_s * Previous node under same parent int Use count mxml_type_t Node type void * User data mxml_value_t Node value struct mxml_node_s An XML node. const char *(*)(mxml_node_t *, int) Save callback function void(*)(mxml_node_t *, mxml_sax_event_t, void *) SAX callback function SAX event type. CDATA node Comment node Data node Processing directive node Element closed Element opened enum mxml_sax_event_e SAX event type. An XML text value. @private@ char * Fragment string int Leading whitespace? struct mxml_text_s An XML text value. @private@ The XML node type. Custom data @since Mini-XML 2.1@ XML element with attributes Ignore/throw away node @since Mini-XML 2.3@ Integer value Opaque string Real value Text fragment The XML node type. enum mxml_type_e union mxml_value_u An XML node value. @private@ An XML node value. @private@ mxml_custom_t Custom data @since Mini-XML 2.1@ mxml_element_t Element int Integer number char * Opaque string double Real number mxml_text_t Text fragment cmtk-3.3.1/Utilities/mxml/mxmldoc.c000066400000000000000000004323731276303427400172370ustar00rootroot00000000000000/*#define DEBUG 1*/ /* * "$Id: mxmldoc.c 440 2011-08-11 18:51:26Z mike $" * * Documentation generator using Mini-XML, a small XML-like file parsing * library. * * Copyright 2003-2011 by Michael R Sweet. * * These coded instructions, statements, and computer programs are the * property of Michael R Sweet and are protected by Federal copyright * law. Distribution and use rights are outlined in the file "COPYING" * which should have been included with this file. If this file is * missing or damaged, see the license at: * * http://www.minixml.org/ * * Contents: * * main() - Main entry for test program. * add_variable() - Add a variable or argument. * find_public() - Find a public function, type, etc. * get_comment_info() - Get info from comment. * get_text() - Get the text for a node. * load_cb() - Set the type of child nodes. * new_documentation() - Create a new documentation tree. * remove_directory() - Remove a directory. * safe_strcpy() - Copy a string allowing for overlapping strings. * scan_file() - Scan a source file. * sort_node() - Insert a node sorted into a tree. * update_comment() - Update a comment node. * usage() - Show program usage... * write_description() - Write the description text. * write_element() - Write an element's text nodes. * write_file() - Copy a file to the output. * write_function() - Write documentation for a function. * write_html() - Write HTML documentation. * write_html_head() - Write the standard HTML header. * write_man() - Write manpage documentation. * write_scu() - Write a structure, class, or union. * write_string() - Write a string, quoting HTML special chars as needed. * write_toc() - Write a table-of-contents. * write_tokens() - Write nodes for all APIs. * ws_cb() - Whitespace callback for saving. */ /* * Include necessary headers... */ #include "config.h" #include "mxml.h" #include #include #ifndef WIN32 # include # include #endif /* !WIN32 */ #ifdef __APPLE__ # include # include extern char **environ; #endif /* __APPLE__ */ /* * This program scans source and header files and produces public API * documentation for code that conforms to the CUPS Configuration * Management Plan (CMP) coding standards. Please see the following web * page for details: * * http://www.cups.org/cmp.html * * Using Mini-XML, this program creates and maintains an XML representation * of the public API code documentation which can then be converted to HTML * as desired. The following is a poor-man's schema: * * * * * [optional...] * * descriptive text * * * * descriptive text * ... * * * * descriptive text * type string * * * * descriptive text * * descriptive text * type string * * * descriptive text * type string * * function names separated by spaces * * * * descriptive text * type string * * * * descriptive text * ... * ... * * * * descriptive text * ... * * * * descriptive text * ... * ... * ... * ... * ... * * * */ /* * Basic states for file parser... */ #define STATE_NONE 0 /* No state - whitespace, etc. */ #define STATE_PREPROCESSOR 1 /* Preprocessor directive */ #define STATE_C_COMMENT 2 /* Inside a C comment */ #define STATE_CXX_COMMENT 3 /* Inside a C++ comment */ #define STATE_STRING 4 /* Inside a string constant */ #define STATE_CHARACTER 5 /* Inside a character constant */ #define STATE_IDENTIFIER 6 /* Inside a keyword/identifier */ /* * Output modes... */ #define OUTPUT_NONE 0 /* No output */ #define OUTPUT_HTML 1 /* Output HTML */ #define OUTPUT_XML 2 /* Output XML */ #define OUTPUT_MAN 3 /* Output nroff/man */ #define OUTPUT_TOKENS 4 /* Output docset Tokens.xml file */ /* * Local functions... */ static mxml_node_t *add_variable(mxml_node_t *parent, const char *name, mxml_node_t *type); static mxml_node_t *find_public(mxml_node_t *node, mxml_node_t *top, const char *name); static char *get_comment_info(mxml_node_t *description); static char *get_text(mxml_node_t *node, char *buffer, int buflen); static mxml_type_t load_cb(mxml_node_t *node); static mxml_node_t *new_documentation(mxml_node_t **mxmldoc); static int remove_directory(const char *path); static void safe_strcpy(char *dst, const char *src); static int scan_file(const char *filename, FILE *fp, mxml_node_t *doc); static void sort_node(mxml_node_t *tree, mxml_node_t *func); static void update_comment(mxml_node_t *parent, mxml_node_t *comment); static void usage(const char *option); static void write_description(FILE *out, mxml_node_t *description, const char *element, int summary); static void write_element(FILE *out, mxml_node_t *doc, mxml_node_t *element, int mode); static void write_file(FILE *out, const char *file); static void write_function(FILE *out, mxml_node_t *doc, mxml_node_t *function, int level); static void write_html(const char *section, const char *title, const char *footerfile, const char *headerfile, const char *introfile, const char *cssfile, const char *framefile, const char *docset, const char *docversion, const char *feedname, const char *feedurl, mxml_node_t *doc); static void write_html_head(FILE *out, const char *section, const char *title, const char *cssfile); static void write_man(const char *man_name, const char *section, const char *title, const char *headerfile, const char *footerfile, const char *introfile, mxml_node_t *doc); static void write_scu(FILE *out, mxml_node_t *doc, mxml_node_t *scut); static void write_string(FILE *out, const char *s, int mode); static void write_toc(FILE *out, mxml_node_t *doc, const char *introfile, const char *target, int xml); static void write_tokens(FILE *out, mxml_node_t *doc, const char *path); static const char *ws_cb(mxml_node_t *node, int where); /* * 'main()' - Main entry for test program. */ int /* O - Exit status */ main(int argc, /* I - Number of command-line args */ char *argv[]) /* I - Command-line args */ { int i; /* Looping var */ int len; /* Length of argument */ FILE *fp; /* File to read */ mxml_node_t *doc; /* XML documentation tree */ mxml_node_t *mxmldoc; /* mxmldoc node */ const char *cssfile, /* CSS stylesheet file */ *docset, /* Documentation set directory */ *docversion, /* Documentation set version */ *feedname, /* Feed name for documentation set */ *feedurl, /* Feed URL for documentation set */ *footerfile, /* Footer file */ *framefile, /* Framed HTML basename */ *headerfile, /* Header file */ *introfile, /* Introduction file */ *name, /* Name of manpage */ *path, /* Path to help file for tokens */ *section, /* Section/keywords of documentation */ *title, /* Title of documentation */ *xmlfile; /* XML file */ int mode, /* Output mode */ update; /* Updated XML file */ /* * Check arguments... */ cssfile = NULL; doc = NULL; docset = NULL; docversion = NULL; feedname = NULL; feedurl = NULL; footerfile = NULL; framefile = NULL; headerfile = NULL; introfile = NULL; mode = OUTPUT_HTML; mxmldoc = NULL; name = NULL; path = NULL; section = NULL; title = NULL; update = 0; xmlfile = NULL; for (i = 1; i < argc; i ++) if (!strcmp(argv[i], "--help")) { /* * Show help... */ usage(NULL); } else if (!strcmp(argv[i], "--version")) { /* * Show version... */ puts(MXML_VERSION + 10); return (0); } else if (!strcmp(argv[i], "--css") && !cssfile) { /* * Set CSS stylesheet file... */ i ++; if (i < argc) cssfile = argv[i]; else usage(NULL); } else if (!strcmp(argv[i], "--docset") && !docset) { /* * Set documentation set directory... */ i ++; if (i < argc) docset = argv[i]; else usage(NULL); } else if (!strcmp(argv[i], "--docversion") && !docversion) { /* * Set documentation set directory... */ i ++; if (i < argc) docversion = argv[i]; else usage(NULL); } else if (!strcmp(argv[i], "--footer") && !footerfile) { /* * Set footer file... */ i ++; if (i < argc) footerfile = argv[i]; else usage(NULL); } else if (!strcmp(argv[i], "--feedname") && !feedname) { /* * Set documentation set feed name... */ i ++; if (i < argc) feedname = argv[i]; else usage(NULL); } else if (!strcmp(argv[i], "--feedurl") && !feedurl) { /* * Set documentation set feed name... */ i ++; if (i < argc) feedurl = argv[i]; else usage(NULL); } else if (!strcmp(argv[i], "--framed") && !framefile) { /* * Set base filename for framed HTML output... */ i ++; if (i < argc) framefile = argv[i]; else usage(NULL); } else if (!strcmp(argv[i], "--header") && !headerfile) { /* * Set header file... */ i ++; if (i < argc) headerfile = argv[i]; else usage(NULL); } else if (!strcmp(argv[i], "--intro") && !introfile) { /* * Set intro file... */ i ++; if (i < argc) introfile = argv[i]; else usage(NULL); } else if (!strcmp(argv[i], "--man") && !name) { /* * Output manpage... */ i ++; if (i < argc) { mode = OUTPUT_MAN; name = argv[i]; } else usage(NULL); } else if (!strcmp(argv[i], "--no-output")) mode = OUTPUT_NONE; else if (!strcmp(argv[i], "--section") && !section) { /* * Set section/keywords... */ i ++; if (i < argc) section = argv[i]; else usage(NULL); } else if (!strcmp(argv[i], "--title") && !title) { /* * Set title... */ i ++; if (i < argc) title = argv[i]; else usage(NULL); } else if (!strcmp(argv[i], "--tokens")) { /* * Output Tokens.xml file... */ mode = OUTPUT_TOKENS; i ++; if (i < argc) path = argv[i]; else usage(NULL); } else if (argv[i][0] == '-') { /* * Unknown/bad option... */ usage(argv[i]); } else { /* * Process XML or source file... */ len = (int)strlen(argv[i]); if (len > 4 && !strcmp(argv[i] + len - 4, ".xml")) { /* * Set XML file... */ if (xmlfile) usage(NULL); xmlfile = argv[i]; if (!doc) { if ((fp = fopen(argv[i], "r")) != NULL) { /* * Read the existing XML file... */ doc = mxmlLoadFile(NULL, fp, load_cb); fclose(fp); if (!doc) { mxmldoc = NULL; fprintf(stderr, "mxmldoc: Unable to read the XML documentation file " "\"%s\"!\n", argv[i]); } else if ((mxmldoc = mxmlFindElement(doc, doc, "mxmldoc", NULL, NULL, MXML_DESCEND)) == NULL) { fprintf(stderr, "mxmldoc: XML documentation file \"%s\" is missing " " node!!\n", argv[i]); mxmlDelete(doc); doc = NULL; } } else { doc = NULL; mxmldoc = NULL; } if (!doc) doc = new_documentation(&mxmldoc); } } else { /* * Load source file... */ update = 1; if (!doc) doc = new_documentation(&mxmldoc); if ((fp = fopen(argv[i], "r")) == NULL) { fprintf(stderr, "mxmldoc: Unable to open source file \"%s\": %s\n", argv[i], strerror(errno)); mxmlDelete(doc); return (1); } else if (scan_file(argv[i], fp, mxmldoc)) { fclose(fp); mxmlDelete(doc); return (1); } else fclose(fp); } } if (update && xmlfile) { /* * Save the updated XML documentation file... */ if ((fp = fopen(xmlfile, "w")) != NULL) { /* * Write over the existing XML file... */ mxmlSetWrapMargin(0); if (mxmlSaveFile(doc, fp, ws_cb)) { fprintf(stderr, "mxmldoc: Unable to write the XML documentation file \"%s\": " "%s!\n", xmlfile, strerror(errno)); fclose(fp); mxmlDelete(doc); return (1); } fclose(fp); } else { fprintf(stderr, "mxmldoc: Unable to create the XML documentation file \"%s\": " "%s!\n", xmlfile, strerror(errno)); mxmlDelete(doc); return (1); } } switch (mode) { case OUTPUT_HTML : /* * Write HTML documentation... */ write_html(section, title ? title : "Documentation", footerfile, headerfile, introfile, cssfile, framefile, docset, docversion, feedname, feedurl, mxmldoc); break; case OUTPUT_MAN : /* * Write manpage documentation... */ write_man(name, section, title, footerfile, headerfile, introfile, mxmldoc); break; case OUTPUT_TOKENS : fputs("\n" "\n", stdout); write_tokens(stdout, mxmldoc, path); fputs("\n", stdout); break; } /* * Delete the tree and return... */ mxmlDelete(doc); return (0); } /* * 'add_variable()' - Add a variable or argument. */ static mxml_node_t * /* O - New variable/argument */ add_variable(mxml_node_t *parent, /* I - Parent node */ const char *name, /* I - "argument" or "variable" */ mxml_node_t *type) /* I - Type nodes */ { mxml_node_t *variable, /* New variable */ *node, /* Current node */ *next; /* Next node */ char buffer[16384], /* String buffer */ *bufptr; /* Pointer into buffer */ #ifdef DEBUG fprintf(stderr, "add_variable(parent=%p, name=\"%s\", type=%p)\n", parent, name, type); #endif /* DEBUG */ /* * Range check input... */ if (!type || !type->child) return (NULL); /* * Create the variable/argument node... */ variable = mxmlNewElement(parent, name); /* * Check for a default value... */ for (node = type->child; node; node = node->next) if (!strcmp(node->value.text.string, "=")) break; if (node) { /* * Default value found, copy it and add as a "default" attribute... */ for (bufptr = buffer; node; bufptr += strlen(bufptr)) { if (node->value.text.whitespace && bufptr > buffer) *bufptr++ = ' '; strcpy(bufptr, node->value.text.string); next = node->next; mxmlDelete(node); node = next; } mxmlElementSetAttr(variable, "default", buffer); } /* * Extract the argument/variable name... */ if (type->last_child->value.text.string[0] == ')') { /* * Handle "type (*name)(args)"... */ for (node = type->child; node; node = node->next) if (node->value.text.string[0] == '(') break; for (bufptr = buffer; node; bufptr += strlen(bufptr)) { if (node->value.text.whitespace && bufptr > buffer) *bufptr++ = ' '; strcpy(bufptr, node->value.text.string); next = node->next; mxmlDelete(node); node = next; } } else { /* * Handle "type name"... */ strcpy(buffer, type->last_child->value.text.string); mxmlDelete(type->last_child); } /* * Set the name... */ mxmlElementSetAttr(variable, "name", buffer); /* * Add the remaining type information to the variable node... */ mxmlAdd(variable, MXML_ADD_AFTER, MXML_ADD_TO_PARENT, type); /* * Add new new variable node... */ return (variable); } /* * 'find_public()' - Find a public function, type, etc. */ static mxml_node_t * /* I - Found node or NULL */ find_public(mxml_node_t *node, /* I - Current node */ mxml_node_t *top, /* I - Top node */ const char *name) /* I - Name of element */ { mxml_node_t *description, /* Description node */ *comment; /* Comment node */ for (node = mxmlFindElement(node, top, name, NULL, NULL, node == top ? MXML_DESCEND_FIRST : MXML_NO_DESCEND); node; node = mxmlFindElement(node, top, name, NULL, NULL, MXML_NO_DESCEND)) { /* * Get the description for this node... */ description = mxmlFindElement(node, node, "description", NULL, NULL, MXML_DESCEND_FIRST); /* * A missing or empty description signals a private node... */ if (!description) continue; /* * Look for @private@ in the comment text... */ for (comment = description->child; comment; comment = comment->next) if ((comment->type == MXML_TEXT && strstr(comment->value.text.string, "@private@")) || (comment->type == MXML_OPAQUE && strstr(comment->value.opaque, "@private@"))) break; if (!comment) { /* * No @private@, so return this node... */ return (node); } } /* * If we get here, there are no (more) public nodes... */ return (NULL); } /* * 'get_comment_info()' - Get info from comment. */ static char * /* O - Info from comment */ get_comment_info( mxml_node_t *description) /* I - Description node */ { char text[10240], /* Description text */ since[255], /* @since value */ *ptr; /* Pointer into text */ static char info[1024]; /* Info string */ if (!description) return (""); get_text(description, text, sizeof(text)); for (ptr = strchr(text, '@'); ptr; ptr = strchr(ptr + 1, '@')) { if (!strncmp(ptr, "@deprecated@", 12)) return (" DEPRECATED "); else if (!strncmp(ptr, "@since ", 7)) { strncpy(since, ptr + 7, sizeof(since) - 1); since[sizeof(since) - 1] = '\0'; if ((ptr = strchr(since, '@')) != NULL) *ptr = '\0'; snprintf(info, sizeof(info), " %s ", since); return (info); } } return (""); } /* * 'get_text()' - Get the text for a node. */ static char * /* O - Text in node */ get_text(mxml_node_t *node, /* I - Node to get */ char *buffer, /* I - Buffer */ int buflen) /* I - Size of buffer */ { char *ptr, /* Pointer into buffer */ *end; /* End of buffer */ int len; /* Length of node */ mxml_node_t *current; /* Current node */ ptr = buffer; end = buffer + buflen - 1; for (current = node->child; current && ptr < end; current = current->next) { if (current->type == MXML_TEXT) { if (current->value.text.whitespace) *ptr++ = ' '; len = (int)strlen(current->value.text.string); if (len > (int)(end - ptr)) len = (int)(end - ptr); memcpy(ptr, current->value.text.string, len); ptr += len; } else if (current->type == MXML_OPAQUE) { len = (int)strlen(current->value.opaque); if (len > (int)(end - ptr)) len = (int)(end - ptr); memcpy(ptr, current->value.opaque, len); ptr += len; } } *ptr = '\0'; return (buffer); } /* * 'load_cb()' - Set the type of child nodes. */ static mxml_type_t /* O - Node type */ load_cb(mxml_node_t *node) /* I - Node */ { if (!strcmp(node->value.element.name, "description")) return (MXML_OPAQUE); else return (MXML_TEXT); } /* * 'new_documentation()' - Create a new documentation tree. */ static mxml_node_t * /* O - New documentation */ new_documentation(mxml_node_t **mxmldoc)/* O - mxmldoc node */ { mxml_node_t *doc; /* New documentation */ /* * Create an empty XML documentation file... */ doc = mxmlNewXML(NULL); *mxmldoc = mxmlNewElement(doc, "mxmldoc"); mxmlElementSetAttr(*mxmldoc, "xmlns", "http://www.easysw.com"); mxmlElementSetAttr(*mxmldoc, "xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance"); mxmlElementSetAttr(*mxmldoc, "xsi:schemaLocation", "http://www.minixml.org/mxmldoc.xsd"); return (doc); } /* * 'remove_directory()' - Remove a directory. */ static int /* O - 1 on success, 0 on failure */ remove_directory(const char *path) /* I - Directory to remove */ { #ifdef WIN32 /* TODO: Add Windows directory removal code */ #else DIR *dir; /* Directory */ struct dirent *dent; /* Current directory entry */ char filename[1024]; /* Current filename */ struct stat fileinfo; /* File information */ if ((dir = opendir(path)) == NULL) { fprintf(stderr, "mxmldoc: Unable to open directory \"%s\": %s\n", path, strerror(errno)); return (0); } while ((dent = readdir(dir)) != NULL) { /* * Skip "." and ".."... */ if (!strcmp(dent->d_name, ".") || !strcmp(dent->d_name, "..")) continue; /* * See if we have a file or directory... */ snprintf(filename, sizeof(filename), "%s/%s", path, dent->d_name); if (stat(filename, &fileinfo)) { fprintf(stderr, "mxmldoc: Unable to stat \"%s\": %s\n", filename, strerror(errno)); closedir(dir); return (0); } if (S_ISDIR(fileinfo.st_mode)) { if (!remove_directory(filename)) { closedir(dir); return (0); } } else if (unlink(filename)) { fprintf(stderr, "mxmldoc: Unable to remove \"%s\": %s\n", filename, strerror(errno)); closedir(dir); return (0); } } closedir(dir); if (rmdir(path)) { fprintf(stderr, "mxmldoc: Unable to remove directory \"%s\": %s\n", path, strerror(errno)); return (0); } #endif /* WIN32 */ return (1); } /* * 'safe_strcpy()' - Copy a string allowing for overlapping strings. */ static void safe_strcpy(char *dst, /* I - Destination string */ const char *src) /* I - Source string */ { while (*src) *dst++ = *src++; *dst = '\0'; } /* * 'scan_file()' - Scan a source file. */ static int /* O - 0 on success, -1 on error */ scan_file(const char *filename, /* I - Filename */ FILE *fp, /* I - File to scan */ mxml_node_t *tree) /* I - Function tree */ { int state, /* Current parser state */ braces, /* Number of braces active */ parens; /* Number of active parenthesis */ int ch; /* Current character */ char buffer[65536], /* String buffer */ *bufptr; /* Pointer into buffer */ const char *scope; /* Current variable/function scope */ mxml_node_t *comment, /* node */ *constant, /* node */ *enumeration, /* node */ *function, /* node */ *fstructclass, /* function struct/class node */ *structclass, /* or node */ *typedefnode, /* node */ *variable, /* or node */ *returnvalue, /* node */ *type, /* node */ *description, /* node */ *node, /* Current node */ *next; /* Next node */ #if DEBUG > 1 mxml_node_t *temp; /* Temporary node */ int oldstate, /* Previous state */ oldch; /* Old character */ static const char *states[] = /* State strings */ { "STATE_NONE", "STATE_PREPROCESSOR", "STATE_C_COMMENT", "STATE_CXX_COMMENT", "STATE_STRING", "STATE_CHARACTER", "STATE_IDENTIFIER" }; #endif /* DEBUG > 1 */ #ifdef DEBUG fprintf(stderr, "scan_file(filename=\"%s\", fp=%p, tree=%p)\n", filename, fp, tree); #endif /* DEBUG */ /* * Initialize the finite state machine... */ state = STATE_NONE; braces = 0; parens = 0; bufptr = buffer; comment = mxmlNewElement(MXML_NO_PARENT, "temp"); constant = NULL; enumeration = NULL; function = NULL; variable = NULL; returnvalue = NULL; type = NULL; description = NULL; typedefnode = NULL; structclass = NULL; fstructclass = NULL; if (!strcmp(tree->value.element.name, "class")) scope = "private"; else scope = NULL; /* * Read until end-of-file... */ while ((ch = getc(fp)) != EOF) { #if DEBUG > 1 oldstate = state; oldch = ch; #endif /* DEBUG > 1 */ switch (state) { case STATE_NONE : /* No state - whitespace, etc. */ switch (ch) { case '/' : /* Possible C/C++ comment */ ch = getc(fp); bufptr = buffer; if (ch == '*') state = STATE_C_COMMENT; else if (ch == '/') state = STATE_CXX_COMMENT; else { ungetc(ch, fp); if (type) { #ifdef DEBUG fputs("Identifier: <<<< / >>>\n", stderr); #endif /* DEBUG */ ch = type->last_child->value.text.string[0]; mxmlNewText(type, isalnum(ch) || ch == '_', "/"); } } break; case '#' : /* Preprocessor */ #ifdef DEBUG fputs(" #preprocessor...\n", stderr); #endif /* DEBUG */ state = STATE_PREPROCESSOR; break; case '\'' : /* Character constant */ state = STATE_CHARACTER; bufptr = buffer; *bufptr++ = ch; break; case '\"' : /* String constant */ state = STATE_STRING; bufptr = buffer; *bufptr++ = ch; break; case '{' : #ifdef DEBUG fprintf(stderr, " open brace, function=%p, type=%p...\n", function, type); if (type) fprintf(stderr, " type->child=\"%s\"...\n", type->child->value.text.string); #endif /* DEBUG */ if (function) { if (fstructclass) { sort_node(fstructclass, function); fstructclass = NULL; } else sort_node(tree, function); function = NULL; } else if (type && type->child && ((!strcmp(type->child->value.text.string, "typedef") && type->child->next && (!strcmp(type->child->next->value.text.string, "struct") || !strcmp(type->child->next->value.text.string, "union") || !strcmp(type->child->next->value.text.string, "class"))) || !strcmp(type->child->value.text.string, "union") || !strcmp(type->child->value.text.string, "struct") || !strcmp(type->child->value.text.string, "class"))) { /* * Start of a class or structure... */ if (!strcmp(type->child->value.text.string, "typedef")) { #ifdef DEBUG fputs(" starting typedef...\n", stderr); #endif /* DEBUG */ typedefnode = mxmlNewElement(MXML_NO_PARENT, "typedef"); mxmlDelete(type->child); } else typedefnode = NULL; structclass = mxmlNewElement(MXML_NO_PARENT, type->child->value.text.string); #ifdef DEBUG fprintf(stderr, "%c%s: <<<< %s >>>\n", toupper(type->child->value.text.string[0]), type->child->value.text.string + 1, type->child->next ? type->child->next->value.text.string : "(noname)"); fputs(" type =", stderr); for (node = type->child; node; node = node->next) fprintf(stderr, " \"%s\"", node->value.text.string); putc('\n', stderr); fprintf(stderr, " scope = %s\n", scope ? scope : "(null)"); #endif /* DEBUG */ if (type->child->next) { mxmlElementSetAttr(structclass, "name", type->child->next->value.text.string); sort_node(tree, structclass); } if (typedefnode && type->child) type->child->value.text.whitespace = 0; else if (structclass && type->child && type->child->next && type->child->next->next) { for (bufptr = buffer, node = type->child->next->next; node; bufptr += strlen(bufptr)) { if (node->value.text.whitespace && bufptr > buffer) *bufptr++ = ' '; strcpy(bufptr, node->value.text.string); next = node->next; mxmlDelete(node); node = next; } mxmlElementSetAttr(structclass, "parent", buffer); mxmlDelete(type); type = NULL; } else { mxmlDelete(type); type = NULL; } if (typedefnode && comment->last_child) { /* * Copy comment for typedef as well as class/struct/union... */ mxmlNewText(comment, 0, comment->last_child->value.text.string); description = mxmlNewElement(typedefnode, "description"); #ifdef DEBUG fprintf(stderr, " duplicating comment %p/%p for typedef...\n", comment->last_child, comment->child); #endif /* DEBUG */ update_comment(typedefnode, comment->last_child); mxmlAdd(description, MXML_ADD_AFTER, MXML_ADD_TO_PARENT, comment->last_child); } description = mxmlNewElement(structclass, "description"); #ifdef DEBUG fprintf(stderr, " adding comment %p/%p to %s...\n", comment->last_child, comment->child, structclass->value.element.name); #endif /* DEBUG */ update_comment(structclass, comment->last_child); mxmlAdd(description, MXML_ADD_AFTER, MXML_ADD_TO_PARENT, comment->last_child); if (scan_file(filename, fp, structclass)) { mxmlDelete(comment); return (-1); } #ifdef DEBUG fputs(" ended typedef...\n", stderr); #endif /* DEBUG */ structclass = NULL; break; } else if (type && type->child && type->child->next && (!strcmp(type->child->value.text.string, "enum") || (!strcmp(type->child->value.text.string, "typedef") && !strcmp(type->child->next->value.text.string, "enum")))) { /* * Enumeration type... */ if (!strcmp(type->child->value.text.string, "typedef")) { #ifdef DEBUG fputs(" starting typedef...\n", stderr); #endif /* DEBUG */ typedefnode = mxmlNewElement(MXML_NO_PARENT, "typedef"); mxmlDelete(type->child); } else typedefnode = NULL; enumeration = mxmlNewElement(MXML_NO_PARENT, "enumeration"); #ifdef DEBUG fprintf(stderr, "Enumeration: <<<< %s >>>\n", type->child->next ? type->child->next->value.text.string : "(noname)"); #endif /* DEBUG */ if (type->child->next) { mxmlElementSetAttr(enumeration, "name", type->child->next->value.text.string); sort_node(tree, enumeration); } if (typedefnode && type->child) type->child->value.text.whitespace = 0; else { mxmlDelete(type); type = NULL; } if (typedefnode && comment->last_child) { /* * Copy comment for typedef as well as class/struct/union... */ mxmlNewText(comment, 0, comment->last_child->value.text.string); description = mxmlNewElement(typedefnode, "description"); #ifdef DEBUG fprintf(stderr, " duplicating comment %p/%p for typedef...\n", comment->last_child, comment->child); #endif /* DEBUG */ update_comment(typedefnode, comment->last_child); mxmlAdd(description, MXML_ADD_AFTER, MXML_ADD_TO_PARENT, comment->last_child); } description = mxmlNewElement(enumeration, "description"); #ifdef DEBUG fprintf(stderr, " adding comment %p/%p to enumeration...\n", comment->last_child, comment->child); #endif /* DEBUG */ update_comment(enumeration, comment->last_child); mxmlAdd(description, MXML_ADD_AFTER, MXML_ADD_TO_PARENT, comment->last_child); } else if (type && type->child && !strcmp(type->child->value.text.string, "extern")) { if (scan_file(filename, fp, tree)) { mxmlDelete(comment); return (-1); } } else if (type) { mxmlDelete(type); type = NULL; } braces ++; function = NULL; variable = NULL; break; case '}' : #ifdef DEBUG fputs(" close brace...\n", stderr); #endif /* DEBUG */ if (structclass) scope = NULL; if (!typedefnode) enumeration = NULL; constant = NULL; structclass = NULL; if (braces > 0) braces --; else { mxmlDelete(comment); return (0); } break; case '(' : if (type) { #ifdef DEBUG fputs("Identifier: <<<< ( >>>\n", stderr); #endif /* DEBUG */ mxmlNewText(type, 0, "("); } parens ++; break; case ')' : if (type && parens) { #ifdef DEBUG fputs("Identifier: <<<< ) >>>\n", stderr); #endif /* DEBUG */ mxmlNewText(type, 0, ")"); } if (function && type && !parens) { /* * Check for "void" argument... */ if (type->child && type->child->next) variable = add_variable(function, "argument", type); else mxmlDelete(type); type = NULL; } if (parens > 0) parens --; break; case ';' : #ifdef DEBUG fputs("Identifier: <<<< ; >>>\n", stderr); fprintf(stderr, " enumeration=%p, function=%p, type=%p, type->child=%p, typedefnode=%p\n", enumeration, function, type, type ? type->child : NULL, typedefnode); #endif /* DEBUG */ if (function) { if (!strcmp(tree->value.element.name, "class")) { #ifdef DEBUG fputs(" ADDING FUNCTION TO CLASS\n", stderr); #endif /* DEBUG */ sort_node(tree, function); } else mxmlDelete(function); function = NULL; variable = NULL; } if (type) { /* * See if we have a typedef... */ if (type->child && !strcmp(type->child->value.text.string, "typedef")) { /* * Yes, add it! */ typedefnode = mxmlNewElement(MXML_NO_PARENT, "typedef"); for (node = type->child->next; node; node = node->next) if (!strcmp(node->value.text.string, "(")) break; if (node) { for (node = node->next; node; node = node->next) if (strcmp(node->value.text.string, "*")) break; } if (!node) node = type->last_child; #ifdef DEBUG fprintf(stderr, " ADDING TYPEDEF FOR %p(%s)...\n", node, node->value.text.string); #endif /* DEBUG */ mxmlElementSetAttr(typedefnode, "name", node->value.text.string); sort_node(tree, typedefnode); if (type->child != node) mxmlDelete(type->child); mxmlDelete(node); if (type->child) type->child->value.text.whitespace = 0; mxmlAdd(typedefnode, MXML_ADD_AFTER, MXML_ADD_TO_PARENT, type); type = NULL; break; } else if (typedefnode && enumeration) { /* * Add enum typedef... */ node = type->child; #ifdef DEBUG fprintf(stderr, " ADDING TYPEDEF FOR %p(%s)...\n", node, node->value.text.string); #endif /* DEBUG */ mxmlElementSetAttr(typedefnode, "name", node->value.text.string); sort_node(tree, typedefnode); mxmlDelete(type); type = mxmlNewElement(typedefnode, "type"); mxmlNewText(type, 0, "enum"); mxmlNewText(type, 1, mxmlElementGetAttr(enumeration, "name")); enumeration = NULL; type = NULL; break; } mxmlDelete(type); type = NULL; } break; case ':' : if (type) { #ifdef DEBUG fputs("Identifier: <<<< : >>>\n", stderr); #endif /* DEBUG */ mxmlNewText(type, 1, ":"); } break; case '*' : if (type) { #ifdef DEBUG fputs("Identifier: <<<< * >>>\n", stderr); #endif /* DEBUG */ ch = type->last_child->value.text.string[0]; mxmlNewText(type, isalnum(ch) || ch == '_', "*"); } break; case ',' : if (type && !enumeration) { #ifdef DEBUG fputs("Identifier: <<<< , >>>\n", stderr); #endif /* DEBUG */ mxmlNewText(type, 0, ","); } break; case '&' : if (type) { #ifdef DEBUG fputs("Identifier: <<<< & >>>\n", stderr); #endif /* DEBUG */ mxmlNewText(type, 1, "&"); } break; case '+' : if (type) { #ifdef DEBUG fputs("Identifier: <<<< + >>>\n", stderr); #endif /* DEBUG */ ch = type->last_child->value.text.string[0]; mxmlNewText(type, isalnum(ch) || ch == '_', "+"); } break; case '-' : if (type) { #ifdef DEBUG fputs("Identifier: <<<< - >>>\n", stderr); #endif /* DEBUG */ ch = type->last_child->value.text.string[0]; mxmlNewText(type, isalnum(ch) || ch == '_', "-"); } break; case '=' : if (type) { #ifdef DEBUG fputs("Identifier: <<<< = >>>\n", stderr); #endif /* DEBUG */ ch = type->last_child->value.text.string[0]; mxmlNewText(type, isalnum(ch) || ch == '_', "="); } break; default : /* Other */ if (isalnum(ch) || ch == '_' || ch == '.' || ch == ':' || ch == '~') { state = STATE_IDENTIFIER; bufptr = buffer; *bufptr++ = ch; } break; } break; case STATE_PREPROCESSOR : /* Preprocessor directive */ if (ch == '\n') state = STATE_NONE; else if (ch == '\\') getc(fp); break; case STATE_C_COMMENT : /* Inside a C comment */ switch (ch) { case '\n' : while ((ch = getc(fp)) != EOF) if (ch == '*') { ch = getc(fp); if (ch == '/') { *bufptr = '\0'; if (comment->child != comment->last_child) { #ifdef DEBUG fprintf(stderr, " removing comment %p(%20.20s), last comment %p(%20.20s)...\n", comment->child, comment->child ? comment->child->value.text.string : "", comment->last_child, comment->last_child ? comment->last_child->value.text.string : ""); #endif /* DEBUG */ mxmlDelete(comment->child); #ifdef DEBUG fprintf(stderr, " new comment %p, last comment %p...\n", comment->child, comment->last_child); #endif /* DEBUG */ } #ifdef DEBUG fprintf(stderr, " processing comment, variable=%p, " "constant=%p, typedefnode=%p, tree=\"%s\"\n", variable, constant, typedefnode, tree->value.element.name); #endif /* DEBUG */ if (variable) { if (strstr(buffer, "@private@")) { /* * Delete private variables... */ mxmlDelete(variable); } else { description = mxmlNewElement(variable, "description"); #ifdef DEBUG fprintf(stderr, " adding comment %p/%p to variable...\n", comment->last_child, comment->child); #endif /* DEBUG */ mxmlNewText(comment, 0, buffer); update_comment(variable, mxmlNewText(description, 0, buffer)); } variable = NULL; } else if (constant) { if (strstr(buffer, "@private@")) { /* * Delete private constants... */ mxmlDelete(constant); } else { description = mxmlNewElement(constant, "description"); #ifdef DEBUG fprintf(stderr, " adding comment %p/%p to constant...\n", comment->last_child, comment->child); #endif /* DEBUG */ mxmlNewText(comment, 0, buffer); update_comment(constant, mxmlNewText(description, 0, buffer)); } constant = NULL; } else if (typedefnode) { if (strstr(buffer, "@private@")) { /* * Delete private typedefs... */ mxmlDelete(typedefnode); if (structclass) { mxmlDelete(structclass); structclass = NULL; } if (enumeration) { mxmlDelete(enumeration); enumeration = NULL; } } else { description = mxmlNewElement(typedefnode, "description"); #ifdef DEBUG fprintf(stderr, " adding comment %p/%p to typedef %s...\n", comment->last_child, comment->child, mxmlElementGetAttr(typedefnode, "name")); #endif /* DEBUG */ mxmlNewText(comment, 0, buffer); update_comment(typedefnode, mxmlNewText(description, 0, buffer)); if (structclass) { description = mxmlNewElement(structclass, "description"); update_comment(structclass, mxmlNewText(description, 0, buffer)); } else if (enumeration) { description = mxmlNewElement(enumeration, "description"); update_comment(enumeration, mxmlNewText(description, 0, buffer)); } } typedefnode = NULL; } else if (strcmp(tree->value.element.name, "mxmldoc") && !mxmlFindElement(tree, tree, "description", NULL, NULL, MXML_DESCEND_FIRST)) { description = mxmlNewElement(tree, "description"); #ifdef DEBUG fprintf(stderr, " adding comment %p/%p to parent...\n", comment->last_child, comment->child); #endif /* DEBUG */ mxmlNewText(comment, 0, buffer); update_comment(tree, mxmlNewText(description, 0, buffer)); } else { #ifdef DEBUG fprintf(stderr, " before adding comment, child=%p, last_child=%p\n", comment->child, comment->last_child); #endif /* DEBUG */ mxmlNewText(comment, 0, buffer); #ifdef DEBUG fprintf(stderr, " after adding comment, child=%p, last_child=%p\n", comment->child, comment->last_child); #endif /* DEBUG */ } #ifdef DEBUG fprintf(stderr, "C comment: <<<< %s >>>\n", buffer); #endif /* DEBUG */ state = STATE_NONE; break; } else ungetc(ch, fp); } else if (ch == '\n' && bufptr > buffer && bufptr < (buffer + sizeof(buffer) - 1)) *bufptr++ = ch; else if (!isspace(ch)) break; if (ch != EOF) ungetc(ch, fp); if (bufptr > buffer && bufptr < (buffer + sizeof(buffer) - 1)) *bufptr++ = '\n'; break; case '/' : if (ch == '/' && bufptr > buffer && bufptr[-1] == '*') { while (bufptr > buffer && (bufptr[-1] == '*' || isspace(bufptr[-1] & 255))) bufptr --; *bufptr = '\0'; if (comment->child != comment->last_child) { #ifdef DEBUG fprintf(stderr, " removing comment %p(%20.20s), last comment %p(%20.20s)...\n", comment->child, comment->child ? comment->child->value.text.string : "", comment->last_child, comment->last_child ? comment->last_child->value.text.string : ""); #endif /* DEBUG */ mxmlDelete(comment->child); #ifdef DEBUG fprintf(stderr, " new comment %p, last comment %p...\n", comment->child, comment->last_child); #endif /* DEBUG */ } #ifdef DEBUG fprintf(stderr, " processing comment, variable=%p, " "constant=%p, typedefnode=%p, tree=\"%s\"\n", variable, constant, typedefnode, tree->value.element.name); #endif /* DEBUG */ if (variable) { if (strstr(buffer, "@private@")) { /* * Delete private variables... */ mxmlDelete(variable); } else { description = mxmlNewElement(variable, "description"); #ifdef DEBUG fprintf(stderr, " adding comment %p/%p to variable...\n", comment->last_child, comment->child); #endif /* DEBUG */ mxmlNewText(comment, 0, buffer); update_comment(variable, mxmlNewText(description, 0, buffer)); } variable = NULL; } else if (constant) { if (strstr(buffer, "@private@")) { /* * Delete private constants... */ mxmlDelete(constant); } else { description = mxmlNewElement(constant, "description"); #ifdef DEBUG fprintf(stderr, " adding comment %p/%p to constant...\n", comment->last_child, comment->child); #endif /* DEBUG */ mxmlNewText(comment, 0, buffer); update_comment(constant, mxmlNewText(description, 0, buffer)); } constant = NULL; } else if (typedefnode) { if (strstr(buffer, "@private@")) { /* * Delete private typedefs... */ mxmlDelete(typedefnode); if (structclass) { mxmlDelete(structclass); structclass = NULL; } if (enumeration) { mxmlDelete(enumeration); enumeration = NULL; } } else { description = mxmlNewElement(typedefnode, "description"); #ifdef DEBUG fprintf(stderr, " adding comment %p/%p to typedef %s...\n", comment->last_child, comment->child, mxmlElementGetAttr(typedefnode, "name")); #endif /* DEBUG */ mxmlNewText(comment, 0, buffer); update_comment(typedefnode, mxmlNewText(description, 0, buffer)); if (structclass) { description = mxmlNewElement(structclass, "description"); update_comment(structclass, mxmlNewText(description, 0, buffer)); } else if (enumeration) { description = mxmlNewElement(enumeration, "description"); update_comment(enumeration, mxmlNewText(description, 0, buffer)); } } typedefnode = NULL; } else if (strcmp(tree->value.element.name, "mxmldoc") && !mxmlFindElement(tree, tree, "description", NULL, NULL, MXML_DESCEND_FIRST)) { description = mxmlNewElement(tree, "description"); #ifdef DEBUG fprintf(stderr, " adding comment %p/%p to parent...\n", comment->last_child, comment->child); #endif /* DEBUG */ mxmlNewText(comment, 0, buffer); update_comment(tree, mxmlNewText(description, 0, buffer)); } else mxmlNewText(comment, 0, buffer); #ifdef DEBUG fprintf(stderr, "C comment: <<<< %s >>>\n", buffer); #endif /* DEBUG */ state = STATE_NONE; break; } default : if (ch == ' ' && bufptr == buffer) break; if (bufptr < (buffer + sizeof(buffer) - 1)) *bufptr++ = ch; break; } break; case STATE_CXX_COMMENT : /* Inside a C++ comment */ if (ch == '\n') { state = STATE_NONE; *bufptr = '\0'; if (comment->child != comment->last_child) { #ifdef DEBUG fprintf(stderr, " removing comment %p(%20.20s), last comment %p(%20.20s)...\n", comment->child, comment->child ? comment->child->value.text.string : "", comment->last_child, comment->last_child ? comment->last_child->value.text.string : ""); #endif /* DEBUG */ mxmlDelete(comment->child); #ifdef DEBUG fprintf(stderr, " new comment %p, last comment %p...\n", comment->child, comment->last_child); #endif /* DEBUG */ } if (variable) { if (strstr(buffer, "@private@")) { /* * Delete private variables... */ mxmlDelete(variable); } else { description = mxmlNewElement(variable, "description"); #ifdef DEBUG fprintf(stderr, " adding comment %p/%p to variable...\n", comment->last_child, comment->child); #endif /* DEBUG */ mxmlNewText(comment, 0, buffer); update_comment(variable, mxmlNewText(description, 0, buffer)); } variable = NULL; } else if (constant) { if (strstr(buffer, "@private@")) { /* * Delete private constants... */ mxmlDelete(constant); } else { description = mxmlNewElement(constant, "description"); #ifdef DEBUG fprintf(stderr, " adding comment %p/%p to constant...\n", comment->last_child, comment->child); #endif /* DEBUG */ mxmlNewText(comment, 0, buffer); update_comment(constant, mxmlNewText(description, 0, buffer)); } constant = NULL; } else if (typedefnode) { if (strstr(buffer, "@private@")) { /* * Delete private typedefs... */ mxmlDelete(typedefnode); typedefnode = NULL; if (structclass) { mxmlDelete(structclass); structclass = NULL; } if (enumeration) { mxmlDelete(enumeration); enumeration = NULL; } } else { description = mxmlNewElement(typedefnode, "description"); #ifdef DEBUG fprintf(stderr, " adding comment %p/%p to typedef %s...\n", comment->last_child, comment->child, mxmlElementGetAttr(typedefnode, "name")); #endif /* DEBUG */ mxmlNewText(comment, 0, buffer); update_comment(typedefnode, mxmlNewText(description, 0, buffer)); if (structclass) { description = mxmlNewElement(structclass, "description"); update_comment(structclass, mxmlNewText(description, 0, buffer)); } else if (enumeration) { description = mxmlNewElement(enumeration, "description"); update_comment(enumeration, mxmlNewText(description, 0, buffer)); } } } else if (strcmp(tree->value.element.name, "mxmldoc") && !mxmlFindElement(tree, tree, "description", NULL, NULL, MXML_DESCEND_FIRST)) { description = mxmlNewElement(tree, "description"); #ifdef DEBUG fprintf(stderr, " adding comment %p/%p to parent...\n", comment->last_child, comment->child); #endif /* DEBUG */ mxmlNewText(comment, 0, buffer); update_comment(tree, mxmlNewText(description, 0, buffer)); } else mxmlNewText(comment, 0, buffer); #ifdef DEBUG fprintf(stderr, "C++ comment: <<<< %s >>>\n", buffer); #endif /* DEBUG */ } else if (ch == ' ' && bufptr == buffer) break; else if (bufptr < (buffer + sizeof(buffer) - 1)) *bufptr++ = ch; break; case STATE_STRING : /* Inside a string constant */ *bufptr++ = ch; if (ch == '\\') *bufptr++ = getc(fp); else if (ch == '\"') { *bufptr = '\0'; if (type) mxmlNewText(type, type->child != NULL, buffer); state = STATE_NONE; } break; case STATE_CHARACTER : /* Inside a character constant */ *bufptr++ = ch; if (ch == '\\') *bufptr++ = getc(fp); else if (ch == '\'') { *bufptr = '\0'; if (type) mxmlNewText(type, type->child != NULL, buffer); state = STATE_NONE; } break; case STATE_IDENTIFIER : /* Inside a keyword or identifier */ if (isalnum(ch) || ch == '_' || ch == '[' || ch == ']' || (ch == ',' && (parens > 1 || (type && !enumeration && !function))) || ch == ':' || ch == '.' || ch == '~') { if (bufptr < (buffer + sizeof(buffer) - 1)) *bufptr++ = ch; } else { ungetc(ch, fp); *bufptr = '\0'; state = STATE_NONE; #ifdef DEBUG fprintf(stderr, " braces=%d, type=%p, type->child=%p, buffer=\"%s\"\n", braces, type, type ? type->child : NULL, buffer); #endif /* DEBUG */ if (!braces) { if (!type || !type->child) { if (!strcmp(tree->value.element.name, "class")) { if (!strcmp(buffer, "public") || !strcmp(buffer, "public:")) { scope = "public"; #ifdef DEBUG fputs(" scope = public\n", stderr); #endif /* DEBUG */ break; } else if (!strcmp(buffer, "private") || !strcmp(buffer, "private:")) { scope = "private"; #ifdef DEBUG fputs(" scope = private\n", stderr); #endif /* DEBUG */ break; } else if (!strcmp(buffer, "protected") || !strcmp(buffer, "protected:")) { scope = "protected"; #ifdef DEBUG fputs(" scope = protected\n", stderr); #endif /* DEBUG */ break; } } } if (!type) type = mxmlNewElement(MXML_NO_PARENT, "type"); #ifdef DEBUG fprintf(stderr, " function=%p (%s), type->child=%p, ch='%c', parens=%d\n", function, function ? mxmlElementGetAttr(function, "name") : "null", type->child, ch, parens); #endif /* DEBUG */ if (!function && ch == '(') { if (type->child && !strcmp(type->child->value.text.string, "extern")) { /* * Remove external declarations... */ mxmlDelete(type); type = NULL; break; } if (type->child && !strcmp(type->child->value.text.string, "static") && !strcmp(tree->value.element.name, "mxmldoc")) { /* * Remove static functions... */ mxmlDelete(type); type = NULL; break; } function = mxmlNewElement(MXML_NO_PARENT, "function"); if ((bufptr = strchr(buffer, ':')) != NULL && bufptr[1] == ':') { *bufptr = '\0'; bufptr += 2; if ((fstructclass = mxmlFindElement(tree, tree, "class", "name", buffer, MXML_DESCEND_FIRST)) == NULL) fstructclass = mxmlFindElement(tree, tree, "struct", "name", buffer, MXML_DESCEND_FIRST); } else bufptr = buffer; mxmlElementSetAttr(function, "name", bufptr); if (scope) mxmlElementSetAttr(function, "scope", scope); #ifdef DEBUG fprintf(stderr, "function: %s\n", buffer); fprintf(stderr, " scope = %s\n", scope ? scope : "(null)"); fprintf(stderr, " comment = %p\n", comment); fprintf(stderr, " child = (%p) %s\n", comment->child, comment->child ? comment->child->value.text.string : "(null)"); fprintf(stderr, " last_child = (%p) %s\n", comment->last_child, comment->last_child ? comment->last_child->value.text.string : "(null)"); #endif /* DEBUG */ if (type->last_child && strcmp(type->last_child->value.text.string, "void")) { returnvalue = mxmlNewElement(function, "returnvalue"); mxmlAdd(returnvalue, MXML_ADD_AFTER, MXML_ADD_TO_PARENT, type); description = mxmlNewElement(returnvalue, "description"); #ifdef DEBUG fprintf(stderr, " adding comment %p/%p to returnvalue...\n", comment->last_child, comment->child); #endif /* DEBUG */ update_comment(returnvalue, comment->last_child); mxmlAdd(description, MXML_ADD_AFTER, MXML_ADD_TO_PARENT, comment->last_child); } else mxmlDelete(type); description = mxmlNewElement(function, "description"); #ifdef DEBUG fprintf(stderr, " adding comment %p/%p to function...\n", comment->last_child, comment->child); #endif /* DEBUG */ update_comment(function, comment->last_child); mxmlAdd(description, MXML_ADD_AFTER, MXML_ADD_TO_PARENT, comment->last_child); type = NULL; } else if (function && ((ch == ')' && parens == 1) || ch == ',')) { /* * Argument definition... */ if (strcmp(buffer, "void")) { mxmlNewText(type, type->child != NULL && type->last_child->value.text.string[0] != '(' && type->last_child->value.text.string[0] != '*', buffer); #ifdef DEBUG fprintf(stderr, "Argument: <<<< %s >>>\n", buffer); #endif /* DEBUG */ variable = add_variable(function, "argument", type); } else mxmlDelete(type); type = NULL; } else if (type->child && !function && (ch == ';' || ch == ',')) { #ifdef DEBUG fprintf(stderr, " got semicolon, typedefnode=%p, structclass=%p\n", typedefnode, structclass); #endif /* DEBUG */ if (typedefnode || structclass) { #ifdef DEBUG fprintf(stderr, "Typedef/struct/class: <<<< %s >>>>\n", buffer); #endif /* DEBUG */ if (typedefnode) { mxmlElementSetAttr(typedefnode, "name", buffer); sort_node(tree, typedefnode); } if (structclass && !mxmlElementGetAttr(structclass, "name")) { #ifdef DEBUG fprintf(stderr, "setting struct/class name to %s!\n", type->last_child->value.text.string); #endif /* DEBUG */ mxmlElementSetAttr(structclass, "name", buffer); sort_node(tree, structclass); structclass = NULL; } if (typedefnode) mxmlAdd(typedefnode, MXML_ADD_BEFORE, MXML_ADD_TO_PARENT, type); else mxmlDelete(type); type = NULL; typedefnode = NULL; } else if (type->child && !strcmp(type->child->value.text.string, "typedef")) { /* * Simple typedef... */ #ifdef DEBUG fprintf(stderr, "Typedef: <<<< %s >>>\n", buffer); #endif /* DEBUG */ typedefnode = mxmlNewElement(MXML_NO_PARENT, "typedef"); mxmlElementSetAttr(typedefnode, "name", buffer); mxmlDelete(type->child); sort_node(tree, typedefnode); if (type->child) type->child->value.text.whitespace = 0; mxmlAdd(typedefnode, MXML_ADD_AFTER, MXML_ADD_TO_PARENT, type); type = NULL; } else if (!parens) { /* * Variable definition... */ if (type->child && !strcmp(type->child->value.text.string, "static") && !strcmp(tree->value.element.name, "mxmldoc")) { /* * Remove static functions... */ mxmlDelete(type); type = NULL; break; } mxmlNewText(type, type->child != NULL && type->last_child->value.text.string[0] != '(' && type->last_child->value.text.string[0] != '*', buffer); #ifdef DEBUG fprintf(stderr, "Variable: <<<< %s >>>>\n", buffer); fprintf(stderr, " scope = %s\n", scope ? scope : "(null)"); #endif /* DEBUG */ variable = add_variable(MXML_NO_PARENT, "variable", type); type = NULL; sort_node(tree, variable); if (scope) mxmlElementSetAttr(variable, "scope", scope); } } else { #ifdef DEBUG fprintf(stderr, "Identifier: <<<< %s >>>>\n", buffer); #endif /* DEBUG */ mxmlNewText(type, type->child != NULL && type->last_child->value.text.string[0] != '(' && type->last_child->value.text.string[0] != '*', buffer); } } else if (enumeration && !isdigit(buffer[0] & 255)) { #ifdef DEBUG fprintf(stderr, "Constant: <<<< %s >>>\n", buffer); #endif /* DEBUG */ constant = mxmlNewElement(MXML_NO_PARENT, "constant"); mxmlElementSetAttr(constant, "name", buffer); sort_node(enumeration, constant); } else if (type) { mxmlDelete(type); type = NULL; } } break; } #if DEBUG > 1 if (state != oldstate) { fprintf(stderr, " changed states from %s to %s on receipt of character '%c'...\n", states[oldstate], states[state], oldch); fprintf(stderr, " variable = %p\n", variable); if (type) { fputs(" type =", stderr); for (temp = type->child; temp; temp = temp->next) fprintf(stderr, " \"%s\"", temp->value.text.string); fputs("\n", stderr); } } #endif /* DEBUG > 1 */ } mxmlDelete(comment); /* * All done, return with no errors... */ return (0); } /* * 'sort_node()' - Insert a node sorted into a tree. */ static void sort_node(mxml_node_t *tree, /* I - Tree to sort into */ mxml_node_t *node) /* I - Node to add */ { mxml_node_t *temp; /* Current node */ const char *tempname, /* Name of current node */ *nodename, /* Name of node */ *scope; /* Scope */ #if DEBUG > 1 fprintf(stderr, " sort_node(tree=%p, node=%p)\n", tree, node); #endif /* DEBUG > 1 */ /* * Range check input... */ if (!tree || !node || node->parent == tree) return; /* * Get the node name... */ if ((nodename = mxmlElementGetAttr(node, "name")) == NULL) return; if (nodename[0] == '_') return; /* Hide private names */ #if DEBUG > 1 fprintf(stderr, " nodename=%p (\"%s\")\n", nodename, nodename); #endif /* DEBUG > 1 */ /* * Delete any existing definition at this level, if one exists... */ if ((temp = mxmlFindElement(tree, tree, node->value.element.name, "name", nodename, MXML_DESCEND_FIRST)) != NULL) { /* * Copy the scope if needed... */ if ((scope = mxmlElementGetAttr(temp, "scope")) != NULL && mxmlElementGetAttr(node, "scope") == NULL) { #ifdef DEBUG fprintf(stderr, " copying scope %s for %s\n", scope, nodename); #endif /* DEBUG */ mxmlElementSetAttr(node, "scope", scope); } mxmlDelete(temp); } /* * Add the node into the tree at the proper place... */ for (temp = tree->child; temp; temp = temp->next) { #if DEBUG > 1 fprintf(stderr, " temp=%p\n", temp); #endif /* DEBUG > 1 */ if ((tempname = mxmlElementGetAttr(temp, "name")) == NULL) continue; #if DEBUG > 1 fprintf(stderr, " tempname=%p (\"%s\")\n", tempname, tempname); #endif /* DEBUG > 1 */ if (strcmp(nodename, tempname) < 0) break; } if (temp) mxmlAdd(tree, MXML_ADD_BEFORE, temp, node); else mxmlAdd(tree, MXML_ADD_AFTER, MXML_ADD_TO_PARENT, node); } /* * 'update_comment()' - Update a comment node. */ static void update_comment(mxml_node_t *parent, /* I - Parent node */ mxml_node_t *comment) /* I - Comment node */ { char *ptr; /* Pointer into comment */ #ifdef DEBUG fprintf(stderr, "update_comment(parent=%p, comment=%p)\n", parent, comment); #endif /* DEBUG */ /* * Range check the input... */ if (!parent || !comment) return; /* * Convert "\/" to "/"... */ for (ptr = strstr(comment->value.text.string, "\\/"); ptr; ptr = strstr(ptr, "\\/")) safe_strcpy(ptr, ptr + 1); /* * Update the comment... */ ptr = comment->value.text.string; if (*ptr == '\'') { /* * Convert "'name()' - description" to "description". */ for (ptr ++; *ptr && *ptr != '\''; ptr ++); if (*ptr == '\'') { ptr ++; while (isspace(*ptr & 255)) ptr ++; if (*ptr == '-') ptr ++; while (isspace(*ptr & 255)) ptr ++; safe_strcpy(comment->value.text.string, ptr); } } else if (!strncmp(ptr, "I ", 2) || !strncmp(ptr, "O ", 2) || !strncmp(ptr, "IO ", 3)) { /* * 'Convert "I - description", "IO - description", or "O - description" * to description + direction attribute. */ ptr = strchr(ptr, ' '); *ptr++ = '\0'; if (!strcmp(parent->value.element.name, "argument")) mxmlElementSetAttr(parent, "direction", comment->value.text.string); while (isspace(*ptr & 255)) ptr ++; if (*ptr == '-') ptr ++; while (isspace(*ptr & 255)) ptr ++; safe_strcpy(comment->value.text.string, ptr); } /* * Eliminate leading and trailing *'s... */ for (ptr = comment->value.text.string; *ptr == '*'; ptr ++); for (; isspace(*ptr & 255); ptr ++); if (ptr > comment->value.text.string) safe_strcpy(comment->value.text.string, ptr); for (ptr = comment->value.text.string + strlen(comment->value.text.string) - 1; ptr > comment->value.text.string && *ptr == '*'; ptr --) *ptr = '\0'; for (; ptr > comment->value.text.string && isspace(*ptr & 255); ptr --) *ptr = '\0'; #ifdef DEBUG fprintf(stderr, " updated comment = %s\n", comment->value.text.string); #endif /* DEBUG */ } /* * 'usage()' - Show program usage... */ static void usage(const char *option) /* I - Unknown option */ { if (option) printf("mxmldoc: Bad option \"%s\"!\n\n", option); puts("Usage: mxmldoc [options] [filename.xml] [source files] >filename.html"); puts("Options:"); puts(" --css filename.css Set CSS stylesheet file"); puts(" --docset bundleid.docset Generate documentation set"); puts(" --docversion version Set documentation version"); puts(" --feedname name Set documentation set feed name"); puts(" --feedurl url Set documentation set feed URL"); puts(" --footer footerfile Set footer file"); puts(" --framed basename Generate framed HTML to basename*.html"); puts(" --header headerfile Set header file"); puts(" --intro introfile Set introduction file"); puts(" --man name Generate man page"); puts(" --no-output Do no generate documentation file"); puts(" --section section Set section name"); puts(" --title title Set documentation title"); puts(" --tokens path Generate Xcode docset Tokens.xml file"); puts(" --version Show mxmldoc/Mini-XML version"); exit(1); } /* * 'write_description()' - Write the description text. */ static void write_description( FILE *out, /* I - Output file */ mxml_node_t *description, /* I - Description node */ const char *element, /* I - HTML element, if any */ int summary) /* I - Show summary */ { char text[10240], /* Text for description */ *start, /* Start of code/link */ *ptr; /* Pointer into text */ int col; /* Current column */ if (!description) return; get_text(description, text, sizeof(text)); ptr = strstr(text, "\n\n"); if (summary) { if (ptr) *ptr = '\0'; ptr = text; } else if (!ptr || !ptr[2]) return; else ptr += 2; if (element && *element) fprintf(out, "<%s class=\"%s\">", element, summary ? "description" : "discussion"); else if (!summary) fputs(".PP\n", out); for (col = 0; *ptr; ptr ++) { if (*ptr == '@' && (!strncmp(ptr + 1, "deprecated@", 11) || !strncmp(ptr + 1, "since ", 6))) { ptr ++; while (*ptr && *ptr != '@') ptr ++; if (!*ptr) return; } else if (!strncmp(ptr, "@code ", 6)) { for (ptr += 6; isspace(*ptr & 255); ptr ++); for (start = ptr, ptr ++; *ptr && *ptr != '@'; ptr ++); if (*ptr) *ptr = '\0'; else ptr --; if (element && *element) fprintf(out, "%s", start); else if (element) fputs(start, out); else fprintf(out, "\\fB%s\\fR", start); } else if (!strncmp(ptr, "@link ", 6)) { for (ptr += 6; isspace(*ptr & 255); ptr ++); for (start = ptr, ptr ++; *ptr && *ptr != '@'; ptr ++); if (*ptr) *ptr = '\0'; else ptr --; if (element && *element) fprintf(out, "%s", start, start); else if (element) fputs(start, out); else fprintf(out, "\\fI%s\\fR", start); } else if (element) { if (*ptr == '&') fputs("&", out); else if (*ptr == '<') fputs("<", out); else if (*ptr == '>') fputs(">", out); else if (*ptr == '\"') fputs(""", out); else if (*ptr & 128) { /* * Convert UTF-8 to Unicode constant... */ int ch; /* Unicode character */ ch = *ptr & 255; if ((ch & 0xe0) == 0xc0) { ch = ((ch & 0x1f) << 6) | (ptr[1] & 0x3f); ptr ++; } else if ((ch & 0xf0) == 0xe0) { ch = ((((ch * 0x0f) << 6) | (ptr[1] & 0x3f)) << 6) | (ptr[2] & 0x3f); ptr += 2; } if (ch == 0xa0) { /* * Handle non-breaking space as-is... */ fputs(" ", out); } else fprintf(out, "&#x%x;", ch); } else if (*ptr == '\n' && ptr[1] == '\n' && ptr[2] && ptr[2] != '@') { fputs("
      \n
      \n", out); ptr ++; } else putc(*ptr, out); } else if (*ptr == '\n' && ptr[1] == '\n' && ptr[2] && ptr[2] != '@') { fputs("\n.PP\n", out); ptr ++; } else { if (*ptr == '\\' || (*ptr == '.' && col == 0)) putc('\\', out); putc(*ptr, out); if (*ptr == '\n') col = 0; else col ++; } } if (element && *element) fprintf(out, "\n", element); else if (!element) putc('\n', out); } /* * 'write_element()' - Write an element's text nodes. */ static void write_element(FILE *out, /* I - Output file */ mxml_node_t *doc, /* I - Document tree */ mxml_node_t *element, /* I - Element to write */ int mode) /* I - Output mode */ { mxml_node_t *node; /* Current node */ if (!element) return; for (node = element->child; node; node = mxmlWalkNext(node, element, MXML_NO_DESCEND)) if (node->type == MXML_TEXT) { if (node->value.text.whitespace) putc(' ', out); if (mode == OUTPUT_HTML && (mxmlFindElement(doc, doc, "class", "name", node->value.text.string, MXML_DESCEND) || mxmlFindElement(doc, doc, "enumeration", "name", node->value.text.string, MXML_DESCEND) || mxmlFindElement(doc, doc, "struct", "name", node->value.text.string, MXML_DESCEND) || mxmlFindElement(doc, doc, "typedef", "name", node->value.text.string, MXML_DESCEND) || mxmlFindElement(doc, doc, "union", "name", node->value.text.string, MXML_DESCEND))) { fputs("value.text.string, mode); fputs("\">", out); write_string(out, node->value.text.string, mode); fputs("", out); } else write_string(out, node->value.text.string, mode); } if (!strcmp(element->value.element.name, "type") && element->last_child->value.text.string[0] != '*') putc(' ', out); } /* * 'write_file()' - Copy a file to the output. */ static void write_file(FILE *out, /* I - Output file */ const char *file) /* I - File to copy */ { FILE *fp; /* Copy file */ char line[8192]; /* Line from file */ if ((fp = fopen(file, "r")) == NULL) { fprintf(stderr, "mxmldoc: Unable to open \"%s\": %s\n", file, strerror(errno)); return; } while (fgets(line, sizeof(line), fp)) fputs(line, out); fclose(fp); } /* * 'write_function()' - Write documentation for a function. */ static void write_function(FILE *out, /* I - Output file */ mxml_node_t *doc, /* I - Document */ mxml_node_t *function, /* I - Function */ int level) /* I - Base heading level */ { mxml_node_t *arg, /* Current argument */ *adesc, /* Description of argument */ *description, /* Description of function */ *type, /* Type for argument */ *node; /* Node in description */ const char *name, /* Name of function/type */ *defval; /* Default value */ char prefix; /* Prefix character */ char *sep; /* Newline separator */ name = mxmlElementGetAttr(function, "name"); description = mxmlFindElement(function, function, "description", NULL, NULL, MXML_DESCEND_FIRST); fprintf(out, "%s%s\n", level, level == 3 ? "function" : "method", get_comment_info(description), name, name, level); if (description) write_description(out, description, "p", 1); fputs("

      \n", out); arg = mxmlFindElement(function, function, "returnvalue", NULL, NULL, MXML_DESCEND_FIRST); if (arg) write_element(out, doc, mxmlFindElement(arg, arg, "type", NULL, NULL, MXML_DESCEND_FIRST), OUTPUT_HTML); else fputs("void ", out); fprintf(out, "%s ", name); for (arg = mxmlFindElement(function, function, "argument", NULL, NULL, MXML_DESCEND_FIRST), prefix = '('; arg; arg = mxmlFindElement(arg, function, "argument", NULL, NULL, MXML_NO_DESCEND), prefix = ',') { type = mxmlFindElement(arg, arg, "type", NULL, NULL, MXML_DESCEND_FIRST); fprintf(out, "%c
      \n    ", prefix); if (type->child) write_element(out, doc, type, OUTPUT_HTML); fputs(mxmlElementGetAttr(arg, "name"), out); if ((defval = mxmlElementGetAttr(arg, "default")) != NULL) fprintf(out, " %s", defval); } if (prefix == '(') fputs("(void);

      \n", out); else { fprintf(out, "
      \n);

      \n" "Parameters\n" "
      \n", level + 1, level + 1); for (arg = mxmlFindElement(function, function, "argument", NULL, NULL, MXML_DESCEND_FIRST); arg; arg = mxmlFindElement(arg, function, "argument", NULL, NULL, MXML_NO_DESCEND)) { fprintf(out, "
      %s
      \n", mxmlElementGetAttr(arg, "name")); adesc = mxmlFindElement(arg, arg, "description", NULL, NULL, MXML_DESCEND_FIRST); write_description(out, adesc, "dd", 1); write_description(out, adesc, "dd", 0); } fputs("
      \n", out); } arg = mxmlFindElement(function, function, "returnvalue", NULL, NULL, MXML_DESCEND_FIRST); if (arg) { fprintf(out, "Return Value\n", level + 1, level + 1); adesc = mxmlFindElement(arg, arg, "description", NULL, NULL, MXML_DESCEND_FIRST); write_description(out, adesc, "p", 1); write_description(out, adesc, "p", 0); } if (description) { for (node = description->child; node; node = node->next) if (node->value.text.string && (sep = strstr(node->value.text.string, "\n\n")) != NULL) { sep += 2; if (*sep && strncmp(sep, "@since ", 7) && strncmp(sep, "@deprecated@", 12)) break; } if (node) { fprintf(out, "Discussion\n", level + 1, level + 1); write_description(out, description, "p", 0); } } } /* * 'write_html()' - Write HTML documentation. */ static void write_html(const char *section, /* I - Section */ const char *title, /* I - Title */ const char *footerfile, /* I - Footer file */ const char *headerfile, /* I - Header file */ const char *introfile, /* I - Intro file */ const char *cssfile, /* I - Stylesheet file */ const char *framefile, /* I - Framed HTML basename */ const char *docset, /* I - Documentation set directory */ const char *docversion, /* I - Documentation set version */ const char *feedname, /* I - Feed name for doc set */ const char *feedurl, /* I - Feed URL for doc set */ mxml_node_t *doc) /* I - XML documentation */ { FILE *out; /* Output file */ mxml_node_t *function, /* Current function */ *scut, /* Struct/class/union/typedef */ *arg, /* Current argument */ *description, /* Description of function/var */ *type; /* Type for argument */ const char *name, /* Name of function/type */ *defval, /* Default value */ *basename; /* Base filename for framed output */ char filename[1024]; /* Current output filename */ if (framefile) { /* * Get the basename of the frame file... */ if ((basename = strrchr(framefile, '/')) != NULL) basename ++; else basename = framefile; if (strstr(basename, ".html")) fputs("mxmldoc: Frame base name should not contain .html extension!\n", stderr); /* * Create the container HTML file for the frames... */ snprintf(filename, sizeof(filename), "%s.html", framefile); if ((out = fopen(filename, "w")) == NULL) { fprintf(stderr, "mxmldoc: Unable to create \"%s\": %s\n", filename, strerror(errno)); return; } fputs("\n" "\n" "\n" "\t", out); write_string(out, title, OUTPUT_HTML); fputs("\n", out); if (section) fprintf(out, "\t\n", section); fputs("\t\n" "\t\n" "\n", out); fputs("\n", out); fprintf(out, "\n", basename); fprintf(out, "\n", basename); fputs("\n" "\n" "<h1>", out); write_string(out, title, OUTPUT_HTML); fprintf(out, "</h1>\n" "<ul>\n" "\t<li><a href=\"%s-toc.html\">Table of Contents</a></li>\n" "\t<li><a href=\"%s-body.html\">Body</a></li>\n" "</ul>\n", basename, basename); fputs("\n" "\n", out); fclose(out); /* * Write the table-of-contents file... */ snprintf(filename, sizeof(filename), "%s-toc.html", framefile); if ((out = fopen(filename, "w")) == NULL) { fprintf(stderr, "mxmldoc: Unable to create \"%s\": %s\n", filename, strerror(errno)); return; } write_html_head(out, section, title, cssfile); snprintf(filename, sizeof(filename), "%s-body.html", basename); fputs("
      \n", out); fprintf(out, "

      ", filename); write_string(out, title, OUTPUT_HTML); fputs("

      \n", out); write_toc(out, doc, introfile, filename, 0); fputs("
      \n" "\n" "\n", out); fclose(out); /* * Finally, open the body file... */ snprintf(filename, sizeof(filename), "%s-body.html", framefile); if ((out = fopen(filename, "w")) == NULL) { fprintf(stderr, "mxmldoc: Unable to create \"%s\": %s\n", filename, strerror(errno)); return; } } else if (docset) { /* * Create an Xcode documentation set - start by removing any existing * output directory... */ #ifdef __APPLE__ const char *id; /* Identifier */ if (!access(docset, 0) && !remove_directory(docset)) return; /* * Then make the Apple standard bundle directory structure... */ if (mkdir(docset, 0755)) { fprintf(stderr, "mxmldoc: Unable to create \"%s\": %s\n", docset, strerror(errno)); return; } snprintf(filename, sizeof(filename), "%s/Contents", docset); if (mkdir(filename, 0755)) { fprintf(stderr, "mxmldoc: Unable to create \"%s\": %s\n", filename, strerror(errno)); return; } snprintf(filename, sizeof(filename), "%s/Contents/Resources", docset); if (mkdir(filename, 0755)) { fprintf(stderr, "mxmldoc: Unable to create \"%s\": %s\n", filename, strerror(errno)); return; } snprintf(filename, sizeof(filename), "%s/Contents/Resources/Documentation", docset); if (mkdir(filename, 0755)) { fprintf(stderr, "mxmldoc: Unable to create \"%s\": %s\n", filename, strerror(errno)); return; } /* * The Info.plist file, which describes the documentation set... */ if ((id = strrchr(docset, '/')) != NULL) id ++; else id = docset; snprintf(filename, sizeof(filename), "%s/Contents/Info.plist", docset); if ((out = fopen(filename, "w")) == NULL) { fprintf(stderr, "mxmldoc: Unable to create \"%s\": %s\n", filename, strerror(errno)); return; } fputs("\n" "\n" "\n" "\n" "\tCFBundleIdentifier\n" "\t", out); write_string(out, id, OUTPUT_HTML); fputs("\n" "\tCFBundleName\n" "\t", out); write_string(out, title, OUTPUT_HTML); fputs("\n" "\tCFBundleVersion\n" "\t", out); write_string(out, docversion ? docversion : "0.0", OUTPUT_HTML); fputs("\n" "\tCFBundleShortVersionString\n" "\t", out); write_string(out, docversion ? docversion : "0.0", OUTPUT_HTML); fputs("\n", out); if (feedname) { fputs("\tDocSetFeedName\n" "\t", out); write_string(out, feedname ? feedname : title, OUTPUT_HTML); fputs("\n", out); } if (feedurl) { fputs("\tDocSetFeedURL\n" "\t", out); write_string(out, feedurl, OUTPUT_HTML); fputs("\n", out); } fputs("\n" "\n", out); fclose(out); /* * Next the Nodes.xml file... */ snprintf(filename, sizeof(filename), "%s/Contents/Resources/Nodes.xml", docset); if ((out = fopen(filename, "w")) == NULL) { fprintf(stderr, "mxmldoc: Unable to create \"%s\": %s\n", filename, strerror(errno)); return; } fputs("\n" "\n" "\n" "\n" "", out); write_string(out, title, OUTPUT_HTML); fputs("\n" "Documentation/index.html\n" "\n", out); write_toc(out, doc, introfile, NULL, 1); fputs("\n" "\n" "\n" "\n", out); fclose(out); /* * Then the Tokens.xml file... */ snprintf(filename, sizeof(filename), "%s/Contents/Resources/Tokens.xml", docset); if ((out = fopen(filename, "w")) == NULL) { fprintf(stderr, "mxmldoc: Unable to create \"%s\": %s\n", filename, strerror(errno)); return; } fputs("\n" "\n", out); write_tokens(out, doc, "index.html"); fputs("\n", out); fclose(out); /* * Finally the HTML file... */ snprintf(filename, sizeof(filename), "%s/Contents/Resources/Documentation/index.html", docset); if ((out = fopen(filename, "w")) == NULL) { fprintf(stderr, "mxmldoc: Unable to create \"%s\": %s\n", filename, strerror(errno)); return; } #else fputs("mxmldoc: Xcode documentation sets can only be created on " "Mac OS X.\n", stderr); return; #endif /* __APPLE__ */ } else out = stdout; /* * Standard header... */ write_html_head(out, section, title, cssfile); fputs("
      \n", out); /* * Header... */ if (headerfile) { /* * Use custom header... */ write_file(out, headerfile); } else { /* * Use standard header... */ fputs("

      ", out); write_string(out, title, OUTPUT_HTML); fputs("

      \n", out); } /* * Table of contents... */ if (!framefile) write_toc(out, doc, introfile, NULL, 0); /* * Intro... */ if (introfile) write_file(out, introfile); /* * List of classes... */ if ((scut = find_public(doc, doc, "class")) != NULL) { fputs("

      Classes

      \n", out); while (scut) { write_scu(out, doc, scut); scut = find_public(scut, doc, "class"); } } /* * List of functions... */ if ((function = find_public(doc, doc, "function")) != NULL) { fputs("

      Functions

      \n", out); while (function) { write_function(out, doc, function, 3); function = find_public(function, doc, "function"); } } /* * List of types... */ if ((scut = find_public(doc, doc, "typedef")) != NULL) { fputs("

      Data Types

      \n", out); while (scut) { name = mxmlElementGetAttr(scut, "name"); description = mxmlFindElement(scut, scut, "description", NULL, NULL, MXML_DESCEND_FIRST); fprintf(out, "

      %s%s

      \n", get_comment_info(description), name, name); if (description) write_description(out, description, "p", 1); fputs("

      \n" "typedef ", out); type = mxmlFindElement(scut, scut, "type", NULL, NULL, MXML_DESCEND_FIRST); for (type = type->child; type; type = type->next) if (!strcmp(type->value.text.string, "(")) break; else { if (type->value.text.whitespace) putc(' ', out); if (mxmlFindElement(doc, doc, "class", "name", type->value.text.string, MXML_DESCEND) || mxmlFindElement(doc, doc, "enumeration", "name", type->value.text.string, MXML_DESCEND) || mxmlFindElement(doc, doc, "struct", "name", type->value.text.string, MXML_DESCEND) || mxmlFindElement(doc, doc, "typedef", "name", type->value.text.string, MXML_DESCEND) || mxmlFindElement(doc, doc, "union", "name", type->value.text.string, MXML_DESCEND)) { fputs("value.text.string, OUTPUT_HTML); fputs("\">", out); write_string(out, type->value.text.string, OUTPUT_HTML); fputs("", out); } else write_string(out, type->value.text.string, OUTPUT_HTML); } if (type) { /* * Output function type... */ if (type->prev && type->prev->value.text.string[0] != '*') putc(' ', out); fprintf(out, "(*%s", name); for (type = type->next->next; type; type = type->next) { if (type->value.text.whitespace) putc(' ', out); if (mxmlFindElement(doc, doc, "class", "name", type->value.text.string, MXML_DESCEND) || mxmlFindElement(doc, doc, "enumeration", "name", type->value.text.string, MXML_DESCEND) || mxmlFindElement(doc, doc, "struct", "name", type->value.text.string, MXML_DESCEND) || mxmlFindElement(doc, doc, "typedef", "name", type->value.text.string, MXML_DESCEND) || mxmlFindElement(doc, doc, "union", "name", type->value.text.string, MXML_DESCEND)) { fputs("value.text.string, OUTPUT_HTML); fputs("\">", out); write_string(out, type->value.text.string, OUTPUT_HTML); fputs("", out); } else write_string(out, type->value.text.string, OUTPUT_HTML); } fputs(";\n", out); } else { type = mxmlFindElement(scut, scut, "type", NULL, NULL, MXML_DESCEND_FIRST); if (type->last_child->value.text.string[0] != '*') putc(' ', out); fprintf(out, "%s;\n", name); } fputs("

      \n", out); scut = find_public(scut, doc, "typedef"); } } /* * List of structures... */ if ((scut = find_public(doc, doc, "struct")) != NULL) { fputs("

      Structures

      \n", out); while (scut) { write_scu(out, doc, scut); scut = find_public(scut, doc, "struct"); } } /* * List of unions... */ if ((scut = find_public(doc, doc, "union")) != NULL) { fputs("

      Unions

      \n", out); while (scut) { write_scu(out, doc, scut); scut = find_public(scut, doc, "union"); } } /* * Variables... */ if ((arg = find_public(doc, doc, "variable")) != NULL) { fputs("

      Variables

      \n", out); while (arg) { name = mxmlElementGetAttr(arg, "name"); description = mxmlFindElement(arg, arg, "description", NULL, NULL, MXML_DESCEND_FIRST); fprintf(out, "

      %s%s

      \n", get_comment_info(description), name, name); if (description) write_description(out, description, "p", 1); fputs("

      ", out); write_element(out, doc, mxmlFindElement(arg, arg, "type", NULL, NULL, MXML_DESCEND_FIRST), OUTPUT_HTML); fputs(mxmlElementGetAttr(arg, "name"), out); if ((defval = mxmlElementGetAttr(arg, "default")) != NULL) fprintf(out, " %s", defval); fputs(";

      \n", out); arg = find_public(arg, doc, "variable"); } } /* * List of enumerations... */ if ((scut = find_public(doc, doc, "enumeration")) != NULL) { fputs("

      Constants

      \n", out); while (scut) { name = mxmlElementGetAttr(scut, "name"); description = mxmlFindElement(scut, scut, "description", NULL, NULL, MXML_DESCEND_FIRST); fprintf(out, "

      %s%s

      \n", get_comment_info(description), name, name); if (description) write_description(out, description, "p", 1); fputs("

      Constants

      \n" "
      \n", out); for (arg = mxmlFindElement(scut, scut, "constant", NULL, NULL, MXML_DESCEND_FIRST); arg; arg = mxmlFindElement(arg, scut, "constant", NULL, NULL, MXML_NO_DESCEND)) { description = mxmlFindElement(arg, arg, "description", NULL, NULL, MXML_DESCEND_FIRST); fprintf(out, "
      %s %s
      \n", mxmlElementGetAttr(arg, "name"), get_comment_info(description)); write_description(out, description, "dd", 1); write_description(out, description, "dd", 0); } fputs("
      \n", out); scut = find_public(scut, doc, "enumeration"); } } /* * Footer... */ if (footerfile) { /* * Use custom footer... */ write_file(out, footerfile); } fputs("
      \n" "\n" "\n", out); /* * Close output file as needed... */ if (out != stdout) fclose(out); #ifdef __APPLE__ /* * When generating document sets, run the docsetutil program to index it... */ if (docset) { const char *args[4]; /* Argument array */ pid_t pid; /* Process ID */ int status; /* Exit status */ args[0] = "/Developer/usr/bin/docsetutil"; args[1] = "index"; args[2] = docset; args[3] = NULL; if (posix_spawn(&pid, args[0], NULL, NULL, (char **)args, environ)) { fprintf(stderr, "mxmldoc: Unable to index documentation set \"%s\": %s\n", docset, strerror(errno)); } else { while (wait(&status) != pid); if (status) { if (WIFEXITED(status)) fprintf(stderr, "mxmldoc: docsetutil exited with status %d\n", WEXITSTATUS(status)); else fprintf(stderr, "mxmldoc: docsetutil crashed with signal %d\n", WTERMSIG(status)); } else { /* * Remove unneeded temporary XML files... */ snprintf(filename, sizeof(filename), "%s/Contents/Resources/Nodes.xml", docset); unlink(filename); snprintf(filename, sizeof(filename), "%s/Contents/Resources/Tokens.xml", docset); unlink(filename); } } } #endif /* __APPLE__ */ } /* * 'write_html_head()' - Write the standard HTML header. */ static void write_html_head(FILE *out, /* I - Output file */ const char *section, /* I - Section */ const char *title, /* I - Title */ const char *cssfile) /* I - Stylesheet */ { fputs("\n" "\n", out); if (section) fprintf(out, "\n", section); fputs("\n" "\t", out); write_string(out, title, OUTPUT_HTML); fputs("\t\n", out); if (section) fprintf(out, "\t\n", section); fputs("\t\n" "\t\n" "\n" "\n" "\n", out); } /* * 'write_man()' - Write manpage documentation. */ static void write_man(const char *man_name, /* I - Name of manpage */ const char *section, /* I - Section */ const char *title, /* I - Title */ const char *footerfile, /* I - Footer file */ const char *headerfile, /* I - Header file */ const char *introfile, /* I - Intro file */ mxml_node_t *doc) /* I - XML documentation */ { int i; /* Looping var */ mxml_node_t *function, /* Current function */ *scut, /* Struct/class/union/typedef */ *arg, /* Current argument */ *description, /* Description of function/var */ *type; /* Type for argument */ const char *name, /* Name of function/type */ *cname, /* Class name */ *defval, /* Default value */ *parent; /* Parent class */ int inscope; /* Variable/method scope */ char prefix; /* Prefix character */ time_t curtime; /* Current time */ struct tm *curdate; /* Current date */ char buffer[1024]; /* String buffer */ static const char * const scopes[] = /* Scope strings */ { "private", "protected", "public" }; /* * Standard man page... */ curtime = time(NULL); curdate = localtime(&curtime); strftime(buffer, sizeof(buffer), "%x", curdate); printf(".TH %s %s \"%s\" \"%s\" \"%s\"\n", man_name, section ? section : "3", title ? title : "", buffer, title ? title : ""); /* * Header... */ if (headerfile) { /* * Use custom header... */ write_file(stdout, headerfile); } else { /* * Use standard header... */ puts(".SH NAME"); printf("%s \\- %s\n", man_name, title ? title : man_name); } /* * Intro... */ if (introfile) write_file(stdout, introfile); /* * List of classes... */ if (find_public(doc, doc, "class")) { puts(".SH CLASSES"); for (scut = find_public(doc, doc, "class"); scut; scut = find_public(scut, doc, "class")) { cname = mxmlElementGetAttr(scut, "name"); description = mxmlFindElement(scut, scut, "description", NULL, NULL, MXML_DESCEND_FIRST); printf(".SS %s\n", cname); write_description(stdout, description, NULL, 1); printf(".PP\n" ".nf\n" "class %s", cname); if ((parent = mxmlElementGetAttr(scut, "parent")) != NULL) printf(" %s", parent); puts("\n{"); for (i = 0; i < 3; i ++) { inscope = 0; for (arg = mxmlFindElement(scut, scut, "variable", "scope", scopes[i], MXML_DESCEND_FIRST); arg; arg = mxmlFindElement(arg, scut, "variable", "scope", scopes[i], MXML_NO_DESCEND)) { if (!inscope) { inscope = 1; printf(" %s:\n", scopes[i]); } printf(" "); write_element(stdout, doc, mxmlFindElement(arg, arg, "type", NULL, NULL, MXML_DESCEND_FIRST), OUTPUT_MAN); printf("%s;\n", mxmlElementGetAttr(arg, "name")); } for (function = mxmlFindElement(scut, scut, "function", "scope", scopes[i], MXML_DESCEND_FIRST); function; function = mxmlFindElement(function, scut, "function", "scope", scopes[i], MXML_NO_DESCEND)) { if (!inscope) { inscope = 1; printf(" %s:\n", scopes[i]); } name = mxmlElementGetAttr(function, "name"); printf(" "); arg = mxmlFindElement(function, function, "returnvalue", NULL, NULL, MXML_DESCEND_FIRST); if (arg) write_element(stdout, doc, mxmlFindElement(arg, arg, "type", NULL, NULL, MXML_DESCEND_FIRST), OUTPUT_MAN); else if (strcmp(cname, name) && strcmp(cname, name + 1)) fputs("void ", stdout); printf("%s", name); for (arg = mxmlFindElement(function, function, "argument", NULL, NULL, MXML_DESCEND_FIRST), prefix = '('; arg; arg = mxmlFindElement(arg, function, "argument", NULL, NULL, MXML_NO_DESCEND), prefix = ',') { type = mxmlFindElement(arg, arg, "type", NULL, NULL, MXML_DESCEND_FIRST); putchar(prefix); if (prefix == ',') putchar(' '); if (type->child) write_element(stdout, doc, type, OUTPUT_MAN); fputs(mxmlElementGetAttr(arg, "name"), stdout); if ((defval = mxmlElementGetAttr(arg, "default")) != NULL) printf(" %s", defval); } if (prefix == '(') puts("(void);"); else puts(");"); } } puts("};\n" ".fi"); write_description(stdout, description, NULL, 0); } } /* * List of enumerations... */ if (find_public(doc, doc, "enumeration")) { puts(".SH ENUMERATIONS"); for (scut = find_public(doc, doc, "enumeration"); scut; scut = find_public(scut, doc, "enumeration")) { name = mxmlElementGetAttr(scut, "name"); description = mxmlFindElement(scut, scut, "description", NULL, NULL, MXML_DESCEND_FIRST); printf(".SS %s\n", name); write_description(stdout, description, NULL, 1); write_description(stdout, description, NULL, 0); for (arg = mxmlFindElement(scut, scut, "constant", NULL, NULL, MXML_DESCEND_FIRST); arg; arg = mxmlFindElement(arg, scut, "constant", NULL, NULL, MXML_NO_DESCEND)) { description = mxmlFindElement(arg, arg, "description", NULL, NULL, MXML_DESCEND_FIRST); printf(".TP 5\n%s\n.br\n", mxmlElementGetAttr(arg, "name")); write_description(stdout, description, NULL, 1); } } } /* * List of functions... */ if (find_public(doc, doc, "function")) { puts(".SH FUNCTIONS"); for (function = find_public(doc, doc, "function"); function; function = find_public(function, doc, "function")) { name = mxmlElementGetAttr(function, "name"); description = mxmlFindElement(function, function, "description", NULL, NULL, MXML_DESCEND_FIRST); printf(".SS %s\n", name); write_description(stdout, description, NULL, 1); puts(".PP\n" ".nf"); arg = mxmlFindElement(function, function, "returnvalue", NULL, NULL, MXML_DESCEND_FIRST); if (arg) write_element(stdout, doc, mxmlFindElement(arg, arg, "type", NULL, NULL, MXML_DESCEND_FIRST), OUTPUT_MAN); else fputs("void", stdout); printf(" %s ", name); for (arg = mxmlFindElement(function, function, "argument", NULL, NULL, MXML_DESCEND_FIRST), prefix = '('; arg; arg = mxmlFindElement(arg, function, "argument", NULL, NULL, MXML_NO_DESCEND), prefix = ',') { type = mxmlFindElement(arg, arg, "type", NULL, NULL, MXML_DESCEND_FIRST); printf("%c\n ", prefix); if (type->child) write_element(stdout, doc, type, OUTPUT_MAN); fputs(mxmlElementGetAttr(arg, "name"), stdout); if ((defval = mxmlElementGetAttr(arg, "default")) != NULL) printf(" %s", defval); } if (prefix == '(') puts("(void);"); else puts("\n);"); puts(".fi"); write_description(stdout, description, NULL, 0); } } /* * List of structures... */ if (find_public(doc, doc, "struct")) { puts(".SH STRUCTURES"); for (scut = find_public(doc, doc, "struct"); scut; scut = find_public(scut, doc, "struct")) { cname = mxmlElementGetAttr(scut, "name"); description = mxmlFindElement(scut, scut, "description", NULL, NULL, MXML_DESCEND_FIRST); printf(".SS %s\n", cname); write_description(stdout, description, NULL, 1); printf(".PP\n" ".nf\n" "struct %s\n{\n", cname); for (arg = mxmlFindElement(scut, scut, "variable", NULL, NULL, MXML_DESCEND_FIRST); arg; arg = mxmlFindElement(arg, scut, "variable", NULL, NULL, MXML_NO_DESCEND)) { printf(" "); write_element(stdout, doc, mxmlFindElement(arg, arg, "type", NULL, NULL, MXML_DESCEND_FIRST), OUTPUT_MAN); printf("%s;\n", mxmlElementGetAttr(arg, "name")); } for (function = mxmlFindElement(scut, scut, "function", NULL, NULL, MXML_DESCEND_FIRST); function; function = mxmlFindElement(function, scut, "function", NULL, NULL, MXML_NO_DESCEND)) { name = mxmlElementGetAttr(function, "name"); printf(" "); arg = mxmlFindElement(function, function, "returnvalue", NULL, NULL, MXML_DESCEND_FIRST); if (arg) write_element(stdout, doc, mxmlFindElement(arg, arg, "type", NULL, NULL, MXML_DESCEND_FIRST), OUTPUT_MAN); else if (strcmp(cname, name) && strcmp(cname, name + 1)) fputs("void ", stdout); fputs(name, stdout); for (arg = mxmlFindElement(function, function, "argument", NULL, NULL, MXML_DESCEND_FIRST), prefix = '('; arg; arg = mxmlFindElement(arg, function, "argument", NULL, NULL, MXML_NO_DESCEND), prefix = ',') { type = mxmlFindElement(arg, arg, "type", NULL, NULL, MXML_DESCEND_FIRST); putchar(prefix); if (prefix == ',') putchar(' '); if (type->child) write_element(stdout, doc, type, OUTPUT_MAN); fputs(mxmlElementGetAttr(arg, "name"), stdout); if ((defval = mxmlElementGetAttr(arg, "default")) != NULL) printf(" %s", defval); } if (prefix == '(') puts("(void);"); else puts(");"); } puts("};\n" ".fi"); write_description(stdout, description, NULL, 0); } } /* * List of types... */ if (find_public(doc, doc, "typedef")) { puts(".SH TYPES"); for (scut = find_public(doc, doc, "typedef"); scut; scut = find_public(scut, doc, "typedef")) { name = mxmlElementGetAttr(scut, "name"); description = mxmlFindElement(scut, scut, "description", NULL, NULL, MXML_DESCEND_FIRST); printf(".SS %s\n", name); write_description(stdout, description, NULL, 1); fputs(".PP\n" ".nf\n" "typedef ", stdout); type = mxmlFindElement(scut, scut, "type", NULL, NULL, MXML_DESCEND_FIRST); for (type = type->child; type; type = type->next) if (!strcmp(type->value.text.string, "(")) break; else { if (type->value.text.whitespace) putchar(' '); write_string(stdout, type->value.text.string, OUTPUT_MAN); } if (type) { /* * Output function type... */ printf(" (*%s", name); for (type = type->next->next; type; type = type->next) { if (type->value.text.whitespace) putchar(' '); write_string(stdout, type->value.text.string, OUTPUT_MAN); } puts(";"); } else printf(" %s;\n", name); puts(".fi"); write_description(stdout, description, NULL, 0); } } /* * List of unions... */ if (find_public(doc, doc, "union")) { puts(".SH UNIONS"); for (scut = find_public(doc, doc, "union"); scut; scut = find_public(scut, doc, "union")) { name = mxmlElementGetAttr(scut, "name"); description = mxmlFindElement(scut, scut, "description", NULL, NULL, MXML_DESCEND_FIRST); printf(".SS %s\n", name); write_description(stdout, description, NULL, 1); printf(".PP\n" ".nf\n" "union %s\n{\n", name); for (arg = mxmlFindElement(scut, scut, "variable", NULL, NULL, MXML_DESCEND_FIRST); arg; arg = mxmlFindElement(arg, scut, "variable", NULL, NULL, MXML_NO_DESCEND)) { printf(" "); write_element(stdout, doc, mxmlFindElement(arg, arg, "type", NULL, NULL, MXML_DESCEND_FIRST), OUTPUT_MAN); printf("%s;\n", mxmlElementGetAttr(arg, "name")); } puts("};\n" ".fi"); write_description(stdout, description, NULL, 0); } } /* * Variables... */ if (find_public(doc, doc, "variable")) { puts(".SH VARIABLES"); for (arg = find_public(doc, doc, "variable"); arg; arg = find_public(arg, doc, "variable")) { name = mxmlElementGetAttr(arg, "name"); description = mxmlFindElement(arg, arg, "description", NULL, NULL, MXML_DESCEND_FIRST); printf(".SS %s\n", name); write_description(stdout, description, NULL, 1); puts(".PP\n" ".nf"); write_element(stdout, doc, mxmlFindElement(arg, arg, "type", NULL, NULL, MXML_DESCEND_FIRST), OUTPUT_MAN); fputs(mxmlElementGetAttr(arg, "name"), stdout); if ((defval = mxmlElementGetAttr(arg, "default")) != NULL) printf(" %s", defval); puts(";\n" ".fi"); write_description(stdout, description, NULL, 0); } } if (footerfile) { /* * Use custom footer... */ write_file(stdout, footerfile); } } /* * 'write_scu()' - Write a structure, class, or union. */ static void write_scu(FILE *out, /* I - Output file */ mxml_node_t *doc, /* I - Document */ mxml_node_t *scut) /* I - Structure, class, or union */ { int i; /* Looping var */ mxml_node_t *function, /* Current function */ *arg, /* Current argument */ *description, /* Description of function/var */ *type; /* Type for argument */ const char *name, /* Name of function/type */ *cname, /* Class name */ *defval, /* Default value */ *parent, /* Parent class */ *scope; /* Scope for variable/function */ int inscope, /* Variable/method scope */ maxscope; /* Maximum scope */ char prefix; /* Prefix character */ static const char * const scopes[] = /* Scope strings */ { "private", "protected", "public" }; cname = mxmlElementGetAttr(scut, "name"); description = mxmlFindElement(scut, scut, "description", NULL, NULL, MXML_DESCEND_FIRST); fprintf(out, "

      %s%s

      \n", scut->value.element.name, get_comment_info(description), cname, cname); if (description) write_description(out, description, "p", 1); fprintf(out, "

      %s %s", scut->value.element.name, cname); if ((parent = mxmlElementGetAttr(scut, "parent")) != NULL) fprintf(out, " %s", parent); fputs(" {
      \n", out); maxscope = !strcmp(scut->value.element.name, "class") ? 3 : 1; for (i = 0; i < maxscope; i ++) { inscope = maxscope == 1; for (arg = mxmlFindElement(scut, scut, "variable", NULL, NULL, MXML_DESCEND_FIRST); arg; arg = mxmlFindElement(arg, scut, "variable", NULL, NULL, MXML_NO_DESCEND)) { if (maxscope > 1 && ((scope = mxmlElementGetAttr(arg, "scope")) == NULL || strcmp(scope, scopes[i]))) continue; if (!inscope) { inscope = 1; fprintf(out, "  %s:
      \n", scopes[i]); } fputs("    ", out); write_element(out, doc, mxmlFindElement(arg, arg, "type", NULL, NULL, MXML_DESCEND_FIRST), OUTPUT_HTML); fprintf(out, "%s;
      \n", mxmlElementGetAttr(arg, "name")); } for (function = mxmlFindElement(scut, scut, "function", NULL, NULL, MXML_DESCEND_FIRST); function; function = mxmlFindElement(function, scut, "function", NULL, NULL, MXML_NO_DESCEND)) { if (maxscope > 1 && ((scope = mxmlElementGetAttr(arg, "scope")) == NULL || strcmp(scope, scopes[i]))) continue; if (!inscope) { inscope = 1; fprintf(out, "  %s:
      \n", scopes[i]); } name = mxmlElementGetAttr(function, "name"); fputs("    ", out); arg = mxmlFindElement(function, function, "returnvalue", NULL, NULL, MXML_DESCEND_FIRST); if (arg) write_element(out, doc, mxmlFindElement(arg, arg, "type", NULL, NULL, MXML_DESCEND_FIRST), OUTPUT_HTML); else if (strcmp(cname, name) && strcmp(cname, name + 1)) fputs("void ", out); fprintf(out, "%s", cname, name, name); for (arg = mxmlFindElement(function, function, "argument", NULL, NULL, MXML_DESCEND_FIRST), prefix = '('; arg; arg = mxmlFindElement(arg, function, "argument", NULL, NULL, MXML_NO_DESCEND), prefix = ',') { type = mxmlFindElement(arg, arg, "type", NULL, NULL, MXML_DESCEND_FIRST); putc(prefix, out); if (prefix == ',') putc(' ', out); if (type->child) write_element(out, doc, type, OUTPUT_HTML); fputs(mxmlElementGetAttr(arg, "name"), out); if ((defval = mxmlElementGetAttr(arg, "default")) != NULL) fprintf(out, " %s", defval); } if (prefix == '(') fputs("(void);
      \n", out); else fputs(");
      \n", out); } } fputs("};

      \n" "

      Members

      \n" "
      \n", out); for (arg = mxmlFindElement(scut, scut, "variable", NULL, NULL, MXML_DESCEND_FIRST); arg; arg = mxmlFindElement(arg, scut, "variable", NULL, NULL, MXML_NO_DESCEND)) { description = mxmlFindElement(arg, arg, "description", NULL, NULL, MXML_DESCEND_FIRST); fprintf(out, "
      %s %s
      \n", mxmlElementGetAttr(arg, "name"), get_comment_info(description)); write_description(out, description, "dd", 1); write_description(out, description, "dd", 0); } fputs("
      \n", out); for (function = mxmlFindElement(scut, scut, "function", NULL, NULL, MXML_DESCEND_FIRST); function; function = mxmlFindElement(function, scut, "function", NULL, NULL, MXML_NO_DESCEND)) { write_function(out, doc, function, 4); } } /* * 'write_string()' - Write a string, quoting HTML special chars as needed. */ static void write_string(FILE *out, /* I - Output file */ const char *s, /* I - String to write */ int mode) /* I - Output mode */ { switch (mode) { case OUTPUT_HTML : case OUTPUT_XML : while (*s) { if (*s == '&') fputs("&", out); else if (*s == '<') fputs("<", out); else if (*s == '>') fputs(">", out); else if (*s == '\"') fputs(""", out); else if (*s & 128) { /* * Convert UTF-8 to Unicode constant... */ int ch; /* Unicode character */ ch = *s & 255; if ((ch & 0xe0) == 0xc0) { ch = ((ch & 0x1f) << 6) | (s[1] & 0x3f); s ++; } else if ((ch & 0xf0) == 0xe0) { ch = ((((ch * 0x0f) << 6) | (s[1] & 0x3f)) << 6) | (s[2] & 0x3f); s += 2; } if (ch == 0xa0) { /* * Handle non-breaking space as-is... */ fputs(" ", out); } else fprintf(out, "&#x%x;", ch); } else putc(*s, out); s ++; } break; case OUTPUT_MAN : while (*s) { if (*s == '\\' || *s == '-') putc('\\', out); putc(*s++, out); } break; } } /* * 'write_toc()' - Write a table-of-contents. */ static void write_toc(FILE *out, /* I - Output file */ mxml_node_t *doc, /* I - Document */ const char *introfile, /* I - Introduction file */ const char *target, /* I - Target name */ int xml) /* I - Write XML nodes? */ { FILE *fp; /* Intro file */ mxml_node_t *function, /* Current function */ *scut, /* Struct/class/union/typedef */ *arg, /* Current argument */ *description; /* Description of function/var */ const char *name, /* Name of function/type */ *targetattr; /* Target attribute, if any */ int xmlid = 1; /* Current XML node ID */ /* * If target is set, it is the frame file that contains the body. * Otherwise, we are creating a single-file... */ if (target) targetattr = " target=\"body\""; else targetattr = ""; /* * The table-of-contents is a nested unordered list. Start by * reading any intro file to see if there are any headings there. */ if (!xml) fputs("

      Contents

      \n" "
        \n", out); if (introfile && (fp = fopen(introfile, "r")) != NULL) { char line[8192], /* Line from file */ *ptr, /* Pointer in line */ *end, /* End of line */ *anchor, /* Anchor name */ quote, /* Quote character for value */ level = '2', /* Current heading level */ newlevel; /* New heading level */ int inelement; /* In an element? */ while (fgets(line, sizeof(line), fp)) { /* * See if this line has a heading... */ if ((ptr = strstr(line, "') inelement = 0; *ptr++ = '\0'; } /* * Write text until we see ""... */ if (xml) { if (newlevel < level) fputs("\n" "\n", out); else if (newlevel > level && newlevel == '3') fputs("\n", out); else if (xmlid > 1) fputs("\n", out); level = newlevel; fprintf(out, "\n" "Documentation/index.html\n" "%s\n" "", xmlid ++, anchor); quote = 0; while (*ptr) { if (inelement) { if (*ptr == quote) quote = 0; else if (*ptr == '>') inelement = 0; else if (*ptr == '\'' || *ptr == '\"') quote = *ptr; } else if (*ptr == '<') { if (!strncmp(ptr, "", 4) || !strncmp(ptr, "", 4)) break; inelement = 1; } else putc(*ptr, out); ptr ++; } fputs("\n", out); } else { if (newlevel < level) fputs("\n" "
      \n", out); else if (newlevel > level) fputs("\n", out); } } fclose(fp); } /* * Next the classes... */ if ((scut = find_public(doc, doc, "class")) != NULL) { if (xml) fprintf(out, "\n" "Documentation/index.html\n" "CLASSES\n" "Classes\n" "\n", xmlid ++); else fprintf(out, "
    • Classes" "
        \n", target ? target : "", targetattr); while (scut) { name = mxmlElementGetAttr(scut, "name"); description = mxmlFindElement(scut, scut, "description", NULL, NULL, MXML_DESCEND_FIRST); if (xml) { fprintf(out, "\n" "Documentation/index.html\n" "%s\n" "%s\n" "\n", xmlid ++, name, name); } else { fprintf(out, "\t
      • %s
      • \n", name); } scut = find_public(scut, doc, "class"); } if (xml) fputs("\n", out); else fputs("
    • \n", out); } /* * Functions... */ if ((function = find_public(doc, doc, "function")) != NULL) { if (xml) fprintf(out, "\n" "Documentation/index.html\n" "FUNCTIONS\n" "Functions\n" "\n", xmlid ++); else fprintf(out, "
    • Functions" "
        \n", target ? target : "", targetattr); while (function) { name = mxmlElementGetAttr(function, "name"); description = mxmlFindElement(function, function, "description", NULL, NULL, MXML_DESCEND_FIRST); if (xml) { fprintf(out, "\n" "Documentation/index.html\n" "%s\n" "%s\n" "\n", xmlid ++, name, name); } else { fprintf(out, "\t
      • %s
      • \n", name); } function = find_public(function, doc, "function"); } if (xml) fputs("\n", out); else fputs("
    • \n", out); } /* * Data types... */ if ((scut = find_public(doc, doc, "typedef")) != NULL) { if (xml) fprintf(out, "\n" "Documentation/index.html\n" "TYPES\n" "Data Types\n" "\n", xmlid ++); else fprintf(out, "
    • Data Types" "
        \n", target ? target : "", targetattr); while (scut) { name = mxmlElementGetAttr(scut, "name"); description = mxmlFindElement(scut, scut, "description", NULL, NULL, MXML_DESCEND_FIRST); if (xml) { fprintf(out, "\n" "Documentation/index.html\n" "%s\n" "%s\n" "\n", xmlid ++, name, name); } else { fprintf(out, "\t
      • %s
      • \n", name); } scut = find_public(scut, doc, "typedef"); } if (xml) fputs("\n", out); else fputs("
    • \n", out); } /* * Structures... */ if ((scut = find_public(doc, doc, "struct")) != NULL) { if (xml) fprintf(out, "\n" "Documentation/index.html\n" "STRUCTURES\n" "Structures\n" "\n", xmlid ++); else fprintf(out, "
    • Structures" "
        \n", target ? target : "", targetattr); while (scut) { name = mxmlElementGetAttr(scut, "name"); description = mxmlFindElement(scut, scut, "description", NULL, NULL, MXML_DESCEND_FIRST); if (xml) { fprintf(out, "\n" "Documentation/index.html\n" "%s\n" "%s\n" "\n", xmlid ++, name, name); } else { fprintf(out, "\t
      • %s
      • \n", name); } scut = find_public(scut, doc, "struct"); } if (xml) fputs("\n", out); else fputs("
    • \n", out); } /* * Unions... */ if ((scut = find_public(doc, doc, "union")) != NULL) { if (xml) fprintf(out, "\n" "Documentation/index.html\n" "UNIONS\n" "Unions\n" "\n", xmlid ++); else fprintf(out, "
    • Unions
        \n", target ? target : "", targetattr); while (scut) { name = mxmlElementGetAttr(scut, "name"); description = mxmlFindElement(scut, scut, "description", NULL, NULL, MXML_DESCEND_FIRST); if (xml) { fprintf(out, "\n" "Documentation/index.html\n" "%s\n" "%s\n" "\n", xmlid ++, name, name); } else { fprintf(out, "\t
      • %s
      • \n", name); } scut = find_public(scut, doc, "union"); } if (xml) fputs("\n", out); else fputs("
    • \n", out); } /* * Globals variables... */ if ((arg = find_public(doc, doc, "variable")) != NULL) { if (xml) fprintf(out, "\n" "Documentation/index.html\n" "VARIABLES\n" "Variables\n" "\n", xmlid ++); else fprintf(out, "
    • Variables" "
        \n", target ? target : "", targetattr); while (arg) { name = mxmlElementGetAttr(arg, "name"); description = mxmlFindElement(arg, arg, "description", NULL, NULL, MXML_DESCEND_FIRST); if (xml) { fprintf(out, "\n" "Documentation/index.html\n" "%s\n" "%s\n" "\n", xmlid ++, name, name); } else { fprintf(out, "\t
      • %s
      • \n", name); } arg = find_public(arg, doc, "variable"); } if (xml) fputs("\n", out); else fputs("
    • \n", out); } /* * Enumerations/constants... */ if ((scut = find_public(doc, doc, "enumeration")) != NULL) { if (xml) fprintf(out, "\n" "Documentation/index.html\n" "ENUMERATIONS\n" "Constants\n" "\n", xmlid ++); else fprintf(out, "
    • Constants" "
        \n", target ? target : "", targetattr); while (scut) { name = mxmlElementGetAttr(scut, "name"); description = mxmlFindElement(scut, scut, "description", NULL, NULL, MXML_DESCEND_FIRST); if (xml) { fprintf(out, "\n" "Documentation/index.html\n" "%s\n" "%s\n" "\n", xmlid ++, name, name); } else { fprintf(out, "\t
      • %s
      • \n", name); } scut = find_public(scut, doc, "enumeration"); } if (xml) fputs("\n", out); else fputs("
    • \n", out); } /* * Close out the HTML table-of-contents list as needed... */ if (!xml) fputs("\n", out); } /* * 'write_tokens()' - Write nodes for all APIs. */ static void write_tokens(FILE *out, /* I - Output file */ mxml_node_t *doc, /* I - Document */ const char *path) /* I - Path to help file */ { mxml_node_t *function, /* Current function */ *scut, /* Struct/class/union/typedef */ *arg, /* Current argument */ *description, /* Description of function/var */ *type, /* Type node */ *node; /* Current child node */ const char *name, /* Name of function/type */ *cename, /* Current class/enum name */ *defval; /* Default value for argument */ char prefix; /* Prefix for declarations */ /* * Classes... */ if ((scut = find_public(doc, doc, "class")) != NULL) { while (scut) { cename = mxmlElementGetAttr(scut, "name"); description = mxmlFindElement(scut, scut, "description", NULL, NULL, MXML_DESCEND_FIRST); fprintf(out, "\n" "Documentation/%s\n" "%s\n" "//apple_ref/cpp/cl/%s\n" "", path, cename, cename); write_description(out, description, "", 1); fputs("\n" "\n", out); if ((function = find_public(scut, scut, "function")) != NULL) { while (function) { name = mxmlElementGetAttr(function, "name"); description = mxmlFindElement(function, function, "description", NULL, NULL, MXML_DESCEND_FIRST); fprintf(out, "\n" "Documentation/%s\n" "%s.%s\n" "//apple_ref/cpp/clm/%s/%s", path, cename, name, cename, name); arg = mxmlFindElement(function, function, "returnvalue", NULL, NULL, MXML_DESCEND_FIRST); if (arg && (type = mxmlFindElement(arg, arg, "type", NULL, NULL, MXML_DESCEND_FIRST)) != NULL) { for (node = type->child; node; node = node->next) fputs(node->value.text.string, out); } else if (strcmp(cename, name) && strcmp(cename, name + 1)) fputs("void", out); fputs("/", out); for (arg = mxmlFindElement(function, function, "argument", NULL, NULL, MXML_DESCEND_FIRST), prefix = '('; arg; arg = mxmlFindElement(arg, function, "argument", NULL, NULL, MXML_NO_DESCEND), prefix = ',') { type = mxmlFindElement(arg, arg, "type", NULL, NULL, MXML_DESCEND_FIRST); putc(prefix, out); for (node = type->child; node; node = node->next) fputs(node->value.text.string, out); fputs(mxmlElementGetAttr(arg, "name"), out); } if (prefix == '(') fputs("(void", out); fputs(")\n" "", out); write_description(out, description, "", 1); fputs("\n" "", out); arg = mxmlFindElement(function, function, "returnvalue", NULL, NULL, MXML_DESCEND_FIRST); if (arg) write_element(out, doc, mxmlFindElement(arg, arg, "type", NULL, NULL, MXML_DESCEND_FIRST), OUTPUT_XML); else if (strcmp(cename, name) && strcmp(cename, name + 1)) fputs("void ", out); fputs(name, out); for (arg = mxmlFindElement(function, function, "argument", NULL, NULL, MXML_DESCEND_FIRST), prefix = '('; arg; arg = mxmlFindElement(arg, function, "argument", NULL, NULL, MXML_NO_DESCEND), prefix = ',') { type = mxmlFindElement(arg, arg, "type", NULL, NULL, MXML_DESCEND_FIRST); putc(prefix, out); if (prefix == ',') putc(' ', out); if (type->child) write_element(out, doc, type, OUTPUT_XML); fputs(mxmlElementGetAttr(arg, "name"), out); if ((defval = mxmlElementGetAttr(arg, "default")) != NULL) fprintf(out, " %s", defval); } if (prefix == '(') fputs("(void);", out); else fputs(");", out); fputs("\n" "\n", out); function = find_public(function, doc, "function"); } } scut = find_public(scut, doc, "class"); } } /* * Functions... */ if ((function = find_public(doc, doc, "function")) != NULL) { while (function) { name = mxmlElementGetAttr(function, "name"); description = mxmlFindElement(function, function, "description", NULL, NULL, MXML_DESCEND_FIRST); fprintf(out, "\n" "Documentation/%s\n" "%s\n" "//apple_ref/c/func/%s\n" "", path, name, name); write_description(out, description, "", 1); fputs("\n" "", out); arg = mxmlFindElement(function, function, "returnvalue", NULL, NULL, MXML_DESCEND_FIRST); if (arg) write_element(out, doc, mxmlFindElement(arg, arg, "type", NULL, NULL, MXML_DESCEND_FIRST), OUTPUT_XML); else // if (strcmp(cname, name) && strcmp(cname, name + 1)) fputs("void ", out); fputs(name, out); for (arg = mxmlFindElement(function, function, "argument", NULL, NULL, MXML_DESCEND_FIRST), prefix = '('; arg; arg = mxmlFindElement(arg, function, "argument", NULL, NULL, MXML_NO_DESCEND), prefix = ',') { type = mxmlFindElement(arg, arg, "type", NULL, NULL, MXML_DESCEND_FIRST); putc(prefix, out); if (prefix == ',') putc(' ', out); if (type->child) write_element(out, doc, type, OUTPUT_XML); fputs(mxmlElementGetAttr(arg, "name"), out); if ((defval = mxmlElementGetAttr(arg, "default")) != NULL) fprintf(out, " %s", defval); } if (prefix == '(') fputs("(void);", out); else fputs(");", out); fputs("\n" "\n", out); function = find_public(function, doc, "function"); } } /* * Data types... */ if ((scut = find_public(doc, doc, "typedef")) != NULL) { while (scut) { name = mxmlElementGetAttr(scut, "name"); description = mxmlFindElement(scut, scut, "description", NULL, NULL, MXML_DESCEND_FIRST); fprintf(out, "\n" "Documentation/%s\n" "%s\n" "//apple_ref/c/tdef/%s\n" "", path, name, name); write_description(out, description, "", 1); fputs("\n" "\n", out); scut = find_public(scut, doc, "typedef"); } } /* * Structures... */ if ((scut = find_public(doc, doc, "struct")) != NULL) { while (scut) { name = mxmlElementGetAttr(scut, "name"); description = mxmlFindElement(scut, scut, "description", NULL, NULL, MXML_DESCEND_FIRST); fprintf(out, "\n" "Documentation/%s\n" "%s\n" "//apple_ref/c/tag/%s\n" "", path, name, name); write_description(out, description, "", 1); fputs("\n" "\n", out); scut = find_public(scut, doc, "struct"); } } /* * Unions... */ if ((scut = find_public(doc, doc, "union")) != NULL) { while (scut) { name = mxmlElementGetAttr(scut, "name"); description = mxmlFindElement(scut, scut, "description", NULL, NULL, MXML_DESCEND_FIRST); fprintf(out, "\n" "Documentation/%s\n" "%s\n" "//apple_ref/c/tag/%s\n" "", path, name, name); write_description(out, description, "", 1); fputs("\n" "\n", out); scut = find_public(scut, doc, "union"); } } /* * Globals variables... */ if ((arg = find_public(doc, doc, "variable")) != NULL) { while (arg) { name = mxmlElementGetAttr(arg, "name"); description = mxmlFindElement(arg, arg, "description", NULL, NULL, MXML_DESCEND_FIRST); fprintf(out, "\n" "Documentation/%s\n" "%s\n" "//apple_ref/c/data/%s\n" "", path, name, name); write_description(out, description, "", 1); fputs("\n" "\n", out); arg = find_public(arg, doc, "variable"); } } /* * Enumerations/constants... */ if ((scut = find_public(doc, doc, "enumeration")) != NULL) { while (scut) { cename = mxmlElementGetAttr(scut, "name"); description = mxmlFindElement(scut, scut, "description", NULL, NULL, MXML_DESCEND_FIRST); fprintf(out, "\n" "Documentation/%s\n" "%s\n" "//apple_ref/c/tag/%s\n" "", path, cename, cename); write_description(out, description, "", 1); fputs("\n" "\n", out); for (arg = mxmlFindElement(scut, scut, "constant", NULL, NULL, MXML_DESCEND_FIRST); arg; arg = mxmlFindElement(arg, scut, "constant", NULL, NULL, MXML_NO_DESCEND)) { name = mxmlElementGetAttr(arg, "name"); description = mxmlFindElement(arg, arg, "description", NULL, NULL, MXML_DESCEND_FIRST); fprintf(out, "\n" "Documentation/%s\n" "%s\n" "//apple_ref/c/econst/%s\n" "", path, cename, name); write_description(out, description, "", 1); fputs("\n" "\n", out); } scut = find_public(scut, doc, "enumeration"); } } } /* * 'ws_cb()' - Whitespace callback for saving. */ static const char * /* O - Whitespace string or NULL for none */ ws_cb(mxml_node_t *node, /* I - Element node */ int where) /* I - Where value */ { const char *name; /* Name of element */ int depth; /* Depth of node */ static const char *spaces = " "; /* Whitespace (40 spaces) for indent */ name = node->value.element.name; switch (where) { case MXML_WS_BEFORE_CLOSE : if (strcmp(name, "argument") && strcmp(name, "class") && strcmp(name, "constant") && strcmp(name, "enumeration") && strcmp(name, "function") && strcmp(name, "mxmldoc") && strcmp(name, "namespace") && strcmp(name, "returnvalue") && strcmp(name, "struct") && strcmp(name, "typedef") && strcmp(name, "union") && strcmp(name, "variable")) return (NULL); for (depth = -4; node; node = node->parent, depth += 2); if (depth > 40) return (spaces); else if (depth < 2) return (NULL); else return (spaces + 40 - depth); case MXML_WS_AFTER_CLOSE : return ("\n"); case MXML_WS_BEFORE_OPEN : for (depth = -4; node; node = node->parent, depth += 2); if (depth > 40) return (spaces); else if (depth < 2) return (NULL); else return (spaces + 40 - depth); default : case MXML_WS_AFTER_OPEN : if (strcmp(name, "argument") && strcmp(name, "class") && strcmp(name, "constant") && strcmp(name, "enumeration") && strcmp(name, "function") && strcmp(name, "mxmldoc") && strcmp(name, "namespace") && strcmp(name, "returnvalue") && strcmp(name, "struct") && strcmp(name, "typedef") && strcmp(name, "union") && strcmp(name, "variable") && strncmp(name, "?xml", 4)) return (NULL); else return ("\n"); } } /* * End of "$Id: mxmldoc.c 440 2011-08-11 18:51:26Z mike $". */ cmtk-3.3.1/Utilities/mxml/test.xml000066400000000000000000000015431276303427400171200ustar00rootroot00000000000000 123 Now is the time for all good men to come to the aid of their country. cmtk-3.3.1/Utilities/mxml/test/000077500000000000000000000000001276303427400163735ustar00rootroot00000000000000cmtk-3.3.1/Utilities/mxml/test/class.cxx000066400000000000000000000025441276303427400202310ustar00rootroot00000000000000class foo_c : public bar_c // Foo class derived from bar { float foo; /* Real number */ int bar; /* Integer */ public: foo_c(float f, int b); ~foo_c(); // 'get_bar()' - Get the value of bar. int // O - Value of bar get_bar() { return (bar); } // 'get_foo()' - Get the value of foo. float // O - Value of foo get_foo() { return (foo); } // 'set_bar()' - Set the value of bar. void set_bar(int b) // I - Value of bar { bar = b; } // 'set_foo()' - Set the value of foo. void set_foo(float f) // I - Value of foo { foo = f; } // 'set_foobar()' - Set foo and optionally bar (should show default args). void set_foobar(float f, // I - Value of foo int b = 0) // I - Value of bar { foo = f; bar = b; } protected: static int global; /* Global integer */ // 'get_global()' - Get the global integer. int // O - Integer get_global() { return (global); } private: int barfoo; // Another private integer public: // 'get_barfoo()' - Get the barfoo value. int // O - Barfoo value get_barfoo() { return (barfoo); } } // 'foo_c::foo_c()' - Create a foo_c class. foo_c::foo_c(float f, // I - Value of foo int b) // I - Value of bar { foo = f; bar = b; } // 'foo_c::~foo_c()' - Destroy a foo_c class. foo_c::~foo_c() { } cmtk-3.3.1/Utilities/mxml/test/dotest.sh000077500000000000000000000015421276303427400202360ustar00rootroot00000000000000#!/bin/sh (cd ..; make mxmldoc-static) files="" mode="" while test $# -gt 0; do arg="$1" shift case "$arg" in -f) framed="--framed framed" ;; -g) mode="gdb" ;; -v) mode="valgrind" ;; *.h | *.c | *.cxx) files="$files $arg" ;; *) echo "Usage: ./dotest.sh [-f] [-g] [-v] [files]" exit 1 ;; esac done if test "$files" = ""; then files=*.cxx fi rm -f test.xml case "$mode" in gdb) echo "break malloc_error_break" >.gdbcmds echo "set env DYLD_INSERT_LIBRARIES /usr/lib/libgmalloc.dylib" >>.gdbcmds echo "run $framed test.xml $files >test.html 2>test.log" >>.gdbcmds gdb -x .gdbcmds ../mxmldoc-static ;; valgrind) valgrind --log-fd=3 --leak-check=yes \ ../mxmldoc-static $framed test.xml $files \ >test.html 2>test.log 3>test.valgrind ;; *) ../mxmldoc-static $framed test.xml $files >test.html 2>test.log ;; esac cmtk-3.3.1/Utilities/mxml/test/enum.cxx000066400000000000000000000007611276303427400200670ustar00rootroot00000000000000typedef enum foo_enum_e /* Sample enumeration type */ { FOO_ONE, /* One fish */ FOO_TWO, /* Two fish */ FOO_RED, /* Red fish */ FOO_BLUE, /* Blue fish */ FOO_PRIVATE /* Private fish @private@ */ } foo_enum_t; typedef enum foo_enum2_e /* Sample enumeration type #2 */ { FOO2_ONE, /* One fish #2 */ FOO2_TWO, /* Two fish #2 */ FOO2_RED, /* Red fish #2 */ FOO2_BLUE, /* Blue fish #2 */ FOO2_PRIVATE /* Private fish #2 @private@ */ } foo_enum2_t; cmtk-3.3.1/Utilities/mxml/test/function.cxx000066400000000000000000000031351276303427400207460ustar00rootroot00000000000000/* * 'foo_void_function()' - Do foo with bar. * * Use the @link foo_float_function@ or @link foo_int_function@ functions * instead. Pass @code NULL@ for "three" then there is no string to print. * * @deprecated@ */ void foo_void_function(int one, /* I - Integer */ float *two, /* O - Real number */ const char *three) /* I - String */ { if (one) { puts("Hello, World!"); } else puts(three); *two = 2.0f; } /* * 'foo_float_function()' - Do foo with bar. * * @since 1.2@ */ float /* O - Real number */ foo_float_function(int one, /* I - Integer */ const char *two) /* I - String */ { if (one) { puts("Hello, World!"); } else puts(two); return (2.0f); } /* * 'foo_default_string()' - Do something with a defaulted string arg. */ int /* O - Integer value */ foo_default_string(int one, /* I - Integer */ const char *two = "2") /* I - String */ { if (one) { puts("Hello, World!"); } else puts(two); return (2); } /* * 'foo_default_int()' - Do something with a defaulted int arg. */ int /* O - Integer value */ foo_default_int(int one, /* I - Integer */ int two = 2) /* I - Integer */ { if (one) { puts("Hello, World!"); } else puts(two); return (2); } /* * 'foo_void_func()' - Function taking no arguments. */ void foo_void_func(void) { puts("foo_void_func()"); } /* * 'foo_private_func()' - Private function. * * @private@ */ void foo_private_func(void) { puts("foo_private_func()"); } cmtk-3.3.1/Utilities/mxml/test/functype.cxx000066400000000000000000000001151276303427400207510ustar00rootroot00000000000000typedef int (*foo_func_t)(void *foo, int bar); /**** Foo function type ****/ cmtk-3.3.1/Utilities/mxml/test/struct.cxx000066400000000000000000000016621276303427400204500ustar00rootroot00000000000000typedef struct foo_s /* Foo structure */ { float foo; /* Real number */ int bar; /* Integer */ foo_s(float f, int b); ~foo_s(); // 'get_bar()' - Get the value of bar. int // O - Value of bar get_bar() { return (bar); } // 'get_foo()' - Get the value of foo. float // O - Value of foo get_foo() { return (foo); } // 'set_bar()' - Set the value of bar. void set_bar(int b) // I - Value of bar { bar = b; } // 'set_foo()' - Set the value of foo. void set_foo(float f) // I - Value of foo { foo = f; } } foo_t; // 'foo_s::foo_s()' - Create a foo_s structure. foo_s::foo_s(float f, // I - Value of foo int b) // I - Value of bar { foo = f; bar = b; } // 'foo_s::~foo_s()' - Destroy a foo_s structure. foo_s::~foo_s() { } typedef struct foo_private_s /* @private@ */ { int a; /* Value of "a" */ char b[255]; /* Value of "b" */ } foo_private_t; cmtk-3.3.1/Utilities/mxml/test/type.cxx000066400000000000000000000001501276303427400200740ustar00rootroot00000000000000typedef int foo_simple_t; /* Simple integer type */ typedef int foo_simple_private_t; /* @private@ */ cmtk-3.3.1/Utilities/mxml/testmxml.c000066400000000000000000000432631276303427400174450ustar00rootroot00000000000000/* * "$Id: testmxml.c 439 2011-04-13 15:43:32Z mike $" * * Test program for Mini-XML, a small XML-like file parsing library. * * Copyright 2003-2011 by Michael R Sweet. * * These coded instructions, statements, and computer programs are the * property of Michael R Sweet and are protected by Federal copyright * law. Distribution and use rights are outlined in the file "COPYING" * which should have been included with this file. If this file is * missing or damaged, see the license at: * * http://www.minixml.org/ * * Contents: * * main() - Main entry for test program. * sax_cb() - SAX callback. * type_cb() - XML data type callback for mxmlLoadFile()... * whitespace_cb() - Let the mxmlSaveFile() function know when to insert * newlines and tabs... */ /* * Include necessary headers... */ #include "config.h" #include "mxml.h" #ifndef WIN32 # include #endif /* !WIN32 */ #include #ifndef O_BINARY # define O_BINARY 0 #endif /* !O_BINARY */ /* * Globals... */ int event_counts[6]; /* * Local functions... */ void sax_cb(mxml_node_t *node, mxml_sax_event_t event, void *data); mxml_type_t type_cb(mxml_node_t *node); const char *whitespace_cb(mxml_node_t *node, int where); /* * 'main()' - Main entry for test program. */ int /* O - Exit status */ main(int argc, /* I - Number of command-line args */ char *argv[]) /* I - Command-line args */ { int i; /* Looping var */ FILE *fp; /* File to read */ int fd; /* File descriptor */ mxml_node_t *tree, /* XML tree */ *node; /* Node which should be in test.xml */ mxml_index_t *ind; /* XML index */ char buffer[16384]; /* Save string */ static const char *types[] = /* Strings for node types */ { "MXML_ELEMENT", "MXML_INTEGER", "MXML_OPAQUE", "MXML_REAL", "MXML_TEXT" }; /* * Check arguments... */ if (argc != 2) { fputs("Usage: testmxml filename.xml\n", stderr); return (1); } /* * Test the basic functionality... */ tree = mxmlNewElement(MXML_NO_PARENT, "element"); if (!tree) { fputs("ERROR: No parent node in basic test!\n", stderr); return (1); } if (tree->type != MXML_ELEMENT) { fprintf(stderr, "ERROR: Parent has type %s (%d), expected MXML_ELEMENT!\n", tree->type < MXML_ELEMENT || tree->type > MXML_TEXT ? "UNKNOWN" : types[tree->type], tree->type); mxmlDelete(tree); return (1); } if (strcmp(tree->value.element.name, "element")) { fprintf(stderr, "ERROR: Parent value is \"%s\", expected \"element\"!\n", tree->value.element.name); mxmlDelete(tree); return (1); } mxmlNewInteger(tree, 123); mxmlNewOpaque(tree, "opaque"); mxmlNewReal(tree, 123.4f); mxmlNewText(tree, 1, "text"); mxmlLoadString(tree, "string string string", MXML_NO_CALLBACK); mxmlLoadString(tree, "1 2 3", MXML_INTEGER_CALLBACK); mxmlLoadString(tree, "1.0 2.0 3.0", MXML_REAL_CALLBACK); mxmlLoadString(tree, "opaque opaque opaque", MXML_OPAQUE_CALLBACK); mxmlLoadString(tree, "valuevalue2" "", MXML_OPAQUE_CALLBACK); node = tree->child; if (!node) { fputs("ERROR: No first child node in basic test!\n", stderr); mxmlDelete(tree); return (1); } if (node->type != MXML_INTEGER) { fprintf(stderr, "ERROR: First child has type %s (%d), expected MXML_INTEGER!\n", node->type < MXML_ELEMENT || node->type > MXML_TEXT ? "UNKNOWN" : types[node->type], node->type); mxmlDelete(tree); return (1); } if (node->value.integer != 123) { fprintf(stderr, "ERROR: First child value is %d, expected 123!\n", node->value.integer); mxmlDelete(tree); return (1); } node = node->next; if (!node) { fputs("ERROR: No second child node in basic test!\n", stderr); mxmlDelete(tree); return (1); } if (node->type != MXML_OPAQUE) { fprintf(stderr, "ERROR: Second child has type %s (%d), expected MXML_OPAQUE!\n", node->type < MXML_ELEMENT || node->type > MXML_TEXT ? "UNKNOWN" : types[node->type], node->type); mxmlDelete(tree); return (1); } if (!node->value.opaque || strcmp(node->value.opaque, "opaque")) { fprintf(stderr, "ERROR: Second child value is \"%s\", expected \"opaque\"!\n", node->value.opaque ? node->value.opaque : "(null)"); mxmlDelete(tree); return (1); } node = node->next; if (!node) { fputs("ERROR: No third child node in basic test!\n", stderr); mxmlDelete(tree); return (1); } if (node->type != MXML_REAL) { fprintf(stderr, "ERROR: Third child has type %s (%d), expected MXML_REAL!\n", node->type < MXML_ELEMENT || node->type > MXML_TEXT ? "UNKNOWN" : types[node->type], node->type); mxmlDelete(tree); return (1); } if (node->value.real != 123.4f) { fprintf(stderr, "ERROR: Third child value is %f, expected 123.4!\n", node->value.real); mxmlDelete(tree); return (1); } node = node->next; if (!node) { fputs("ERROR: No fourth child node in basic test!\n", stderr); mxmlDelete(tree); return (1); } if (node->type != MXML_TEXT) { fprintf(stderr, "ERROR: Fourth child has type %s (%d), expected MXML_TEXT!\n", node->type < MXML_ELEMENT || node->type > MXML_TEXT ? "UNKNOWN" : types[node->type], node->type); mxmlDelete(tree); return (1); } if (!node->value.text.whitespace || !node->value.text.string || strcmp(node->value.text.string, "text")) { fprintf(stderr, "ERROR: Fourth child value is %d,\"%s\", expected 1,\"text\"!\n", node->value.text.whitespace, node->value.text.string ? node->value.text.string : "(null)"); mxmlDelete(tree); return (1); } for (i = 0; i < 4; i ++) { node = node->next; if (!node) { fprintf(stderr, "ERROR: No group #%d child node in basic test!\n", i + 1); mxmlDelete(tree); return (1); } if (node->type != MXML_ELEMENT) { fprintf(stderr, "ERROR: Group child #%d has type %s (%d), expected MXML_ELEMENT!\n", i + 1, node->type < MXML_ELEMENT || node->type > MXML_TEXT ? "UNKNOWN" : types[node->type], node->type); mxmlDelete(tree); return (1); } } /* * Test mxmlFindPath... */ node = mxmlFindPath(tree, "*/two"); if (!node) { fputs("ERROR: Unable to find value for \"*/two\".\n", stderr); mxmlDelete(tree); return (1); } else if (node->type != MXML_OPAQUE || strcmp(node->value.opaque, "value")) { fputs("ERROR: Bad value for \"*/two\".\n", stderr); mxmlDelete(tree); return (1); } node = mxmlFindPath(tree, "foo/*/two"); if (!node) { fputs("ERROR: Unable to find value for \"foo/*/two\".\n", stderr); mxmlDelete(tree); return (1); } else if (node->type != MXML_OPAQUE || strcmp(node->value.opaque, "value")) { fputs("ERROR: Bad value for \"foo/*/two\".\n", stderr); mxmlDelete(tree); return (1); } node = mxmlFindPath(tree, "foo/bar/one/two"); if (!node) { fputs("ERROR: Unable to find value for \"foo/bar/one/two\".\n", stderr); mxmlDelete(tree); return (1); } else if (node->type != MXML_OPAQUE || strcmp(node->value.opaque, "value")) { fputs("ERROR: Bad value for \"foo/bar/one/two\".\n", stderr); mxmlDelete(tree); return (1); } /* * Test indices... */ ind = mxmlIndexNew(tree, NULL, NULL); if (!ind) { fputs("ERROR: Unable to create index of all nodes!\n", stderr); mxmlDelete(tree); return (1); } if (ind->num_nodes != 10) { fprintf(stderr, "ERROR: Index of all nodes contains %d " "nodes; expected 10!\n", ind->num_nodes); mxmlIndexDelete(ind); mxmlDelete(tree); return (1); } mxmlIndexReset(ind); if (!mxmlIndexFind(ind, "group", NULL)) { fputs("ERROR: mxmlIndexFind for \"group\" failed!\n", stderr); mxmlIndexDelete(ind); mxmlDelete(tree); return (1); } mxmlIndexDelete(ind); ind = mxmlIndexNew(tree, "group", NULL); if (!ind) { fputs("ERROR: Unable to create index of groups!\n", stderr); mxmlDelete(tree); return (1); } if (ind->num_nodes != 4) { fprintf(stderr, "ERROR: Index of groups contains %d " "nodes; expected 4!\n", ind->num_nodes); mxmlIndexDelete(ind); mxmlDelete(tree); return (1); } mxmlIndexReset(ind); if (!mxmlIndexEnum(ind)) { fputs("ERROR: mxmlIndexEnum failed!\n", stderr); mxmlIndexDelete(ind); mxmlDelete(tree); return (1); } mxmlIndexDelete(ind); ind = mxmlIndexNew(tree, NULL, "type"); if (!ind) { fputs("ERROR: Unable to create index of type attributes!\n", stderr); mxmlDelete(tree); return (1); } if (ind->num_nodes != 3) { fprintf(stderr, "ERROR: Index of type attributes contains %d " "nodes; expected 3!\n", ind->num_nodes); mxmlIndexDelete(ind); mxmlDelete(tree); return (1); } mxmlIndexReset(ind); if (!mxmlIndexFind(ind, NULL, "string")) { fputs("ERROR: mxmlIndexFind for \"string\" failed!\n", stderr); mxmlIndexDelete(ind); mxmlDelete(tree); return (1); } mxmlIndexDelete(ind); ind = mxmlIndexNew(tree, "group", "type"); if (!ind) { fputs("ERROR: Unable to create index of elements and attributes!\n", stderr); mxmlDelete(tree); return (1); } if (ind->num_nodes != 3) { fprintf(stderr, "ERROR: Index of elements and attributes contains %d " "nodes; expected 3!\n", ind->num_nodes); mxmlIndexDelete(ind); mxmlDelete(tree); return (1); } mxmlIndexReset(ind); if (!mxmlIndexFind(ind, "group", "string")) { fputs("ERROR: mxmlIndexFind for \"string\" failed!\n", stderr); mxmlIndexDelete(ind); mxmlDelete(tree); return (1); } mxmlIndexDelete(ind); /* * Check the mxmlDelete() works properly... */ for (i = 0; i < 9; i ++) { if (tree->child) mxmlDelete(tree->child); else { fprintf(stderr, "ERROR: Child pointer prematurely NULL on child #%d\n", i + 1); mxmlDelete(tree); return (1); } } if (tree->child) { fputs("ERROR: Child pointer not NULL after deleting all children!\n", stderr); return (1); } if (tree->last_child) { fputs("ERROR: Last child pointer not NULL after deleting all children!\n", stderr); return (1); } mxmlDelete(tree); /* * Open the file... */ if (argv[1][0] == '<') tree = mxmlLoadString(NULL, argv[1], type_cb); else if ((fp = fopen(argv[1], "rb")) == NULL) { perror(argv[1]); return (1); } else { /* * Read the file... */ tree = mxmlLoadFile(NULL, fp, type_cb); fclose(fp); } if (!tree) { fputs("Unable to read XML file!\n", stderr); return (1); } if (!strcmp(argv[1], "test.xml")) { /* * Verify that mxmlFindElement() and indirectly mxmlWalkNext() work * properly... */ if ((node = mxmlFindElement(tree, tree, "choice", NULL, NULL, MXML_DESCEND)) == NULL) { fputs("Unable to find first element in XML tree!\n", stderr); mxmlDelete(tree); return (1); } if (!mxmlFindElement(node, tree, "choice", NULL, NULL, MXML_NO_DESCEND)) { fputs("Unable to find second element in XML tree!\n", stderr); mxmlDelete(tree); return (1); } } /* * Print the XML tree... */ mxmlSaveFile(tree, stdout, whitespace_cb); /* * Save the XML tree to a string and print it... */ if (mxmlSaveString(tree, buffer, sizeof(buffer), whitespace_cb) > 0) fputs(buffer, stderr); /* * Delete the tree... */ mxmlDelete(tree); /* * Read from/write to file descriptors... */ if (argv[1][0] != '<') { /* * Open the file again... */ if ((fd = open(argv[1], O_RDONLY | O_BINARY)) < 0) { perror(argv[1]); return (1); } /* * Read the file... */ tree = mxmlLoadFd(NULL, fd, type_cb); close(fd); /* * Create filename.xmlfd... */ snprintf(buffer, sizeof(buffer), "%sfd", argv[1]); if ((fd = open(buffer, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0666)) < 0) { perror(buffer); mxmlDelete(tree); return (1); } /* * Write the file... */ mxmlSaveFd(tree, fd, whitespace_cb); close(fd); /* * Delete the tree... */ mxmlDelete(tree); } /* * Test SAX methods... */ memset(event_counts, 0, sizeof(event_counts)); if (argv[1][0] == '<') mxmlSAXLoadString(NULL, argv[1], type_cb, sax_cb, NULL); else if ((fp = fopen(argv[1], "rb")) == NULL) { perror(argv[1]); return (1); } else { /* * Read the file... */ mxmlSAXLoadFile(NULL, fp, type_cb, sax_cb, NULL); fclose(fp); } if (!strcmp(argv[1], "test.xml")) { if (event_counts[MXML_SAX_CDATA] != 1) { fprintf(stderr, "MXML_SAX_CDATA seen %d times, expected 1 times!\n", event_counts[MXML_SAX_CDATA]); return (1); } if (event_counts[MXML_SAX_COMMENT] != 1) { fprintf(stderr, "MXML_SAX_COMMENT seen %d times, expected 1 times!\n", event_counts[MXML_SAX_COMMENT]); return (1); } if (event_counts[MXML_SAX_DATA] != 60) { fprintf(stderr, "MXML_SAX_DATA seen %d times, expected 60 times!\n", event_counts[MXML_SAX_DATA]); return (1); } if (event_counts[MXML_SAX_DIRECTIVE] != 1) { fprintf(stderr, "MXML_SAX_DIRECTIVE seen %d times, expected 1 times!\n", event_counts[MXML_SAX_DIRECTIVE]); return (1); } if (event_counts[MXML_SAX_ELEMENT_CLOSE] != 20) { fprintf(stderr, "MXML_SAX_ELEMENT_CLOSE seen %d times, expected 20 times!\n", event_counts[MXML_SAX_ELEMENT_CLOSE]); return (1); } if (event_counts[MXML_SAX_ELEMENT_OPEN] != 20) { fprintf(stderr, "MXML_SAX_ELEMENT_OPEN seen %d times, expected 20 times!\n", event_counts[MXML_SAX_ELEMENT_OPEN]); return (1); } } /* * Return... */ return (0); } /* * 'sax_cb()' - Process nodes via SAX. */ void sax_cb(mxml_node_t *node, /* I - Current node */ mxml_sax_event_t event, /* I - SAX event */ void *data) /* I - SAX user data */ { /* * This SAX callback just counts the different events. */ event_counts[event] ++; } /* * 'type_cb()' - XML data type callback for mxmlLoadFile()... */ mxml_type_t /* O - Data type */ type_cb(mxml_node_t *node) /* I - Element node */ { const char *type; /* Type string */ /* * You can lookup attributes and/or use the element name, hierarchy, etc... */ if ((type = mxmlElementGetAttr(node, "type")) == NULL) type = node->value.element.name; if (!strcmp(type, "integer")) return (MXML_INTEGER); else if (!strcmp(type, "opaque") || !strcmp(type, "pre")) return (MXML_OPAQUE); else if (!strcmp(type, "real")) return (MXML_REAL); else return (MXML_TEXT); } /* * 'whitespace_cb()' - Let the mxmlSaveFile() function know when to insert * newlines and tabs... */ const char * /* O - Whitespace string or NULL */ whitespace_cb(mxml_node_t *node, /* I - Element node */ int where) /* I - Open or close tag? */ { mxml_node_t *parent; /* Parent node */ int level; /* Indentation level */ const char *name; /* Name of element */ static const char *tabs = "\t\t\t\t\t\t\t\t"; /* Tabs for indentation */ /* * We can conditionally break to a new line before or after any element. * These are just common HTML elements... */ name = node->value.element.name; if (!strcmp(name, "html") || !strcmp(name, "head") || !strcmp(name, "body") || !strcmp(name, "pre") || !strcmp(name, "p") || !strcmp(name, "h1") || !strcmp(name, "h2") || !strcmp(name, "h3") || !strcmp(name, "h4") || !strcmp(name, "h5") || !strcmp(name, "h6")) { /* * Newlines before open and after close... */ if (where == MXML_WS_BEFORE_OPEN || where == MXML_WS_AFTER_CLOSE) return ("\n"); } else if (!strcmp(name, "dl") || !strcmp(name, "ol") || !strcmp(name, "ul")) { /* * Put a newline before and after list elements... */ return ("\n"); } else if (!strcmp(name, "dd") || !strcmp(name, "dt") || !strcmp(name, "li")) { /* * Put a tab before
    • 's,
      's, and
      's, and a newline after them... */ if (where == MXML_WS_BEFORE_OPEN) return ("\t"); else if (where == MXML_WS_AFTER_CLOSE) return ("\n"); } else if (!strncmp(name, "?xml", 4)) { if (where == MXML_WS_AFTER_OPEN) return ("\n"); else return (NULL); } else if (where == MXML_WS_BEFORE_OPEN || ((!strcmp(name, "choice") || !strcmp(name, "option")) && where == MXML_WS_BEFORE_CLOSE)) { for (level = -1, parent = node->parent; parent; level ++, parent = parent->parent); if (level > 8) level = 8; else if (level < 0) level = 0; return (tabs + 8 - level); } else if (where == MXML_WS_AFTER_CLOSE || ((!strcmp(name, "group") || !strcmp(name, "option") || !strcmp(name, "choice")) && where == MXML_WS_AFTER_OPEN)) return ("\n"); else if (where == MXML_WS_AFTER_OPEN && !node->child) return ("\n"); /* * Return NULL for no added whitespace... */ return (NULL); } /* * End of "$Id: testmxml.c 439 2011-04-13 15:43:32Z mike $". */ cmtk-3.3.1/Utilities/mxml/vcnet/000077500000000000000000000000001276303427400165335ustar00rootroot00000000000000cmtk-3.3.1/Utilities/mxml/vcnet/config.h000066400000000000000000000054611276303427400201570ustar00rootroot00000000000000/* * "$Id: config.h 408 2010-09-19 05:26:46Z mike $" * * Configuration file for Mini-XML, a small XML-like file parsing library. * * Copyright 2003-2010 by Michael R Sweet. * * These coded instructions, statements, and computer programs are the * property of Michael R Sweet and are protected by Federal copyright * law. Distribution and use rights are outlined in the file "COPYING" * which should have been included with this file. If this file is * missing or damaged, see the license at: * * http://www.minixml.org/ */ /* * Beginning with VC2005, Microsoft breaks ISO C and POSIX conformance * by deprecating a number of functions in the name of security, even * when many of the affected functions are otherwise completely secure. * The _CRT_SECURE_NO_DEPRECATE definition ensures that we won't get * warnings from their use... * * Then Microsoft decided that they should ignore this in VC2008 and use * yet another define (_CRT_SECURE_NO_WARNINGS) instead. Bastards. */ #define _CRT_SECURE_NO_DEPRECATE #define _CRT_SECURE_NO_WARNINGS /* * Include necessary headers... */ #include #include #include #include #include #include /* * Microsoft also renames the POSIX functions to _name, and introduces * a broken compatibility layer using the original names. As a result, * random crashes can occur when, for example, strdup() allocates memory * from a different heap than used by malloc() and free(). * * To avoid moronic problems like this, we #define the POSIX function * names to the corresponding non-standard Microsoft names. */ #define close _close #define open _open #define read _read #define snprintf _snprintf #define strdup _strdup #define vsnprintf _vsnprintf #define write _write /* * Version number... */ #define MXML_VERSION "Mini-XML v2.7" /* * Inline function support... */ #define inline _inline /* * Long long support... */ #define HAVE_LONG_LONG 1 /* * Do we have the snprintf() and vsnprintf() functions? */ #define HAVE_SNPRINTF 1 #define HAVE_VSNPRINTF 1 /* * Do we have the strXXX() functions? */ #define HAVE_STRDUP 1 /* * Define prototypes for string functions as needed... */ # ifndef HAVE_STRDUP extern char *_mxml_strdup(const char *); # define strdup _mxml_strdup # endif /* !HAVE_STRDUP */ extern char *_mxml_strdupf(const char *, ...); extern char *_mxml_vstrdupf(const char *, va_list); # ifndef HAVE_SNPRINTF extern int _mxml_snprintf(char *, size_t, const char *, ...); # define snprintf _mxml_snprintf # endif /* !HAVE_SNPRINTF */ # ifndef HAVE_VSNPRINTF extern int _mxml_vsnprintf(char *, size_t, const char *, va_list); # define vsnprintf _mxml_vsnprintf # endif /* !HAVE_VSNPRINTF */ /* * End of "$Id: config.h 408 2010-09-19 05:26:46Z mike $". */ cmtk-3.3.1/Utilities/mxml/vcnet/mxml.sln000066400000000000000000000056531276303427400202370ustar00rootroot00000000000000Microsoft Visual Studio Solution File, Format Version 10.00 # Visual Studio 2008 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "mxmldoc", "mxmldoc.vcproj", "{D909892E-520A-4322-9A47-DAEBDA9CC7A7}" ProjectSection(ProjectDependencies) = postProject {E5AA9476-9751-4654-8109-B1A2112D5E73} = {E5AA9476-9751-4654-8109-B1A2112D5E73} EndProjectSection EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "mxml1", "mxml1.vcproj", "{E5AA9476-9751-4654-8109-B1A2112D5E73}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "testmxml", "testmxml.vcproj", "{75CAC6C4-A6BC-4935-A3C9-8F0AE0744227}" ProjectSection(ProjectDependencies) = postProject {E5AA9476-9751-4654-8109-B1A2112D5E73} = {E5AA9476-9751-4654-8109-B1A2112D5E73} EndProjectSection EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Win32 = Debug|Win32 Debug|x64 = Debug|x64 Release|Win32 = Release|Win32 Release|x64 = Release|x64 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {D909892E-520A-4322-9A47-DAEBDA9CC7A7}.Debug|Win32.ActiveCfg = Debug|Win32 {D909892E-520A-4322-9A47-DAEBDA9CC7A7}.Debug|Win32.Build.0 = Debug|Win32 {D909892E-520A-4322-9A47-DAEBDA9CC7A7}.Debug|x64.ActiveCfg = Debug|x64 {D909892E-520A-4322-9A47-DAEBDA9CC7A7}.Debug|x64.Build.0 = Debug|x64 {D909892E-520A-4322-9A47-DAEBDA9CC7A7}.Release|Win32.ActiveCfg = Release|Win32 {D909892E-520A-4322-9A47-DAEBDA9CC7A7}.Release|Win32.Build.0 = Release|Win32 {D909892E-520A-4322-9A47-DAEBDA9CC7A7}.Release|x64.ActiveCfg = Release|x64 {D909892E-520A-4322-9A47-DAEBDA9CC7A7}.Release|x64.Build.0 = Release|x64 {E5AA9476-9751-4654-8109-B1A2112D5E73}.Debug|Win32.ActiveCfg = Debug|Win32 {E5AA9476-9751-4654-8109-B1A2112D5E73}.Debug|Win32.Build.0 = Debug|Win32 {E5AA9476-9751-4654-8109-B1A2112D5E73}.Debug|x64.ActiveCfg = Debug|x64 {E5AA9476-9751-4654-8109-B1A2112D5E73}.Debug|x64.Build.0 = Debug|x64 {E5AA9476-9751-4654-8109-B1A2112D5E73}.Release|Win32.ActiveCfg = Release|Win32 {E5AA9476-9751-4654-8109-B1A2112D5E73}.Release|Win32.Build.0 = Release|Win32 {E5AA9476-9751-4654-8109-B1A2112D5E73}.Release|x64.ActiveCfg = Release|x64 {E5AA9476-9751-4654-8109-B1A2112D5E73}.Release|x64.Build.0 = Release|x64 {75CAC6C4-A6BC-4935-A3C9-8F0AE0744227}.Debug|Win32.ActiveCfg = Debug|Win32 {75CAC6C4-A6BC-4935-A3C9-8F0AE0744227}.Debug|Win32.Build.0 = Debug|Win32 {75CAC6C4-A6BC-4935-A3C9-8F0AE0744227}.Debug|x64.ActiveCfg = Debug|x64 {75CAC6C4-A6BC-4935-A3C9-8F0AE0744227}.Debug|x64.Build.0 = Debug|x64 {75CAC6C4-A6BC-4935-A3C9-8F0AE0744227}.Release|Win32.ActiveCfg = Release|Win32 {75CAC6C4-A6BC-4935-A3C9-8F0AE0744227}.Release|Win32.Build.0 = Release|Win32 {75CAC6C4-A6BC-4935-A3C9-8F0AE0744227}.Release|x64.ActiveCfg = Release|x64 {75CAC6C4-A6BC-4935-A3C9-8F0AE0744227}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection EndGlobal cmtk-3.3.1/Utilities/mxml/vcnet/mxml1.def000066400000000000000000000024261276303427400202550ustar00rootroot00000000000000LIBRARY "MXML1" EXPORTS _mxml_strdupf _mxml_vstrdupf mxml_ignore_cb mxml_integer_cb mxml_opaque_cb mxml_real_cb mxmlAdd mxmlDelete mxmlElementDeleteAttr mxmlElementGetAttr mxmlElementSetAttr mxmlElementSetAttrf mxmlEntityAddCallback mxmlEntityGetName mxmlEntityGetValue mxmlEntityRemoveCallback mxmlFindElement mxmlFindPath mxmlGetCDATA mxmlGetCustom mxmlGetElement mxmlGetFirstChild mxmlGetInteger mxmlGetLastChild mxmlGetNextSibling mxmlGetOpaque mxmlGetParent mxmlGetPrevSibling mxmlGetReal mxmlGetRefCount mxmlGetText mxmlGetType mxmlGetUserData mxmlIndexDelete mxmlIndexEnum mxmlIndexFind mxmlIndexGetCount mxmlIndexNew mxmlIndexReset mxmlLoadFd mxmlLoadFile mxmlLoadString mxmlNewCDATA mxmlNewCustom mxmlNewElement mxmlNewInteger mxmlNewOpaque mxmlNewReal mxmlNewText mxmlNewTextf mxmlNewXML mxmlRelease mxmlRemove mxmlRetain mxmlSaveAllocString mxmlSaveFd mxmlSaveFile mxmlSaveString mxmlSAXLoadFd mxmlSAXLoadFile mxmlSAXLoadString mxmlSetCDATA mxmlSetCustom mxmlSetCustomHandlers mxmlSetElement mxmlSetErrorCallback mxmlSetInteger mxmlSetOpaque mxmlSetReal mxmlSetText mxmlSetTextf mxmlSetUserData mxmlSetWrapMargin mxmlWalkNext mxmlWalkPrev cmtk-3.3.1/Utilities/mxml/vcnet/mxml1.vcproj000066400000000000000000000216031276303427400210200ustar00rootroot00000000000000 cmtk-3.3.1/Utilities/mxml/vcnet/mxmldoc.vcproj000066400000000000000000000165631276303427400214360ustar00rootroot00000000000000 cmtk-3.3.1/Utilities/mxml/vcnet/testmxml.vcproj000066400000000000000000000173471276303427400216510ustar00rootroot00000000000000 cmtk-3.3.1/apps/000077500000000000000000000000001276303427400134275ustar00rootroot00000000000000cmtk-3.3.1/apps/CMakeLists.txt000066400000000000000000000110211276303427400161620ustar00rootroot00000000000000## ## Copyright 1997-2012 Torsten Rohlfing ## ## Copyright 2004-2013 SRI International ## ## This file is part of the Computational Morphometry Toolkit. ## ## http://www.nitrc.org/projects/cmtk/ ## ## The Computational Morphometry Toolkit is free software: you can ## redistribute it and/or modify it under the terms of the GNU General Public ## License as published by the Free Software Foundation, either version 3 of ## the License, or (at your option) any later version. ## ## The Computational Morphometry Toolkit is distributed in the hope that it ## will be useful, but WITHOUT ANY WARRANTY; without even the implied ## warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License along ## with the Computational Morphometry Toolkit. If not, see ## . ## ## $Revision: 5343 $ ## ## $LastChangedDate: 2014-05-01 11:57:39 -0700 (Thu, 01 May 2014) $ ## ## $LastChangedBy: torstenrohlfing $ ## SET(CMTK_LIBRARIES "cmtkSegmentation;cmtkRecon;cmtkRegistration;cmtkIO;cmtkBase;cmtkNumerics;cmtkSystem") IF(CMTK_BUILD_UNSTABLE) SET(CMTK_LIBRARIES "cmtkUnstable;${CMTK_LIBRARIES}") ENDIF(CMTK_BUILD_UNSTABLE) SET(APPS avg_adm describe groupwise_init groupwise_affine groupwise_warp asegment average_affine average_images average_labels concat_affine convertx convert_warp destripe dof2mat dwi_mask_bad_slices epiunwarp fibxform fib2image film filter fit_affine_dfield fit_affine_xform fit_affine_xform_landmarks fit_spline_dfield fit_spline_xform geomatch glm gmm gregxform hausdorff histogram imagemath interleaved_bad_slices jidb levelset lsba lmsba lvote make_initial_affine mat2dof mk_adni_phantom mk_analyze_hdr mk_nifti_hdr mk_phantom_3d mrbias overlap probe pxsearch reformatx registration registrationx regress reorient sba sbai sequence similarity split statistics stream_pixels streamxform sympl symplx ttest unsplit unwarp_image_phantom vol2csv volume_injection volume_reconstruction vtkxform warp warpx warp2ps xform2dfield xform2itk xform2scalar) IF(CMTK_USE_CUDA) SET(CUDA_APPS levelset_cuda mrbias_cuda symplx_cuda) ENDIF(CMTK_USE_CUDA) IF(CMTK_USE_SQLITE) LIST(APPEND APPS dbtool) ENDIF(CMTK_USE_SQLITE) IF(CMTK_ROOT_PATH_SRI24) LIST(APPEND APPS asegment_sri24) ENDIF(CMTK_ROOT_PATH_SRI24) IF(HAVE_STL_HASH_MAP) LIST(APPEND APPS mcaffine mcwarp) ENDIF(HAVE_STL_HASH_MAP) IF(CMTK_USE_DCMTK) LIST(APPEND APPS dcm2image) ENDIF(CMTK_USE_DCMTK) IF(CMTK_USE_FFTW_FOUND) LIST(APPEND APPS detect_spheres_matched_filter detect_adni_phantom) ENDIF(CMTK_USE_FFTW_FOUND) FOREACH(APPLICATION ${APPS}) ADD_EXECUTABLE(${APPLICATION} ${APPLICATION}.cxx) TARGET_LINK_LIBRARIES(${APPLICATION} ${CMTK_LIBRARIES} ${CMTK_SYSTEM_LIBRARIES}) IF(CMTK_BUILD_WRAPPER) INSTALL(TARGETS ${APPLICATION} RUNTIME DESTINATION ${CMTK_INSTALL_LIB_DIR}/bin COMPONENT tools) SET(manpage cmtk-${APPLICATION}.1) ELSE(CMTK_BUILD_WRAPPER) INSTALL(TARGETS ${APPLICATION} RUNTIME DESTINATION ${CMTK_INSTALL_BIN_DIR} COMPONENT tools) SET(manpage ${APPLICATION}.1) ENDIF(CMTK_BUILD_WRAPPER) IF(BUILD_MANPAGES) ADD_CUSTOM_COMMAND( OUTPUT ${manpage} COMMAND ${APPLICATION} --man ">" ${manpage} DEPENDS ${APPLICATION}) ADD_CUSTOM_TARGET(man_${APPLICATION} ALL DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/${manpage}) INSTALL(FILES ${CMAKE_CURRENT_BINARY_DIR}/${manpage} DESTINATION share/man/man1 COMPONENT tools) ENDIF(BUILD_MANPAGES) ENDFOREACH(APPLICATION ${APPS}) FOREACH(APPLICATION ${CUDA_APPS}) CUDA_ADD_EXECUTABLE(${APPLICATION} ${APPLICATION}.cxx) TARGET_LINK_LIBRARIES(${APPLICATION} cmtkGPU ${CMTK_LIBRARIES} ${CMTK_SYSTEM_LIBRARIES}) IF(CMTK_BUILD_WRAPPER) INSTALL(TARGETS ${APPLICATION} RUNTIME DESTINATION ${CMTK_INSTALL_LIB_DIR}/bin COMPONENT tools) ELSE(CMTK_BUILD_WRAPPER) INSTALL(TARGETS ${APPLICATION} RUNTIME DESTINATION ${CMTK_INSTALL_BIN_DIR} COMPONENT tools) ENDIF(CMTK_BUILD_WRAPPER) ENDFOREACH(APPLICATION ${CUDA_APPS}) IF(CMTK_USE_CUDA) CUDA_ADD_EXECUTABLE(cudatest cudatest.cxx) IF(CMTK_BUILD_WRAPPER) INSTALL(TARGETS cudatest RUNTIME DESTINATION ${CMTK_INSTALL_LIB_DIR}/bin COMPONENT tools) ELSE(CMTK_BUILD_WRAPPER) INSTALL(TARGETS cudatest RUNTIME DESTINATION ${CMTK_INSTALL_BIN_DIR} COMPONENT tools) ENDIF(CMTK_BUILD_WRAPPER) ENDIF(CMTK_USE_CUDA) cmtk-3.3.1/apps/asegment.cxx000066400000000000000000000101571276303427400157620ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2011, 2013 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4834 $ // // $LastChangedDate: 2013-09-12 15:15:51 -0700 (Thu, 12 Sep 2013) $ // // $LastChangedBy: torstenrohlfing $ // */ #include #include #include #include #include #include #include #include int doMain( const int argc, const char* argv[] ) { bool fast = false; std::string targetImageName; std::string atlasImageName; std::string atlasLabelName; std::string outImageName; try { cmtk::CommandLine cl( cmtk::CommandLine::PROPS_XML ); cl.SetProgramInfo( cmtk::CommandLine::PRG_TITLE, "Atlas-based segmentation" ); cl.SetProgramInfo( cmtk::CommandLine::PRG_DESCR, "Register a target image to an atlas, using affine followed by nonrigid B-spline registration, then reformat the atlas label map to the target image." ); cl.SetProgramInfo( cmtk::CommandLine::PRG_CATEG, "CMTK.Segmentation" ); typedef cmtk::CommandLine::Key Key; cl.AddSwitch( Key( 'f', "fast" ), &fast, true, "Fast mode." ); cl.AddParameter( &targetImageName, "TargetImage", "Target image path. This is the image to be segmented." ) ->SetProperties( cmtk::CommandLine::PROPS_IMAGE ); cl.AddParameter( &atlasImageName, "AtlasImage", "Atlas image path. This is the structural channel (e.g., T1-weighted MRI) of the atlas to be used." ) ->SetProperties( cmtk::CommandLine::PROPS_IMAGE ); cl.AddParameter( &atlasLabelName, "AtlasLabels", "Atlas label image path. This is the label map to be reformatted to the target image." ) ->SetProperties( cmtk::CommandLine::PROPS_IMAGE | cmtk::CommandLine::PROPS_LABELS ); cl.AddParameter( &outImageName, "OutputImage", "Output image path. This is where the reformatted label map is written." ) ->SetProperties( cmtk::CommandLine::PROPS_IMAGE | cmtk::CommandLine::PROPS_LABELS | cmtk::CommandLine::PROPS_OUTPUT ); cl.Parse( argc, argv ); } catch ( const cmtk::CommandLine::Exception& e ) { cmtk::StdErr << e << "\n"; throw cmtk::ExitException( 1 ); } // Instantiate programm progress indicator. cmtk::ProgressConsole progressIndicator( "Atlas-based Segmentation" ); cmtk::UniformVolume::SmartPtr targetImg( cmtk::VolumeIO::ReadOriented( targetImageName ) ); if ( !targetImg ) { cmtk::StdErr << "ERROR: could not read target image " << targetImageName << "\n"; throw cmtk::ExitException( 1 ); } cmtk::UniformVolume::SmartPtr atlasImg( cmtk::VolumeIO::ReadOriented( atlasImageName ) ); if ( !atlasImg ) { cmtk::StdErr << "ERROR: could not read atlas image " << atlasImageName << "\n"; throw cmtk::ExitException( 1 ); } cmtk::UniformVolume::SmartPtr atlasLbl( cmtk::VolumeIO::ReadOriented( atlasLabelName ) ); if ( !atlasLbl ) { cmtk::StdErr << "ERROR: could not read atlas labels " << atlasLabelName << "\n"; throw cmtk::ExitException( 1 ); } cmtk::AtlasSegmentation segment( targetImg, atlasImg, atlasLbl ); segment.SetFast( fast ); cmtk::VolumeIO::Write( *(segment.GetLabelMap()), outImageName ); return 0; } #include "cmtkSafeMain" cmtk-3.3.1/apps/asegment_sri24.cxx000066400000000000000000000123561276303427400170100ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2013 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 326 $ // // $LastChangedDate: 2009-07-29 15:08:42 -0700 (Wed, 29 Jul 2009) $ // // $LastChangedBy: torstenrohlfing $ // */ #include #include #include #include #include #include #include #include #include int doMain( const int argc, const char* argv[] ) { bool fast = false; std::string targetImageName; std::string outImageName; std::string channelSRI24( "spgr" ); std::string labelsSRI24( "tzo116plus" ); try { cmtk::CommandLine cl( cmtk::CommandLine::PROPS_XML ); cl.SetProgramInfo( cmtk::CommandLine::PRG_TITLE, "Segmentation using SRI24 atlas" ); cl.SetProgramInfo( cmtk::CommandLine::PRG_DESCR, "Register a target image to a selected channel of the SRI24 human brain atlas, then reformat one of the atlas label maps to the target image. " "Note: it is assume that the target image is skull-stripped, i.e., contains only the brain." ); cl.SetProgramInfo( cmtk::CommandLine::PRG_CATEG, "CMTK.Segmentation" ); typedef cmtk::CommandLine::Key Key; cl.AddSwitch( Key( 'f', "fast" ), &fast, true, "Fast mode." ); cmtk::CommandLine::EnumGroup::SmartPtr channelGroup = cl.AddEnum( "registration-channel", &channelSRI24, "The SRI24 channel used for registration to the target image." ); channelGroup->AddSwitch( Key( "spgr" ), "spgr", "SPGR (T1-weighted) structural channel" ); channelGroup->AddSwitch( Key( "early-fse" ), "erly", "Early-echo (PD-weighted) fast spin echo channel" ); channelGroup->AddSwitch( Key( "late-fse" ), "late", "Late-echo (T2-weighted) fast spin echo channel" ); channelGroup->AddSwitch( Key( "fa" ), "fa", "Fractional anisotropy channel, derived from diffusion tensor images" ); cmtk::CommandLine::EnumGroup::SmartPtr labelsGroup = cl.AddEnum( "label-map", &labelsSRI24, "The SRI24 label map that is reformatted to the target image." ); labelsGroup->AddSwitch( Key( "tzo116plus" ), "tzo116plus", "Extended cortical parcellation template based on Tzourio-Mazoyer AAL 116 region template." ); labelsGroup->AddSwitch( Key( "lpba40" ), "lpba40", "Template based on the 40 subject LONI Probabilistic Brain Atlas segmentation." ); labelsGroup->AddSwitch( Key( "tissue" ), "tissue", "SRI24 maximum likelihood three compartment (GM, WM, CSF) tissue segmentation map." ); cl.AddParameter( &targetImageName, "TargetImage", "Target image path. This is the image to be segmented." ) ->SetProperties( cmtk::CommandLine::PROPS_IMAGE ); cl.AddParameter( &outImageName, "OutputImage", "Output image path. This is where the reformatted label map is written." ) ->SetProperties( cmtk::CommandLine::PROPS_IMAGE | cmtk::CommandLine::PROPS_LABELS | cmtk::CommandLine::PROPS_OUTPUT ); cl.Parse( argc, argv ); } catch ( const cmtk::CommandLine::Exception& e ) { cmtk::StdErr << e << "\n"; throw cmtk::ExitException( 1 ); } // Instantiate programm progress indicator. cmtk::ProgressConsole progressIndicator( "Segmentation Using the SRI24 Atlas" ); cmtk::UniformVolume::SmartPtr targetImg( cmtk::VolumeIO::ReadOriented( targetImageName ) ); if ( !targetImg ) { cmtk::StdErr << "ERROR: could not read target image " << targetImageName << "\n"; throw cmtk::ExitException( 1 ); } std::string atlasImageName = std::string( CMTK_ROOT_PATH_SRI24 ) + "/" + channelSRI24 + ".nii"; cmtk::UniformVolume::SmartPtr atlasImg( cmtk::VolumeIO::ReadOriented( atlasImageName ) ); if ( !atlasImg ) { cmtk::StdErr << "ERROR: could not read atlas image " << atlasImageName << "\n"; throw cmtk::ExitException( 1 ); } std::string atlasLabelName = std::string( CMTK_ROOT_PATH_SRI24 ) + "/" + labelsSRI24 + ".nii"; cmtk::UniformVolume::SmartPtr atlasLbl( cmtk::VolumeIO::ReadOriented( atlasLabelName ) ); if ( !atlasLbl ) { cmtk::StdErr << "ERROR: could not read atlas labels " << atlasLabelName << "\n"; throw cmtk::ExitException( 1 ); } cmtk::AtlasSegmentation segment( targetImg, atlasImg, atlasLbl ); segment.SetFast( fast ); cmtk::VolumeIO::Write( *(segment.GetLabelMap()), outImageName ); return 0; } #include "cmtkSafeMain" cmtk-3.3.1/apps/average_affine.cxx000066400000000000000000000126001276303427400170740ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2014 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 5129 $ // // $LastChangedDate: 2014-01-09 13:48:15 -0800 (Thu, 09 Jan 2014) $ // // $LastChangedBy: torstenrohlfing $ // */ #include #include #include #include #include #include #include #include #include const char* OutputName = "average.xform"; bool AppendToOutput = false; bool InvertOutput = false; bool IncludeReference = false; int doMain( const int argc, const char* argv[] ) { std::list xformList; try { cmtk::CommandLine cl; cl.SetProgramInfo( cmtk::CommandLine::PRG_TITLE, "Average affine transformations" ); cl.SetProgramInfo( cmtk::CommandLine::PRG_DESCR, "This tool computes the average of a sequence of user-provided affine coordinate transformations." ); cl.SetProgramInfo( cmtk::CommandLine::PRG_SYNTX, "average_affine [options] x0 [x1 ...] \n WHERE x0 ... xN is [{-i,--inverse}] affine transformation #. " "(If the first transformation in the sequence is inverted, then '--inverse' must be preceded by '--', i.e., use '-- --inverse xform.path')." ); typedef cmtk::CommandLine::Key Key; cl.AddSwitch( Key( 'r', "include-reference" ), &IncludeReference, true, "Include reference coordinate system in averaging." ); cl.AddOption( Key( 'o', "outfile" ), &OutputName, "Output transformation." ); cl.AddSwitch( Key( 'a', "append" ), &AppendToOutput, true, "Append to output file [default: overwrite]." ); cl.AddSwitch( Key( 'I', "invert-output" ), &InvertOutput, true, "Invert averaged transformation before output [default: no]." ); cl.Parse( argc, argv ); const cmtk::AffineXform* firstXform = NULL; const char* next = cl.GetNextOptional(); while ( next ) { const bool inverse = ! strcmp( next, "-i" ) || ! strcmp( next, "--inverse" ); if ( inverse ) next = cl.GetNext(); cmtk::Xform::SmartPtr xform( cmtk::XformIO::Read( next ) ); if ( ! xform ) { cmtk::StdErr << "ERROR: could not read transformation from " << next << "\n"; throw cmtk::ExitException( 1 ); } cmtk::AffineXform::SmartPtr affine( cmtk::AffineXform::SmartPtr::DynamicCastFrom( xform ) ); if ( ! affine ) { cmtk::StdErr << "ERROR: transformation " << next << " is not affine.\n"; throw cmtk::ExitException( 1 ); } if ( inverse ) { try { affine = affine->GetInverse(); } catch ( const cmtk::AffineXform::MatrixType::SingularMatrixException& ) { cmtk::StdErr << "ERROR: singular matrix encountered in cmtk::AffineXform::GetInverse()\n"; throw cmtk::ExitException( 1 ); } } if ( firstXform ) { affine->ChangeCenter( cmtk::FixedVector<3,cmtk::Types::Coordinate>::FromPointer( firstXform->RetCenter() ) ); } else { firstXform = affine; } affine->SetUseLogScaleFactors( true ); xformList.push_back( affine ); next = cl.GetNextOptional(); } } catch ( const cmtk::CommandLine::Exception& e ) { cmtk::StdErr << e << "\n"; throw cmtk::ExitException( 1 ); } cmtk::AffineXform average; average.SetUseLogScaleFactors( true ); const size_t numberOfXforms = xformList.size(); if ( numberOfXforms ) { cmtk::CoordinateVector v, vx; for ( std::list::const_iterator xit = xformList.begin(); xit != xformList.end(); ++xit ) { if ( xit == xformList.begin() ) { (*xit)->GetParamVector( v ); } else { (*xit)->GetParamVector( vx ); for ( size_t p = 0; p < 12; ++p ) { v[p] += vx[p]; } } } for ( size_t p = 0; p < 12; ++p ) { if ( IncludeReference ) v[p] /= numberOfXforms+1; else v[p] /= numberOfXforms; } average.SetParamVector( v ); } cmtk::ClassStreamOutput outStream; if ( AppendToOutput ) outStream.Open( OutputName, cmtk::ClassStreamOutput::MODE_APPEND ); else outStream.Open( OutputName, cmtk::ClassStreamOutput::MODE_WRITE ); if ( InvertOutput ) { try { outStream << (*average.GetInverse()); } catch ( const cmtk::AffineXform::MatrixType::SingularMatrixException& ) { cmtk::StdErr << "ERROR: singular matrix encountered in cmtk::AffineXform::GetInverse()\n"; throw cmtk::ExitException( 1 ); } } else { outStream << average; } return 0; } #include "cmtkSafeMain" cmtk-3.3.1/apps/average_images.cxx000066400000000000000000000222751276303427400171220ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2012 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4375 $ // // $LastChangedDate: 2012-05-30 13:01:18 -0700 (Wed, 30 May 2012) $ // // $LastChangedBy: torstenrohlfing $ // */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include const char* OutputFileName = "average.nii"; bool ApplyLog = false; bool ApplyAbs = false; bool Normalize = false; bool Padding = false; cmtk::Types::DataItem PaddingValue = 0; size_t NumberHistogramBins = 64; typedef enum { MODE_AVG, MODE_STDEV, MODE_VAR, MODE_ZSCORE, MODE_ENTROPY } ModeEnum; ModeEnum Mode = MODE_AVG; cmtk::ScalarDataType DataType = cmtk::TYPE_FLOAT; std::list imagePathList; void GetNormalizationCoefficients ( const cmtk::TypedArray* floatingData, const cmtk::Types::DataItem refMean, const cmtk::Types::DataItem refVariance, cmtk::Types::DataItem& scale, cmtk::Types::DataItem& offset ) { cmtk::Types::DataItem fltMean, fltVariance; floatingData->GetStatistics( fltMean, fltVariance ); scale = sqrt( refVariance ) / sqrt( fltVariance ); offset = refMean - scale * fltMean; cmtk::DebugOutput( 1 ).GetStream().printf( "Converted grey values: %f +- %f -> %f +- %f\n", fltMean, sqrt( fltVariance ), refMean, sqrt( refVariance ) ); } int doMain( const int argc, const char* argv[] ) { try { cmtk::CommandLine cl; cl.SetProgramInfo( cmtk::CommandLine::PRG_TITLE, "Average images" ); cl.SetProgramInfo( cmtk::CommandLine::PRG_DESCR, "This tool computes pixelwiase average, variance, standard deviation, z-score, or entropy images from a list of user-provided intensity images." ); cl.SetProgramInfo( cmtk::CommandLine::PRG_SYNTX, "average_images [options] image0 ..." ); typedef cmtk::CommandLine::Key Key; cmtk::CommandLine::EnumGroup::SmartPtr modeGroup = cl.AddEnum( "mode", &Mode, "Mode of averaging operation" ); modeGroup->AddSwitch( Key( "avg" ), MODE_AVG, "Compute average (i.e., mean) image" ); modeGroup->AddSwitch( Key( "var" ), MODE_VAR, "Compute variance image" ); modeGroup->AddSwitch( Key( "stdev" ), MODE_STDEV, "Compute standard deviation image" ); modeGroup->AddSwitch( Key( "zscore" ), MODE_ZSCORE, "Compute z-score image" ); modeGroup->AddSwitch( Key( "entropy" ), MODE_ENTROPY, "Compute pixel-by-pixel population entropy image" ); cl.BeginGroup( "Preprocessing", "Data Preprocessing" ); cl.AddSwitch( Key( 'l', "log" ), &ApplyLog, true, "Apply log to input data" ); cl.AddSwitch( Key( 'a', "abs" ), &ApplyAbs, true, "Use absolute input values" ); cl.AddSwitch( Key( 'n', "normalize-mean-stdev" ), &Normalize, true, "Normalize image intensities using means and standard deviations" ); cl.AddOption( Key( "set-padding-value" ), &PaddingValue, "Define padding value in input images", &Padding ); cl.EndGroup(); cl.BeginGroup( "Output", "Output Options" ); cl.AddOption( Key( 'o', "outfile-name" ), &OutputFileName, "Output file name" ); cmtk::CommandLine::EnumGroup::SmartPtr typeGroup = cl.AddEnum( "type", &DataType, "Scalar data type of output image." ); typeGroup->AddSwitch( Key( "float" ), cmtk::TYPE_FLOAT, "Single-precision float." ); typeGroup->AddSwitch( Key( "double" ), cmtk::TYPE_DOUBLE, "Double-precision float." ); cl.EndGroup(); cl.Parse( argc, argv ); const char* next = cl.GetNext(); while ( next ) { imagePathList.push_back( next ); next = cl.GetNextOptional(); } } catch ( const cmtk::CommandLine::Exception& e ) { cmtk::StdErr << e << "\n"; throw cmtk::ExitException(1); } cmtk::UniformVolume::SmartPtr volume( NULL ); cmtk::TypedArray::SmartPtr outputData( NULL ); bool firstImage = true; cmtk::Types::DataItem refMean, refVariance; cmtk::Types::DataItemRange imagesValueRange( 0, 0 ); std::list dataList; std::list::const_iterator it; for ( it = imagePathList.begin(); it != imagePathList.end(); ++it ) { cmtk::UniformVolume::SmartPtr nextVolume( cmtk::VolumeIO::ReadOriented( *it) ); if ( ! nextVolume ) { cmtk::StdErr << "ERROR: Could not open image " << *it << "\n"; throw cmtk::ExitException( 1 ); } if ( ! volume ) { volume = nextVolume; } cmtk::TypedArray::SmartPtr data = nextVolume->GetData(); data->Convert( DataType ); if ( Padding ) { data->SetPaddingValue( PaddingValue ); } if ( ApplyLog ) { data->ApplyFunctionDouble( cmtk::Wrappers::Log ); } if ( ApplyAbs ) { data->ApplyFunctionDouble( cmtk::Wrappers::Abs ); } if ( Normalize ) { if ( firstImage ) { firstImage = false; data->GetStatistics( refMean, refVariance ); } else { cmtk::Types::DataItem normFactor, normOffset; GetNormalizationCoefficients( data, refMean, refVariance, normFactor, normOffset ); data->Rescale( normFactor, normOffset ); } } const cmtk::Types::DataItemRange dataRange = data->GetRange(); if ( firstImage ) { imagesValueRange = dataRange; } else { imagesValueRange.m_LowerBound = std::min( imagesValueRange.m_LowerBound, dataRange.m_LowerBound ); imagesValueRange.m_UpperBound = std::min( imagesValueRange.m_UpperBound, dataRange.m_UpperBound ); } dataList.push_back( data ); } // this is only used in "Entropy" mode, but we'll instantiate it anyway to save time cmtk::Histogram histogram( NumberHistogramBins ); histogram.SetRange( imagesValueRange ); if ( ! outputData ) { outputData = cmtk::TypedArray::SmartPtr( cmtk::TypedArray::Create( DataType, volume->GetNumberOfPixels() ) ); outputData->SetPaddingValue( std::numeric_limits::signaling_NaN() ); } cmtk::ProgressConsole progressIndicator; const int pixelsPerPercent = volume->GetNumberOfPixels() / 100; cmtk::Progress::Begin( 0, 100, 1, "Image averaging" ); std::vector pixelData( dataList.size() ); for ( size_t i = 0; i < volume->GetNumberOfPixels(); ++i ) { if ( !(i % pixelsPerPercent) ) cmtk::Progress::SetProgress( i / pixelsPerPercent ); pixelData.resize( dataList.size() ); size_t actualSize = 0; std::list::const_iterator dit; for ( dit = dataList.begin(); dit != dataList.end(); ++dit ) { cmtk::Types::DataItem v; if ( (*dit)->Get( v, i ) ) { pixelData[actualSize++] = v; } } if ( actualSize ) { pixelData.resize( actualSize ); switch ( Mode ) { case MODE_AVG: { const cmtk::Types::DataItem avg = cmtk::MathUtil::Mean( pixelData ); outputData->Set( avg, i ); break; } case MODE_VAR: { const cmtk::Types::DataItem avg = cmtk::MathUtil::Mean( pixelData ); const cmtk::Types::DataItem var = cmtk::MathUtil::Variance( pixelData, avg ); outputData->Set( var, i ); break; } case MODE_STDEV: { const cmtk::Types::DataItem avg = cmtk::MathUtil::Mean( pixelData ); const cmtk::Types::DataItem var = cmtk::MathUtil::Variance( pixelData, avg ); outputData->Set( sqrt(var), i ); break; } case MODE_ENTROPY: { histogram.Reset(); for ( size_t idx = 0; idx < actualSize; ++idx ) histogram.IncrementFractional( histogram.ValueToBinFractional( pixelData[idx] ) ); outputData->Set( histogram.GetEntropy(), i ); break; } case MODE_ZSCORE: { const cmtk::Types::DataItem avg = cmtk::MathUtil::Mean( pixelData ); const cmtk::Types::DataItem var = cmtk::MathUtil::Variance( pixelData, avg ); outputData->Set( avg / sqrt(var), i ); break; } } } else { outputData->SetPaddingAt( i ); } } cmtk::Progress::Done(); volume->SetData( outputData ); cmtk::VolumeIO::Write( *volume, OutputFileName ); return 0; } #include "cmtkSafeMain" cmtk-3.3.1/apps/average_labels.cxx000066400000000000000000000126041276303427400171120ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2012 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4538 $ // // $LastChangedDate: 2012-10-02 15:16:13 -0700 (Tue, 02 Oct 2012) $ // // $LastChangedBy: torstenrohlfing $ // */ #include #include #include #include #include #include #include #include #include #ifdef CMTK_USE_GCD # include #endif typedef std::deque< std::pair > XVQueue; XVQueue XformVolumeList; cmtk::UniformVolume::SmartPtr ReferenceImage; const char* OutputImagePath = "average_labels_output.nii"; int doMain( const int argc, const char* argv[] ) { try { cmtk::CommandLine cl; cl.SetProgramInfo( cmtk::CommandLine::PRG_TITLE, "Label image averaging" ); cl.SetProgramInfo( cmtk::CommandLine::PRG_DESCR, "Average co-registered label images using partial volumes" ); cl.SetProgramInfo( cmtk::CommandLine::PRG_SYNTX, "average_labels [options] reference xform0 atlas0 [xform1 atlas1 ...]" ); typedef cmtk::CommandLine::Key Key; cl.AddOption( Key( 'o', "output" ), &OutputImagePath, "Output image path" ); cl.Parse( argc, argv ); const char* refImagePath = cl.GetNext(); ReferenceImage = cmtk::UniformVolume::SmartPtr( cmtk::VolumeIO::ReadGridOriented( refImagePath ) ); if ( !ReferenceImage ) { cmtk::StdErr << "ERROR: cannot read reference volume " << refImagePath << "\n"; throw cmtk::ExitException( 1 ); } const char* nextXform = cl.GetNext(); const char* nextVolume = cl.GetNext(); while ( nextXform && nextVolume ) { cmtk::Xform::SmartPtr xform( cmtk::XformIO::Read( nextXform ) ); if ( !xform ) { cmtk::StdErr << "ERROR: cannot read transformation " << nextXform << "\n"; throw cmtk::ExitException( 1 ); } cmtk::UniformVolume::SmartPtr volume( cmtk::VolumeIO::ReadOriented( nextVolume ) ); if ( !volume ) { cmtk::StdErr << "ERROR: cannot read volume " << nextVolume << "\n"; throw cmtk::ExitException( 1 ); } XformVolumeList.push_back( std::pair( xform, volume ) ); nextXform = cl.GetNextOptional(); nextVolume = cl.GetNextOptional(); } } catch ( const cmtk::CommandLine::Exception& e ) { cmtk::StdErr << e << "\n"; throw cmtk::ExitException( 1 ); } cmtk::TypedArray::SmartPtr result( cmtk::TypedArray::Create( cmtk::TYPE_SHORT, ReferenceImage->GetNumberOfPixels() ) ); short* resultPtr = static_cast( result->GetDataPtr() ); const cmtk::DataGrid::IndexType& dims = ReferenceImage->GetDims(); cmtk::Progress::Begin( 0, dims[2], 1, "Label image averaging" ); #ifdef CMTK_USE_GCD const cmtk::Threads::Stride stride( dims[2] ); dispatch_apply( stride.NBlocks(), dispatch_get_global_queue(0, 0), ^(size_t b) { for ( size_t z = stride.From( b ); z < stride.To( b ); ++z ) #else #pragma omp parallel for for ( int z = 0; z < dims[2]; ++z ) #endif { cmtk::Progress::SetProgress( z ); size_t offset = z * dims[0] * dims[1]; float labelWeights[256]; for ( int y = 0; y < dims[1]; ++y ) for ( int x = 0; x < dims[0]; ++x, ++offset ) { memset( labelWeights, 0, sizeof( labelWeights ) ); cmtk::Vector3D v = ReferenceImage->GetGridLocation( x, y, z ); for ( XVQueue::iterator it = XformVolumeList.begin(); it != XformVolumeList.end(); ++it ) { const cmtk::Xform* xform = it->first; const cmtk::UniformVolume* volume = it->second; const cmtk::Vector3D vx( xform->Apply( v ) ); cmtk::ProbeInfo probeInfo; if ( volume->ProbeNoXform( probeInfo, vx ) ) for ( int corner = 0; corner < 8; ++corner ) labelWeights[static_cast( probeInfo.Values[corner] )] += static_cast( probeInfo.GetWeight( corner ) ); } short maxLabel = -1; float maxLabelWeight = -1; for ( int l = 0; l < 256; ++l ) { if ( labelWeights[l] > maxLabelWeight ) { maxLabelWeight = labelWeights[l]; maxLabel = l; } else { if ( labelWeights[l] == maxLabelWeight ) { maxLabel = -1; } } } resultPtr[offset] = maxLabel; // need to access array via direct ptr to work around GCD bug } } #ifdef CMTK_USE_GCD }); #endif cmtk::Progress::Done(); ReferenceImage->SetData( result ); cmtk::VolumeIO::Write( *ReferenceImage, OutputImagePath ); return 0; } #include "cmtkSafeMain" cmtk-3.3.1/apps/avg_adm.cxx000066400000000000000000000307331276303427400155570ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2014 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 5129 $ // // $LastChangedDate: 2014-01-09 13:48:15 -0800 (Thu, 09 Jan 2014) $ // // $LastChangedBy: torstenrohlfing $ // */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include bool Labels = false; bool Jacobian = false; bool AutoScale = false; cmtk::ScalarDataType UserDataType = cmtk::TYPE_NONE; cmtk::Interpolators::InterpolationEnum Interpolation = cmtk::Interpolators::LINEAR; const char* ReplaceFrom; std::map ReplaceMap; void AddReplaceFrom( const char* arg ) { ReplaceFrom = arg; } void AddReplaceTo( const char* arg ) { ReplaceMap[std::string(ReplaceFrom)] = std::string( arg ); } const char* OutImageName = NULL; const char* OutWarpName = NULL; const char* InWarpName = NULL; bool IncludeScale = true; bool IncludeReferenceModel = true; bool IncludeReferenceData = true; bool WriteIncludeAffine = false; float PaddingValue = 0; bool SetPaddingValue = false; float NewPaddingValue = 0; bool ReplacePaddingValue = false; cmtk::Types::DataItem PadOutValue = 0; bool HavePadOutValue = false; int doMain( int argc, const char* argv[] ) { std::list inFileList; try { cmtk::CommandLine cl; cl.SetProgramInfo( cmtk::CommandLine::PRG_TITLE, "Average using ADM" ); cl.SetProgramInfo( cmtk::CommandLine::PRG_DESCR, "Compute average-shape average-intensity images and deformation maps using an active deformation model." ); cl.SetProgramInfo( cmtk::CommandLine::PRG_SYNTX, "avg_adm [options] list0 [list1 ...]" ); typedef cmtk::CommandLine::Key Key; cl.AddSwitch( Key( 'l', "label" ), &Labels, true, "Label mode (as opposed to grey)" ); cl.AddOption( Key( "set-padding" ), &PaddingValue, "Set padding value in input files", &SetPaddingValue ); cl.AddOption( Key( "replace-padding" ), &NewPaddingValue, "Replace padding value in input files", &ReplacePaddingValue ); cl.AddOption( Key( 'p', "pad-out" ), &PadOutValue, "Padding value for output file", &HavePadOutValue ); cl.AddSwitch( Key( 'j', "jacobian" ), &Jacobian, true, "Average Jacobian determinants of deformations." ); cl.AddSwitch( Key( 'a', "auto-scale" ), &AutoScale, true, "Auto-scale image intensities" ); cl.AddSwitch( Key( "char" ), &UserDataType, cmtk::TYPE_CHAR, "Output is 8 bits, signed [default: automatic]" ); cl.AddSwitch( Key( "byte" ), &UserDataType, cmtk::TYPE_BYTE, "Output is 8 bits, unsigned" ); cl.AddSwitch( Key( "short" ), &UserDataType, cmtk::TYPE_SHORT, "Output is 16 bits, signed" ); cl.AddSwitch( Key( "ushort" ), &UserDataType, cmtk::TYPE_USHORT, "Output is 16 bits, unsigned" ); cl.AddSwitch( Key( "int" ), &UserDataType, cmtk::TYPE_INT, "Output is 32 bits signed" ); cl.AddSwitch( Key( "float" ), &UserDataType, cmtk::TYPE_FLOAT, "Output is 32 bits floating point" ); cl.AddSwitch( Key( "double" ), &UserDataType, cmtk::TYPE_DOUBLE, "Output is 64 bits floating point\n" ); cl.AddSwitch( Key( 'S', "no-scale-model" ), &IncludeScale, false, "Exclude scale from statistical deformation model" ); cl.AddSwitch( Key( 'R', "no-ref-model" ), &IncludeReferenceModel, false, "Exclude reference coordinate frame from deformation model" ); cl.AddSwitch( Key( 'r', "no-ref-data" ), &IncludeReferenceData, false, "Exclude reference data from averaging" ); cl.AddOption( Key( 'o', "output" ), &OutImageName, "Output file name [FORMAT:]path" ); cl.AddOption( Key( 'I', "input-warp" ), &InWarpName, "Input file name for average warp" ); cl.AddOption( Key( 'O', "output-warp" ), &OutWarpName, "Output file name for average warp" ); cl.AddSwitch( Key( 'A', "with-affine" ), &WriteIncludeAffine, true, "Include affine component of first warp in output" ); cl.AddSwitch( Key( "nn" ), &Interpolation, cmtk::Interpolators::NEAREST_NEIGHBOR, "Use nearest neigbor interpolation" ); cl.AddSwitch( Key( "cubic" ), &Interpolation, cmtk::Interpolators::CUBIC, "Use tri-cubic interpolation" ); cl.AddCallback( Key( "replace-from" ), AddReplaceFrom, "Replace from pattern" ); cl.AddCallback( Key( "replace-to" ), AddReplaceTo, "Replace to pattern" ); cl.Parse( argc, const_cast( argv ) ); inFileList.push_back( cl.GetNext() ); const char* next = cl.GetNextOptional(); while ( next ) { inFileList.push_back( next ); next = cl.GetNextOptional(); } } catch ( const cmtk::CommandLine::Exception& e ) { cmtk::StdErr << e; throw cmtk::ExitException( 1 ); } std::vector warpList; std::list splineWarpList; std::vector volumeList; cmtk::TypedStreamStudylist studylist; char* referenceStudy = NULL; size_t idx = 0; for ( std::list::const_iterator inFile = inFileList.begin(); inFile != inFileList.end(); ++inFile ) { cmtk::DebugOutput( 1 ) << "Opening studylist" << *inFile << "\n"; if ( !studylist.Read( *inFile ) ) { cmtk::StdErr << "ERROR: Unable to read studylist " << *inFile << "\n"; throw cmtk::ExitException( 1 ); } if ( ! idx ) { referenceStudy = strdup( studylist.GetReferenceStudyPath() ); } else { if ( strcmp( studylist.GetReferenceStudyPath(), referenceStudy ) ) { cmtk::StdErr.printf( "ERROR: Studylist #%u has a different reference study.\n", idx ); //continue; throw cmtk::ExitException( 1 ); } } cmtk::SplineWarpXform::SmartPtr splineWarp = cmtk::SplineWarpXform::SmartPtr::DynamicCastFrom( studylist.GetWarpXform() ); if ( splineWarp ) { std::string actualPath = cmtk::StrReplaceByRules( studylist.GetFloatingStudyPath(), ReplaceMap ); cmtk::UniformVolume::SmartPtr nextVolume; if ( OutImageName && !Jacobian ) // no out image, no need for input images nextVolume = cmtk::UniformVolume::SmartPtr( cmtk::VolumeIO::ReadOriented( actualPath ) ); if ( OutImageName && !Jacobian && !nextVolume ) { cmtk::StdErr.printf( "WARNING: Cannot read volume %s in studylist #%u.\n", actualPath.c_str(), idx ); } else { // everything worked out, so let's push this item onto lists if ( SetPaddingValue ) { nextVolume->GetData()->SetPaddingValue( PaddingValue ); if ( ReplacePaddingValue ) { nextVolume->GetData()->ReplacePaddingData( NewPaddingValue ); } } warpList.push_back( splineWarp ); splineWarpList.push_back( splineWarp ); if ( OutImageName ) volumeList.push_back( nextVolume ); // and increase valid warp counter. ++idx; } } else { cmtk::StdErr.printf( "ERROR: Studylist #%u has no spline warp.\n", idx ); throw cmtk::ExitException( 1 ); } } std::string actualPath = cmtk::StrReplaceByRules( studylist.GetReferenceStudyPath(), ReplaceMap ); cmtk::UniformVolume::SmartPtr refVolume( cmtk::VolumeIO::ReadOriented( actualPath ) ); if ( ! refVolume ) { cmtk::StdErr.printf( "ERROR: Cannot read reference volume %s.\n", actualPath.c_str() ); throw cmtk::ExitException( 1 ); } // everything worked out, so let's push this item onto lists if ( SetPaddingValue ) { refVolume->GetData()->SetPaddingValue( PaddingValue ); if ( ReplacePaddingValue ) { refVolume->GetData()->ReplacePaddingData( NewPaddingValue ); } } if ( AutoScale ) { if ( Jacobian ) { cmtk::StdErr << "Warning: we don't really use image data for Jacobian map -- \nSkipping auto normalization step.\n"; } else { cmtk::Types::DataItem meanRef, varRef; refVolume->GetData()->GetStatistics( meanRef, varRef ); for ( size_t volIdx = 0; volIdx < volumeList.size(); ++volIdx ) { cmtk::TypedArray::SmartPtr data( volumeList[volIdx]->GetData() ); cmtk::Types::DataItem meanFlt, varFlt; data->GetStatistics( meanFlt, varFlt ); cmtk::DebugOutput( 1 ).GetStream().printf( "Before auto-rescale: [1] %f +/- %f, [2] %f +/- %f\n", meanRef, sqrt( varRef ), meanFlt, sqrt( varFlt ) ); // auto rescale, that is, determine scaling factor and offset so that // after scaling, the intensities in both images have the same mean and // standard deviation. Note that due to the squaring of values in // std.dev. computation, the spread will not be exactly identical. const cmtk::Types::DataItem factor = sqrt(varRef) / sqrt(varFlt); data->Rescale( factor, meanRef - factor * meanFlt ); if ( cmtk::DebugOutput::GetGlobalLevel() > 0 ) { data->GetStatistics( meanFlt, varFlt ); cmtk::DebugOutput( 1 ).GetStream().printf( "After auto-rescale: [1] %f +/- %f, [2] %f +/- %f\n", meanRef, sqrt( varRef ), meanFlt, sqrt( varFlt ) ); } } } } if ( Labels ) { refVolume->GetData()->SetDataClass( cmtk::DATACLASS_LABEL ); } cmtk::WarpXform::SmartPtr warpXform; if ( InWarpName ) { cmtk::ClassStreamInput stream( InWarpName ); if ( stream.IsValid() ) { stream >> warpXform; } else { cmtk::StdErr << "ERROR: cannot read transformation from " << InWarpName << "\n"; throw cmtk::ExitException( 1 ); } } else { cmtk::SmartPointer adm( new cmtk::SplineActiveDeformationModel( splineWarpList, 0, IncludeScale, IncludeReferenceModel ) ); adm->Compose(); warpXform = cmtk::WarpXform::SmartPtr::DynamicCastFrom( adm ); } if ( OutWarpName ) { if ( WriteIncludeAffine ) { try { warpXform->ReplaceInitialAffine( (*(warpList.begin()))->GetInitialAffineXform() ); } catch ( const cmtk::AffineXform::MatrixType::SingularMatrixException& ) { cmtk::StdErr << "ERROR: singular matrix encountered in call to cmtk::WarpXform::ReplaceInitialAffine()\n"; throw cmtk::ExitException( 1 ); } } cmtk::ClassStreamOutput stream( OutWarpName, cmtk::ClassStreamOutput::MODE_WRITE ); if ( stream.IsValid() ) { stream << warpXform; } else { cmtk::StdErr << "ERROR: cannot write to " << OutWarpName << "\n"; } } if ( OutImageName ) { cmtk::ProgressConsole progressIndicator; cmtk::ReformatVolume reformat; reformat.SetReferenceVolume( refVolume ); reformat.SetWarpXform( warpXform ); reformat.SetInterpolation( Interpolation ); reformat.SetUsePaddingValue( HavePadOutValue ); reformat.SetPaddingValue( PadOutValue ); if ( UserDataType != cmtk::TYPE_NONE ) reformat.SetUserDataType( UserDataType ); cmtk::UniformVolume::SmartPtr average; if ( Jacobian ) { average = cmtk::UniformVolume::SmartPtr( reformat.GetTransformedReferenceJacobianAvg( &warpList, NULL /*origin*/, IncludeReferenceData ) ); } else { average = cmtk::UniformVolume::SmartPtr( reformat.GetTransformedReference( &warpList, &volumeList, NULL /*origin*/, IncludeReferenceData ) ); } cmtk::VolumeIO::Write( *average, OutImageName ); } free( referenceStudy ); return 0; } #include "cmtkSafeMain" cmtk-3.3.1/apps/cmtkSafeMain000066400000000000000000000037451276303427400157250ustar00rootroot00000000000000/* // // Copyright 2010-2012 SRI International // // Copyright 2015 Google, Inc. // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 5392 $ // // $LastChangedDate: 2015-11-02 19:42:41 -0800 (Mon, 02 Nov 2015) $ // // $LastChangedBy: torstenrohlfing $ // */ #if defined(_MSC_VER) && _MSC_VER < 1900 # include #endif #ifdef CMTK_BUILD_STACKTRACE #include #endif // #ifdef CMTK_BUILD_STACKTRACE #ifdef CMTK_BUILD_RTRACKER #include #endif // #ifdef CMTK_BUILD_RTRACKER #include int main( const int argc, const char* argv[] ) { #if defined(_MSC_VER) && _MSC_VER < 1900 _set_output_format( _TWO_DIGIT_EXPONENT ); #endif cmtk::Threads::CheckEnvironment(); // need this to check for "CMTK_NUM_THREADS" and constrain OpenMP accordingly #ifdef CMTK_BUILD_STACKTRACE cmtk::StackBacktrace::Static(); #endif #ifdef CMTK_BUILD_RTRACKER // initialize optional regression tracker cmtk::RegressionTracker::Static(); #endif int exitCode = 0; try { exitCode = doMain( argc, argv ); } catch ( const cmtk::ExitException& ex ) { exitCode = ex.ExitCode(); } return exitCode; } cmtk-3.3.1/apps/concat_affine.cxx000066400000000000000000000111571276303427400167370ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2013 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 5129 $ // // $LastChangedDate: 2014-01-09 13:48:15 -0800 (Thu, 09 Jan 2014) $ // // $LastChangedBy: torstenrohlfing $ // */ #include #include #include #include #include #include #include #include const char* OutputName = "concat.xform"; bool AppendToOutput = false; bool InvertOutput = false; int doMain( const int argc, const char* argv[] ) { cmtk::AffineXform concat; try { cmtk::CommandLine cl; cl.SetProgramInfo( cmtk::CommandLine::PRG_TITLE, "Concatenate affine transformations" ); cl.SetProgramInfo( cmtk::CommandLine::PRG_DESCR, "This tool computes the explicit concatenation of multiple affine coordinate transformations, each of which can be optionally inverted." ); cl.SetProgramInfo( cmtk::CommandLine::PRG_SYNTX, "concat_affine [options] x0 [x1 ...] \n WHERE x0 ... xN is [{-i,--inverse}] affine transformation #. " "(If the first transformation in the sequence is inverted, then '--inverse' must be preceded by '--', i.e., use '-- --inverse xform.path')."); typedef cmtk::CommandLine::Key Key; cl.AddOption( Key( 'o', "outfile" ), &OutputName, "Output transformation." ); cl.AddSwitch( Key( 'a', "append" ), &AppendToOutput, true, "Append to output file [default: overwrite]." ); cl.AddSwitch( Key( 'I', "invert-output" ), &InvertOutput, true, "Invert concatenated transformation before output [default: no]." ); cl.Parse( argc, argv ); bool firstXform = true; const char* next = cl.GetNextOptional(); while ( next ) { const bool inverse = ! strcmp( next, "-i" ) || ! strcmp( next, "--inverse" ); if ( inverse ) next = cl.GetNext(); cmtk::Xform::SmartPtr xform( cmtk::XformIO::Read( next ) ); if ( ! xform ) { cmtk::StdErr << "ERROR: could not read transformation from " << next << "\n"; throw cmtk::ExitException( 1 ); } cmtk::AffineXform::SmartPtr affine( cmtk::AffineXform::SmartPtr::DynamicCastFrom( xform ) ); if ( ! affine ) { cmtk::StdErr << "ERROR: transformation " << next << " is not affine.\n"; throw cmtk::ExitException( 1 ); } if ( inverse ) { try { affine = affine->GetInverse(); } catch ( const cmtk::AffineXform::MatrixType::SingularMatrixException& ) { cmtk::StdErr << "ERROR: input transformation " << next << " has singular matrix and cannot be inverted\n"; throw cmtk::ExitException( 1 ); } } concat.Concat( *affine ); if ( firstXform ) { concat.ChangeCenter( cmtk::FixedVector<3,cmtk::Types::Coordinate>::FromPointer( affine->RetCenter() ) ); firstXform = false; } next = cl.GetNextOptional(); } } catch ( const cmtk::CommandLine::Exception& e ) { cmtk::StdErr << e << "\n"; throw cmtk::ExitException( 1 ); } cmtk::ClassStreamOutput outStream; if ( AppendToOutput ) outStream.Open( OutputName, cmtk::ClassStreamOutput::MODE_APPEND ); else outStream.Open( OutputName, cmtk::ClassStreamOutput::MODE_WRITE ); if ( outStream.IsValid() ) { if ( InvertOutput ) { try { outStream << (*concat.GetInverse()); } catch ( const cmtk::AffineXform::MatrixType::SingularMatrixException& ) { cmtk::StdErr << "ERROR: output transformation has singular matrix and cannot be inverted\n"; throw cmtk::ExitException( 1 ); } } else { outStream << concat; } } else { cmtk::StdErr << "ERROR: could not open output file " << OutputName << "\n"; throw cmtk::ExitException( 1 ); } return 0; } #include "cmtkSafeMain" cmtk-3.3.1/apps/convert_warp.cxx000066400000000000000000000102441276303427400166650ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2014 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 5129 $ // // $LastChangedDate: 2014-01-09 13:48:15 -0800 (Thu, 09 Jan 2014) $ // // $LastChangedBy: torstenrohlfing $ // */ #include #include #include #include #include #include #include #include std::string inXformPath; std::string outXformPath; float Fractional = -1; bool DeformationOnly = false; int doMain ( const int argc, const char* argv[] ) { try { cmtk::CommandLine cl; cl.SetProgramInfo( cmtk::CommandLine::PRG_TITLE, "Convert nonrigd transformations." ); cl.SetProgramInfo( cmtk::CommandLine::PRG_DESCR, "This tool converts nonrigid B-spline free-format deformation coordinate transformations between different representations (e.g., absolute vs. relative vectors). Also creates fractional transformations." ); typedef cmtk::CommandLine::Key Key; cl.AddOption( Key( 'f', "fractional" ), &Fractional, "Write fractional deformation. Range: 0=affine to 1=full nonrigid; Default: 1" ); cl.AddSwitch( Key( 'd', "deformation-only" ), &DeformationOnly, true, "Write only deformation part of transformation (minus global affine component)" ); cl.AddParameter( &inXformPath, "InputPath", "Input transformation path" )->SetProperties( cmtk::CommandLine::PROPS_XFORM ); cl.AddParameter( &outXformPath, "OutputPath", "Output transformation path" )->SetProperties( cmtk::CommandLine::PROPS_XFORM | cmtk::CommandLine::PROPS_OUTPUT ); cl.Parse( argc, argv ); } catch ( const cmtk::CommandLine::Exception& e ) { cmtk::StdErr << e << "\n"; throw cmtk::ExitException( 1 ); } cmtk::Xform::SmartPtr xform( cmtk::XformIO::Read( inXformPath ) ); cmtk::SplineWarpXform::SmartPtr splineWarpXform = cmtk::SplineWarpXform::SmartPtr::DynamicCastFrom( xform ); if ( splineWarpXform ) { cmtk::AffineXform::SmartPtr initialAffine = splineWarpXform->GetInitialAffineXform(); try { splineWarpXform->ReplaceInitialAffine(); } catch ( const cmtk::AffineXform::MatrixType::SingularMatrixException& ) { cmtk::StdErr << "ERROR: singular matrix encountered in call to cmtk::WarpXform::ReplaceInitialAffine()\n"; throw cmtk::ExitException( 1 ); } if ( (Fractional >= 0) && (Fractional <= 1) ) { const size_t numberOfControlPoints = splineWarpXform->GetNumberOfControlPoints(); for ( size_t idx = 0; idx < numberOfControlPoints; ++idx ) { cmtk::Vector3D v0 = splineWarpXform->GetOriginalControlPointPositionByOffset( idx ); cmtk::Vector3D v1 = splineWarpXform->GetShiftedControlPointPositionByOffset( idx ); ((v1 -= v0) *= Fractional) += v0; splineWarpXform->SetShiftedControlPointPositionByOffset( v1, idx ); } } if ( ! DeformationOnly ) { try { splineWarpXform->ReplaceInitialAffine( initialAffine ); } catch ( const cmtk::AffineXform::MatrixType::SingularMatrixException& ) { cmtk::StdErr << "ERROR: singular matrix encountered in call to cmtk::WarpXform::ReplaceInitialAffine()\n"; throw cmtk::ExitException( 1 ); } } } cmtk::XformIO::Write( xform, outXformPath ); return 0; } #include "cmtkSafeMain" cmtk-3.3.1/apps/convertx.cxx000066400000000000000000000457151276303427400160370ustar00rootroot00000000000000/* // // Copyright 1997-2010 Torsten Rohlfing // // Copyright 2004-2014 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 5322 $ // // $LastChangedDate: 2014-04-11 15:21:19 -0700 (Fri, 11 Apr 2014) $ // // $LastChangedBy: torstenrohlfing $ // */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef HAVE_IEEEFP_H # include #endif int doMain( const int argc, const char* argv[] ) { std::string imagePathIn; std::string imagePathOut; #ifdef CMTK_USE_SQLITE const char* updateDB = NULL; #endif try { cmtk::CommandLine cl; cl.SetProgramInfo( cmtk::CommandLine::PRG_TITLE, "Convert between image file formats and data types." ); cl.SetProgramInfo( cmtk::CommandLine::PRG_DESCR, "This tool converts between image file formats and pixel data types. It can also apply simple, general-purpose image operations in the process. " "An arbitrary number of operations can be specified on the command line, which will be applied exactly in the order given." ); cl.SetProgramInfo( cmtk::CommandLine::PRG_SYNTX, "convertx [options] infile outfile" ); typedef cmtk::CommandLine::Key Key; #ifdef CMTK_USE_SQLITE cl.BeginGroup( "Database", "Image/Transformation Database" ); cl.AddOption( Key( "db" ), &updateDB, "Path to image/transformation database that should be updated with the newly created image." ); cl.EndGroup(); #endif cl.BeginGroup( "Input", "Input Image Controls" ); cl.AddCallback( Key( "set-padding" ), &cmtk::ImageOperationSetPadding::New, "Set padding value: all pixels in the input image that have this value will be ignored in all subsequent operations." ); cl.AddCallback( Key( "unset-padding" ), &cmtk::ImageOperationSetPadding::NewUnset, "Unset padding value: for all subsequent operations, all pixels will be treated according to their value." ); cl.AddCallback( Key( "labels" ), &cmtk::ImageOperationSetDataClass::NewLabels, "Specify that the image values are to be treated as discrete labels. " "This will result in the appropriate intent code to be set in output files in NIfTI format." ); cl.AddCallback( Key( "grey" ), &cmtk::ImageOperationSetDataClass::NewGrey, "Specify that the image values are to be treated as continuous grey levels. " "This will reset the intent code in files read from NIfTI format." ); cl.EndGroup(); cl.BeginGroup( "Conversion", "Data Type Conversion" ); cl.AddCallback( Key( "char" ), &cmtk::ImageOperationConvertType::NewChar, "8 bits, signed integer" ); cl.AddCallback( Key( "byte" ), &cmtk::ImageOperationConvertType::NewByte, "8 bits, unsigned integer" ); cl.AddCallback( Key( "short" ), &cmtk::ImageOperationConvertType::NewShort, "16 bits, signed integer" ); cl.AddCallback( Key( "ushort" ), &cmtk::ImageOperationConvertType::NewUShort, "16 bits, unsigned integer" ); cl.AddCallback( Key( "int" ), &cmtk::ImageOperationConvertType::NewInt, "32 bits signed integer" ); cl.AddCallback( Key( "uint" ), &cmtk::ImageOperationConvertType::NewUInt, "32 bits unsigned integer" ); cl.AddCallback( Key( "float" ), &cmtk::ImageOperationConvertType::NewFloat, "32 bits floating point" ); cl.AddCallback( Key( "double" ), &cmtk::ImageOperationConvertType::NewDouble, "64 bits floating point\n" ); cl.EndGroup(); cl.BeginGroup( "Mappings", "Value Mappings" ); cl.AddCallback( Key( "map-values" ), &cmtk::ImageOperationMapValues::New, "Apply mapping function to pixel values. Mapping is defined as 'VAL0[,VAL1,...][:NEWVAL]' to map values VAL0, VAL1, etc. to new value NEWVAL. " "If NEWVAL is not given, values are set to padding." ); cl.AddCallback( Key( "map-values-only" ), &cmtk::ImageOperationMapValues::NewExclusive, "Apply mapping function to pixel values and replace unmapped pixels with padding. Multiple such mapping rules can be concatenated as " "RULE0+RULE1[+...]; all concatenated rules will be applied simultaneously." "Mapping is defined as 'VAL0[,VAL1,...][:NEWVAL]' to map values VAL0, VAL1, etc. to new value NEWVAL. If NEWVAL is not given, values are set to padding. Multiple such mapping rules can be concatenated as " "RULE0+RULE1[+...]; all concatenated rules will be applied simultaneously." ); cl.AddCallback( Key( "replace-padding" ), &cmtk::ImageOperationReplace::NewReplacePadding, "Replace padded pixel data with given value." ); cl.AddCallback( Key( "replace-inf-nan" ), &cmtk::ImageOperationReplace::NewReplaceInfNaN, "Replace all infinite and not-a-number pixels with given value." ); cl.EndGroup(); cl.BeginGroup( "Flipping", "Image Flipping" ); cl.AddCallback( Key( "flip-x" ), &cmtk::ImageOperationFlip::NewX, "Flip (mirror) along x-direction" ); cl.AddCallback( Key( "flip-y" ), &cmtk::ImageOperationFlip::NewY, "Flip (mirror) along y-direction" ); cl.AddCallback( Key( "flip-z" ), &cmtk::ImageOperationFlip::NewZ, "Flip (mirror) along z-direction" ); cl.EndGroup(); cl.BeginGroup( "MaskAndThreshold", "Image Masking and Thresholding" ); cl.AddCallback( Key( "mask" ), &cmtk::ImageOperationApplyMask::New, "Binary mask file name: eliminate all image pixels where mask is 0. " "Masked-out pixels will NOT be set to zero, but will instead be replaced with the currently-set padding value. Use '--set-padding 0' prior to '--mask' to force setting to zero." ); cl.AddCallback( Key( "mask-inverse" ), &cmtk::ImageOperationApplyMask::NewInverse, "Inverse binary mask file name eliminate all image pixels where mask is NOT 0. See also notes regarding padding under '--mask' above." ); cl.AddCallback( Key( "thresh-below" ), &cmtk::ImageOperationThreshold::NewBelow, "Set all values below threshold to threshold value." ); cl.AddCallback( Key( "thresh-above" ), &cmtk::ImageOperationThreshold::NewAbove, "Set all values above threshold to threshold value." ); cl.AddCallback( Key( "thresh-below-to-padding" ), &cmtk::ImageOperationThreshold::NewBelowToPadding, "Set all values below threshold to padding value." ); cl.AddCallback( Key( "thresh-above-to-padding" ), &cmtk::ImageOperationThreshold::NewAboveToPadding, "Set all values above threshold to padding value." ); cl.AddCallback( Key( "binarize-thresh" ), &cmtk::ImageOperationThreshold::NewBinarize, "Set all values below threshold to 0, all values equal or above to 1." ); cl.AddCallback( Key( "otsu-thresh" ), &cmtk::ImageOperationOtsuThreshold::New, "Binarize image to 0/1 using threshold computed with Otsu's method. " "Argument is number of histogram bins for threshold computation." ); cl.AddCallback( Key( "otsu-thresh-nbins" ), &cmtk::ImageOperationOtsuThreshold::NewBins, "Binarization using Otsu's method with user-defined number of histogram bins " "for threshold computation." ); cl.AddCallback( Key( "prune-histogram" ), &cmtk::ImageOperationPruneHistogram::New, "Threshold image by 'intensity histogram pruning', i.e., for given argument n [histogram bins] " "determine thresholds such that the 1/n-th fraction of highest and lowest voxels are thresholded." ); cl.AddCallback( Key( "prune-histogram-high" ), &cmtk::ImageOperationPruneHistogram::NewHigh, "Like '--prune-histograms', but only remove high intensities." ); cl.AddCallback( Key( "prune-histogram-low" ), &cmtk::ImageOperationPruneHistogram::NewLow, "Like '--prune-histograms', but only remove low intensities." ); cl.EndGroup(); cl.BeginGroup( "Intensity", "Intensity Transformations" ); cl.AddCallback( Key( "scale-to-range" ), &cmtk::ImageOperationScaleToRange::New, "Scale image intensities to range 'from:to', e.g., '0:255' before conversion to byte data." ); cl.AddCallback( Key( "histogram-equalization" ), &cmtk::ImageOperationHistogramEqualization::New, "Apply histogram equalization." ); cl.AddCallback( Key( "histogram-equalization-nbins" ), &cmtk::ImageOperationHistogramEqualization::NewBins, "Apply histogram equalization with number of bins." ); cl.AddCallback( Key( "match-histograms" ), cmtk::ImageOperationMatchIntensities::NewMatchHistograms, "Transform intensities to match the distribution in the image provided as the argument for this command." ); cl.AddCallback( Key( "match-mean-sdev" ), cmtk::ImageOperationMatchIntensities::NewMatchMeanSDev, "Scale intensities to match the mean and standard distribution of intensities in the image provided as the argument for this command." ); cl.EndGroup(); cl.BeginGroup( "Morphological", "Morphological Operations" ); cl.AddCallback( Key( "revert" ), &cmtk::ImageOperationRevert::New, "Revert a binary mask, i.e., exchange foreground and background." ); cl.AddCallback( Key( "erode" ), &cmtk::ImageOperationErodeDilate::NewErode, "Morphological erosion operator (by pixels)" ); cl.AddCallback( Key( "dilate" ), &cmtk::ImageOperationErodeDilate::NewDilate, "Morphological dilation operator (by pixels)" ); cl.AddCallback( Key( "erode-distance" ), &cmtk::ImageOperationErodeDilateDistance::NewErode, "Morphological erosion operator (by distance). Often preferable for anisotropic data." ); cl.AddCallback( Key( "erode-distance-multilabel" ), &cmtk::ImageOperationErodeDilateDistance::NewErodeMultiLabels, "Morphological erosion operator (by distance) for multi-label maps. " "The result will be either byte, unsigned short, or unsigned int data, depending on the index of the largest used label in the input." ); cl.AddCallback( Key( "dilate-distance" ), &cmtk::ImageOperationErodeDilateDistance::NewDilate, "Morphological dilation operator (by distance). Oftern preferable for anisotropic data." ); cl.AddCallback( Key( "connected-components" ), &cmtk::ImageOperationConnectedComponents::New, "Create connected components map with regions numbered by decreasing component size" ); cl.AddCallback( Key( "boundary-map" ), &cmtk::ImageOperationBoundaryMap::New, "Create boundary map" ); cl.AddCallback( Key( "multi-boundary-map" ), &cmtk::ImageOperationBoundaryMap::NewMulti, "Create multi-valued boundary map" ); cl.AddCallback( Key( "distance-map" ), &cmtk::ImageOperationDistanceMap::NewUnsigned, "Compute unsigned Euclidean distance map. Input image is interpreted as binary mask." ); cl.AddCallback( Key( "signed-distance-map" ), &cmtk::ImageOperationDistanceMap::NewSigned, "Compute signed (inside=negative, outside=positive) Euclidean distance map" ); cl.EndGroup(); cl.BeginGroup( "Filtering", "Filter Operations" ); cl.AddCallback( Key( "median-filter" ), &cmtk::ImageOperationRegionFilter::NewMedian, "Median filter. This operation takes the filter radius in pixels as the parameter. " "A single integer defines the kernel radius in all three dimensions. Three comma-separated integers define separate radii for the three dimensions." ); cl.AddCallback( Key( "mean-filter" ), &cmtk::ImageOperationRegionFilter::NewMean, "Regional mean filter. This operation takes the filter radius in pixels as the parameter. " "A single integer defines the kernel radius in all three dimensions. Three comma-separated integers define separate radii for the three dimensions." ); cl.AddCallback( Key( "fast-mean-filter" ), &cmtk::ImageOperationRegionFilter::NewFastMean, "Regional mean filter (fast, linear time implementation). This operation takes the filter radius in pixels as the parameter. " "A single integer defines the kernel radius in all three dimensions. Three comma-separated integers define separate radii for the three dimensions." ); cl.AddCallback( Key( "variance-filter" ), &cmtk::ImageOperationRegionFilter::NewVariance, "Regional variance filter. " "This operation takes the filter radius in pixels as the parameter. " "A single integer defines the kernel radius in all three dimensions. Three comma-separated integers define separate radii for the three dimensions." ); cl.AddCallback( Key( "fast-variance-filter" ), &cmtk::ImageOperationRegionFilter::NewFastVariance, "Fast (linear-time) regional variance filter. " "This operation takes the filter radius in pixels as the parameter. " "A single integer defines the kernel radius in all three dimensions. Three comma-separated integers define separate radii for the three dimensions." ); cl.AddCallback( Key( "third-moment-filter" ), &cmtk::ImageOperationRegionFilter::NewThirdMoment, "Regional third moment filter. " "This operation takes the filter radius in pixels as the parameter. " "A single integer defines the kernel radius in all three dimensions. Three comma-separated integers define separate radii for the three dimensions." ); cl.AddCallback( Key( "standard-deviation-filter" ), &cmtk::ImageOperationRegionFilter::NewStandardDeviation, "Regional standard deviation filter. " "This operation takes the filter radius in pixels as the parameter. " "A single integer defines the kernel radius in all three dimensions. Three comma-separated integers define separate radii for the three dimensions." ); cl.AddCallback( Key( "smoothness-filter" ), &cmtk::ImageOperationRegionFilter::NewSmoothness, "Regional 'smoothness' filter. " "This operation takes the filter radius in pixels as the parameter. " "A single integer defines the kernel radius in all three dimensions. Three comma-separated integers define separate radii for the three dimensions." ); cl.AddCallback( Key( "gaussian-filter-sigma" ), &cmtk::ImageOperationGaussFilter::NewSigma, "Filter image with Gaussian kernel. This operation takes a single real-valued parameter, which specifies the kernel coefficient sigma in world units [e.g., mm] as the parameter." ); cl.AddCallback( Key( "gaussian-filter-fwhm" ), &cmtk::ImageOperationGaussFilter::NewFWHM, "Filter image with Gaussian kernel. This operation takes a single real-valued parameter, which specifies the kernel full width at half maximum in world units [e.g., mm]." ); cl.AddCallback( Key( "laplace-filter" ), &cmtk::ImageOperationLaplaceFilter::New, "Filter image with edge-enhancing Laplacian kernel." ); cl.EndGroup(); cl.BeginGroup( "Grid", "Grid Operations" ); cl.AddCallback( Key( "downsample-select" ), &cmtk::ImageOperationDownsample::NewSelect, "Downsample image by pixel selection using per-axis factors 'Fx,Fy,Fz' or using single factor 'Fxyz'" ); cl.AddCallback( Key( "downsample-average" ), &cmtk::ImageOperationDownsample::NewAverage, "Downsample image by averaging using per-axis factors 'Fx,Fy,Fz' or using single factor 'Fxyz'" ); cl.AddCallback( Key( "resample" ), &cmtk::ImageOperationResampleIsotropic::New, "Resample image to near-isotropic pixels while preserving the image field-of-view. " "Takes one argument, the target resolution in world units [e.g., mm]" ); cl.AddCallback( Key( "resample-exact" ), &cmtk::ImageOperationResampleIsotropic::NewExact, "Resample image to exactly isotropic pixels of the given resolution while matching the image field-of-view as closely as possible. " "Takes one argument, the target resolution in world units [e.g., mm]" ); cl.AddCallback( Key( "crop-by-index" ), &cmtk::ImageOperationCropRegion::New, "Crop image to a region specified by a set of six grid index coordinates given as comma-separated integers x0,y0,z0,x1,y1,z1" ); cl.AddCallback( Key( "crop-by-threshold" ), &cmtk::ImageOperationCropThreshold::New, "Crop image to region determined via a given threshold. " "The resulting image will contain all pixels larger than the given parameter." ); cl.AddCallback( Key( "crop-by-threshold-write-region" ), &cmtk::ImageOperationCropThreshold::NewWriteRegion, "Crop image to region determined via a given threshold and write cropping region to standard output." ); cl.AddCallback( Key( "crop-by-threshold-write-xform" ), &cmtk::ImageOperationCropThreshold::NewWriteXform, "Crop image to region determined via a given threshold and write cropping transformation to standard output." ); cl.EndGroup(); cl.AddParameter( &imagePathIn, "InputImage", "Input image path" )->SetProperties( cmtk::CommandLine::PROPS_IMAGE ); cl.AddParameter( &imagePathOut, "OutputImage", "Output image path" )->SetProperties( cmtk::CommandLine::PROPS_IMAGE | cmtk::CommandLine::PROPS_OUTPUT ); if ( ! cl.Parse( argc, argv ) ) return 1; } catch ( const cmtk::CommandLine::Exception& ex ) { cmtk::StdErr << ex << "\n"; return false; } cmtk::UniformVolume::SmartPtr volume( cmtk::VolumeIO::ReadOriented( imagePathIn ) ); cmtk::TypedArray::SmartPtr volumeData = volume->GetData(); volume = cmtk::ImageOperation::ApplyAll( volume ); cmtk::DebugOutput( 1 ) << "Writing to file " << imagePathOut << "\n"; cmtk::VolumeIO::Write( *volume, imagePathOut ); return 0; } #include "cmtkSafeMain" cmtk-3.3.1/apps/cudatest.cxx000066400000000000000000000060471276303427400157760ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2011 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 3095 $ // // $LastChangedDate: 2011-04-06 12:54:02 -0700 (Wed, 06 Apr 2011) $ // // $LastChangedBy: torstenrohlfing $ // */ #include #include #include int main( const int, const char*[] ) { int version = 0; if ( cudaDriverGetVersion( &version ) != cudaSuccess ) { std::cerr << "Call to cudaDriverGetVersion() failed." << std::endl; return 1; } std::cerr << "Found CUDA driver version " << version << std::endl; int deviceCount = 0; if ( cudaGetDeviceCount( &deviceCount ) != cudaSuccess ) { std::cerr << "Call to cudaGetDeviceCount() failed." << std::endl; return 1; } std::cerr << "CUDA reports " << deviceCount << " device(s)." << std::endl; for ( int device = 0; device < deviceCount; ++device ) { std::cerr << std::endl << "Device #" << device << ":" << std::endl; struct cudaDeviceProp props; if ( cudaGetDeviceProperties( &props, device ) != cudaSuccess ) { std::cerr << "\tFailed to get device properties." << std::endl; } else { std::cerr << "\tName: " << props.name << std::endl << std::endl; std::cerr << "\tMultiprocessor count: " << props.multiProcessorCount << std::endl; std::cerr << "\tCompute capability: " << props.major << "." << props.minor << std::endl; std::cerr << "\tTotal memory: " << props.totalGlobalMem << std::endl; std::cerr << "\tConstant memory: " << props.totalConstMem << std::endl; std::cerr << "\tShared memory per block: " << props.sharedMemPerBlock << std::endl << std::endl; std::cerr << "\tWarp size: " << props.warpSize << std::endl; std::cerr << "\tMax threads per block: " << props.maxThreadsPerBlock << std::endl; std::cerr << "\tMaximum thread block size: (" << props.maxThreadsDim[0] << "," << props.maxThreadsDim[1] << "," << props.maxThreadsDim[2] << ")" << std::endl; std::cerr << "\tMaximum grid size size: (" << props.maxGridSize[0] << "," << props.maxGridSize[1] << "," << props.maxGridSize[2] << ")" << std::endl; } } // if we got here, the program probably ran return 0; } cmtk-3.3.1/apps/dbtool.cxx000066400000000000000000000167311276303427400154460ustar00rootroot00000000000000/* // // Copyright 2010-2011, 2013 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4834 $ // // $LastChangedDate: 2013-09-12 15:15:51 -0700 (Thu, 12 Sep 2013) $ // // $LastChangedBy: torstenrohlfing $ // */ #include #include #include #include #include bool DebugMode = false; int addImages( const int argc, const char* argv[] ) { try { std::string dbpath; std::string space; std::vector images; cmtk::CommandLine cl; cl.SetProgramInfo( cmtk::CommandLine::PRG_TITLE, "Add images to the database" ); cl.SetProgramInfo( cmtk::CommandLine::PRG_DESCR, "This command manually adds a new image to the database, potentially assigning it to the space of an existing image. " "This is useful, for example, when multiple images were acquired in spatial alignment, or when images were created using tools that do not support database updating." ); cl.AddParameter( &dbpath, "Database", "Database path." ); cl.AddParameter( &space, "Space", "Image that defines coordinate space. If this image is not yet in the database, it will be added first." ); cl.AddParameterVector( &images, "Image", "Other images that reside in the same anatomical space (same subject, same acquisition) as the specified space." ); cl.Parse( argc, argv ); try { cmtk::ImageXformDB db( dbpath ); if ( DebugMode ) db.DebugModeOn(); db.AddImage( space ); for ( size_t i = 0; i < images.size(); ++i ) db.AddImage( space, images[i] ); } catch ( const cmtk::ImageXformDB::Exception& e ) { cmtk::StdErr << e.what() << "\n"; return 1; } } catch ( const cmtk::CommandLine::Exception& e ) { cmtk::StdErr << e << "\n"; return 1; } return 0; } int listSpace( const int argc, const char* argv[] ) { try { std::string dbpath; std::string image; bool sortById = false; cmtk::CommandLine cl; cl.SetProgramInfo( cmtk::CommandLine::PRG_TITLE, "List image space contents" ); cl.SetProgramInfo( cmtk::CommandLine::PRG_DESCR, "This command queries the database to list all images that are in the same space as the given one." ); typedef cmtk::CommandLine::Key Key; cl.AddSwitch( Key( "sort-id" ), &sortById, true, "Sort by internal database ID" ); cl.AddParameter( &dbpath, "Database", "Database path." ); cl.AddParameter( &image, "Image", "Query image path." ); cl.Parse( argc, argv ); try { cmtk::ImageXformDB db( dbpath, true /*readOnly*/ ); if ( DebugMode ) db.DebugModeOn(); std::vector list = db.GetSpaceImageList( db.FindImageSpaceID( image ), sortById ); for ( size_t i = 0; i < list.size(); ++i ) cmtk::StdOut << list[i] << "\n"; } catch ( const cmtk::ImageXformDB::Exception& e ) { cmtk::StdErr << e.what() << "\n"; return 1; } } catch ( const cmtk::CommandLine::Exception& e ) { cmtk::StdErr << e << "\n"; return 1; } return 0; } int getXform( const int argc, const char* argv[] ) { try { std::string dbpath; std::string rimage; std::string fimage; bool all = false; cmtk::CommandLine cl; cl.SetProgramInfo( cmtk::CommandLine::PRG_TITLE, "Get transformation link between two images" ); cl.SetProgramInfo( cmtk::CommandLine::PRG_DESCR, "This command queries the database to find transformation links that map from a given reference to a given floating image space." ); typedef cmtk::CommandLine::Key Key; cl.AddSwitch( Key( "all" ), &all, true, "Print list of all transformations." ); cl.AddParameter( &dbpath, "Database", "Database path." ); cl.AddParameter( &rimage, "RefImage", "Reference image path: this is the image FROM which to map." ); cl.AddParameter( &fimage, "FltImage", "Floating image path. this is the image TO which to map." ); cl.Parse( argc, argv ); try { cmtk::ImageXformDB db( dbpath, true /*readOnly*/ ); if ( DebugMode ) db.DebugModeOn(); if ( all ) { const std::vector allXformsFwd = db.FindAllXforms( rimage, fimage ); for ( size_t i = 0; i < allXformsFwd.size(); ++i ) { cmtk::StdOut << allXformsFwd[i] << "\n"; } const std::vector allXformsBwd = db.FindAllXforms( fimage, rimage ); for ( size_t i = 0; i < allXformsBwd.size(); ++i ) { cmtk::StdOut << "--inverse " << allXformsBwd[i] << "\n"; } } else { std::string xform; bool inverse; if ( db.FindXform( rimage, fimage, xform, inverse ) ) { if ( inverse ) { cmtk::StdOut << "--inverse "; } cmtk::StdOut << xform << "\n"; } else { cmtk::StdErr << "ERROR: no transformation can be found that maps from " << rimage << " to " << fimage << "\n"; return 1; } } } catch ( const cmtk::ImageXformDB::Exception& e ) { cmtk::StdErr << e.what() << "\n"; return 1; } } catch ( const cmtk::CommandLine::Exception& e ) { cmtk::StdErr << e << "\n"; return 1; } return 0; } int doMain( const int argc, const char* argv[] ) { int exitCode = 0; try { std::string command; cmtk::CommandLine cl; cl.SetProgramInfo( cmtk::CommandLine::PRG_TITLE, "Image/transformation database maintenance and query tool" ); cl.SetProgramInfo( cmtk::CommandLine::PRG_DESCR, "This tool modifies and queries the database of images and transformations between them." ); typedef cmtk::CommandLine::Key Key; cl.AddSwitch( Key( "debug" ), &DebugMode, true, "Turn on debug mode: print all SQL database commands and queries." ); cl.AddParameter( &command, "Command", "Database command. One of \"add_images\", \"list_space\", \"get_xform\". Use ' --help' for detailed help." ); cl.Parse( argc, argv ); // get effective argc and argv for command const size_t cargc = argc-cl.GetNextIndex()+1; std::vector cargv( cargc ); cargv[0] = command.c_str(); for ( size_t i = 1; i < cargc; ++i ) cargv[i] = cl.GetNext(); // run commands if ( command == "add_images" ) exitCode = addImages( cargc, &cargv[0] ); else if ( command == "list_space" ) exitCode = listSpace( cargc, &cargv[0] ); else if ( command == "get_xform" ) exitCode = getXform( cargc, &cargv[0] ); else { cmtk::StdErr << "Unknown command: " << command << "\n"; exitCode = 1; } } catch ( const cmtk::CommandLine::Exception& e ) { cmtk::StdErr << e << "\n"; throw cmtk::ExitException( 1 ); } return exitCode; } #include "cmtkSafeMain" cmtk-3.3.1/apps/dcm2image.cxx000066400000000000000000000450611276303427400160110ustar00rootroot00000000000000/* // // Copyright 2014 Google Inc. // // Copyright 1997-2012 Torsten Rohlfing // // Copyright 2004-2014 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 5375 $ // // $LastChangedDate: 2014-12-11 17:29:44 -0800 (Thu, 11 Dec 2014) $ // // $LastChangedBy: torsten_at_home $ // */ #include #include #include #include #include #include #include #include #include #ifndef CMTK_USE_DCMTK #error Build system is broken: this application should not be build if CMTK_USE_DCMTK is not set. #endif #ifdef _MSC_VER # include #else # include # include # include #endif #include #include #include #include #include #include #include #include #include #include #include void AddPatternToMap( std::map& map, const char* pattern ) { const char* equals = strchr( pattern, '=' ); if ( equals ) { const std::string tagName( pattern, static_cast( equals-pattern ) ); const DcmDictEntry* dictEntry = dcmDataDict.rdlock().findEntry( tagName.c_str() ); dcmDataDict.unlock(); if ( dictEntry ) { map.insert( std::pair( dictEntry->getKey(), equals+1 ) ); } else { cmtk::StdErr << "WARNING: DCMTK data dictionary does not have a tag entry named '" << tagName << "' - ignoring this\n"; } } } // Map of file includion patterns std::map IncludePatterns; void CallbackAddInclude( const char* pattern ) { AddPatternToMap( IncludePatterns, pattern ); } // Map of file exclusion patterns std::map ExcludePatterns; void CallbackAddExclude( const char* pattern ) { AddPatternToMap( ExcludePatterns, pattern ); } const char* OutPathPattern = "image%n.nii"; std::vector SearchRootDirVector; std::ofstream cnull( "/dev/null" ); bool Progress = true; bool Recursive = false; /// Enum type to select sort order key. typedef enum { /// No sorting - take files in order stored in file system. SORT_NONE = 0, /// Sort by file name. SORT_FILENAME = 1, /// Sort by instance number. SORT_INSTANCE_NUMBER = 2 } SortKeyEnum; SortKeyEnum SortFiles = SORT_INSTANCE_NUMBER; bool WriteXML = false; bool IncludeIdentifiers = false; bool WriteSingleSlices = false; bool DisableOrientationCheck = false; double Tolerance = 1e-5; bool IgnoreAcquisitionNumber = false; using cmtk::ImageFileDICOM; using cmtk::ImageStackDICOM; /// Selector for embedded image information. ImageStackDICOM::EmbedInfoEnum EmbedInfo = ImageStackDICOM::EMBED_NONE; /// Class handling a list of image stacks, i.e., volumes. class VolumeList : public std::vector { public: /// This class. typedef VolumeList Self; /// Constructor. VolumeList( const cmtk::Types::Coordinate tolerance = 0 /*!< Tolerance for floating point comparisons, e.g., when testing for uniform pixel/slice spacings.*/ ) : m_Tolerance( tolerance ) {} /// Build a volume list by traversing a directory. void AppendFromDirectory( const std::string& path, const char *wildcard ); /** Write all volumes in this list. *\return Return code - 0, no problems; 3, at least one volume was not written correctly (e.g., due to non-uniform slice spacing). */ int WriteVolumes(); private: /// Stored floating point tolerance. cmtk::Types::Coordinate m_Tolerance; /// Add a new image file to the correct stack or create a new stack. void AddImageFile( ImageFileDICOM::SmartConstPtr& newImage ); }; std::string PutNumberAndSanitize( const std::string& path, const std::string& numberString ) { if ( numberString.empty() ) return cmtk::StrReplace( cmtk::StrReplace( path, "%n", "" ), "%N", "" ); else return cmtk::StrReplace( cmtk::StrReplace( path, "%n", numberString ), "%N", "-" + numberString ); } int VolumeList::WriteVolumes() { int returnCode = 0; size_t cntSingleImages = 0; std::map pathToVolumeMap; for ( const_iterator it = this->begin(); it != this->end(); ++it ) { if ( ((*it)->size() > 1) || (*(*it)->begin())->m_IsMultislice || WriteSingleSlices ) { // replace place holders std::string path( OutPathPattern ); path = cmtk::StrReplace( path, "%D", cmtk::StrMakeLegalInPath( (*it)[0][0]->GetTagValue( DCM_SeriesDescription ) ) ); path = cmtk::StrReplace( path, "%R", cmtk::StrMakeLegalInPath( (*it)[0][0]->GetTagValue( DCM_RepetitionTime ) ) ); path = cmtk::StrReplace( path, "%E", cmtk::StrMakeLegalInPath( (*it)[0][0]->GetTagValue( DCM_EchoTime ) ) ); path = cmtk::StrReplace( path, "%T", (*it)[0][0]->m_RawDataType ); // replace %0 with file name path = cmtk::StrReplace( path, "%0", (*it)[0][0]->m_FileName ); // and replace %1 through %9 with directory components of decreasing depth char replaceThis[3] = { '%', '0', 0 }; // this will be modified to be the current replacement std::string directory = (*it)[0][0]->m_FileDir; // start with full directory for ( size_t dirIdx = 1; (dirIdx < 10) && !directory.empty(); ++dirIdx ) { replaceThis[1] = '0' + dirIdx; const size_t lastSlash = directory.rfind( CMTK_PATH_SEPARATOR ); if ( lastSlash == std::string::npos ) { path = cmtk::StrReplace( path, replaceThis, directory ); directory.clear(); } else { path = cmtk::StrReplace( path, replaceThis, directory.substr( lastSlash+1 ) ); directory = directory.substr( 0, lastSlash ); // trim trailing slashes, both forward and back const size_t lastNotSlash = directory.find_last_not_of( "/\\" ); if ( lastNotSlash != std::string::npos ) { directory.erase( lastNotSlash+1 ); } else { directory.clear(); } } } if ( path.length() > PATH_MAX ) cmtk::StdErr << "ERROR: output path exceeds maximum path length"; else pathToVolumeMap[path].push_back( *it ); } else { ++cntSingleImages; } } if ( cntSingleImages ) { cmtk::DebugOutput( 1 ) << "\n====================================================\n"; cmtk::DebugOutput( 1 ) << "WARNING: " << cntSingleImages << " single image(s) could not be assigned to multi-image stacks:\n\n"; for ( const_iterator it = begin(); it != end(); ++it ) { if ( ((*it)->size() == 1) && !(*(*it)->begin())->m_IsMultislice ) { (*(*it))[0]->Print(); cmtk::DebugOutput( 1 ) << "\n"; } } cmtk::DebugOutput( 1 ) << "\n====================================================\n"; } for ( std::map::const_iterator it = pathToVolumeMap.begin(); it != pathToVolumeMap.end(); ++it ) { const size_t nVolumes = it->second.size(); // if there is only one volume with the given output path, just write it if ( nVolumes == 1 ) { // if there's a "number" tag, get rid of it. std::string uniquePath = PutNumberAndSanitize( it->first, "" ); cmtk::UniformVolume::SmartConstPtr volume = it->second[0]->WriteImage( uniquePath.c_str(), EmbedInfo ); if ( volume ) { if ( WriteXML ) { it->second[0]->WriteXML( (uniquePath+".xml").c_str(), *volume, IncludeIdentifiers ); } } else { returnCode = 3; } } else { // otherwise, make unique paths for each of them for ( size_t i = 0; i < nVolumes; ++i ) { std::ostringstream numberString; numberString.width( 1 + static_cast( log( (double)nVolumes ) / M_LN10 ) ); numberString.fill( '0' ); numberString << std::right << 1+i; std::string uniquePath = PutNumberAndSanitize( it->first, numberString.str() ); cmtk::UniformVolume::SmartConstPtr volume = it->second[i]->WriteImage( uniquePath.c_str(), EmbedInfo ); if ( volume) { if ( WriteXML ) { it->second[i]->WriteXML( (uniquePath + ".xml").c_str(), *volume, IncludeIdentifiers ); } } else { returnCode = 3; } } } } return returnCode; } void VolumeList::AddImageFile( ImageFileDICOM::SmartConstPtr& newImage ) { if ( !empty() ) { const_iterator it = this->begin(); while ( it != this->end() ) { ImageStackDICOM::SmartPtr study = *it; if ( study->Match( *newImage, this->m_Tolerance, DisableOrientationCheck, IgnoreAcquisitionNumber) ) { study->AddImageFile( newImage ); return; } else { ++it; } } } ImageStackDICOM::SmartPtr newImageStack( new ImageStackDICOM( this->m_Tolerance ) ); newImageStack->AddImageFile( newImage ); push_back( newImageStack ); } void VolumeList::AppendFromDirectory( const std::string& path, const char *wildcard ) { std::vector fileList; #ifdef _MSC_VER static int progress = 0; const char progress_chars[] = "-\\|/"; WIN32_FIND_DATA fData; const std::string pattern = path + "\\" + wildcard; HANDLE hFind = FindFirstFile( pattern.c_str(), &fData); do { const std::string fullname = path + "\\" + fData.cFileName; if ( fData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY ) { if ( Recursive && (fData.cFileName[0] != '.') ) { this->AppendFromDirectory( fullname, wildcard ); } } else { try { ImageFileDICOM::SmartPtr newFile( new ImageFileDICOM( fullname ) ); if ( newFile->MatchAllPatterns( IncludePatterns ) && !newFile->MatchAnyPattern( ExcludePatterns ) ) { newFile->ReleaseDocument(); fileList.push_back( newFile ); } if ( Progress ) { (cmtk::StdErr << "\r" << progress_chars[ ++progress % 4 ]).flush(); } } catch ( ... ) { // not a valid DICOM file } } } while (FindNextFile(hFind, &fData) != 0); #else DIR *dir_pointer = opendir ( path.c_str() ); if ( dir_pointer != NULL ) { static int progress = 0; const char progress_chars[] = "-\\|/"; struct dirent *entry_pointer; while ( (entry_pointer = readdir(dir_pointer)) ) { const std::string fullname = path + "/" + entry_pointer->d_name; struct stat entry_status; if ( !stat(fullname.c_str(), &entry_status) ) { if ( S_ISDIR( entry_status.st_mode ) && Recursive && (entry_pointer->d_name[0] != '.') ) { this->AppendFromDirectory( fullname + "/" , wildcard ); } else { if ( !fnmatch(wildcard,entry_pointer->d_name,FNM_PERIOD) ) { try { ImageFileDICOM::SmartPtr newFile( new ImageFileDICOM( fullname ) ); if ( newFile->MatchAllPatterns( IncludePatterns ) && !newFile->MatchAnyPattern( ExcludePatterns ) ) { newFile->ReleaseDocument(); fileList.push_back( newFile ); } if ( Progress ) { (cmtk::StdErr << "\r" << progress_chars[ ++progress % 4 ]).flush(); } } catch ( ... ) { // not a valid DICOM file } } } } } (void) closedir(dir_pointer); } #endif switch ( SortFiles ) { case SORT_NONE: default: break; case SORT_FILENAME: std::sort( fileList.begin(), fileList.end(), ImageFileDICOM::lessFileName ); break; case SORT_INSTANCE_NUMBER: std::sort( fileList.begin(), fileList.end(), ImageFileDICOM::lessInstanceNumber ); break; } for ( std::vector::iterator it = fileList.begin(); it != fileList.end(); ++it ) { try { this->AddImageFile( *it ); } catch (int) { } } if ( Progress ) { cmtk::StdErr << "\r"; } } int doMain ( const int argc, const char *argv[] ) { if (! dcmDataDict.isDictionaryLoaded() ) { #ifdef CMTK_DCMDICTPATH if ( dcmDataDict.wrlock().loadDictionary( CMTK_DCMDICTPATH ) ) { dcmDataDict.unlock(); } else #endif #ifdef CMTK_DCMDICTPATH_INSTALL if ( dcmDataDict.wrlock().loadDictionary( CMTK_DCMDICTPATH_INSTALL ) ) { dcmDataDict.unlock(); } else #endif { cmtk::StdErr << "Data dictionary not avaliable. Please set DCMDICTPATH variable as path to dicom.dic file.\n"; throw cmtk::ExitException( 1 ); } } try { cmtk::CommandLine cl; cl.SetProgramInfo( cmtk::CommandLine::PRG_TITLE, "DICOM to Image" ); cl.SetProgramInfo( cmtk::CommandLine::PRG_DESCR, "Combine sets of DICOM slices to 3D image stacks" ); typedef cmtk::CommandLine::Key Key; cl.AddSwitch( Key( "no-progress" ), &Progress, false, "Disable progress reporting." ); cl.BeginGroup( "Input", "Input Options"); cl.AddSwitch( Key( 'r', "recurse" ), &Recursive, true, "Recurse into directories" ); cl.EndGroup(); cl.BeginGroup( "Output", "Output Options"); cl.AddOption( Key( 'O', "out-pattern" ), &OutPathPattern, "Output image path pattern. Use the following substitutions: " "printf-style %d variante (image number); " "%n (image number with automatic number of digits); " "%N (like %n, but with a hyphen '-' before number if there is more than one image); " "%D (DICOM SeriesDescription); " "%R (DICOM RepetitionTime - MRI only); " "%E (DICOM EchoTime - MRI only); " "%T (RawDataType - vendor-specific, currently GE MRI only)" ); cl.AddSwitch( Key( 'x', "xml" ), &WriteXML, true, "Write XML sidecar file for each created image." ); cl.AddSwitch( Key( "include-identifiers" ), &IncludeIdentifiers, true, "Include potentially protected identifying information (e.g., UIDs, device serial numbers, dates) in the created XML sidecar files." ); cmtk::CommandLine::EnumGroup::SmartPtr embedGroup = cl.AddEnum( "embed", &EmbedInfo, "Embed DICOM information into output images as 'description' (if supported by output file format)." ); embedGroup->AddSwitch( Key( "StudyID_StudyDate" ), cmtk::ImageStackDICOM::EMBED_STUDYID_STUDYDATE, "StudyID, tag (0020,0010), then underscore, followed by StudyDate, tag (0008,0020). " "Date is appended because StudyID is four digits only and will repeat sooner or later." ); embedGroup->AddSwitch( Key( "PatientName" ), cmtk::ImageStackDICOM::EMBED_PATIENTNAME, "Patient name, tag (0010,0010)" ); embedGroup->AddSwitch( Key( "SeriesDescription" ), cmtk::ImageStackDICOM::EMBED_SERIESDESCR, "Series description, tag (0008,103e)" ); embedGroup->AddSwitch( Key( "None" ), cmtk::ImageStackDICOM::EMBED_NONE, "Embed no information - leave 'description' field empty." ); cl.EndGroup(); cl.BeginGroup( "Filtering", "Filtering Options")->SetProperties( cmtk::CommandLine::PROPS_ADVANCED ); cl.AddCallback( Key( "filter" ), &CallbackAddInclude, "Filter DICOM files and include only those matching the given pattern of the form 'TagName=text', such that the value of the DICOM tag with the given name contains the given " "text. If multiple filter patterns are provided via repeated use of this option, only files that match ALL patterns are included." ); cl.AddCallback( Key( "exclude" ), &CallbackAddExclude, "Exclude all DICOM files matching the given pattern of the form 'TagName=text', such that the value of the DICOM tag with the given name contains the given text. " "If multiple exclusion patterns are provided, all files are excluded that match ANY of the patterns." ); cl.EndGroup(); cl.BeginGroup( "Sorting", "Sorting Options")->SetProperties( cmtk::CommandLine::PROPS_ADVANCED ); cl.AddSwitch( Key( "no-sort" ), &SortFiles, SORT_NONE, "Do NOT sort files by file name (sorting determines image stack order when resolving spatial collisions)" ); cl.AddSwitch( Key( "sort-by-name" ), &SortFiles, SORT_FILENAME, "Sort files lexicographically by file name. Use this when instance numbers are non-unique." ); cl.AddSwitch( Key( "sort-by-instance" ), &SortFiles, SORT_INSTANCE_NUMBER, "Sort files by image instance number. Use this when file names are different lengths, etc." ); cl.EndGroup(); cl.BeginGroup( "Stacking", "Stacking Options")->SetProperties( cmtk::CommandLine::PROPS_ADVANCED ); cl.AddSwitch( Key( "write-single-slices" ), &WriteSingleSlices, true, "Also write output images for single-slice DICOM files that could not be assigned to any 3D stacks. By default, these are skipped." ); cl.AddSwitch( Key( "ignore-acq-number" ), &IgnoreAcquisitionNumber, true, "Ignore 'AcquisitionNumber' tag for image grouping, i.e., do not separate stacks based on this tag." ); cl.AddSwitch( Key( "no-orientation-check" ), &DisableOrientationCheck, true, "Disable checking of image orientations (to avoid rounding issues)" ); cl.AddOption( Key( "tolerance" ), &Tolerance, "Tolerance for floating-point comparisons (must be >= 0; 0 = exact matches only; default: 1e-5). " "If one or more volumes cannot be stacked because of non-uniform slice spacing that exceeds this threshold, dcm2image will return an exit code of 3." ); cl.EndGroup(); cl.AddParameterVector( &SearchRootDirVector, "SearchDirList", "List of directories to search for DICOM files. Subdirectories are also search if '--recurse' option is used." ); cl.Parse( argc, const_cast( argv ) ); } catch ( const cmtk::CommandLine::Exception& ex ) { cmtk::StdErr << ex << "\n"; throw cmtk::ExitException( 1 ); } if ( !dcmDataDict.rdlock().findEntry( "RawDataType_ImageType" ) ) { dcmDataDict.unlock(); dcmDataDict.wrlock().addEntry( new DcmDictEntry( 0x0043, 0x102f, EVR_SS, "RawDataType_ImageType", 1, 1, NULL, OFFalse, "GE" ) ); dcmDataDict.unlock(); } VolumeList volumeList( Tolerance ); for ( std::vector::const_iterator it = SearchRootDirVector.begin(); it != SearchRootDirVector.end(); ++it ) { volumeList.AppendFromDirectory( *it, "*" ); } return volumeList.WriteVolumes(); } #include "cmtkSafeMain" cmtk-3.3.1/apps/describe.cxx000066400000000000000000000350021276303427400157330ustar00rootroot00000000000000/* // // Copyright 1997-2010 Torsten Rohlfing // // Copyright 2004-2013 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4818 $ // // $LastChangedDate: 2013-09-10 11:28:54 -0700 (Tue, 10 Sep 2013) $ // // $LastChangedBy: torstenrohlfing $ // */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include const char* ReadOrientation = NULL; bool MachineReadable = false; void DescribeImage( const char* path ) { cmtk::UniformVolume::SmartPtr volume; try { if ( ReadOrientation ) volume = cmtk::VolumeIO::ReadOriented( path, ReadOrientation ); else volume = cmtk::VolumeIO::Read( path ); } catch (...) { if ( ReadOrientation ) volume = cmtk::VolumeIO::ReadGridOriented( path, ReadOrientation ); else volume = cmtk::VolumeIO::ReadGrid( path ); } const char* orientOriginal = volume->GetMetaInfo( cmtk::META_IMAGE_ORIENTATION_ORIGINAL ).c_str(); const cmtk::TypedArray* dataArray = volume->GetData(); if ( MachineReadable ) { fprintf( stdout, "FNAME\t%s\n", path ); fprintf( stdout, "FORMAT\t%s\n", volume->GetMetaInfo( cmtk::META_FILEFORMAT_ORIGINAL ).c_str() ); if ( volume->MetaKeyExists( cmtk::META_IMAGE_DESCRIPTION ) ) fprintf( stdout, "DESCRIP\t\"%s\"\n", volume->GetMetaInfo( cmtk::META_IMAGE_DESCRIPTION ).c_str() ); fprintf( stdout, "XDIM\t%d\nYDIM\t%d\nZDIM\t%d\n", volume->GetDims()[cmtk::AXIS_X], volume->GetDims()[cmtk::AXIS_Y], volume->GetDims()[cmtk::AXIS_Z] ); fprintf( stdout, "ORIENT\t%s\n", orientOriginal ? orientOriginal : "UNKNOWN" ); fprintf( stdout, "GRID\tUniform\nXPIX\t%f\nYPIX\t%f\nZPIX\t%f\nXFOV\t%f\nYFOV\t%f\nZFOV\t%f\n", volume->m_Delta[0], volume->m_Delta[1], volume->m_Delta[2], volume->m_Size[0], volume->m_Size[1], volume->m_Size[2] ); fprintf( stdout, "XORIGIN\t%f\nYORIGIN\t%f\nZORIGIN\t%f\n", volume->m_Offset[0], volume->m_Offset[1], volume->m_Offset[2] ); if ( volume->MetaKeyExists(cmtk::META_SPACE_UNITS_STRING ) ) fprintf( stdout, "UNITS\t%s\n", volume->GetMetaInfo( cmtk::META_SPACE_UNITS_STRING ).c_str() ); const cmtk::AffineXform::MatrixType a2p = volume->GetImageToPhysicalMatrix(); fprintf( stdout, "I2PMAT0\t%f\t%f\t%f\t%f\nI2PMAT1\t%f\t%f\t%f\t%f\nI2PMAT2\t%f\t%f\t%f\t%f\nI2PMAT3\t%f\t%f\t%f\t%f\n", a2p[0][0], a2p[1][0], a2p[2][0], a2p[3][0], a2p[0][1], a2p[1][1], a2p[2][1], a2p[3][1], a2p[0][2], a2p[1][2], a2p[2][2], a2p[3][2], a2p[0][3], a2p[1][3], a2p[2][3], a2p[3][3] ); if ( dataArray ) { fprintf( stdout, "DTYPE\t%s\n", cmtk::DataTypeName[ dataArray->GetType() ] ); if ( dataArray->GetDataSize() ) { const cmtk::Types::DataItemRange range = dataArray->GetRange(); fprintf( stdout, "MINDATA\t%f\nMAXDATA\t%f\n", static_cast( range.m_LowerBound ), static_cast( range.m_UpperBound ) ); } } } else { fprintf( stdout, "File: %s\n", path ); fprintf( stdout, "File format: %s\n", volume->GetMetaInfo( cmtk::META_FILEFORMAT_ORIGINAL ).c_str() ); if ( volume->MetaKeyExists( cmtk::META_IMAGE_DESCRIPTION ) ) fprintf( stdout, "Description: \"%s\"\n", volume->GetMetaInfo( cmtk::META_IMAGE_DESCRIPTION ).c_str() ); fprintf( stdout, "%d x %d x %d voxels\n", volume->GetDims()[cmtk::AXIS_X], volume->GetDims()[cmtk::AXIS_Y], volume->GetDims()[cmtk::AXIS_Z] ); fprintf( stdout, "Original image orientation: %s\n", orientOriginal ? orientOriginal : "UNKNOWN" ); const char* spaceUnits = ""; if ( volume->MetaKeyExists(cmtk::META_SPACE_UNITS_STRING ) ) spaceUnits = volume->GetMetaInfo( cmtk::META_SPACE_UNITS_STRING ).c_str(); fprintf( stdout, "Uniform volume\n%f x %f x %f [%s] voxel size\n%f x %f x %f [%s] volume size\n", volume->m_Delta[0], volume->m_Delta[1], volume->m_Delta[2], spaceUnits, volume->m_Size[0], volume->m_Size[1], volume->m_Size[2], spaceUnits ); fprintf( stdout, "Volume origin (%f,%f,%f)\n", volume->m_Offset[0], volume->m_Offset[1], volume->m_Offset[2] ); const cmtk::AffineXform::MatrixType a2p = volume->GetImageToPhysicalMatrix(); fprintf( stdout, "\nImage-to-physical matrix:\n %f %f %f %f \n %f %f %f %f \n %f %f %f %f \n %f %f %f %f \n\n", a2p[0][0], a2p[1][0], a2p[2][0], a2p[3][0], a2p[0][1], a2p[1][1], a2p[2][1], a2p[3][1], a2p[0][2], a2p[1][2], a2p[2][2], a2p[3][2], a2p[0][3], a2p[1][3], a2p[2][3], a2p[3][3] ); if ( dataArray ) { cmtk::StdOut.printf( "Data type %s", cmtk::DataTypeName[ dataArray->GetType() ] ); if ( dataArray->GetDataSize() ) { const cmtk::Types::DataItemRange range = dataArray->GetRange(); cmtk::StdOut.printf( ", range [%f .. %f]\n", static_cast( range.m_LowerBound ), static_cast( range.m_UpperBound ) ); } else { cmtk::StdOut << "\n"; } } else { cmtk::StdOut << "Image does not contain valid data.\n"; } } } void DescribeWarpXform( const cmtk::WarpXform& warpXform ) { if ( MachineReadable ) { cmtk::StdOut << "DOMAINX\t" << warpXform.m_Domain[0] << "\n"; cmtk::StdOut << "DOMAINY\t" << warpXform.m_Domain[1] << "\n"; cmtk::StdOut << "DOMAINZ\t" << warpXform.m_Domain[2] << "\n"; cmtk::StdOut << "GRIDX\t" << warpXform.m_Dims[0] << "\n"; cmtk::StdOut << "GRIDY\t" << warpXform.m_Dims[1] << "\n"; cmtk::StdOut << "GRIDZ\t" << warpXform.m_Dims[2] << "\n"; cmtk::StdOut << "DELTAX\t" << warpXform.m_Spacing[0] << "\n"; cmtk::StdOut << "DELTAY\t" << warpXform.m_Spacing[1] << "\n"; cmtk::StdOut << "DELTAZ\t" << warpXform.m_Spacing[2] << "\n"; cmtk::StdOut << "OFFSETX\t" << warpXform.m_Offset[0] << "\n"; cmtk::StdOut << "OFFSETY\t" << warpXform.m_Offset[1] << "\n"; cmtk::StdOut << "OFFSETZ\t" << warpXform.m_Offset[2] << "\n"; cmtk::AffineXform::SmartConstPtr initialAffine = warpXform.GetInitialAffineXform(); if ( initialAffine ) { cmtk::StdOut << "BULK_XLATEX\t" << initialAffine->RetXlate()[0] << "\n"; cmtk::StdOut << "BULK_XLATEY\t" << initialAffine->RetXlate()[1] << "\n"; cmtk::StdOut << "BULK_XLATEZ\t" << initialAffine->RetXlate()[2] << "\n"; cmtk::StdOut << "BULK_ANGLEX\t" << initialAffine->RetAngles()[0] << "\n"; cmtk::StdOut << "BULK_ANGLEY\t" << initialAffine->RetAngles()[1] << "\n"; cmtk::StdOut << "BULK_ANGLEZ\t" << initialAffine->RetAngles()[2] << "\n"; cmtk::StdOut << "BULK_SCALEX\t" << initialAffine->RetScales()[0] << "\n"; cmtk::StdOut << "BULK_SCALEY\t" << initialAffine->RetScales()[1] << "\n"; cmtk::StdOut << "BULK_SCALEZ\t" << initialAffine->RetScales()[2] << "\n"; cmtk::StdOut << "BULK_SHEARX\t" << initialAffine->RetShears()[0] << "\n"; cmtk::StdOut << "BULK_SHEARY\t" << initialAffine->RetShears()[1] << "\n"; cmtk::StdOut << "BULK_SHEARZ\t" << initialAffine->RetShears()[2] << "\n"; } } else { cmtk::StdOut << "Domain: " << warpXform.m_Domain[0] << " x " << warpXform.m_Domain[1] << " x " << warpXform.m_Domain[2] << "\n"; cmtk::StdOut << "Control point grid: " << warpXform.m_Dims[0] << " x " << warpXform.m_Dims[1] << " x " << warpXform.m_Dims[2] << "\n"; cmtk::StdOut << "Control point spacing: " << warpXform.m_Spacing[0] << " x " << warpXform.m_Spacing[1] << " x " << warpXform.m_Spacing[2] << "\n"; cmtk::StdOut << "First control point offset: " << warpXform.m_Offset[0] << " x " << warpXform.m_Offset[1] << " x " << warpXform.m_Offset[2] << "\n"; cmtk::AffineXform::SmartConstPtr initialAffine = warpXform.GetInitialAffineXform(); if ( initialAffine ) { cmtk::StdOut << "Bulk affine transformation:" << "\n"; cmtk::StdOut << "\tTranslation: " << initialAffine->RetXlate()[0] << ", " << initialAffine->RetXlate()[1] << ", " << initialAffine->RetXlate()[2] << "\n"; cmtk::StdOut << "\tRotation angles: " << initialAffine->RetAngles()[0] << ", " << initialAffine->RetAngles()[1] << ", " << initialAffine->RetAngles()[2] << "\n"; cmtk::StdOut << "\tScale factors: " << initialAffine->RetScales()[0] << ", " << initialAffine->RetScales()[1] << ", " << initialAffine->RetScales()[2] << "\n"; cmtk::StdOut << "\tShear coefficients: " << initialAffine->RetShears()[0] << ", " << initialAffine->RetShears()[1] << ", " << initialAffine->RetShears()[2] << "\n"; } } } void DescribeXform( const char* path ) { cmtk::Xform::SmartConstPtr xform = cmtk::XformIO::Read( path ); if ( ! xform ) { cmtk::StdErr << "Could not read transformation from " << path << "\n"; } const std::string fPath = xform->GetMetaInfo( cmtk::META_XFORM_FIXED_IMAGE_PATH, "" ); const std::string mPath = xform->GetMetaInfo( cmtk::META_XFORM_MOVING_IMAGE_PATH, "" ); if ( MachineReadable ) { cmtk::StdOut << "PATH\t" << path << "\n"; cmtk::StdOut << "NPARAMS\t" << xform->ParamVectorDim() << "\n"; if ( fPath != "" ) cmtk::StdOut << "FXIMAGE\t" << fPath << "\n"; if ( mPath != "" ) cmtk::StdOut << "MVIMAGE\t" << mPath << "\n"; } else { cmtk::StdOut << "File: " << path << "\n"; cmtk::StdOut << "Number of parameters: " << xform->ParamVectorDim() << "\n"; if ( fPath != "" ) cmtk::StdOut << "Fixed image path: " << fPath << "\n"; if ( mPath != "" ) cmtk::StdOut << "Moving image path: " << mPath << "\n"; } cmtk::SplineWarpXform::SmartConstPtr splineWarpXform = cmtk::SplineWarpXform::SmartConstPtr::DynamicCastFrom( xform ); if ( splineWarpXform ) { if ( MachineReadable ) { cmtk::StdOut << "MODEL\tBSPLINE\n"; } else { cmtk::StdOut << "Transformation model: B-spline free-form deformation\n"; } DescribeWarpXform( *splineWarpXform ); } cmtk::DeformationField::SmartConstPtr dfield = cmtk::DeformationField::SmartConstPtr::DynamicCastFrom( xform ); if ( dfield ) { if ( MachineReadable ) { cmtk::StdOut << "MODEL\tDFIELD\n"; } else { cmtk::StdOut << "Transformation model: Deformation field\n"; } DescribeWarpXform( *dfield ); } cmtk::AffineXform::SmartConstPtr affine = cmtk::AffineXform::SmartConstPtr::DynamicCastFrom( xform ); if ( affine ) { if ( MachineReadable ) { cmtk::StdOut << "MODEL\tAFFINE\n"; cmtk::StdOut << "XLATEX\t" << affine->RetXlate()[0] << "\n"; cmtk::StdOut << "XLATEY\t" << affine->RetXlate()[1] << "\n"; cmtk::StdOut << "XLATEZ\t" << affine->RetXlate()[2] << "\n"; cmtk::StdOut << "ANGLEX\t" << affine->RetAngles()[0] << "\n"; cmtk::StdOut << "ANGLEY\t" << affine->RetAngles()[1] << "\n"; cmtk::StdOut << "ANGLEZ\t" << affine->RetAngles()[2] << "\n"; cmtk::StdOut << "SCALEX\t" << affine->RetScales()[0] << "\n"; cmtk::StdOut << "SCALEY\t" << affine->RetScales()[1] << "\n"; cmtk::StdOut << "SCALEZ\t" << affine->RetScales()[2] << "\n"; cmtk::StdOut << "SHEARX\t" << affine->RetShears()[0] << "\n"; cmtk::StdOut << "SHEARY\t" << affine->RetShears()[1] << "\n"; cmtk::StdOut << "SHEARZ\t" << affine->RetShears()[2] << "\n"; } else { cmtk::StdOut << "Transformation model: Affine\n"; cmtk::StdOut << "Translation: " << affine->RetXlate()[0] << ", " << affine->RetXlate()[1] << ", " << affine->RetXlate()[2] << "\n"; cmtk::StdOut << "Rotation angles: " << affine->RetAngles()[0] << ", " << affine->RetAngles()[1] << ", " << affine->RetAngles()[2] << "\n"; cmtk::StdOut << "Scale factors: " << affine->RetScales()[0] << ", " << affine->RetScales()[1] << ", " << affine->RetScales()[2] << "\n"; cmtk::StdOut << "Shear coefficients: " << affine->RetShears()[0] << ", " << affine->RetShears()[1] << ", " << affine->RetShears()[2] << "\n"; } } cmtk::StdOut << "\n"; } int doMain( int argc, const char *argv[] ) { try { cmtk::CommandLine cl; cl.SetProgramInfo( cmtk::CommandLine::PRG_TITLE, "Describe image and transformation file formats and parameters" ); cl.SetProgramInfo( cmtk::CommandLine::PRG_DESCR, "This tool prints a detailed description of the input files as either image(s) or transformation(s)." ); cl.SetProgramInfo( cmtk::CommandLine::PRG_SYNTX, "describe [options] file0 [file1 ...]" ); typedef cmtk::CommandLine::Key Key; cl.AddSwitch( Key( 'm', "machine-readable" ), &MachineReadable, true, "Print output in format that is easy to parse automatically." ); cl.AddSwitch( Key( "read-ras" ), &ReadOrientation, "RAS", "Read all images in RAS orientation" ); cl.Parse( argc, const_cast( argv ) ); for ( const char* next = cl.GetNext(); next; next = cl.GetNextOptional() ) { const cmtk::FileFormatID id = cmtk::FileFormat::Identify( next ); switch ( id ) { case cmtk::FILEFORMAT_NEXIST: cmtk::StdErr << "File does not exist: " << next << "\n"; break; case cmtk::FILEFORMAT_UNKNOWN: cmtk::StdErr << "Unknown file format: " << next << "\n"; break; default: if ( cmtk::FileFormat::IsImage( id ) ) { DescribeImage( next ); } else if ( cmtk::FileFormat::IsXform( id ) ) { DescribeXform( next ); } else { cmtk::StdErr << "File is neither image nor transformation: " << next << "\n"; } } } } catch ( const cmtk::CommandLine::Exception& e ) { cmtk::StdErr << e << "\n"; throw cmtk::ExitException( 1 ); } return 0; } #include "cmtkSafeMain" cmtk-3.3.1/apps/destripe.cxx000066400000000000000000000155131276303427400157770ustar00rootroot00000000000000/* // // Copyright 2011, 2013 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 3001 $ // // $LastChangedDate: 2011-03-16 12:14:22 -0700 (Wed, 16 Mar 2011) $ // // $LastChangedBy: torstenrohlfing $ // */ #include #include #include #include #include #include #include #include #include int SliceAxis = -1; double KernelFWHM = 1; int TruncateKernel = 0; bool WriteFloat = false; std::string InputFilePath; std::string OutputFilePath; int doMain ( const int argc, const char* argv[] ) { try { cmtk::CommandLine cl; cl.SetProgramInfo( cmtk::CommandLine::PRG_TITLE, "Destripe volume image data." ); cl.SetProgramInfo( cmtk::CommandLine::PRG_DESCR, "This program corrects stripe artifacts in acquired image stacks which can result from between-slice intensity scale differences." ); typedef cmtk::CommandLine::Key Key; cl.BeginGroup( "SliceOrient", "Slice Orientation" ); cmtk::CommandLine::EnumGroup::SmartPtr sliceGroup = cl.AddEnum( "slice-axis", &SliceAxis, "Define slice direction axis: this is the through-slice direction of the acquisition." ); sliceGroup->AddSwitch( Key( "guess-from-input" ), -1, "Guess from input image" ); sliceGroup->AddSwitch( Key( 'a', "axial" ), (int)cmtk::AXIS_Z, "Sliced axial images" ); sliceGroup->AddSwitch( Key( 's', "sagittal" ),(int)cmtk::AXIS_X, "Sliced sagittal images" ); sliceGroup->AddSwitch( Key( 'c', "coronal" ), (int)cmtk::AXIS_Y, "Sliced coronal images" ); sliceGroup->AddSwitch( Key( 'x', "slice-x" ), (int)cmtk::AXIS_X, "Sliced along x axis" ); sliceGroup->AddSwitch( Key( 'y', "slice-y" ), (int)cmtk::AXIS_Y, "Sliced along y axis" ); sliceGroup->AddSwitch( Key( 'z', "slice-z" ), (int)cmtk::AXIS_Z, "Sliced along z axis" ); cl.AddOption( Key( "kernel-fwhm" ), &KernelFWHM, "Gaussian kernel full width at half maximum (FWHM) in units of slice indexes." ); cl.AddOption( Key( "kernel-radius" ), &TruncateKernel, "Truncate kernel radius to this many samples. By default, kernel width is determined to limit approximation error." ); cl.AddSwitch( Key( "write-float" ), &WriteFloat, true, "Write output image in floating point representation. By default, write using input image pixel data type." ); cl.AddParameter( &InputFilePath, "InputImage", "Input image path" )->SetProperties( cmtk::CommandLine::PROPS_IMAGE ); cl.AddParameter( &OutputFilePath, "OutputImage", "Output image path" )->SetProperties( cmtk::CommandLine::PROPS_IMAGE | cmtk::CommandLine::PROPS_OUTPUT | cmtk::CommandLine::PROPS_OPTIONAL ); cl.Parse( argc, argv ); } catch ( const cmtk::CommandLine::Exception& ex ) { cmtk::StdErr << ex << "\n"; return 1; } cmtk::UniformVolume::SmartPtr volume( cmtk::VolumeIO::ReadOriented( InputFilePath ) ); if ( ! volume ) { cmtk::StdErr << "ERROR: could not read volume " << InputFilePath << "\n"; return 1; } if ( WriteFloat ) { volume->GetData()->Convert( cmtk::TYPE_FLOAT ); } const cmtk::DataGrid::IndexType volumeDims = volume->GetDims(); // guess slice orientation - if two dimensions are equal, the thir is usually the slice direction if ( SliceAxis == -1 ) { if ( volumeDims[0] == volumeDims[1] ) SliceAxis = 2; else if ( volumeDims[0] == volumeDims[2] ) SliceAxis = 1; else if ( volumeDims[1] == volumeDims[2] ) SliceAxis = 0; else { cmtk::StdErr << "ERROR: cannot guess slice axis when all three image dimensions are different.\n"; return 1; } } const int nSlices = volumeDims[SliceAxis]; std::vector sliceProjection( nSlices ); const int idxX = (SliceAxis==1 || SliceAxis==2) ? 0 : 1; const int idxY = (SliceAxis==0 || SliceAxis==2) ? 1 : 2; #pragma omp parallel for for ( int slice = 0; slice < nSlices; ++slice ) { cmtk::DataGrid::IndexType idx; idx[SliceAxis] = slice; cmtk::Types::DataItem sum = 0; size_t count = 0; cmtk::Types::DataItem value; for ( idx[idxX] = 0; idx[idxX] < volumeDims[idxX]; ++idx[idxX] ) { for ( idx[idxY] = 0; idx[idxY] < volumeDims[idxY]; ++idx[idxY] ) { if ( volume->GetDataAt( value, volume->GetOffsetFromIndex( idx ) ) ) { sum += value; ++count; } } } if ( count ) sliceProjection[slice] = sum / count; else sliceProjection[slice] = 0; } const std::vector kernel = cmtk::GaussianKernel::GetHalfKernel( cmtk::Units::GaussianFWHM( KernelFWHM ) ); if ( !TruncateKernel ) TruncateKernel = kernel.size()-1; std::vector smoothed( nSlices, 0.0 ); for ( int slice = 0; slice < nSlices; ++ slice ) { cmtk::Types::DataItem kernelSum = kernel[0]; smoothed[slice] = kernel[0] * sliceProjection[slice]; for ( int ofs = 1; ofs <= TruncateKernel; ++ofs ) { if ( slice - ofs >= 0 ) { kernelSum += kernel[ofs]; smoothed[slice] += kernel[ofs] * sliceProjection[slice-ofs]; } if ( slice + ofs < nSlices ) { kernelSum += kernel[ofs]; smoothed[slice] += kernel[ofs] * sliceProjection[slice+ofs]; } } smoothed[slice] /= kernelSum; } #pragma omp parallel for for ( int slice = 0; slice < nSlices; ++slice ) { cmtk::DataGrid::IndexType idx; idx[SliceAxis] = slice; const cmtk::Types::DataItem correction = smoothed[slice] / sliceProjection[slice]; cmtk::Types::DataItem value; for ( idx[idxX] = 0; idx[idxX] < volumeDims[idxX]; ++idx[idxX] ) { for ( idx[idxY] = 0; idx[idxY] < volumeDims[idxY]; ++idx[idxY] ) { if ( volume->GetDataAt( value, volume->GetOffsetFromIndex( idx ) ) ) { volume->SetDataAt( value * correction, volume->GetOffsetFromIndex( idx ) ); } } } } if ( ! OutputFilePath.empty() ) cmtk::VolumeIO::Write( *volume, OutputFilePath ); return 0; } #include "cmtkSafeMain" cmtk-3.3.1/apps/detect_adni_phantom.cxx000066400000000000000000000216001276303427400201430ustar00rootroot00000000000000/* // // Copyright 1997-2010 Torsten Rohlfing // // Copyright 2004-2014 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 5344 $ // // $LastChangedDate: 2014-05-01 14:23:38 -0700 (Thu, 01 May 2014) $ // // $LastChangedBy: torstenrohlfing $ // */ #include #include #include #include #include #include #include #include #include #include int doMain( const int argc, const char* argv[] ) { std::string inputPath; std::string outputPath; std::string outputLabelPath; std::string outputRigidPath; std::string outputAffinePath; cmtk::DetectPhantomMagphanEMR051::Parameters detectionParameters; try { cmtk::CommandLine cl; cl.SetProgramInfo( cmtk::CommandLine::PRG_TITLE, "Detect ADNI phantom landmarks in phantom image" ); cl.SetProgramInfo( cmtk::CommandLine::PRG_DESCR, "This tool detects the locations of all spherical landmarks in a 3D image of the Magphan EMR051 structural imaging phantom (a.k.a. ADNI Phantom)." ); typedef cmtk::CommandLine::Key Key; cl.BeginGroup( "Detection", "Phantom Detection Options" ); cl.AddSwitch( Key( "tolerant" ), &detectionParameters.m_TolerateTruncation, true, "Be tolerant of issues such as partially truncated marker spheres. " "This should be used with caution only when necessary, and both the phantom image and detection results should be carefully inspected to identify the source of detection problems and verify reliable results." ); cl.AddSwitch( Key( "any-orientation" ), &detectionParameters.m_StandardOrientation, false, "Do not assume standard orientation of the phantom, i.e., allow phantoms scanned upside-down. This makes detection of defective phantoms less robust." ); cl.AddSwitch( Key( "fallback-cnr-centroid" ), &detectionParameters.m_ForceFallbackCentroidCNR, true, "Force a fallback to use centroid of CNR spheres rather than center of SNR sphere for initial orientation. " "This can be used when, for example, the SNR sphere has broken off but is positioned in a way that does not trigger the automatic fallback." ); cl.AddOption( Key( "erode-snr" ), &detectionParameters.m_ErodeSNR, "Erode SNR sphere by this distance prior to computing SNR estimate." ); cl.AddOption( Key( "erode-cnr" ), &detectionParameters.m_ErodeCNR, "Erode each CNR sphere by this distance prior to computing CNR estimate." ); cl.AddSwitch( Key( "refine-xform" ), &detectionParameters.m_RefineXformEachLandmark, true, "Refine estimated affine transformation after each new landmark is added." ); cl.AddSwitch( Key( "refine-outliers" ), &detectionParameters.m_RefineOutliers, true, "Refine outlier landmarks based on estimated transformation after first sphere detection pass." ); cl.AddSwitch( Key( "exclude-outliers" ), &detectionParameters.m_ExcludeOutliers, true, "Exclude outlier landmarks before fitting final transformations." ); cl.AddSwitch( Key( "no-bias-correct-spheres" ), &detectionParameters.m_CorrectSphereBiasField, false, "Disable intensity bias field correction for each detected sphere. This will likely reduce accuracy of SNR/CNR estimates and also affect " "localication accuracy of smaller spheres, but may be helpful in extreme cases where bias correction fails completely." )->SetProperties( cmtk::CommandLine::PROPS_ADVANCED ); cl.EndGroup(); cl.BeginGroup( "Output", "Output Options" ); cl.AddOption( Key( "write-labels" ), &outputLabelPath, "Output label image path. This image contains the mask of detected spheres, each labeled uniquely in their order in CMTK's ADNI phantom fiducial table." ) ->SetProperties( cmtk::CommandLine::PROPS_IMAGE | cmtk::CommandLine::PROPS_OUTPUT ); cl.AddOption( Key( "write-rigid" ), &outputRigidPath, "Output path for the fitted rigid transformation from phantom space into RAS image standard space. This transformation defines where each sphere should be in the image." ) ->SetProperties( cmtk::CommandLine::PROPS_XFORM | cmtk::CommandLine::PROPS_OUTPUT ); cl.AddOption( Key( "write-affine" ), &outputAffinePath, "Output path for the fitted affine transformation from phantom space into RAS image standard space. This is the closest linear-fit transformation, " "and as such it includes scale and shear components not present in the fitted rigid transformations. Since these components are due to scanner miscalibration and distortion, this transformation DOES NOT " "specify the correct spehre locations in the image, but rather, allows for quantification of scale miscalibration.") ->SetProperties( cmtk::CommandLine::PROPS_XFORM | cmtk::CommandLine::PROPS_OUTPUT ); cl.EndGroup(); cl.AddParameter( &inputPath, "InputImage", "Input image path. This is the image in which spheres are detected." )->SetProperties( cmtk::CommandLine::PROPS_IMAGE ); cl.AddParameter( &outputPath, "OutputXML", "Output path for the XML file describing the dected phantom." )->SetProperties( cmtk::CommandLine::PROPS_OUTPUT ); cl.Parse( argc, argv ); } catch ( const cmtk::CommandLine::Exception& e ) { cmtk::StdErr << e << "\n"; return 1; } cmtk::UniformVolume::SmartPtr phantomImage( cmtk::VolumeIO::ReadOriented( inputPath ) ); try { cmtk::DetectPhantomMagphanEMR051 detectionFilter( phantomImage, detectionParameters ); // get expected and actual landmark locations cmtk::LandmarkList expectedLandmarks = detectionFilter.GetExpectedLandmarks(); cmtk::LandmarkList detectedLandmarks = detectionFilter.GetDetectedLandmarks(); try { // bring expected landmark locations from phantom image into physical space const cmtk::AffineXform phantomToPhysical( phantomImage->GetImageToPhysicalMatrix() ); for ( cmtk::LandmarkList::Iterator it = expectedLandmarks.begin(); it != expectedLandmarks.end(); ++it ) { it->m_Location = phantomToPhysical.Apply( it->m_Location ); } // bring detected landmark locations from phantom image into physical space for ( cmtk::LandmarkList::Iterator it = detectedLandmarks.begin(); it != detectedLandmarks.end(); ++it ) { it->m_Location = phantomToPhysical.Apply( it->m_Location ); } } catch ( const cmtk::AffineXform::MatrixType::SingularMatrixException& ) { cmtk::StdErr << "ERROR: singular image-to-physical space matrix; cannot map landmarks to image space.\n"; throw cmtk::ExitException( 1 ); } // match expected and detected landmarks cmtk::LandmarkPairList pairList( expectedLandmarks, detectedLandmarks ); cmtk::DebugOutput( 2 ) << "INFO: detected and matched " << pairList.size() << " out of " << expectedLandmarks.size() << " expected landmarks.\n"; if ( ! outputPath.empty() ) cmtk::PhantomIO::Write( *(detectionFilter.GetDetectedPhantom()), outputPath ); if ( ! outputLabelPath.empty() ) cmtk::VolumeIO::Write( *(detectionFilter.GetDetectedSpheresLabelMap()), outputLabelPath ); if ( ! outputAffinePath.empty() ) cmtk::XformIO::Write( detectionFilter.GetPhantomToImageTransformationAffine(), outputAffinePath ); if ( ! outputRigidPath.empty() ) cmtk::XformIO::Write( detectionFilter.GetPhantomToImageTransformationRigid(), outputRigidPath ); } catch ( const cmtk::DetectPhantomMagphanEMR051::OutsideFieldOfView& ex ) { cmtk::StdErr << "ERROR: estimated location " << ex.m_Location << " puts landmark #" << ex.m_Idx << " (partly) outside image field of view.\n"; throw cmtk::ExitException( 3 ); } catch ( const cmtk::DetectPhantomMagphanEMR051::NoSphereInSearchRegion& ) { cmtk::StdErr << "ERROR: unable to find sphere near expected location - most likely insufficient field of view.\n"; throw cmtk::ExitException( 3 ); } catch ( const cmtk::AffineXform::MatrixType::SingularMatrixException& ) { cmtk::StdErr << "ERROR: singular matrix in cmtk::DetectPhantomMagphanEMR051 constructor\n"; throw cmtk::ExitException( 1 ); } return 0; } #include "cmtkSafeMain" cmtk-3.3.1/apps/detect_spheres_matched_filter.cxx000066400000000000000000000067371276303427400222230ustar00rootroot00000000000000/* // // Copyright 1997-2010 Torsten Rohlfing // // Copyright 2004-2013 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4834 $ // // $LastChangedDate: 2013-09-12 15:15:51 -0700 (Thu, 12 Sep 2013) $ // // $LastChangedBy: torstenrohlfing $ // */ #include #include #include #include #include #include int doMain( const int argc, const char* argv[] ) { std::string inputPath; std::string outputPath; cmtk::Types::Coordinate sphereRadius = 1; int filterMargin = 2; bool normalized = false; try { cmtk::CommandLine cl; cl.SetProgramInfo( cmtk::CommandLine::PRG_TITLE, "Detect spheres" ); cl.SetProgramInfo( cmtk::CommandLine::PRG_DESCR, "This tool detects spherical objects in three-dimensional images." ); typedef cmtk::CommandLine::Key Key; cl.AddOption( Key( 'r', "radius" ), &sphereRadius, "Radius of spheres to detect in physical length units (typically mm)." ); cl.AddOption( Key( "filter-margin" ), &filterMargin, "Half of filter margin width in pixels. This determines the extent of the region around the surface or the sphere where the detection filter is non-zero." ); cl.AddSwitch( Key( "normalized" ), &normalized, true, "Use intensity-normalized filter, effectively computing Normalized Cross Correlation between filter and image." ); cl.AddParameter( &inputPath, "InputImage", "Input image path. This is the image in which spheres are detected." ) ->SetProperties( cmtk::CommandLine::PROPS_IMAGE ); cl.AddParameter( &outputPath, "OutputImage", "Output image path. This image contains the magnitude filter response of the input image with respect to a matched, bipolar spherical correlation filter." ) ->SetProperties( cmtk::CommandLine::PROPS_IMAGE | cmtk::CommandLine::PROPS_OUTPUT ); cl.Parse( argc, argv ); } catch ( const cmtk::CommandLine::Exception& e ) { cmtk::StdErr << e << "\n"; return 1; } cmtk::UniformVolume::SmartPtr volume( cmtk::VolumeIO::ReadOriented( inputPath ) ); if ( normalized ) { cmtk::SphereDetectionNormalizedBipolarMatchedFilterFFT detectionFilter( *volume ); volume->SetData( detectionFilter.GetFilteredImageData( sphereRadius, filterMargin ) ); } else { cmtk::SphereDetectionBipolarMatchedFilterFFT detectionFilter( *volume ); volume->SetData( detectionFilter.GetFilteredImageData( sphereRadius, filterMargin ) ); } cmtk::VolumeIO::Write( *volume, outputPath ); return 0; } #include "cmtkSafeMain" cmtk-3.3.1/apps/dof2mat.cxx000066400000000000000000000056361276303427400155210ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2014 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 5296 $ // // $LastChangedDate: 2014-04-07 11:08:10 -0700 (Mon, 07 Apr 2014) $ // // $LastChangedBy: torstenrohlfing $ // */ #include #include #include #include #include #include #include int doMain( const int argc, const char* argv[] ) { std::string inputFileName; bool transpose = false; bool matrix3x3 = false; try { cmtk::CommandLine cl; cl.SetProgramInfo( cmtk::CommandLine::PRG_TITLE, "Degrees of freedom to matrix" ); cl.SetProgramInfo( cmtk::CommandLine::PRG_DESCR, "Convert affine transformation from degrees-of-freedom representation to matrix form" ); cl.AddParameter( &inputFileName, "InputPath", "Path to input affine transformation." )->SetProperties( cmtk::CommandLine::PROPS_XFORM ); typedef cmtk::CommandLine::Key Key; cl.BeginGroup( "Output", "Output Options" ); cl.AddSwitch( Key( '3', "matrix3x3" ), &matrix3x3, true, "Only the top-left 3x3 sub- matrix (rotation/scale/shear)." ); cl.AddSwitch( Key( "transpose" ), &transpose, true, "Print transpose of transformation matrix." ); cl.EndGroup(); cl.Parse( argc, argv ); } catch ( const cmtk::CommandLine::Exception& e ) { cmtk::StdErr << e; throw cmtk::ExitException( 1 ); } if ( ! inputFileName.empty() ) { cmtk::AffineXform::SmartConstPtr affineXform = cmtk::AffineXform::SmartPtr::DynamicCastFrom( cmtk::XformIO::Read( inputFileName ) ); const size_t printToDim = matrix3x3 ? 3 : 4; if ( affineXform ) { for ( size_t j = 0; j < printToDim; ++j ) { for ( size_t i = 0; i < printToDim; ++i ) { if ( transpose ) { cmtk::StdOut << affineXform->Matrix[i][j] << "\t"; } else { cmtk::StdOut << affineXform->Matrix[j][i] << "\t"; } } cmtk::StdOut << "\n"; } } } return 0; } #include "cmtkSafeMain" cmtk-3.3.1/apps/dwi_mask_bad_slices.cxx000066400000000000000000000207221276303427400201240ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2013 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 3844 $ // // $LastChangedDate: 2012-02-10 15:46:46 -0800 (Fri, 10 Feb 2012) $ // // $LastChangedBy: torstenrohlfing $ // */ #include #include #include #include #include #include #include #include #include #include int doMain ( const int argc, const char *argv[] ) { std::vector dwiImagePaths; int b0ImageCount = 0; int sliceAxis = cmtk::AXIS_Z; cmtk::Types::DataItem standardDeviations = 3.0; const char* outputDir = NULL; cmtk::Types::DataItem paddingValue = -1; cmtk::ScalarDataType outputDataType = cmtk::TYPE_NONE; try { cmtk::CommandLine cl; cl.SetProgramInfo( cmtk::CommandLine::PRG_TITLE, "Find bad slices in set of diffusion-weighted images." ); cl.SetProgramInfo( cmtk::CommandLine::PRG_DESCR, "This tool reads a set of 3D diffusion-weighted MR images and finds bad slices. A bad slice in a diffusion image is detected as one whose mean intensity is outside a specified " "interval around the mean of the means of all corresponding slices from the remaining diffusion images." ); cl.AddParameterVector( &dwiImagePaths, "DiffusionImagePaths", "List of file system paths for all diffusion-weighted images. This will usually not include any b=0 images, although it is possible to include these as well."); typedef cmtk::CommandLine::Key Key; cl.BeginGroup( "Input", "Input Options" ); cl.AddOption( Key( "b0count" ), &b0ImageCount, "Number of b=0 images at the beginning of the image list. These are not included in the bad slice detection or masking, " "but are also written to the output directory for convenience. It is assumed that all b=0 images are at the beginning of the list of input images, but not at the end or in the middle." ); cmtk::CommandLine::EnumGroup::SmartPtr sliceGroup = cl.AddEnum( "slice-orientation", &sliceAxis, "Define slice orientation of the diffusion images." ); sliceGroup->AddSwitch( Key( "axial" ), (int)cmtk::AXIS_Z, "Axial slices" ); sliceGroup->AddSwitch( Key( "sagittal" ),(int)cmtk::AXIS_X, "Sagittal slices" ); sliceGroup->AddSwitch( Key( "coronal" ), (int)cmtk::AXIS_Y, "Coronal slices" ); sliceGroup->AddSwitch( Key( "slice-x" ), (int)cmtk::AXIS_X, "X coordinate axis is slice direction" ); sliceGroup->AddSwitch( Key( "slice-y" ), (int)cmtk::AXIS_Y, "Y coordinate axis is slice direction" ); sliceGroup->AddSwitch( Key( "slice-z" ), (int)cmtk::AXIS_Z, "Z coordinate axis is slice direction" ); cl.EndGroup(); cl.BeginGroup( "detection", "Bad Slice Detection" ); cl.AddOption( Key( "stdev" ), &standardDeviations, "Threshold for bad slice identification in units of intensity standard deviations over all corresponding slices from the remaining diffusion images." ); cl.EndGroup(); cl.BeginGroup( "output", "Output Options" ); cmtk::CommandLine::EnumGroup::SmartPtr typeGroup = cl.AddEnum( "convert-to", &outputDataType, "Scalar data type for the output images. If your padding value is negative but your input data unsigned, for example, make sure to select a signed data type for the output. " "By default, the output data type is the same as the input type."); typeGroup->AddSwitch( Key( "char" ), cmtk::TYPE_CHAR, "8 bits, signed" ); typeGroup->AddSwitch( Key( "byte" ), cmtk::TYPE_BYTE, "8 bits, unsigned" ); typeGroup->AddSwitch( Key( "short" ), cmtk::TYPE_SHORT, "16 bits, signed" ); typeGroup->AddSwitch( Key( "ushort" ), cmtk::TYPE_USHORT, "16 bits, unsigned" ); typeGroup->AddSwitch( Key( "int" ), cmtk::TYPE_INT, "32 bits signed" ); typeGroup->AddSwitch( Key( "uint" ), cmtk::TYPE_UINT, "32 bits unsigned" ); typeGroup->AddSwitch( Key( "float" ), cmtk::TYPE_FLOAT, "32 bits floating point" ); typeGroup->AddSwitch( Key( "double" ), cmtk::TYPE_DOUBLE, "64 bits floating point\n" ); cl.AddOption( Key( 'p', "padding-value" ), &paddingValue, "Padding value to replace data of detected bad slices in output images." ); cl.AddOption( Key( 'o', "output-directory" ), &outputDir, "File system path for writing images with bad slices masked out (i.e., filled with a padding value)." ); cl.EndGroup(); cl.Parse( argc, argv ); if ( dwiImagePaths.size() < 6 ) { throw cmtk::CommandLine::Exception( "At least 6 diffusion images are needed for a DTI acquisition." ); } } catch ( const cmtk::CommandLine::Exception& e ) { cmtk::StdErr << e << "\n"; throw cmtk::ExitException( 1 ); } // read all diffusion images and make sure their grids match std::vector dwiImages( dwiImagePaths.size() ); for ( size_t i = 0; i < dwiImagePaths.size(); ++i ) { dwiImages[i] = cmtk::UniformVolume::SmartPtr( cmtk::VolumeIO::Read( dwiImagePaths[i] ) ); if ( i && ! dwiImages[0]->GridMatches( *dwiImages[i] ) ) { cmtk::StdErr << "ERROR: geometry of image '" << dwiImagePaths[i] << "' does not match that of image '" << dwiImagePaths[0] << "'\n"; throw cmtk::ExitException( 1 ); } if ( outputDataType != cmtk::TYPE_NONE ) { dwiImages[i]->SetData( dwiImages[i]->GetData()->Convert( outputDataType ) ); } } // loop over all slices for ( int slice = 0; slice < dwiImages[0]->m_Dims[sliceAxis]; ++slice ) { // compute the mean intensity for this slice in each volume std::vector sliceMeans( dwiImages.size() ); for ( size_t i = b0ImageCount; i < dwiImages.size(); ++i ) { cmtk::Types::DataItem variance = 0; // dummy variable; don't need variance dwiImages[i]->ExtractSlice( sliceAxis, slice )->GetData()->GetStatistics( sliceMeans[i], variance ); } // test for each image whether this slice is bad in it for ( size_t i = b0ImageCount; i < dwiImages.size(); ++i ) { // get a vector of means without the current test image std::vector meansOtherImages; for ( size_t ii = b0ImageCount; ii < dwiImages.size(); ++ii ) { if ( i != ii ) { meansOtherImages.push_back( sliceMeans[ii] ); } } const cmtk::Types::DataItem otherImagesMean = cmtk::MathUtil::Mean( meansOtherImages ); const cmtk::Types::DataItem otherImagesSdev = sqrt( cmtk::MathUtil::Variance( meansOtherImages, otherImagesMean ) ); const cmtk::Types::DataItem distance = fabs( sliceMeans[i] - otherImagesMean ) / otherImagesSdev; if ( distance > standardDeviations ) { cmtk::DebugOutput( 2 ) << "Bad slice #" << slice << " in image #" << i << " mean=" << sliceMeans[i] << " distance=" << distance << " filename " << dwiImagePaths[i] << "\n"; // if we have an image output path given, mark bad slice using user-provided padding value if ( outputDir ) { cmtk::DataGrid& image = *(dwiImages[i]); const cmtk::DataGrid::RegionType sliceRegion = dwiImages[i]->GetSliceRegion( sliceAxis, slice ); for ( cmtk::RegionIndexIterator it( sliceRegion ); it != it.end(); ++it ) { image.SetDataAt( paddingValue, image.GetOffsetFromIndex( it.Index() ) ); } } } } } if ( outputDir ) { for ( size_t i = 0; i < dwiImages.size(); ++i ) { const std::string outputPath = std::string( outputDir ) + CMTK_PATH_SEPARATOR + cmtk::FileUtils::Basename( dwiImagePaths[i] ); cmtk::VolumeIO::Write( *(dwiImages[i]), outputPath ); } } return 0; } #include "cmtkSafeMain" cmtk-3.3.1/apps/epiunwarp.cxx000066400000000000000000000175521276303427400161770ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2014 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 5332 $ // // $LastChangedDate: 2014-04-25 16:38:49 -0700 (Fri, 25 Apr 2014) $ // // $LastChangedBy: torstenrohlfing $ // */ #include #include #include #include #include #include #include #include int doMain ( const int argc, const char *argv[] ) { std::string inputImagePath1; std::string inputImagePath2; std::string outputImagePath1; std::string outputImagePath2; std::string outputDField; std::string outputDFieldRev; std::string writeJacobianPath1; std::string writeJacobianPath2; byte phaseEncodeDirection = 1; bool flipPEPolar = true; bool initShiftCentersOfMass = true; double smoothnessConstraintWeight = 0; double foldingConstraintWeight = 0; int iterations = 10; double smoothSigmaMax = 8.0; double smoothSigmaMin = 0; double smoothSigmaDiff = 0.25; try { cmtk::CommandLine cl; cl.SetProgramInfo( cmtk::CommandLine::PRG_TITLE, "Unwarp Echo Planar Images" ); cl.SetProgramInfo( cmtk::CommandLine::PRG_DESCR, "Correct B0 field inhomogeneity-induced distortion in Echo Planar Images (e.g., diffusion-weighted images) using two images acquired with opposing phase encoding directions." ); typedef cmtk::CommandLine::Key Key; cl.BeginGroup( "Input", "Input Image Parameters" ); cmtk::CommandLine::EnumGroup::SmartPtr phaseEncodeGroup = cl.AddEnum( "phase-encode", &phaseEncodeDirection, "Define the phase-encoded image coordinate direction." ); phaseEncodeGroup->AddSwitch( Key( 'y', "phase-encode-ap" ), 1, "Anterior/posterior phase encoding (this is the most common case)" ); phaseEncodeGroup->AddSwitch( Key( 'z', "phase-encode-is" ), 2, "Top/bottom phase encoding (this is rare)" ); phaseEncodeGroup->AddSwitch( Key( 'x', "phase-encode-lr" ), 0, "Lateral, left/right phase encoding (this is extremely rare)" ); cl.AddSwitch( Key( "no-flip" ), &flipPEPolar, false, "Use this switch is the reverse phase-encoded image does not need to be flipped prior to unwarping. If normal and reverse phase-encoded image display in the same gross " "orientation in 'triplanar', then flipping is not necessary and must be turned off using this switch." ); cl.EndGroup(); cl.BeginGroup( "Optimization", "Optimization Parameters" ); cl.AddSwitch( Key( "no-init-shift-com" ), &initShiftCentersOfMass, false, "Disable initialization of unwarping by shifting each row to align the centers of mass of forward and reverse acquisition. " "Instead, use all-zero initial deformation field." ); cl.AddOption( Key( "smooth-sigma-max" ), &smoothSigmaMax, "Maximum image smoothing kernel width for coarsest level of multi-scale computation." ); cl.AddOption( Key( "smooth-sigma-min" ), &smoothSigmaMin, "Minimum image smoothing kernel width for finest level of multi-scale computation (0 = no smoothing; original image scale)." ); cl.AddOption( Key( "smooth-sigma-diff" ), &smoothSigmaDiff, "Difference between image smoothing kernel widths between two successive levels of the multi-scale computation." ); cl.AddOption( Key( "smoothness-constraint-weight" ), &smoothnessConstraintWeight, "Weight factor for the second-order smoothness constraint term in the unwarping cost function." ); cl.AddOption( Key( "folding-constraint-weight" ), &foldingConstraintWeight, "Weight factor for the folding-prevention constraint term in the unwarping cost function." ); cl.AddOption( Key( 'i', "iterations" ), &iterations, "Number of L-BFGS optimization iterations (per multi-scale level)." ); cl.EndGroup(); cl.BeginGroup( "Output", "Output Options" ); cl.AddOption( Key( "write-jacobian-fwd" ), &writeJacobianPath1, "Write Jacobian intensity correction map for forward image." ); cl.AddOption( Key( "write-jacobian-rev" ), &writeJacobianPath2, "Write Jacobian intensity correction map for reverse-encoded image." ); cl.EndGroup(); cl.AddParameter( &inputImagePath1, "InputImage1", "First input image path - this is the standard b=0 image." )->SetProperties( cmtk::CommandLine::PROPS_IMAGE ); cl.AddParameter( &inputImagePath2, "InputImage2", "Second input image path - this is the b=0 image with reversed phase encoding direction." )->SetProperties( cmtk::CommandLine::PROPS_IMAGE ); cl.AddParameter( &outputImagePath1, "OutputImage1", "First output image path - this is the unwarped, corrected standard b=0 image." )->SetProperties( cmtk::CommandLine::PROPS_IMAGE | cmtk::CommandLine::PROPS_OUTPUT ); cl.AddParameter( &outputImagePath2, "OutputImage2", "Second output image path - this is the unwarped, corrected reversed-encoding b=0 image." )->SetProperties( cmtk::CommandLine::PROPS_IMAGE | cmtk::CommandLine::PROPS_OUTPUT ); cl.AddParameter( &outputDField, "OutputDField", "Path for deformation field (this can be applied to other images, e.g., diffusion-weighted images." )->SetProperties( cmtk::CommandLine::PROPS_XFORM | cmtk::CommandLine::PROPS_OUTPUT ); cl.AddParameter( &outputDFieldRev, "OutputDFieldRev", "Path for reverse deformation field (again, this can be applied to other images, e.g., diffusion-weighted images." ) ->SetProperties( cmtk::CommandLine::PROPS_XFORM | cmtk::CommandLine::PROPS_OUTPUT | cmtk::CommandLine::PROPS_OPTIONAL ); cl.Parse( argc, argv ); } catch ( const cmtk::CommandLine::Exception& e ) { cmtk::StdErr << e << "\n"; throw cmtk::ExitException( 1 ); } cmtk::UniformVolume::SmartPtr inputImage1 = cmtk::VolumeIO::ReadOriented( inputImagePath1 ); cmtk::UniformVolume::SmartPtr inputImage2 = cmtk::VolumeIO::ReadOriented( inputImagePath2 ); if ( flipPEPolar ) inputImage2->ApplyMirrorPlane( phaseEncodeDirection ); cmtk::EchoPlanarUnwarpFunctional func( inputImage1, inputImage2, phaseEncodeDirection, initShiftCentersOfMass ); func.SetSmoothnessConstraintWeight( smoothnessConstraintWeight ); func.SetFoldingConstraintWeight( foldingConstraintWeight ); func.Optimize( iterations, cmtk::Units::GaussianSigma( smoothSigmaMax ), cmtk::Units::GaussianSigma( smoothSigmaMin ), cmtk::Units::GaussianSigma( smoothSigmaDiff ) ); cmtk::VolumeIO::Write( *func.GetCorrectedImage( +1 ), outputImagePath1 ); cmtk::VolumeIO::Write( *func.GetCorrectedImage( -1 ), outputImagePath2 ); if ( !outputDField.empty() ) { cmtk::DeformationField::SmartPtr dfield( func.GetDeformationField( +1 ) ); cmtk::XformIO::Write( dfield, outputDField ); } if ( !outputDFieldRev.empty() ) { cmtk::DeformationField::SmartPtr dfield( func.GetDeformationField( -1 ) ); cmtk::XformIO::Write( dfield, outputDFieldRev ); } if ( !writeJacobianPath1.empty() ) cmtk::VolumeIO::Write( *func.GetJacobianMap( +1 ), writeJacobianPath1 ); if ( !writeJacobianPath2.empty() ) cmtk::VolumeIO::Write( *func.GetJacobianMap( -1 ), writeJacobianPath2 ); return 0; } #include "cmtkSafeMain" cmtk-3.3.1/apps/fib2image.cxx000066400000000000000000000105321276303427400160010ustar00rootroot00000000000000/* // // Copyright 1997-2012 Torsten Rohlfing // // Copyright 2004-2013 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4866 $ // // $LastChangedDate: 2013-09-24 10:01:06 -0700 (Tue, 24 Sep 2013) $ // // $LastChangedBy: torstenrohlfing $ // */ #include #include #include #include #include #include #include #include #include #include int doMain( const int argc, const char* argv[] ) { std::string trackingImagePath; std::string outputImagePath = "fib2image.nii"; cmtk::Types::DataItem value = 1; try { cmtk::CommandLine cl; cl.SetProgramInfo( cmtk::CommandLine::PRG_TITLE, "Draw image from point coordinates of fiber tracks from .fib file." ); cl.SetProgramInfo( cmtk::CommandLine::PRG_DESCR, "Fiber tracking results from the UNC Fiber Tracking tool are read from Standard Input and all fiber points are drawn into a 3D image. The result is written in one of the supported image file formats." ); typedef cmtk::CommandLine::Key Key; cl.AddOption( Key( "value" ), &value, "Value used for drawing fiber points." ); cl.AddOption( Key( 'o', "output" ), &outputImagePath, "Output image file name." ); cl.AddParameter( &trackingImagePath, "TrackingImage", "Image defining the grid and space in which fiber tracking was performed to correct for differences in orientation and coordinate space." )->SetProperties( cmtk::CommandLine::PROPS_IMAGE ); cl.Parse( argc, argv ); } catch ( cmtk::CommandLine::Exception& ex ) { cmtk::StdErr << ex << "\n"; throw cmtk::ExitException( 1 ); } // read target image in NATIVE orientation because that's what UNC Fiber Tracking uses cmtk::UniformVolume::SmartPtr trackingImage( cmtk::VolumeIO::ReadOriented( trackingImagePath ) ); if ( ! trackingImage ) { cmtk::StdErr << "ERROR: could not read tracking image '" << trackingImagePath << "'\n"; throw cmtk::ExitException( 1 ); } // don't need data trackingImage->SetData( cmtk::TypedArray::SmartPtr( NULL ) ); cmtk::UniformVolume::SmartPtr outputImage( trackingImage->CloneGrid() ); trackingImage->ChangeCoordinateSpace( trackingImage->GetMetaInfo( cmtk::META_SPACE_ORIGINAL ) ); cmtk::AffineXform fromTrackingSpace( trackingImage->GetImageToPhysicalMatrix() ); // don't need data - make new array outputImage->CreateDataArray( cmtk::TYPE_SHORT, true /*setToZero*/ ); cmtk::Xform::SpaceVectorType xyz; cmtk::DataGrid::IndexType ijk; std::string line; while ( !std::cin.eof() ) { std::getline( std::cin, line ); if ( line.compare( 0, 7, "NPoints" ) == 0 ) { const size_t npoints = atoi( line.substr( line.find( '=' )+1, std::string::npos ).c_str() ); // skip "Points = " std::getline( std::cin, line ); for ( size_t n = 0; n> xyz[0] >> xyz[1] >> xyz[2]; // read everything else on the same line to skip. std::string restOfLine; std::getline( std::cin, restOfLine ); // transform from fib space into image space if ( outputImage->GetClosestGridPointIndex( fromTrackingSpace.Apply( xyz ), ijk ) ) { outputImage->SetDataAt( value, trackingImage->GetOffsetFromIndex( ijk ) ); } } } } // write output image cmtk::VolumeIO::Write( *outputImage, outputImagePath ); // if we got here, the program probably ran return 0; } #include "cmtkSafeMain" cmtk-3.3.1/apps/fibxform.cxx000066400000000000000000000147761276303427400160060ustar00rootroot00000000000000/* // // Copyright 1997-2012 Torsten Rohlfing // // Copyright 2004-2014 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 5129 $ // // $LastChangedDate: 2014-01-09 13:48:15 -0800 (Thu, 09 Jan 2014) $ // // $LastChangedBy: torstenrohlfing $ // */ #include #include #include #include #include #include #include #include #include #include int doMain( const int argc, const char* argv[] ) { cmtk::Types::Coordinate inversionTolerance = 0.001; std::vector inputXformPaths; const char* sourceImagePath = NULL; const char* targetImagePath = NULL; try { cmtk::CommandLine cl; cl.SetProgramInfo( cmtk::CommandLine::PRG_TITLE, "Apply coordinate transformations to point coordinates in .fib file." ); cl.SetProgramInfo( cmtk::CommandLine::PRG_DESCR, "A file with fiber tracking results from the UNC Fiber Tracking tool is read from Standard Input and one or more (concatenated) coordinate transformations are applied to all fiber point coordinates. " "The result is written to Standard Output, again in UNC fiber file format." ); typedef cmtk::CommandLine::Key Key; cl.AddOption( Key( "inversion-tolerance" ), &inversionTolerance, "Numerical tolerance of B-spline inversion in mm. Smaller values will lead to more accurate inversion, but may increase failure rate." ); cl.AddOption( Key( "source-image" ), &sourceImagePath, "Set source image of the transformation (i.e., an image defining the space in which fiber tracking was performed) " "to correct for differences in orientation and coordinate space." )->SetProperties( cmtk::CommandLine::PROPS_IMAGE ); cl.AddOption( Key( "target-image" ), &targetImagePath, "Set target image of the transformation (i.e., the image that the fiber track points are mapped into) " "to correct for differences in orientation and coordinate space." )->SetProperties( cmtk::CommandLine::PROPS_IMAGE ); cl.AddParameterVector( &inputXformPaths, "XformList", "List of concatenated transformations. Insert '--inverse' to use the inverse of the transformation listed next." "(If the first transformation in the sequence is inverted, then '--inverse' must be preceded by '--', i.e., use '-- --inverse xform.path')." )->SetProperties( cmtk::CommandLine::PROPS_XFORM ) ->SetProperties( cmtk::CommandLine::PROPS_XFORM | cmtk::CommandLine::PROPS_OPTIONAL ); cl.Parse( argc, argv ); } catch ( cmtk::CommandLine::Exception& ex ) { cmtk::StdErr << ex << "\n"; throw cmtk::ExitException( 1 ); } cmtk::XformList xformList = cmtk::XformListIO::MakeFromStringList( inputXformPaths ); xformList.SetEpsilon( inversionTolerance ); if ( sourceImagePath ) { cmtk::UniformVolume::SmartPtr sourceImage( cmtk::VolumeIO::ReadOriented( sourceImagePath ) ); if ( ! sourceImage ) { cmtk::StdErr << "ERROR: could not read source image '" << sourceImagePath << "'\n"; throw cmtk::ExitException( 1 ); } sourceImage->ChangeCoordinateSpace( sourceImage->GetMetaInfo( cmtk::META_SPACE_ORIGINAL ) ); try { xformList.AddToFront( cmtk::AffineXform::SmartPtr( new cmtk::AffineXform(sourceImage->GetImageToPhysicalMatrix() ) ) ); } catch ( const cmtk::AffineXform::MatrixType::SingularMatrixException& ) { cmtk::StdErr << "ERROR: singular source image-to-physical space matrix encountered\n"; throw cmtk::ExitException( 1 ); } } if ( targetImagePath ) { cmtk::UniformVolume::SmartPtr targetImage( cmtk::VolumeIO::ReadOriented( targetImagePath ) ); if ( ! targetImage ) { cmtk::StdErr << "ERROR: could not read target image '" << targetImagePath << "'\n"; throw cmtk::ExitException( 1 ); } targetImage->ChangeCoordinateSpace( targetImage->GetMetaInfo( cmtk::META_SPACE_ORIGINAL ) ); try { xformList.Add( cmtk::AffineXform::SmartPtr( new cmtk::AffineXform( targetImage->GetImageToPhysicalMatrix().GetInverse() ) ) ); } catch ( const cmtk::AffineXform::MatrixType::SingularMatrixException& ) { cmtk::StdErr << "ERROR: singular target image-to-physical space matrix encountered\n"; throw cmtk::ExitException( 1 ); } } cmtk::Xform::SpaceVectorType xyz; std::vector outputPointLines; std::string line; while ( !std::cin.eof() ) { std::getline( std::cin, line ); if ( line.compare( 0, 7, "NPoints" ) != 0 ) { std::cout << line << std::endl; } else { const size_t npoints = atoi( line.substr( line.find( '=' )+1, std::string::npos ).c_str() ); outputPointLines.resize( npoints ); size_t npointsOut = 0; // skip "Points = " std::getline( std::cin, line ); std::cout << line << "\n"; for ( size_t n = 0; n> xyz[0] >> xyz[1] >> xyz[2]; // read everything else on the same line std::string restOfLine; std::getline( std::cin, restOfLine ); // Apply transformation sequence const bool valid = xformList.ApplyInPlace( xyz ); if ( valid ) { std::stringstream sout; sout << xyz[0] << " " << xyz[1] << " " << xyz[2];; outputPointLines[npointsOut++] = std::string( sout.str() + " " + restOfLine ); } } std::cout << "NPoints = " << outputPointLines.size() << "\n"; std::cout << "Points =\n"; for ( size_t n = 0; n < npointsOut; ++n ) { std::cout << outputPointLines[n] << "\n"; } } } // if we got here, the program probably ran return 0; } #include "cmtkSafeMain" cmtk-3.3.1/apps/film.cxx000066400000000000000000000354761276303427400151210ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2013 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4975 $ // // $LastChangedDate: 2013-10-11 13:53:00 -0700 (Fri, 11 Oct 2013) $ // // $LastChangedBy: torstenrohlfing $ // */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include std::string InputFilePath; std::string OutputFilePath; cmtk::Types::DataItem PaddingValue = 0; bool PaddingFlag = false; int InterleaveAxis = -1; unsigned int NumberOfPasses = 2; int RegistrationMetric = 4; // MSD double InjectionKernelSigma = 0.5; double InjectionKernelRadius = 2; bool FourthOrderError = false; int InverseInterpolationKernel = cmtk::Interpolators::CUBIC; int NumberOfIterations = 20; bool RegionalIntensityTruncation = true; double ConstraintWeightLNorm = 0; std::string ReferenceImagePath; std::string InjectedImagePath; std::string ExportXformsPath; std::string ImportXformsPath; std::map PassWeights; bool WriteImagesAsFloat = false; void CallbackSetPassWeight( const char* argv ) { int pass = 0; float weight = 1.0; if ( 2 == sscanf( argv, "%3d:%10f", &pass, &weight ) ) { PassWeights[pass] = weight; } else { cmtk::StdErr << "ERROR: pass weights must be given as 'pass:weight', where 'pass' is an integer and 'weight' is a number between 0 and 1.\n" << " Parameter provided was '" << argv << "'\n"; throw cmtk::ExitException( 1 ); } } template cmtk::UniformVolume::SmartPtr GetReconstructedImage( cmtk::UniformVolume::SmartPtr& volume, cmtk::UniformVolume::SmartPtr& refImage, std::vector& xformsToPassImages ) { if ( InterleaveAxis < 0 ) InterleaveAxis = cmtk::VolumeInjectionReconstruction::GuessInterleaveAxis( volume ); cmtk::InverseInterpolationVolumeReconstruction volRecon( volume, NumberOfPasses, InterleaveAxis ); for ( std::map::const_iterator it = PassWeights.begin(); it != PassWeights.end(); ++it ) { volRecon.SetPassWeight( it->first, it->second ); } if ( refImage ) volRecon.SetReferenceImage( refImage ); volRecon.SetUseRegionalIntensityTruncation( RegionalIntensityTruncation ); volRecon.SetUseFourthOrderError( FourthOrderError ); volRecon.SetConstraintWeightLNorm( ConstraintWeightLNorm ); if ( xformsToPassImages.size() == NumberOfPasses ) { volRecon.SetTransformationsToPassImages( xformsToPassImages ); } else { cmtk::DebugOutput( 2 ) << "Computing transformations between passes...\n"; volRecon.ComputeTransformationsToPassImages( RegistrationMetric ); xformsToPassImages = volRecon.GetTransformationsToPassImages(); } if ( !ExportXformsPath.empty() ) { cmtk::ClassStreamOutput stream( ExportXformsPath, cmtk::ClassStreamOutput::MODE_WRITE ); if ( stream.IsValid() ) { cmtk::DebugOutput( 2 ) << "Exporting transformations between passes to " << ExportXformsPath << "\n"; for ( unsigned int pass = 0; pass < NumberOfPasses; ++pass ) { stream << dynamic_cast( *xformsToPassImages[pass] ); } } else { cmtk::StdErr << "ERROR: Could not open transformation file" << ExportXformsPath << "\n"; } } cmtk::DebugOutput( 2 ) << "Volume injection...\n"; volRecon.VolumeInjectionAnisotropic( InjectionKernelSigma, InjectionKernelRadius ); if ( !InjectedImagePath.empty() ) { cmtk::UniformVolume::SmartPtr outputImage = volRecon.GetCorrectedImage(); if ( !WriteImagesAsFloat && outputImage->GetData()->GetType() != volume->GetData()->GetType() ) { outputImage = cmtk::UniformVolume::SmartPtr( outputImage->CloneGrid() ); outputImage->SetData( cmtk::TypedArray::SmartPtr( volRecon.GetCorrectedImage()->GetData()->Convert( volume->GetData()->GetType() ) ) ); } cmtk::VolumeIO::Write( *outputImage, InjectedImagePath ); } volRecon.Optimize( NumberOfIterations ); return volRecon.GetCorrectedImage(); } int doMain( const int argc, const char* argv[] ) { /* // Parse command line */ try { cmtk::CommandLine cl( cmtk::CommandLine::PROPS_XML ); typedef cmtk::CommandLine::Key Key; cl.SetProgramInfo( cmtk::CommandLine::PRG_TITLE, "Fix interleaved motion using inverse interpolation" ); cl.SetProgramInfo( cmtk::CommandLine::PRG_DESCR, "This tool splits an interleaved input image into the pass images, co-registers them, and reconstructs a motion-corrected image" ); cl.SetProgramInfo( cmtk::CommandLine::PRG_CATEG, "CMTK.Artifact Correction" ); cl.BeginGroup( "input", "Input Options" ); cl.AddOption( Key( "padding-value" ), &PaddingValue, "Set padding value for input image. Pixels with this value will be ignored.", &PaddingFlag ); cl.EndGroup(); cl.BeginGroup( "interleave", "Interleaving Options" ); cmtk::CommandLine::EnumGroup::SmartPtr interleaveGroup = cl.AddEnum( "interleave-axis", &InterleaveAxis, "Define interleave axis: this is the through-slice direction of the acquisition." ); interleaveGroup->AddSwitch( Key( "guess-from-input" ), -1, "Guess from input image" ); interleaveGroup->AddSwitch( Key( 'a', "axial" ), (int)cmtk::AXIS_Z, "Interleaved axial images" ); interleaveGroup->AddSwitch( Key( 's', "sagittal" ),(int)cmtk::AXIS_X, "Interleaved sagittal images" ); interleaveGroup->AddSwitch( Key( 'c', "coronal" ), (int)cmtk::AXIS_Y, "Interleaved coronal images" ); interleaveGroup->AddSwitch( Key( 'x', "interleave-x" ), (int)cmtk::AXIS_X, "Interleaved along x axis" ); interleaveGroup->AddSwitch( Key( 'y', "interleave-y" ), (int)cmtk::AXIS_Y, "Interleaved along y axis" ); interleaveGroup->AddSwitch( Key( 'z', "interleave-z" ), (int)cmtk::AXIS_Z, "Interleaved along z axis" ); cl.AddOption( Key( 'p', "passes" ), &NumberOfPasses, "Number of interleaved passes" ); cl.AddCallback( Key( 'W', "pass-weight" ), CallbackSetPassWeight, "Set contribution weight for a pass in the form 'pass:weight'" )->SetProperties( cmtk::CommandLine::PROPS_ADVANCED ); ; cl.EndGroup(); cl.BeginGroup( "motion", "Motion Correction / Registration Options" )->SetProperties( cmtk::CommandLine::PROPS_ADVANCED ); cl.AddOption( Key( 'R', "reference-image" ), &ReferenceImagePath, "Use a separate high-resolution reference image for registration" )->SetProperties( cmtk::CommandLine::PROPS_IMAGE ); cmtk::CommandLine::EnumGroup::SmartPtr metricGroup = cl.AddEnum( "registration-metric", &RegistrationMetric, "Registration metric for motion estimation by image-to-image registration." ); metricGroup->SetProperties( cmtk::CommandLine::PROPS_ADVANCED ); metricGroup->AddSwitch( Key( "nmi" ), 0, "Use Normalized Mutual Information for pass-to-refereence registration" ); metricGroup->AddSwitch( Key( "mi" ), 1, "Use standard Mutual Information for pass-to-refereence registration" ); metricGroup->AddSwitch( Key( "cr" ), 2, "Use Correlation Ratio for pass-to-refereence registration" ); metricGroup->AddSwitch( Key( "msd" ), 4, "Use Mean Squared Differences for pass-to-refereence registration" ); metricGroup->AddSwitch( Key( "cc" ), 5, "Use Cross-Correlation for pass-to-refereence registration" ); cl.AddOption( Key( "import-xforms-path" ), &ImportXformsPath, "Path of file from which to import transformations between passes." ) ->SetProperties( cmtk::CommandLine::PROPS_FILENAME | cmtk::CommandLine::PROPS_ADVANCED ); cl.AddOption( Key( "export-xforms-path" ), &ExportXformsPath, "Path of file to which to export transformations between passes." ) ->SetProperties( cmtk::CommandLine::PROPS_FILENAME | cmtk::CommandLine::PROPS_ADVANCED | cmtk::CommandLine::PROPS_OUTPUT ); cl.BeginGroup( "inject", "Initial Volume Injection Options" ); cl.AddOption( Key( 'S', "injection-kernel-sigma" ), &InjectionKernelSigma, "Standard deviation of Gaussian kernel for volume injection in multiples of pixel size in each direction." ); cl.AddOption( Key( 'r', "injection-kernel-radius" ), &InjectionKernelRadius, "Truncation radius factor of injection kernel. The kernel is truncated at sigma*radius, where sigma is the kernel standard deviation." ); cl.EndGroup(); cl.BeginGroup( "invint", "Inverse Interpolation Options" ); cmtk::CommandLine::EnumGroup::SmartPtr kernelGroup = cl.AddEnum( "inverse-interpolation-kernel", &InverseInterpolationKernel, "Kernel for the inverse interpolation reconstruction" ); kernelGroup->AddSwitch( Key( 'C', "cubic" ), cmtk::Interpolators::CUBIC, "Tricubic interpolation" ); kernelGroup->AddSwitch( Key( 'L', "linear" ), cmtk::Interpolators::LINEAR, "Trilinear interpolation (faster but less accurate)" ); kernelGroup->AddSwitch( Key( 'H', "hamming-sinc" ), cmtk::Interpolators::HAMMING_SINC, "Hamming-windowed sinc interpolation" ); kernelGroup->AddSwitch( Key( 'O', "cosine-sinc" ), cmtk::Interpolators::COSINE_SINC, "Cosine-windowed sinc interpolation (most accurate but slowest)" ); cl.AddSwitch( Key( 'f', "fourth-order-error" ), &FourthOrderError, true, "Use fourth-order (rather than second-order) error for optimization." )->SetProperties( cmtk::CommandLine::PROPS_ADVANCED ); cl.AddOption( Key( 'n', "num-iterations" ), &NumberOfIterations, "Maximum number of inverse interpolation iterations" ); cl.EndGroup(); cl.BeginGroup( "regularize", "Reconstruction Regularization Options" )->SetProperties( cmtk::CommandLine::PROPS_ADVANCED ); cl.AddOption( Key( "l-norm-weight" ), &ConstraintWeightLNorm, "Set constraint weight for Tikhonov-type L-Norm regularization (0 disables constraint)" ); cl.AddSwitch( Key( 'T', "no-truncation" ), &RegionalIntensityTruncation, false, "Turn off regional intensity truncatrion" ); cl.EndGroup(); cl.BeginGroup( "output", "Output Options" ); cl.AddOption( Key( "write-injected-image" ), &InjectedImagePath, "Write initial volume injection image to path" )->SetProperties( cmtk::CommandLine::PROPS_IMAGE | cmtk::CommandLine::PROPS_OUTPUT ); cl.AddSwitch( Key( 'F', "write-images-as-float" ), &WriteImagesAsFloat, true, "Write output images as floating point [default: same as input]" ); cl.EndGroup(); cl.AddParameter( &InputFilePath, "InputImage", "Input image path" )->SetProperties( cmtk::CommandLine::PROPS_IMAGE ); cl.AddParameter( &OutputFilePath, "OutputImage", "Output image path" )->SetProperties( cmtk::CommandLine::PROPS_IMAGE | cmtk::CommandLine::PROPS_OUTPUT ); cl.Parse( argc, argv ); } catch ( const cmtk::CommandLine::Exception& e ) { cmtk::StdErr << e << "\n"; return 1; } // Instantiate programm progress indicator. cmtk::ProgressConsole progressIndicator( "Interleaved Image Motion Correction" ); /* // Read input image */ cmtk::UniformVolume::SmartPtr volume( cmtk::VolumeIO::ReadOriented( InputFilePath ) ); if ( ! volume || ! volume->GetData() ) { cmtk::StdErr << "ERROR: Could not read image " << InputFilePath << "\n"; return 1; } if ( PaddingFlag ) { volume->GetData()->SetPaddingValue( PaddingValue ); } cmtk::UniformVolume::SmartPtr refImage; if ( !ReferenceImagePath.empty() ) { refImage = cmtk::UniformVolume::SmartPtr( cmtk::VolumeIO::ReadOriented( ReferenceImagePath ) ); if ( ! refImage || ! refImage->GetData() ) { cmtk::StdErr << "ERROR: Could not read image " << ReferenceImagePath << "\n"; return 1; } } std::vector xformsToPassImages; if ( !ImportXformsPath.empty() ) { cmtk::ClassStreamInput stream( ImportXformsPath ); if ( stream.IsValid() ) { cmtk::DebugOutput( 1 ) << "Importing transformations between passes from " << ImportXformsPath << "\n"; cmtk::AffineXform xform; for ( unsigned int pass = 0; pass < NumberOfPasses; ++pass ) { try { stream >> xform; } catch ( const cmtk::Exception& ex ) { cmtk::StdErr << "ERROR: reading transformation failed - " << ex.what() << "\n"; throw cmtk::ExitException( 1 ); } xformsToPassImages.push_back( cmtk::Xform::SmartPtr( xform.Clone() ) ); } } else { cmtk::StdErr << "ERROR: Could not open transformation file" << ImportXformsPath << "\n"; } } cmtk::UniformVolume::SmartPtr correctedVolume; switch ( InverseInterpolationKernel ) { default: case cmtk::Interpolators::LINEAR: correctedVolume = GetReconstructedImage( volume, refImage, xformsToPassImages ); break; case cmtk::Interpolators::CUBIC: correctedVolume = GetReconstructedImage( volume, refImage, xformsToPassImages ); break; case cmtk::Interpolators::HAMMING_SINC: correctedVolume = GetReconstructedImage< cmtk::Interpolators::HammingSinc<3> >( volume, refImage, xformsToPassImages ); break; case cmtk::Interpolators::COSINE_SINC: correctedVolume = GetReconstructedImage< cmtk::Interpolators::CosineSinc<3> >( volume, refImage, xformsToPassImages ); break; } cmtk::UniformVolume::SmartPtr outputImage = correctedVolume; if ( !WriteImagesAsFloat && outputImage->GetData()->GetType() != volume->GetData()->GetType() ) { outputImage = cmtk::UniformVolume::SmartPtr( outputImage->CloneGrid() ); outputImage->SetData( cmtk::TypedArray::SmartPtr( correctedVolume->GetData()->Convert( volume->GetData()->GetType() ) ) ); } cmtk::VolumeIO::Write( *outputImage, OutputFilePath ); return 0; } #include "cmtkSafeMain" cmtk-3.3.1/apps/filter.cxx000066400000000000000000000203031276303427400154360ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2011, 2013 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4975 $ // // $LastChangedDate: 2013-10-11 13:53:00 -0700 (Fri, 11 Oct 2013) $ // // $LastChangedBy: torstenrohlfing $ // */ #include #include #include #include #include #include #include #include #include #ifdef CMTK_BUILD_UNSTABLE # include #endif #ifdef CMTK_USE_SQLITE # include #endif #include #include bool Studholme = false; bool Rohlfing = false; #ifdef CMTK_BUILD_UNSTABLE bool Coupe = false; cmtk::Types::Coordinate CoupeBeta = 1; //float CoupeBlockRadius = 0; commented out while testing, this is defined in cmtk::FilterVolume.h cmtk::Types::Coordinate CoupeWindowRadius = 5; #endif // #ifdef CMTK_BUILD_UNSTABLE bool Gaussian = false; float GaussianWidth = 0.0; float IntensityGaussianSigma = 0.0; float Radius = 2.0; float IntensityBinWidth = 10.0; std::string InputFileName; std::string OutputFileName; std::string AverageFileName; std::string SubjectFileName; std::string MaskFileName; std::list ImageNameList; #ifdef CMTK_USE_SQLITE std::string updateDB; #endif int doMain( const int argc, const char* argv[] ) { cmtk::ProgressConsole progressIndicator( "Image Filtering" ); try { cmtk::CommandLine cl; cl.SetProgramInfo( cmtk::CommandLine::PRG_TITLE, "Filter a volume image" ); cl.SetProgramInfo( cmtk::CommandLine::PRG_DESCR, "This tool applies spatial filtering operators, including cnotent-sensitive opersators, based on selective Gaussian kernels." ); cl.SetProgramInfo( cmtk::CommandLine::PRG_SYNTX, "filter [options] input output\n" "filter [options] [-s,--studholme] input output average subject img0 [img1...]\n" "filter [options] [-R,--rohlfing] input output subject" ); cl.SetProgramInfo( cmtk::CommandLine::PRG_CATEG, "CMTK.Filtering" ); typedef cmtk::CommandLine::Key Key; cl.AddOption( Key( 'g', "gaussian" ), &GaussianWidth, "Gaussian filter width (sigma)", &Gaussian ); cl.AddOption( Key( 'r', "radius" ), &Radius, "Filter radius (truncated outside)" ); cl.AddOption( Key( 'm', "mask" ), &MaskFileName, "Binary mask file name" ); cl.AddSwitch( Key( 's', "studholme" ), &Studholme, true, "Use Studholme's consistent filtering" ); cl.AddOption( Key( 'b', "bin-width" ), &IntensityBinWidth, "Intensity bin width for consistent filtering" ); #ifdef CMTK_BUILD_UNSTABLE cl.AddSwitch( Key( 'C', "coupe" ), &Coupe, true, "Use Coupe's blockwise nonlocal means denoising filter" ); #endif cl.AddSwitch( Key( 'R', "rohlfing" ), &Rohlfing, true, "Use Rohlfing's single-image consistent filtering" ); cl.AddOption( Key( 'G', "intensity-gaussian" ), &IntensityGaussianSigma, "Intensity gaussian sigma for consistent filtering" ); #ifdef CMTK_USE_SQLITE cl.BeginGroup( "Database", "Image/Transformation Database" ); cl.AddOption( Key( "db" ), &updateDB, "Path to image/transformation database that should be updated with the newly created image." ); cl.EndGroup(); #endif cl.Parse( argc, argv ); InputFileName = cl.GetNext(); OutputFileName = cl.GetNext(); if ( Studholme ) { AverageFileName = cl.GetNext(); SubjectFileName = cl.GetNext(); const char* next = cl.GetNext(); while ( next ) { ImageNameList.push_back( next ); next = cl.GetNextOptional(); } } else { if ( Rohlfing ) { SubjectFileName = cl.GetNext(); } } } catch ( const cmtk::CommandLine::Exception& e ) { cmtk::StdErr << e; throw cmtk::ExitException( 1 ); } cmtk::UniformVolume::SmartPtr volume( cmtk::VolumeIO::ReadOriented( InputFileName ) ); if ( !volume || !volume->GetData() ) { cmtk::StdErr << "ERROR: Could not read volume " << InputFileName << "\n"; throw cmtk::ExitException( 1 ); } cmtk::TypedArray::SmartPtr maskData( NULL ); if ( !MaskFileName.empty() ) { cmtk::UniformVolume::SmartPtr maskVolume( cmtk::VolumeIO::ReadOriented( MaskFileName ) ); if ( maskVolume ) maskData = maskVolume->GetData(); else { cmtk::StdErr << "ERROR: Could not read mask file " << MaskFileName << "\n"; throw cmtk::ExitException( 1 ); } } if ( Studholme ) { cmtk::UniformVolume::SmartPtr average( cmtk::VolumeIO::ReadOriented( AverageFileName ) ); if ( ! average || ! average->GetData() ) { cmtk::StdErr << "ERROR: Could not read average anatomical file " << AverageFileName << "\n"; throw cmtk::ExitException( 1 ); } cmtk::UniformVolume::SmartPtr subject( cmtk::VolumeIO::ReadOriented( SubjectFileName ) ); if ( ! subject || ! subject->GetData() ) { cmtk::StdErr << "ERROR: Could not read subject anatomical file " << SubjectFileName << "\n"; throw cmtk::ExitException( 1 ); } std::list imageList; for ( std::list::const_iterator it = ImageNameList.begin(); it != ImageNameList.end(); ++it ) { cmtk::UniformVolume::SmartPtr next( cmtk::VolumeIO::ReadOriented( *it ) ); if ( ! next || ! next->GetData() ) { cmtk::StdErr << "ERROR: Could not read subject anatomical file " << *it << "\n"; throw cmtk::ExitException( 1 ); } imageList.push_back( next->GetData() ); } cmtk::TypedArray::SmartPtr filtered ( cmtk::FilterVolume::StudholmeFilter( volume, subject->GetData(), average->GetData(), maskData, imageList, IntensityBinWidth, cmtk::Units::GaussianSigma( GaussianWidth ), Radius ) ); volume->SetData( filtered ); } else { if ( Rohlfing ) { cmtk::UniformVolume::SmartPtr subject( cmtk::VolumeIO::ReadOriented( SubjectFileName ) ); if ( ! subject || ! subject->GetData() ) { cmtk::StdErr << "ERROR: Could not read subject anatomical file " << SubjectFileName << "\n"; throw cmtk::ExitException( 1 ); } cmtk::TypedArray::SmartPtr filtered ( cmtk::FilterVolume::RohlfingFilter( volume, subject->GetData(), maskData, cmtk::Units::GaussianSigma( IntensityGaussianSigma ), cmtk::Units::GaussianSigma( GaussianWidth ), Radius ) ); volume->SetData( filtered ); } else { if ( Gaussian ) { cmtk::TypedArray::SmartPtr filtered( cmtk::FilterVolume::GaussianFilter( volume, cmtk::Units::GaussianSigma( GaussianWidth ), Radius, maskData ) ); volume->SetData( filtered ); } else { #ifdef CMTK_BUILD_UNSTABLE if ( Coupe ) { cmtk::TypedArray::SmartPtr filtered( cmtk::FilterVolumeCoupe::CoupeFilter( volume, static_cast( CoupeWindowRadius ), CoupeBeta ) ); volume->SetData( filtered ); } #endif // #ifdef CMTK_BUILD_UNSTABLE } } } cmtk::VolumeIO::Write( *volume, OutputFileName ); #ifdef CMTK_USE_SQLITE if ( !updateDB.empty() ) { try { cmtk::ImageXformDB db( updateDB ); db.AddImage( OutputFileName, InputFileName ); } catch ( const cmtk::SQLite::Exception& ex ) { cmtk::StdErr << "ERROR: " << ex.what() << "\n"; } } #endif return 0; } #include "cmtkSafeMain" cmtk-3.3.1/apps/fit_affine_dfield.cxx000066400000000000000000000053441276303427400175620ustar00rootroot00000000000000/* // // Copyright 1997-2011 Torsten Rohlfing // // Copyright 2004-2014 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 5129 $ // // $LastChangedDate: 2014-01-09 13:48:15 -0800 (Thu, 09 Jan 2014) $ // // $LastChangedBy: torstenrohlfing $ // */ #include #include #include #include #include #include #include #include std::string InputPath; std::string OutputPath; int doMain ( const int argc, const char *argv[] ) { try { cmtk::CommandLine cl; cl.SetProgramInfo( cmtk::CommandLine::PRG_TITLE, "Fit Affine Transformation to Nonrigid Transformation" ); cl.SetProgramInfo( cmtk::CommandLine::PRG_DESCR, "Fit a linear affine transformation to a nonrigid transformation, either a B-spline free-form deformation or a non-parametric deformation field." ); cl.AddParameter( &InputPath, "InputDField", "Input transformation." )->SetProperties( cmtk::CommandLine::PROPS_XFORM ); cl.AddParameter( &OutputPath, "OutputXform", "Path for output fitted affine transformation." )->SetProperties( cmtk::CommandLine::PROPS_XFORM | cmtk::CommandLine::PROPS_OUTPUT ); cl.Parse( argc, argv ); } catch ( const cmtk::CommandLine::Exception& e ) { cmtk::StdErr << e; throw cmtk::ExitException( 1 ); } cmtk::WarpXform::SmartPtr warpXform = cmtk::WarpXform::SmartPtr::DynamicCastFrom( cmtk::XformIO::Read( InputPath ) ); cmtk::FitAffineToWarpXform fitAffine( warpXform ); try { cmtk::XformIO::Write( fitAffine.Fit(), OutputPath ); } catch ( const cmtk::AffineXform::MatrixType::SingularMatrixException& ) { cmtk::StdErr << "ERROR: singular matrix encountered in cmtk::FitAffineToWarpXform::Fit()\n"; throw cmtk::ExitException( 1 ); } return 0; } #include "cmtkSafeMain" cmtk-3.3.1/apps/fit_affine_xform.cxx000066400000000000000000000102171276303427400174610ustar00rootroot00000000000000/* // // Copyright 1997-2011 Torsten Rohlfing // // Copyright 2004-2014 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4432 $ // // $LastChangedDate: 2012-06-13 13:33:51 -0700 (Wed, 13 Jun 2012) $ // // $LastChangedBy: torstenrohlfing $ // */ #include #include #include #include #include #include #include #include #include int doMain ( const int argc, const char *argv[] ) { std::string inputImagePath; std::string outputPath; bool fitRigid = false; cmtk::Types::Coordinate inversionTolerance = 1e-8; std::vector inputXformPaths; try { cmtk::CommandLine cl; cl.SetProgramInfo( cmtk::CommandLine::PRG_TITLE, "Fit Single Affine Transformation to Concatenated List" ); cl.SetProgramInfo( cmtk::CommandLine::PRG_DESCR, "Fit a linear affine transformation to a list of concatenated, optionally inverted, transformations." ); typedef cmtk::CommandLine::Key Key; cl.BeginGroup( "Input", "Input Options" ); cl.AddOption( Key( "inversion-tolerance" ), &inversionTolerance, "Numerical tolerance of B-spline inversion in mm. Smaller values will lead to more accurate inversion, but may increase failure rate." ); cl.EndGroup(); cl.BeginGroup( "Fitting", "Fitting Options" ); cl.AddSwitch( Key( "rigid" ), &fitRigid, true, "Fit rigid transformation (rotation and translation only) using SVD." ); cl.AddSwitch( Key( "affine" ), &fitRigid, false, "Fit full affine transformation (rotation, translation, scales, shears) using pseudoinverse." ); cl.EndGroup(); cl.BeginGroup( "Output", "Output Options" ); cl.AddOption( Key( 'o', "output" ), &outputPath, "Path for the output transformation." )->SetProperties( cmtk::CommandLine::PROPS_XFORM | cmtk::CommandLine::PROPS_OUTPUT ); cl.EndGroup(); cl.AddParameter( &inputImagePath, "InputImage", "Input image path. This image determines the discrete sampling grid where the target transformation is estimated and fitted." )->SetProperties( cmtk::CommandLine::PROPS_IMAGE ); cl.AddParameterVector( &inputXformPaths, "XformList", "List of concatenated transformations. Insert '--inverse' to use the inverse of the transformation listed next. " "(If the first transformation in the sequence is inverted, then '--inverse' must be preceded by '--', i.e., use '-- --inverse xform.path')." )->SetProperties( cmtk::CommandLine::PROPS_XFORM ); cl.Parse( argc, argv ); } catch ( const cmtk::CommandLine::Exception& e ) { cmtk::StdErr << e; throw cmtk::ExitException( 1 ); } cmtk::XformList xformList = cmtk::XformListIO::MakeFromStringList( inputXformPaths ); xformList.SetEpsilon( inversionTolerance ); cmtk::UniformVolume::SmartPtr imageGrid( cmtk::VolumeIO::ReadGridOriented( inputImagePath ) ); try { cmtk::AffineXform::SmartPtr xform = cmtk::FitAffineToXformList( *imageGrid, xformList ).Fit( fitRigid ); cmtk::XformIO::Write( xform, outputPath ); } catch ( const cmtk::AffineXform::MatrixType::SingularMatrixException& ) { cmtk::StdErr << "ERROR: singular matrix encountered in cmtk::FitAffineToXformList::Fit()\n"; return 1; } return 0; } #include "cmtkSafeMain" cmtk-3.3.1/apps/fit_affine_xform_landmarks.cxx000066400000000000000000000107121276303427400215150ustar00rootroot00000000000000/* // // Copyright 1997-2011 Torsten Rohlfing // // Copyright 2004-2014 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 5129 $ // // $LastChangedDate: 2014-01-09 13:48:15 -0800 (Thu, 09 Jan 2014) $ // // $LastChangedBy: torstenrohlfing $ // */ #include #include #include #include #include #include #include #include #include #include #include #include #include int doMain ( const int argc, const char *argv[] ) { std::string lmSourcePath; std::string lmTargetPath; std::string outputPath; bool rigid = false; try { cmtk::CommandLine cl; cl.SetProgramInfo( cmtk::CommandLine::PRG_TITLE, "Fit Affine Transformation to Landmarks" ); cl.SetProgramInfo( cmtk::CommandLine::PRG_DESCR, "Fit a linear affine transformation to a set of matched landmarks." ); typedef cmtk::CommandLine::Key Key; cl.AddParameter( &lmSourcePath, "SourcePath", "Path to file with source-space landmarks." ); cl.AddParameter( &lmTargetPath, "TargetPath", "Path to file with target-space landmarks." ); cl.AddSwitch( Key( "rigid" ), &rigid, true, "Fit only a rigid, 6 degree-of-freedom transformation rather than full affine. This requires fewer landmarks and makes less trict assumptions about their location " "(i.e., three non-collinear landmarks are sufficient to obtain a rigid transformation." ); cl.AddParameter( &outputPath, "OutputXform", "Path for output fitted affine transformation." )->SetProperties( cmtk::CommandLine::PROPS_XFORM | cmtk::CommandLine::PROPS_OUTPUT ); cl.Parse( argc, argv ); } catch ( const cmtk::CommandLine::Exception& e ) { cmtk::StdErr << e; throw cmtk::ExitException( 1 ); } std::ifstream lmSourceStream( lmSourcePath.c_str() ); if ( !lmSourceStream.good() ) { cmtk::StdErr << "ERROR: could not open source landmark file " << lmSourcePath << "\n"; throw cmtk::ExitException( 1 ); } cmtk::LandmarkList sourceLandmarks; lmSourceStream >> sourceLandmarks; cmtk::DebugOutput( 5 ) << "Read " << sourceLandmarks.size() << " source landmarks\n"; std::ifstream lmTargetStream( lmTargetPath.c_str() ); if ( !lmTargetStream.good() ) { cmtk::StdErr << "ERROR: could not open target landmark file " << lmTargetPath << "\n"; throw cmtk::ExitException( 1 ); } cmtk::LandmarkList targetLandmarks; lmTargetStream >> targetLandmarks; cmtk::DebugOutput( 5 ) << "Read " << targetLandmarks.size() << " target landmarks\n"; cmtk::LandmarkPairList landmarkPairs( sourceLandmarks, targetLandmarks ); cmtk::DebugOutput( 5 ) << "Matched " << landmarkPairs.size() << " landmark pairs\n"; cmtk::AffineXform::SmartConstPtr xform; try { xform = cmtk::AffineXform::SmartConstPtr( rigid ? cmtk::FitRigidToLandmarks( landmarkPairs ).GetRigidXform() : cmtk::FitAffineToLandmarks( landmarkPairs ).GetAffineXform() ); } catch ( const cmtk::AffineXform::MatrixType::SingularMatrixException& ) { cmtk::StdErr << "ERROR: singular matrix encountered in cmtk::FitRigidToLandmarks or cmtk::FitAffineToLandmarks\n"; throw cmtk::ExitException( 1 ); } cmtk::XformIO::Write( xform, outputPath ); if ( cmtk::DebugOutput::GetGlobalLevel() >= 5 ) { cmtk::DebugOutput( 5 ) << "INFO: RMS fitting residual = " << sqrt( xform->GetLandmarksMSD( landmarkPairs ) ) << "\n"; } return 0; } #include "cmtkSafeMain" cmtk-3.3.1/apps/fit_spline_dfield.cxx000066400000000000000000000120541276303427400176200ustar00rootroot00000000000000/* // // Copyright 1997-2011 Torsten Rohlfing // // Copyright 2004-2014 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 5129 $ // // $LastChangedDate: 2014-01-09 13:48:15 -0800 (Thu, 09 Jan 2014) $ // // $LastChangedBy: torstenrohlfing $ // */ #include #include #include #include #include #include #include #include #include std::string InputPath; std::string OutputPath; std::string GridDims; cmtk::Types::Coordinate GridSpacing = 0; int Levels = 1; bool AffineFirst = false; bool Absolute = true; int doMain ( const int argc, const char *argv[] ) { try { cmtk::CommandLine cl; cl.SetProgramInfo( cmtk::CommandLine::PRG_TITLE, "Deformation Field to Transformation" ); cl.SetProgramInfo( cmtk::CommandLine::PRG_DESCR, "Fit a parametric nonrigid transformation (B-spline free-form deformation) to a deformation field" ); typedef cmtk::CommandLine::Key Key; cl.BeginGroup( "Input", "Input Options" ); cl.AddSwitch( Key( "absolute" ), &Absolute, true, "Input is an absolute transformation field [x maps to input(x)]" ); cl.AddSwitch( Key( "relative" ), &Absolute, false, "Input is relative deformation field, e.g., a gradient or force field [x maps to x+input(x)]" ); cl.EndGroup(); cl.BeginGroup( "Fitting", "Fitting Options" ); cl.AddOption( Key( "levels" ), &Levels, "Number of levels in the multi-level B-spline approximation procedure." ); cl.AddSwitch( Key( "fit-affine-first" ), &AffineFirst, true, "Fit affine transformation first, then initialize spline with it." ); cl.EndGroup(); cl.BeginGroup( "Output", "Output Options" ); cl.AddOption( Key( "final-cp-spacing" ), &GridSpacing, "Final control point grid spacing of the output B-spline transformation." ); cl.AddOption( Key( "final-cp-dims" ), &GridDims, "Final control point grid dimensions (i.e., number of controlpoints) of the output B-spline transformation. To be provided as 'dimX,dimY,dimZ'." ); cl.EndGroup(); cl.AddParameter( &InputPath, "InputDField", "Input deformation field." )->SetProperties( cmtk::CommandLine::PROPS_XFORM ); cl.AddParameter( &OutputPath, "OutputXform", "Path for the output transformation." )->SetProperties( cmtk::CommandLine::PROPS_XFORM | cmtk::CommandLine::PROPS_OUTPUT ); cl.Parse( argc, argv ); } catch ( const cmtk::CommandLine::Exception& e ) { cmtk::StdErr << e; throw cmtk::ExitException( 1 ); } if ( !GridDims.empty() && GridSpacing ) { cmtk::StdErr << "ERROR: must specify either output spline control point spacing or grid dimensions, but not both.\n"; throw cmtk::ExitException( 1 ); } cmtk::DeformationField::SmartPtr dfield = cmtk::DeformationField::SmartPtr::DynamicCastFrom( cmtk::XformIO::Read( InputPath ) ); cmtk::FitSplineWarpToDeformationField fitSpline( dfield, Absolute ); cmtk::SplineWarpXform::SmartPtr splineWarp; cmtk::AffineXform::SmartPtr affineXform; if ( AffineFirst ) { try { affineXform = cmtk::FitAffineToWarpXform( dfield ).Fit(); } catch ( const cmtk::AffineXform::MatrixType::SingularMatrixException& ) { cmtk::StdErr << "ERROR: singular matrix encountered in cmtk::FitAffineToWarpXform::Fit().\n"; throw cmtk::ExitException( 1 ); } } if ( GridSpacing ) { splineWarp = fitSpline.Fit( GridSpacing, Levels, affineXform ); } else { if ( !GridDims.empty() ) { double dims[3]; if ( 3 != sscanf( GridDims.c_str(), "%20lf,%20lf,%20lf", &(dims[0]), &(dims[1]), &(dims[2]) ) ) { cmtk::StdErr << "ERROR: grid dimensions must be specified as dimsX,dimsY,dimsZ\n"; throw cmtk::ExitException( 1 ); } splineWarp = fitSpline.Fit( cmtk::FixedVector<3,double>::FromPointer( dims ), Levels, affineXform ); } else { cmtk::StdErr << "ERROR: must specify either output spline control point spacing or grid dimensions.\n"; throw cmtk::ExitException( 1 ); } } cmtk::XformIO::Write( splineWarp, OutputPath ); return 0; } #include "cmtkSafeMain" cmtk-3.3.1/apps/fit_spline_xform.cxx000066400000000000000000000134211276303427400175230ustar00rootroot00000000000000/* // // Copyright 1997-2011 Torsten Rohlfing // // Copyright 2004-2014 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 5129 $ // // $LastChangedDate: 2014-01-09 13:48:15 -0800 (Thu, 09 Jan 2014) $ // // $LastChangedBy: torstenrohlfing $ // */ #include #include #include #include #include #include #include #include #include int doMain ( const int argc, const char *argv[] ) { std::string inputImagePath; std::string outputPath; std::string gridDims; cmtk::Types::Coordinate gridSpacing = 0; int levels = 1; cmtk::Types::Coordinate inversionTolerance = 1e-8; std::vector inputXformPaths; bool affineFirst = false; try { cmtk::CommandLine cl; cl.SetProgramInfo( cmtk::CommandLine::PRG_TITLE, "Fit Single B-Spline Transformation to Concatenated Transformation List" ); cl.SetProgramInfo( cmtk::CommandLine::PRG_DESCR, "Fit a parametric nonrigid transformation (B-spline free-form deformation) to a list of concatenated, optionally inverted, transformations." ); typedef cmtk::CommandLine::Key Key; cl.BeginGroup( "Fitting", "Fitting Options" ); cl.AddOption( Key( "levels" ), &levels, "Number of levels in the multi-level B-spline approximation procedure." ); cl.AddSwitch( Key( "fit-affine-first" ), &affineFirst, true, "Fit affine transformation first, then initialize spline with it." ); cl.AddOption( Key( "final-cp-spacing" ), &gridSpacing, "Final control point grid spacing of the output B-spline transformation." ); cl.AddOption( Key( "final-cp-dims" ), &gridDims, "Final control point grid dimensions (i.e., number of controlpoints) of the output B-spline transformation. To be provided as 'dimX,dimY,dimZ'." ); cl.EndGroup(); cl.BeginGroup( "Input", "Input Options" ); cl.AddOption( Key( "inversion-tolerance" ), &inversionTolerance, "Numerical tolerance of B-spline inversion in mm. Smaller values will lead to more accurate inversion, but may increase failure rate." ); cl.EndGroup(); cl.BeginGroup( "Output", "Output Options" ); cl.AddOption( Key( 'o', "output" ), &outputPath, "Path for the output transformation." )->SetProperties( cmtk::CommandLine::PROPS_XFORM | cmtk::CommandLine::PROPS_OUTPUT ); cl.EndGroup(); cl.AddParameter( &inputImagePath, "InputImage", "Input image path. This image determines the discrete sampling grid where the target transformation is estimated and fitted." )->SetProperties( cmtk::CommandLine::PROPS_IMAGE ); cl.AddParameterVector( &inputXformPaths, "XformList", "List of concatenated transformations. Insert '--inverse' to use the inverse of the transformation listed next. " "(If the first transformation in the sequence is inverted, then '--inverse' must be preceded by '--', i.e., use '-- --inverse xform.path')." )->SetProperties( cmtk::CommandLine::PROPS_XFORM ); cl.Parse( argc, argv ); } catch ( const cmtk::CommandLine::Exception& e ) { cmtk::StdErr << e; throw cmtk::ExitException( 1 ); } if ( !gridDims.empty() && gridSpacing ) { cmtk::StdErr << "ERROR: must specify either output spline control point spacing or grid dimensions, but not both.\n"; throw cmtk::ExitException( 1 ); } cmtk::XformList xformList = cmtk::XformListIO::MakeFromStringList( inputXformPaths ); xformList.SetEpsilon( inversionTolerance ); cmtk::UniformVolume::SmartPtr imageGrid( cmtk::VolumeIO::ReadGridOriented( inputImagePath ) ); cmtk::FitSplineWarpToXformList fitSpline( *imageGrid, xformList ); cmtk::SplineWarpXform::SmartPtr splineWarp; if ( gridSpacing ) { try { splineWarp = fitSpline.Fit( gridSpacing, levels, affineFirst ); } catch ( const cmtk::AffineXform::MatrixType::SingularMatrixException& ) { cmtk::StdErr << "ERROR: singular matrix encountered in cmtk::FitSplineWarpToXformList::Fit()\n"; throw cmtk::ExitException( 1 ); } } else { if ( !gridDims.empty() ) { double dims[3]; if ( 3 != sscanf( gridDims.c_str(), "%20lf,%20lf,%20lf", &(dims[0]), &(dims[1]), &(dims[2]) ) ) { cmtk::StdErr << "ERROR: grid dimensions must be specified as dimsX,dimsY,dimsZ\n"; throw cmtk::ExitException( 1 ); } try { splineWarp = fitSpline.Fit( cmtk::FixedVector<3,double>::FromPointer( dims ), levels, affineFirst ); } catch ( const cmtk::AffineXform::MatrixType::SingularMatrixException& ) { cmtk::StdErr << "ERROR: singular matrix encountered in cmtk::FitSplineWarpToXformList::Fit()\n"; throw cmtk::ExitException( 1 ); } } else { cmtk::StdErr << "ERROR: must specify either output spline control point spacing or grid dimensions.\n"; throw cmtk::ExitException( 1 ); } } cmtk::XformIO::Write( splineWarp, outputPath ); return 0; } #include "cmtkSafeMain" cmtk-3.3.1/apps/geomatch.cxx000066400000000000000000000133271276303427400157500ustar00rootroot00000000000000/* // // Copyright 1997-2010 Torsten Rohlfing // // Copyright 2004-2014 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4818 $ // // $LastChangedDate: 2013-09-10 11:28:54 -0700 (Tue, 10 Sep 2013) $ // // $LastChangedBy: torstenrohlfing $ // */ #include #include #include #include #include #include #include #include #include #include #include cmtk::UniformVolume::SmartPtr readVolume( const std::string& path, const char* readOrientation ) { cmtk::UniformVolume::SmartPtr volume; try { if ( readOrientation ) volume = cmtk::VolumeIO::ReadOriented( path, readOrientation ); else volume = cmtk::VolumeIO::Read( path ); } catch (...) { if ( readOrientation ) volume = cmtk::VolumeIO::ReadGridOriented( path, readOrientation ); else volume = cmtk::VolumeIO::ReadGrid( path ); } if ( ! volume ) { cmtk::StdErr << "ERROR: cannot read image from " << path << "\n"; throw cmtk::ExitException( 1 ); } return volume; } int doMain( int argc, const char *argv[] ) { const char* readOrientation = NULL; std::vector imagePaths; bool noCheckXforms = false; bool noCheckPixels = false; double tolerance = 1e-6; double toleranceXlate = 1e-3; try { cmtk::CommandLine cl; cl.SetProgramInfo( cmtk::CommandLine::PRG_TITLE, "Check whether the geometries (e.g., grid dimensions, pixel sizes, spatial coordinates) or two or more images match." ); cl.SetProgramInfo( cmtk::CommandLine::PRG_DESCR, "This tool reads two or more images and tests whether their grid dimensions, pixel sizes, and image-to-space transformations match. " "Optionally, all images are reoriented into standard orientation before performing the test. If all images match, the tool returns with exit code 0, otherwise it returns with exit code 2. " "In case of an error (e.g., one of the images can not be read), the exit code is 1." ); typedef cmtk::CommandLine::Key Key; cl.BeginGroup( "Input" , "Input Options" ); cl.AddSwitch( Key( "read-ras" ), &readOrientation, "RAS", "Read all images in RAS orientation" ); cl.EndGroup(); cl.BeginGroup( "Comparison" , "Image Comparison Options" ); cl.AddSwitch( Key( "no-check-xforms" ), &noCheckXforms, true, "Do not check transformation matrices." ); cl.AddSwitch( Key( "no-check-pixels" ), &noCheckPixels, true, "Do not check pixelsizes." ); cl.AddOption( Key( "tolerance" ), &tolerance, "Numerical tolerance for floating point comparisons of transformation matrices." ); cl.AddOption( Key( "tolerance-xlate" ), &toleranceXlate, "Numerical tolerance for floating point comparisons of the translational components of the transformation matrices." ); cl.EndGroup(); cl.AddParameterVector( &imagePaths, "ImagePaths", "List of image files." ); cl.Parse( argc, const_cast( argv ) ); } catch ( const cmtk::CommandLine::Exception& ex ) { cmtk::StdErr << ex << "\n"; throw cmtk::ExitException( 1 ); } // Check if we have enough parameters if ( imagePaths.size() < 2 ) { cmtk::StdErr << "ERROR: need at least two image paths.\n"; throw cmtk::ExitException( 1 ); } cmtk::UniformVolume::SmartConstPtr firstVolume = readVolume( imagePaths[0], readOrientation ); const cmtk::AffineXform::MatrixType firstVolumeMatrix = firstVolume->GetImageToPhysicalMatrix(); for ( size_t i = 1; i < imagePaths.size(); ++i ) { cmtk::UniformVolume::SmartConstPtr nextVolume = readVolume( imagePaths[i], readOrientation ); // First and always, use the DataGrid member function to check grid dimensions if ( ! firstVolume->DataGrid::GridMatches( *nextVolume ) ) { cmtk::DebugOutput( 1 ) << "MISMATCH: grid dimensions\n"; return 2; } // Check pixels - use default (UniformVolume) member function if ( ! noCheckPixels ) { if ( ! firstVolume->GridMatches( *nextVolume ) ) { cmtk::DebugOutput( 1 ) << "MISMATCH: pixel size\n"; return 2; } } if ( ! noCheckXforms ) { // Check rotational part of image matrices const cmtk::AffineXform::MatrixType nextVolumeMatrix = nextVolume->GetImageToPhysicalMatrix(); if ( (firstVolumeMatrix.GetTopLeft3x3() - nextVolumeMatrix.GetTopLeft3x3()).FrobeniusNorm() > tolerance ) { cmtk::DebugOutput( 1 ) << "MISMATCH: image-to-space matrix (rotational part)\n"; return 2; } // Check translational part of image matrices if ( (firstVolumeMatrix.GetRowVector(3) - nextVolumeMatrix.GetRowVector(3)).MaxAbsValue() > toleranceXlate ) { cmtk::DebugOutput( 1 ) << "MISMATCH: image-to-space matrix (translational part)\n"; return 2; } } } return 0; } #include "cmtkSafeMain" cmtk-3.3.1/apps/glm.cxx000066400000000000000000000376371276303427400147520ustar00rootroot00000000000000/* // // Copyright 1997-2010 Torsten Rohlfing // // Copyright 2004-2013 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4902 $ // // $LastChangedDate: 2013-10-01 10:13:33 -0700 (Tue, 01 Oct 2013) $ // // $LastChangedBy: torstenrohlfing $ // */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include bool DumpStatistics = true; std::set IgnoreSet; std::set SelectSet; std::vector FieldNames; void CallbackIgnore( const char* argv ) { IgnoreSet.insert( atoi( argv ) ); } void CallbackSelect( const char* argv ) { SelectSet.insert( argv ); } bool ExcludeConstant = false; bool NormalizeParameters = false; bool ExponentialModel = false; std::list CtlFileName; std::list ImgFilePatt; const char* OutputFilePatt = "model_%s_%02d_%s.nii"; bool CropImages = false; cmtk::DataGrid::RegionType CropImagesRegion; void CallbackCropImages( const char* arg ) { int cropFrom[3], cropTo[3]; CropImages = (6 == sscanf( arg, "%4d,%4d,%4d,%4d,%4d,%4d", cropFrom, cropFrom+1, cropFrom+2, cropTo,cropTo+1,cropTo+2 ) ); if ( CropImages ) { CropImagesRegion = cmtk::DataGrid::RegionType( cmtk::DataGrid::IndexType::FromPointer( cropFrom ), cmtk::DataGrid::IndexType::FromPointer( cropTo ) ); } } void Import ( const char* ctlFileName, const char* imgFilePatt, cmtk::UniformVolume::SmartPtr& refVolume, std::vector& imagesData, size_t& nParameters, cmtk::Types::DataItem*& parameters, size_t& nParametersTotal ) { nParameters = 0; std::vector vParam; std::ifstream ctlFile( ctlFileName ); if ( ! ctlFile.is_open() ) { std::cerr << "Error opening control/parameter file " << ctlFileName << std::endl; throw cmtk::ExitException( 1 ); } FieldNames.clear(); std::string line; getline( ctlFile, line ); { std::istringstream lineStream( line ); std::string nextFieldName; lineStream >> nextFieldName; // skip ID field while ( ! lineStream.eof() ) { lineStream >> nextFieldName; if ( !SelectSet.empty() ) // positive parameter selection by name { if ( SelectSet.find( nextFieldName ) == SelectSet.end() ) { IgnoreSet.insert( FieldNames.size() ); } } FieldNames.push_back( nextFieldName ); } } FieldNames.push_back( "CONST" ); cmtk::DebugOutput( 2 ) << "\n\nImporting image files.\n"; nParametersTotal = 0; while ( ! ctlFile.eof() ) { getline( ctlFile, line ); if ( line.length() && line[0] != '#' ) { std::istringstream lineStream( line ); std::string imgName; lineStream >> imgName; char imgPath[PATH_MAX]; if ( snprintf( imgPath, sizeof( imgPath ), imgFilePatt, imgName.c_str() ) > PATH_MAX ) { cmtk::StdErr << "ERROR: output path exceeds maximum path length\n"; throw cmtk::ExitException( 1 ); } cmtk::UniformVolume::SmartPtr volume( cmtk::VolumeIO::ReadOriented( imgPath ) ); if ( !volume ) { cmtk::StdErr << "ERROR: Could not read image file " << imgPath << "\n"; throw cmtk::ExitException( 1 ); } if ( CropImages ) { volume->CropRegion() = CropImagesRegion; volume = cmtk::UniformVolume::SmartPtr( volume->GetCroppedVolume() ); } cmtk::TypedArray::SmartPtr imageData( volume->GetData() ); if ( !imageData ) { cmtk::StdErr << "ERROR: no image data in file " << imgPath << "\n"; throw cmtk::ExitException( 1 ); } if ( ExponentialModel ) imageData->ApplyFunctionDouble( cmtk::Wrappers::Log ); if ( ! refVolume ) refVolume = volume; imagesData.push_back( imageData ); cmtk::Types::DataItem param; if ( nParametersTotal ) { for ( size_t p = 0; p < nParametersTotal; ++p ) { if ( lineStream.eof() ) { cmtk::StdErr << "ERROR: insufficient number of model parameters in line '" << line << "'\n"; throw cmtk::ExitException( 1 ); } lineStream >> param; // parameter exclusion by index if ( IgnoreSet.find( p ) == IgnoreSet.end() ) { vParam.push_back( param ); } } if ( ! ExcludeConstant ) vParam.push_back( 1.0 ); } else { while ( ! lineStream.eof() ) { lineStream >> param; if ( IgnoreSet.find( nParametersTotal ) == IgnoreSet.end() ) { vParam.push_back( param ); } else { cmtk::DebugOutput( 1 ) << "INFORMATION: Ignoring parameter #" << nParametersTotal << "\n"; } ++nParametersTotal; } if ( ! ExcludeConstant ) vParam.push_back( 1.0 ); // set nParameters from parameter array to account for ignored // parameters. nParameters = vParam.size(); cmtk::DebugOutput( 1 ) << "NParameters = " << nParameters << "\n"; } } } if ( nParameters * imagesData.size() != vParam.size() ) { cmtk::StdErr << "ERROR: number of parameters does not equal expected number" << "(" << nParameters * imagesData.size() << " != " << vParam.size() << ")\n"; throw cmtk::ExitException( 1 ); } parameters = cmtk::Memory::ArrayC::Allocate( vParam.size() ); for ( size_t i = 0; i < vParam.size(); ++i ) parameters[i] = vParam[i]; if ( cmtk::DebugOutput::GetGlobalLevel() > 0 ) { cmtk::DebugOutput( 1 ) << "\n\nDesign Matrix: \n"; for ( size_t j = 0; j < FieldNames.size(); ++j ) { if ( IgnoreSet.find( j ) != IgnoreSet.end() ) continue; cmtk::DebugOutput( 1 ).GetStream().printf( "%8s\t", FieldNames[j].c_str() ); } cmtk::DebugOutput( 1 ) << "\n"; size_t ofs = 0; for ( size_t i = 0; i < imagesData.size(); ++i ) { for ( size_t j = 0; j < nParameters; ++j, ++ofs ) { cmtk::DebugOutput( 1 ).GetStream().printf( "%8.2f\t", parameters[ofs] ); } cmtk::DebugOutput( 1 ) << "\n"; } } if ( ! ExcludeConstant ) ++nParametersTotal; } int doMain( const int argc, const char* argv[] ) { try { cmtk::CommandLine cl; cl.SetProgramInfo( cmtk::CommandLine::PRG_TITLE, "General Linear Model" ); cl.SetProgramInfo( cmtk::CommandLine::PRG_DESCR, "Statistical modeling of pixel intensities in multiple images using a General Linear Model.\n\n" "The independent variables of the model are defined in one of more control files. Each control file is a text file with one whitespace-separated column per independent variable.\n\n" "The first line of the control file defines the variable names, i.e., the labels that identify each variable. Each following line contains one value per independent variable.\n\n" "Example:\n\n ID age sex\n 01 20 0\n 02 30 1\n\n" "Each control file name is followed by a file name pattern. In that pattern, a single '%s' place holder is replaced by the value found in the first column of each control file row. The resulting " "string is the path of the image read and associated with the model variables listed on that particular control file line.\n\n" "Using the above control file example, the pattern 'images/subject%s.nii' would expand to the image file names 'images/subject01.nii' and 'images/subject02.nii'.\n\n" "Multiple control files can be used, each with a different image file pattern."); cl.SetProgramInfo( cmtk::CommandLine::PRG_SYNTX, "glm [options] ctlfile imgfile_pattern [ctlfile imgfile_pattern ...]" ); cl.SetProgramInfo( cmtk::CommandLine::PRG_CATEG, "CMTK.Statistics and Modeling" ); typedef cmtk::CommandLine::Key Key; cl.BeginGroup( "Input", "Input Settings" ); cl.AddCallback( Key( 'c', "crop" ), CallbackCropImages, "To save space/time, crop images: x0,y0,z0,x1,y1,z2" ); cl.EndGroup(); cl.BeginGroup( "Model", "Model Settings" ); cl.AddSwitch( Key( 'n', "normalize" ), &NormalizeParameters, true, "Normalize model parameters w.r.t. data variances." ); cl.AddSwitch( Key( 'e', "exp" ), &ExponentialModel, true, "Use exponential model rather than linear model." ); cl.EndGroup(); cl.BeginGroup( "Variables", "Selection of Independent Variables" ); cl.AddSwitch( Key( 'x', "exclude-constant" ), &ExcludeConstant, true, "Exclude automatic constant parameter from model." ); cl.AddCallback( Key( 'i', "ignore-parameter" ), CallbackIgnore, "Ignore parameter with given NUMBER (0..n-1). Can be repeated." ); cl.AddCallback( Key( 's', "select-parameter" ), CallbackSelect, "Select parameter with given NAME for model. Can be repeated." ); cl.EndGroup(); cl.BeginGroup( "Output", "Output Settings" ); cl.AddOption( Key( 'O', "output-pattern" ), &OutputFilePatt, "Filename pattern for output.\n\n" " %s is replaced with image type ('fstat', 'tstat', or 'param')\n" " %d is replaced with independent variable number (0 for entire model)\n" " %s is replaced with independent variable name ('model' for entire model)\n" ); cl.EndGroup(); cl.Parse( argc, argv ); CtlFileName.push_back( cl.GetNext() ); ImgFilePatt.push_back( cl.GetNext() ); const char* next = cl.GetNextOptional(); while ( next ) { CtlFileName.push_back( next ); ImgFilePatt.push_back( cl.GetNext() ); next = cl.GetNextOptional(); } } catch ( const cmtk::CommandLine::Exception& e ) { cmtk::StdErr << e; } if ( CtlFileName.empty() || ImgFilePatt.empty() ) { cmtk::StdErr << "ERROR: need both a control file name and an image file pattern\n"; throw cmtk::ExitException( 1 ); } cmtk::ProgressConsole progressIndicator; if ( cmtk::DebugOutput::GetGlobalLevel() > 0 ) { std::set::const_iterator it = SelectSet.begin(); if ( it != SelectSet.end() ) { cmtk::DebugOutput( 1 ) << "Selected model parameters:" << "\n"; while ( it != SelectSet.end() ) { cmtk::DebugOutput( 1 ) << "\t" << *it; ++it; } cmtk::DebugOutput( 1 ) << "\n"; } } cmtk::UniformVolume::SmartPtr refVolume( NULL ); std::vector nParameters( CtlFileName.size(), 0 ); std::vector nParametersTotal( CtlFileName.size(), 0 ); std::vector parameters( CtlFileName.size(), NULL ); std::vector< std::vector > ImagesData; std::vector allImages; std::list::const_iterator itCtlFile = CtlFileName.begin(), itImgFile = ImgFilePatt.begin(); for ( size_t idx = 0; (itCtlFile != CtlFileName.end()) && (itImgFile != ImgFilePatt.end()); ++itCtlFile, ++itImgFile, ++idx ) { std::vector imagesDataNext; Import( *itCtlFile, *itImgFile, refVolume, imagesDataNext, nParameters[idx], parameters[idx], nParametersTotal[idx] ); ImagesData.push_back( imagesDataNext ); if ( nParametersTotal[idx] != nParametersTotal[0] ) { cmtk::StdErr << "Total number of parameters for control file #" << idx << "does not match that for file #0\n"; throw cmtk::ExitException( 1 ); } if ( nParameters[idx] != nParameters[0] ) { cmtk::StdErr << "Number of active parameters for control file #" << idx << "does not match that for file #0\n"; throw cmtk::ExitException( 1 ); } const size_t totalNumberOfImages = allImages.size(); allImages.resize( totalNumberOfImages + ImagesData[idx].size() ); std::copy( ImagesData[idx].begin(), ImagesData[idx].end(), &allImages[totalNumberOfImages] ); } double* allParameters = cmtk::Memory::ArrayC::Allocate( nParameters[0] * allImages.size() ); size_t idx = 0; for ( size_t ctl = 0; ctl < parameters.size(); ++ctl ) { for ( size_t param = 0; param < ImagesData[ctl].size() * nParameters[0]; ++param, ++idx ) { allParameters[idx] = parameters[ctl][param]; } } cmtk::GeneralLinearModel glm( nParameters[0], allImages.size(), allParameters ); cmtk::Memory::ArrayC::Delete( allParameters ); if ( cmtk::DebugOutput::GetGlobalLevel() > 0 ) { cmtk::DebugOutput( 1 ) << "\n\nSingular values:\n"; size_t p = 0; for ( size_t pp = 0; pp < nParametersTotal[0]; ++pp ) { // if this parameter is ignored, continue with next one. if ( IgnoreSet.find( pp ) != IgnoreSet.end() ) continue; cmtk::DebugOutput( 1 ) << "\t" << glm.GetSingularValue( p++ ); } cmtk::DebugOutput( 1 ) << "\n"; cmtk::DebugOutput( 1 ) << "\n\nParameter correlation matrix:\n"; cmtk::Matrix2D* cc = glm.GetCorrelationMatrix(); for ( size_t ppp = 0; ppp < nParameters[0]; ++ppp ) { for ( size_t pp = 0; pp < nParameters[0]; ++pp ) { cmtk::DebugOutput( 1 ).GetStream().printf( "%.2f\t", (*cc)[ppp][pp] ); } cmtk::DebugOutput( 1 ) << "\n"; } delete cc; } glm.FitModel( allImages, NormalizeParameters ); if ( DumpStatistics ) { cmtk::DebugOutput( 1 ) << "\n\nParameter normalization factors:\n"; size_t p = 0; for ( size_t pp = 0; pp < nParametersTotal[0]; ++pp ) { // if this parameter is ignored, continue with next one. if ( IgnoreSet.find( pp ) != IgnoreSet.end() ) continue; cmtk::StdOut.printf( "%d\t%f\n", (int)pp, glm.GetNormFactor( p ) ); // increment actual parameter index. ++p; } } if ( OutputFilePatt ) { cmtk::DebugOutput( 2 ) << "\n\nWriting output image files.\n"; char outFileName[PATH_MAX]; cmtk::TypedArray::SmartPtr fstatData = glm.GetFStat(); if ( snprintf( outFileName, PATH_MAX, OutputFilePatt, "fstat", 0, "model" ) > PATH_MAX ) { cmtk::StdErr << "ERROR: output path exceeds maximum path length\n"; } else { refVolume->SetData( fstatData ); cmtk::VolumeIO::Write( *refVolume, outFileName ); } size_t p = 0; for ( size_t pp = 0; pp < nParametersTotal[0]; ++pp ) { // if this parameter is ignored, continue with next one. if ( IgnoreSet.find( pp ) != IgnoreSet.end() ) continue; cmtk::TypedArray::SmartPtr modelData = glm.GetModel( p ); if ( snprintf( outFileName, sizeof( outFileName ), OutputFilePatt, "param", pp, FieldNames[pp].c_str() ) > PATH_MAX ) { cmtk::StdErr << "ERROR: output path exceeds maximum path length\n"; } else { refVolume->SetData( modelData ); cmtk::VolumeIO::Write( *refVolume, outFileName ); } cmtk::TypedArray::SmartPtr modelTStat = glm.GetTStat( p ); if ( snprintf( outFileName, PATH_MAX, OutputFilePatt, "tstat", pp, FieldNames[pp].c_str() ) > PATH_MAX ) { cmtk::StdErr << "ERROR: output path exceeds maximum path length\n"; } else { refVolume->SetData( modelTStat ); cmtk::VolumeIO::Write( *refVolume, outFileName ); } // increment actual parameter index. ++p; } } return 0; } #include "cmtkSafeMain" cmtk-3.3.1/apps/gmm.cxx000066400000000000000000000211451276303427400147360ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2011, 2013 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4866 $ // // $LastChangedDate: 2013-09-24 10:01:06 -0700 (Tue, 24 Sep 2013) $ // // $LastChangedBy: torstenrohlfing $ // */ #include #include #include #include #include #include #include #include int doMain ( const int argc, const char *argv[] ) { std::string inputImagePath; std::string maskImagePath; std::string outputImagePath; bool writeProbMaps = false; bool priorsInitOnly = false; byte nClasses = 3; byte nIterations = 10; double priorEpsilon = 0; std::vector priorImagePaths; try { cmtk::CommandLine cl( cmtk::CommandLine::PROPS_XML ); cl.SetProgramInfo( cmtk::CommandLine::PRG_TITLE, "Gaussian mixture model segmentation" ); cl.SetProgramInfo( cmtk::CommandLine::PRG_DESCR, "Segment an image into c classes using the EM algorithm for Gaussian mixtures with optional priors." ); typedef cmtk::CommandLine::Key Key; cl.BeginGroup( "General", "General Classification Parameters" ); cl.AddOption( Key( 'm', "mask" ), &maskImagePath, "Path to foreground mask image. If this is not provided, the input image is used as its own mask, but this does not work properly if the input image itself has pixels with " "zero or negative values." ); cl.AddOption( Key( 'c', "classes" ), &nClasses, "Number of classes." ); cl.AddOption( Key( 'n', "iterations" ), &nIterations, "Number of EM iterations." ); cl.EndGroup(); cl.BeginGroup( "Priors", "Handling of Priors" ); cl.AddSwitch( Key( "priors-init-only" ), &priorsInitOnly, true, "Use priors for initialization only." ); cl.AddOption( Key( 'e', "prior-epsilon" ), &priorEpsilon, "Small value to add to all class priors to eliminate zero priors." ); cl.BeginGroup( "Output", "Output Parameters" ); cl.AddSwitch( Key( 'p', "probability-maps" ), &writeProbMaps, true, "Write probability maps. The file names for these maps will be generated from the output image path by inserting '_prob#' before the file format suffix, " "where '#' is the index of the respective class, numbered starting at 1 (zero is background)." ); cl.EndGroup(); cl.AddParameter( &inputImagePath, "InputImage", "Input image path" )->SetProperties( cmtk::CommandLine::PROPS_IMAGE ); cl.AddParameter( &outputImagePath, "OutputImage", "Output image path" )->SetProperties( cmtk::CommandLine::PROPS_IMAGE | cmtk::CommandLine::PROPS_OUTPUT ); cl.AddParameterVector( &priorImagePaths, "PriorImages", "Prior image paths" )->SetProperties( cmtk::CommandLine::PROPS_IMAGE ); cl.Parse( argc, argv ); } catch ( const cmtk::CommandLine::Exception& e ) { cmtk::StdErr << e << "\n"; throw cmtk::ExitException( 1 ); } if ( priorImagePaths.size() && (priorImagePaths.size() != nClasses) ) { cmtk::StdErr << "ERROR: must provide one prior image per class.\n"; throw cmtk::ExitException( 1 ); } cmtk::UniformVolume::SmartPtr inputImage = cmtk::VolumeIO::ReadOriented( inputImagePath ); cmtk::UniformVolume::SmartConstPtr maskImage = inputImage; if ( !maskImagePath.empty() ) { maskImage = cmtk::VolumeIO::ReadOriented( maskImagePath ); if ( ! inputImage->GridMatches( *(maskImage) ) ) { cmtk::StdErr << "ERROR: mask image must have the same discrete grid as the input image.\n"; throw cmtk::ExitException( 1 ); } } const size_t nPixels = inputImage->GetNumberOfPixels(); std::vector priorImages( nClasses ); for ( size_t k = 0; k < nClasses; ++k ) { priorImages[k] = cmtk::VolumeIO::ReadOriented( priorImagePaths[k] ); if ( ! inputImage->GridMatches( *(priorImages[k]) ) ) { cmtk::StdErr << "ERROR: all prior images must have the same discrete grid as the input image.\n"; throw cmtk::ExitException( 1 ); } } // based on tutorial by Carlo Tomasi: // http://www.cs.duke.edu/courses/spring04/cps196.1/handouts/EM/tomasiEM.pdf std::vector classMu( nClasses ), classSigma( nClasses ), pTotal( nClasses ); // initialize probabilities with priors std::vector pMaps( nClasses ); for ( size_t k = 0; k < nClasses; ++k ) { pMaps[k] = priorImages[k]->CloneGrid(); pMaps[k]->SetData( priorImages[k]->GetData()->Convert( cmtk::TYPE_DOUBLE ) ); } // Properly mask all non-foreground pixels for ( size_t n = 0; n < nPixels; ++n ) { if ( maskImage->GetDataAt( n ) <= 0 ) { for ( size_t k = 0; k < nClasses; ++k ) { pMaps[k]->SetDataAt( 0, n ); } } } // run EM iterations for ( size_t i = 0; i < nIterations; ++i ) { #pragma omp parallel for for ( int k = 0; k < nClasses; ++k ) { classMu[k] = pTotal[k] = 0; for ( size_t n = 0; n < nPixels; ++n ) { if ( maskImage->GetDataAt( n ) > 0 ) { const double w = pMaps[k]->GetDataAt( n ); classMu[k] += w * inputImage->GetDataAt( n ); pTotal[k] += w; } } classMu[k] /= pTotal[k]; classSigma[k] = 0; for ( size_t n = 0; n < nPixels; ++n ) { if ( maskImage->GetDataAt( n ) > 0 ) { const double w = pMaps[k]->GetDataAt( n ); classSigma[k] += w * cmtk::MathUtil::Square( classMu[k] - inputImage->GetDataAt( n ) ); } } classSigma[k] = sqrt( classSigma[k] / pTotal[k] ); } cmtk::StdOut.printf( "Iteration %u\n", i ); for ( size_t k = 0; k < nClasses; ++k ) { cmtk::StdOut.printf( "Class %u: %f +/- %f\t", k, classMu[k], classSigma[k] ); } cmtk::StdOut << "\n"; #pragma omp parallel for for ( int n = 0; n < static_cast( nPixels ); ++n ) { if ( maskImage->GetDataAt( n ) > 0 ) { double pTotalPixel = 0; for ( size_t k = 0; k < nClasses; ++k ) { double kernel = cmtk::GaussianKernel::GetValue( inputImage->GetDataAt( n ), classMu[k], classSigma[k] ); if ( ! priorsInitOnly ) kernel *= priorImages[k]->GetDataAt( n ) + priorEpsilon; pMaps[k]->SetDataAt( kernel, n ); pTotalPixel += kernel; } if ( pTotalPixel > 0 ) { for ( size_t k = 0; k < nClasses; ++k ) { pMaps[k]->SetDataAt( pMaps[k]->GetDataAt( n ) / pTotalPixel, n ); } } } } } if ( writeProbMaps ) { char path[PATH_MAX+1]; for ( size_t k = 0; k < nClasses; ++k ) { strncpy( path, outputImagePath.c_str(), PATH_MAX ); path[PATH_MAX] = 0; // for safety - terminate string char* slash = strrchr( path, '/' ); if ( ! slash ) slash = path; char* period = strchr( slash, '.' ); if ( ! period ) period = path + strlen( path ); snprintf( period, PATH_MAX - (period-path), "_prob%d%s", static_cast( 1+k ), outputImagePath.c_str() + (period-path) ); cmtk::VolumeIO::Write( *(pMaps[k]), path ); } } #pragma omp parallel for for ( int n = 0; n < static_cast( nPixels ); ++n ) { if ( maskImage->GetDataAt( n ) > 0 ) { byte maxLabel = 0; double maxValue = pMaps[0]->GetDataAt( n ); for ( size_t k = 1; k < nClasses; ++k ) { const double pClass = pMaps[k]->GetDataAt( n ); // if two classes have same probability, pick the one with max prior. if ( (pClass > maxValue) || ( ( pClass == maxValue ) && ( priorImages[k]->GetDataAt( n ) > priorImages[maxLabel]->GetDataAt( n ) ) ) ) { maxLabel = k; maxValue = pClass; } } inputImage->SetDataAt( 1+maxLabel, n ); } else { inputImage->SetDataAt( 0, n ); } } cmtk::VolumeIO::Write( *(inputImage), outputImagePath ); return 0; } #include "cmtkSafeMain" cmtk-3.3.1/apps/gregxform.cxx000066400000000000000000000221151276303427400161540ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2014 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 5129 $ // // $LastChangedDate: 2014-01-09 13:48:15 -0800 (Thu, 09 Jan 2014) $ // // $LastChangedBy: torstenrohlfing $ // */ #include #include #include #include #include #include #include #include #include #include #include #include #include cmtk::Types::Coordinate Accuracy = 0.01; bool NoCheck = false; bool Forward = false; const char* StudyList = NULL; // Greg's additions: option to calculate jacobian at given point bool Jacobian = false; bool NormalisedJacobian = false; // return the scaling from the affine component only bool ReturnGlobalScaling = false; bool AffineOnly = false; // read and write binary data bool Binary = false; // Input Files const char* InputPath=NULL; const char* OutputPath=NULL; const char* FallbackInversePath = NULL; FILE *outfile = stdout; FILE *infile=stdin; int doMain( const int argc, const char *argv[] ) { try { cmtk::CommandLine cl; cl.SetProgramInfo( cmtk::CommandLine::PRG_TITLE, "Apply coordinate transformations to lists of point coordinates" ); cl.SetProgramInfo( cmtk::CommandLine::PRG_DESCR, "THIS TOOL IS DEPRECATED. PLEASE USE streamxform INSTEAD.\n\n" "This tool reads a list of 3D coordinates and applies a coordimnate transformation to them. The transformation can optionally be inverted. The transformed coordinates are then written to standard output." ); cl.SetProgramInfo( cmtk::CommandLine::PRG_SYNTX, "gregxform [options] transformation" ); typedef cmtk::CommandLine::Key Key; cl.AddOption( Key( 'a', "accuracy" ), &Accuracy, "Approximation accuracy (maximum error)" ); cl.AddSwitch( Key( 'x', "no-check" ), &NoCheck, true, "Disable accuracy checking and always output something" ); cl.AddSwitch( Key( 'f', "forward" ), &Forward, true, "Apply forward transformation (default: backward)" ); cl.AddOption( Key( 'F', "fallback-inverse" ), &FallbackInversePath, "Fallback inverse transformation (used for initialization and in case of failure)" ); // Greg's additions: cl.AddOption( Key( 'i', "input-file" ), &InputPath, "Input path [default: STDIN]. "); cl.AddOption( Key( 'o', "output-file" ), &OutputPath, "Output path [default: STDOUT]. "); cl.AddSwitch( Key( 'n', "affine" ), &AffineOnly, true, "Apply affine transformation even if warp studylist specified" ); cl.AddSwitch( Key( 'b', "binary" ), &Binary, true, "Read Binary input and produce binary output (floats)" ); cl.AddSwitch( Key( 'j', "jacobian" ), &Jacobian, true, "Calculate jacobian determinant rather than mapping point (default: false)\ \n\tNB: JDet is >1 if the sample volume is larger than the reference" ); cl.AddSwitch( Key( 'J', "normalised-jacobian" ), &NormalisedJacobian, true, "Calculate jacobian determinant normalised by global scaling factor (default: false)" ); cl.AddSwitch( Key( 'g', "return-global-scaling" ), &ReturnGlobalScaling, true, "Return global scaling factor ie the scaling due to initial affine" ); cl.Parse( argc, argv ); StudyList = cl.GetNext(); } catch ( const cmtk::CommandLine::Exception& e ) { cmtk::StdErr << e; throw cmtk::ExitException( 1 ); } cmtk::Xform::SmartPtr xform( cmtk::XformIO::Read( StudyList ) ); if ( ! xform ) { cmtk::StdErr << "ERROR: could not read transformation\n"; throw cmtk::ExitException( 1 ); } cmtk::WarpXform::SmartPtr warpXform = cmtk::WarpXform::SmartPtr::DynamicCastFrom( xform ); cmtk::AffineXform::SmartPtr affineXform = cmtk::AffineXform::SmartPtr::DynamicCastFrom( xform ); cmtk::SplineWarpXform::SmartPtr splineWarp = cmtk::SplineWarpXform::SmartPtr ::DynamicCastFrom( warpXform ); if ( !affineXform && warpXform ) affineXform = warpXform->GetInitialAffineXform(); if( !affineXform && AffineOnly ) { cmtk::StdErr << "Unable to obtain affine transform from: "<GetInverse(); } catch ( const cmtk::AffineXform::MatrixType::SingularMatrixException& ) { cmtk::StdErr << "ERROR: singular matrix encountered in cmtk::AffineXform::GetInverse()\n"; throw cmtk::ExitException( 1 ); } const cmtk::Types::Coordinate globalScaling = (splineWarp) ? splineWarp->GetGlobalScaling() : affineXform->GetGlobalScaling(); cmtk::Xform::SmartPtr fallbackInverseXform; if ( FallbackInversePath ) { fallbackInverseXform = cmtk::Xform::SmartPtr( cmtk::XformIO::Read( FallbackInversePath ) ); } // GJ: Now open files if required if(InputPath) { infile = fopen( InputPath, "r" ); if(infile==NULL) { cmtk::StdErr << "Unable to open input path: "< v = cmtk::FixedVector<3,cmtk::Types::Coordinate>::FromPointer( xyz ); cmtk::FixedVector<3,cmtk::Types::Coordinate> u( v ); cmtk::FixedVector<3,cmtk::Types::Coordinate> uu; cmtk::Types::Coordinate error = 0; bool success = true; if ( splineWarp && ! AffineOnly ) { if( Jacobian || NormalisedJacobian ) { cmtk::Types::Coordinate j = splineWarp->GetJacobianDeterminant(u); if (NormalisedJacobian) j /= globalScaling; if(Binary) { fwrite( &j, sizeof(float), 1, outfile ); } else { fprintf( outfile, "%f\n", j ); } continue; } if ( Forward ) { v = splineWarp->Apply( v ); success = true; } else { if ( fallbackInverseXform ) { cmtk::Vector3D initialEstimate( fallbackInverseXform->Apply( v ) ); success = splineWarp->ApplyInverseWithInitial( v, v, initialEstimate, Accuracy ); } else { success = splineWarp->ApplyInverse( v, v, Accuracy ); } } if ( !success ) { uu = splineWarp->Apply( v ) - u; error = uu.RootSumOfSquares(); fprintf( stderr, "ERROR: %f %f %f is not inside target image or inversion failed (error = %f)\n", u[0], u[1], u[2], error ); } } else { if ( Forward ) { if ( affineXform ) v = affineXform->Apply( v ); } else { if ( inverseAffineXform ) v = inverseAffineXform->Apply( v ); } } if ( success || NoCheck ) { const float outxyz[3]={(float) v[0],(float) v[1], (float) v[2]}; if (Binary) { fwrite( outxyz, sizeof(float), 3, outfile ); if ( success ) { cmtk::DebugOutput( 1 ).GetStream().printf( "%f %f %f\n", outxyz[0], outxyz[1], outxyz[2] ); } else { cmtk::DebugOutput( 1 ).GetStream().printf( "%f %f %f E %f\n", outxyz[0], outxyz[1], outxyz[2], error ); } } else { if ( success ) { fprintf( outfile, "%f %f %f\n", outxyz[0], outxyz[1], outxyz[2] ); } else { fprintf( outfile, "%f %f %f E %f\n", outxyz[0], outxyz[1], outxyz[2], error ); } } } else { if(Binary) { const float nan3[3] = { std::numeric_limits::signaling_NaN(), std::numeric_limits::signaling_NaN(), std::numeric_limits::signaling_NaN()}; fwrite( nan3, sizeof(float), 3, outfile ); } else { fputs( "ERR ERR ERR\n", outfile ); } } } } return 0; } #include "cmtkSafeMain" cmtk-3.3.1/apps/groupwise_affine.cxx000066400000000000000000000435631276303427400175220ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2013 SRI International // // Copyright 2015 Google, Inc. // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 2702 $ // // $LastChangedDate: 2011-01-11 15:06:02 -0800 (Tue, 11 Jan 2011) $ // // $LastChangedBy: torstenrohlfing $ // */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include bool OptimizeRMI = false; int DownsampleFrom = 4; int DownsampleTo = 1; byte UserBackgroundValue = 0; bool UserBackgroundFlag = false; float SamplingDensity = -1.0; bool UseSamplingDensity = false; bool ForceZeroSum = false; unsigned int ForceZeroSumFirstN = 0; unsigned int NormalGroupFirstN = 0; bool UseSmoothSigmaFactor = false; cmtk::Types::Coordinate SmoothSigmaFactor = -1.0; bool UseNumberOfHistogramBins = false; unsigned int NumberOfHistogramBins = 0; bool CropImageHistograms = false; std::string PreDefinedTemplatePath = ""; cmtk::UniformVolume::SmartPtr PreDefinedTemplate; bool UseTemplateData = false; bool TransformationsFromArchive = false; const char* OutputRootDirectory = NULL; const char* OutputArchive = "groupwise.xforms"; const char* OutputStudyListGroup = "groupwise.list"; const char* OutputStudyListIndividual = "pairs"; const char* AverageImagePath = "average.nii"; cmtk::Interpolators::InterpolationEnum AverageImageInterpolation = cmtk::Interpolators::LINEAR; std::vector NumberDOFs; bool AlignCentersOfMass = false; bool InitScales = false; bool HistogramMatching = false; bool FreeAndRereadImages = false; cmtk::Types::Coordinate Accuracy = 0.01; cmtk::Types::Coordinate Exploration = 0.25; cmtk::Types::Coordinate OptimizerStepFactor = 0.5; cmtk::Optimizer::ReturnType OptimizerDeltaFThreshold = 0; bool DisableOptimization = false; bool OptimizerAggressive = false; int OptimizerRepeatLevel = 5; // this vector holds all target image filenames std::vector fileNameList; // this vector holds the original (not downsampled) images. std::vector imageListOriginal; int doMain( int argc, const char* argv[] ) { try { cmtk::CommandLine cl; cl.SetProgramInfo( cmtk::CommandLine::PRG_TITLE, "Affine population registration" ); cl.SetProgramInfo( cmtk::CommandLine::PRG_DESCR, "This tool registers a population of input images simultaneously, without a template, using either the 'congealing' algorithm or a groupwise similarity measure based on " "a continuous approximation of mutual information ('RMI')." ); cl.SetProgramInfo( cmtk::CommandLine::PRG_SYNTX, "groupwise_affine [options] image0 [image1 ...]" ); cl.SetProgramInfo( cmtk::CommandLine::PRG_CATEG, "CMTK.Image Registration" ); typedef cmtk::CommandLine::Key Key; cl.BeginGroup( "Metric", "Registration Metric Options" ); cl.AddSwitch( Key( "rmi" ), &OptimizeRMI, true, "Use the RMI (a.k.a. regional mutual information) metric to drive the registration)." ); cl.AddSwitch( Key( "congeal" ), &OptimizeRMI, false, "Use the congealing algorithm using pixelwise stack entropies to drive the registration." ); cl.EndGroup(); cl.BeginGroup( "Template", "Template Image Options" ); cl.AddOption( Key( 't', "template" ), &PreDefinedTemplatePath, "Input filename for pre-defined template image." ); cl.AddOption( Key( 'T', "template-with-data" ), &PreDefinedTemplatePath, "Use user-supplied template images's pixel data in registration", &UseTemplateData ); cl.EndGroup(); cl.BeginGroup( "Output", "Output Options" ); cl.AddOption( Key( 'O', "output-root" ), &OutputRootDirectory, "Root directory for all output files." ); cl.AddOption( Key( 'o', "output" ), &OutputArchive, "Output filename for groupwise registration archive." ); cl.AddOption( Key( "output-average" ), &AverageImagePath, "Output filename for registered average image." ); cl.AddSwitch( Key( "average-linear" ), &AverageImageInterpolation, cmtk::Interpolators::LINEAR, "Use linear interpolation for average image" ); cl.AddSwitch( Key( "average-cubic" ), &AverageImageInterpolation, cmtk::Interpolators::CUBIC, "Use cubic interpolation for average image" ); cl.AddSwitch( Key( "no-output-average" ), &AverageImagePath, (const char*)NULL, "Do not write average image." ); cl.EndGroup(); cl.BeginGroup( "Multiresolution", "Multiresolution Parameters" ); cl.AddOption( Key( 'd', "downsample-from" ), &DownsampleFrom, "Initial downsampling factor" ); cl.AddOption( Key( 'D', "downsample-to" ), &DownsampleTo, "Final downsampling factor." ); cl.AddOption( Key( 's', "sampling-density" ), &SamplingDensity, "Probabilistic sampling density. Legal values between 0 and 1.", &UseSamplingDensity ); cl.EndGroup(); cl.BeginGroup( "Image", "Image Options and Operations" ); cl.AddOption( Key( 'B', "force-background" ), &UserBackgroundValue, "Force background pixels (outside FOV) to given (bin) value.", &UserBackgroundFlag ); cl.AddOption( Key( 'H', "histogram-bins" ), &NumberOfHistogramBins, "Manually set number of histogram bins for entropy evaluation", &UseNumberOfHistogramBins ); cl.AddSwitch( Key( "crop-histograms" ), &CropImageHistograms, true, "Crop image histograms to make better use of histogram bins." ); cl.AddOption( Key( "smooth" ), &SmoothSigmaFactor, "Sigma of Gaussian smoothing kernel in multiples of template image pixel size.", &UseSmoothSigmaFactor ); cl.AddSwitch( Key( "match-histograms" ), &HistogramMatching, true, "Match all image histograms to template data (or first image, if no template image is given)" ); cl.AddSwitch( Key( "free-and-reread" ), &FreeAndRereadImages, true, "Free memory allocated for original image whenever these are not needed and re-read image files as needed." " This can be useful when running on a machine with limited memory resources." ); cl.EndGroup(); cl.BeginGroup( "Transformation", "Transformation Parameters" ); cl.AddVector( Key( "dofs" ), NumberDOFs, "Add DOFs to list [default: one pass, 6 DOF]" ); cl.AddSwitch( Key( 'z', "zero-sum" ), &ForceZeroSum, true, "Enforce zero-sum computation." ); cl.AddOption( Key( 'N', "normal-group-first-n" ), &NormalGroupFirstN, "First N images are from the normal group and should be registered unbiased." ); cl.AddOption( Key( 'Z', "zero-sum-first-n" ), &ForceZeroSumFirstN, "Enforce zero-sum computation for first N images.", &ForceZeroSum ); cl.BeginGroup( "Initialization", "Transformation Initialization" ); cl.AddSwitch( Key( "align-bounding-boxes" ), &AlignCentersOfMass, false, "Initially align centers of bounding boxes of all images by translations" ); cl.AddSwitch( Key( "align-centers-of-mass" ), &AlignCentersOfMass, true, "Initially align centers of mass by translations" ); cl.AddSwitch( Key( "init-scales" ), &InitScales, true, "Initialize scale factors using first-order moments" ); cl.EndGroup(); cl.BeginGroup( "Optimization", "Optimization Parameters" ); cl.AddOption( Key( 'e', "exploration" ), &Exploration, "Exploration of optimization in pixels" ); cl.AddOption( Key( 'a', "accuracy" ), &Accuracy, "Accuracy of optimization in pixels" ); cl.AddOption( Key( 'r', "repeat-level" ), &OptimizerRepeatLevel, "Number of repetitions per optimization level" ); cl.AddOption( Key( 'S', "step-factor" ), &OptimizerStepFactor, "Step factor for successive optimization passes" ); cl.AddOption( Key( "delta-f-threshold" ), &OptimizerDeltaFThreshold, "Optional threshold to terminate optimization (level) if relative change of target function drops below this value." ); cl.AddSwitch( Key( "disable-optimization" ), &DisableOptimization, true, "Disable optimization and output initial configuration." ); cl.EndGroup(); cl.Parse( argc, const_cast( argv ) ); const char* next = cl.GetNext(); while ( next ) { fileNameList.push_back( next ); next = cl.GetNextOptional(); } } catch ( const cmtk::CommandLine::Exception& e ) { cmtk::StdErr << e << "\n"; throw cmtk::ExitException( 1 ); } // Make sure we don't exceed maximum number of supported images. This is due to // using, for example, "byte" for pixelwise image counts in averaging. if ( fileNameList.size() > 255 ) { cmtk::StdErr << "ERROR: no more than 255 images are supported.\n"; throw cmtk::ExitException( 1 ); } if ( NumberDOFs.empty() ) NumberDOFs.push_back( 6 ); cmtk::GroupwiseRegistrationFunctionalXformTemplate::SmartPtr functional; if ( OptimizeRMI ) functional = cmtk::AffineGroupwiseRegistrationRMIFunctional::SmartPtr( new cmtk::AffineGroupwiseRegistrationRMIFunctional ); else functional = cmtk::AffineCongealingFunctional::SmartPtr( new cmtk::AffineCongealingFunctional ); functional->SetForceZeroSum( ForceZeroSum ); functional->SetForceZeroSumFirstN( ForceZeroSumFirstN ); functional->SetFreeAndRereadImages( FreeAndRereadImages ); functional->SetCropImageHistograms( CropImageHistograms ); if ( UserBackgroundFlag ) functional->SetUserBackgroundValue( UserBackgroundValue ); if ( UseNumberOfHistogramBins ) { // must be done IMMEDIATELY after creating the functional! // otherwise, scaling and conversion of input images goes // WRONG! functional->SetNumberOfHistogramBins( NumberOfHistogramBins ); } if ( cmtk::FileFormat::Identify( fileNameList[0] ) == cmtk::FILEFORMAT_TYPEDSTREAM ) { if ( fileNameList.size() > 1 ) { cmtk::StdErr << "First input file is an archive, but additional arguments are given.\n" << "I am terminating just to make sure not to do something stupid.\n"; throw cmtk::ExitException( 1 ); } cmtk::ClassStreamInput inStream( fileNameList[0] ); if ( inStream.IsValid() ) { inStream >> *functional; if ( PreDefinedTemplatePath.empty() ) { PreDefinedTemplate = functional->GetTemplateGrid(); } imageListOriginal = functional->GetOriginalTargetImages(); TransformationsFromArchive = true; } else { cmtk::StdErr << "Could not open input groupwise archive " << fileNameList[0] << "\n"; throw cmtk::ExitException( 1 ); } } else { int idx = 0; for ( std::vector::const_iterator fnIt = fileNameList.begin(); fnIt != fileNameList.end(); ++fnIt, ++idx ) { cmtk::UniformVolume::SmartPtr nextImage; cmtk::UniformVolume::SmartPtr image( cmtk::VolumeIO::ReadOriented( *fnIt ) ); if ( ! image || ! image->GetData() ) { cmtk::StdErr << "ERROR: Could not read image " << *fnIt << "\n"; throw cmtk::ExitException( 1 ); } nextImage = image; imageListOriginal.push_back( nextImage ); } } if ( !PreDefinedTemplatePath.empty() ) { if ( UseTemplateData ) { PreDefinedTemplate = cmtk::UniformVolume::SmartPtr( cmtk::VolumeIO::ReadOriented( PreDefinedTemplatePath ) ); } else { PreDefinedTemplate = cmtk::UniformVolume::SmartPtr( cmtk::VolumeIO::ReadGridOriented( PreDefinedTemplatePath ) ); } if ( ! PreDefinedTemplate ) { cmtk::StdErr << "ERROR: could not read template grid/image " << PreDefinedTemplatePath << "\n"; throw cmtk::ExitException( 2 ); } } if ( HistogramMatching ) { const cmtk::TypedArray* referenceDataForHistogramMatching = NULL; bool useTemplateForHistogramMatching = true; if ( PreDefinedTemplate && UseTemplateData ) { referenceDataForHistogramMatching = PreDefinedTemplate->GetData(); } if ( !referenceDataForHistogramMatching ) { useTemplateForHistogramMatching = false; referenceDataForHistogramMatching = imageListOriginal[0]->GetData(); } if ( referenceDataForHistogramMatching ) { for ( size_t idx = useTemplateForHistogramMatching?0:1; idx < imageListOriginal.size(); ++idx ) { imageListOriginal[idx]->GetData()->ApplyFunctionObject( cmtk::TypedArrayFunctionHistogramMatching( *(imageListOriginal[idx]->GetData()), *referenceDataForHistogramMatching ) ); } } } if ( UseSamplingDensity ) { functional->SetProbabilisticSampleDensity( SamplingDensity ); } const double timeBaselineProcess = cmtk::Timers::GetTimeProcess(); const int downsampleFrom = std::max( DownsampleFrom, DownsampleTo ); const int downsampleTo = std::min( DownsampleFrom, DownsampleTo ); cmtk::CoordinateVector v; for ( int downsample = downsampleFrom; downsample >= downsampleTo; downsample = downsample?downsample/2:-1 ) { functional->SetTargetImages( imageListOriginal ); if ( PreDefinedTemplate ) functional->SetTemplateGrid( PreDefinedTemplate, std::max( 1, downsample ), UseTemplateData ); else functional->CreateTemplateGridFromTargets( imageListOriginal, std::max( 1, downsample ) ); cmtk::UniformVolume::SmartPtr templateGrid = functional->GetTemplateGrid(); if ( UseSmoothSigmaFactor && downsample ) { functional->SetGaussianSmoothImagesSigma( SmoothSigmaFactor * templateGrid->GetMinDelta() ); } functional->AllocateStorage(); cmtk::DebugOutput( 1 ).GetStream().printf( "Template grid is %d x %d x %d pixels of size %f x %f x %f\n", templateGrid->m_Dims[0], templateGrid->m_Dims[1], templateGrid->m_Dims[2], templateGrid->m_Delta[0], templateGrid->m_Delta[1], templateGrid->m_Delta[2] ); if ( downsampleFrom == downsample ) { if ( !TransformationsFromArchive ) cmtk::GroupwiseRegistrationFunctionalAffineInitializer::InitializeXforms( *functional, true /*alignCenters*/, AlignCentersOfMass, InitScales ); functional->SetFreeAndRereadImages( true ); // can now get rid of unused original images functional->GetParamVector( v ); } else { functional->SetParamVector( v ); } if ( ! DisableOptimization ) { cmtk::BestDirectionOptimizer optimizer( OptimizerStepFactor ); optimizer.SetAggressiveMode( OptimizerAggressive ); optimizer.SetRepeatLevelCount( OptimizerRepeatLevel ); optimizer.SetDeltaFThreshold( OptimizerDeltaFThreshold ); optimizer.SetFunctional( functional ); for ( std::vector::const_iterator itDOF = NumberDOFs.begin(); itDOF != NumberDOFs.end(); ++itDOF ) { functional->SetXformNumberDOFs( *itDOF ); try { // do we have a normal subgroup? if ( NormalGroupFirstN ) { // yes: first run normal group by itself cmtk::StdErr << "Running normal subgroup...\n"; functional->SetForceZeroSum( ForceZeroSum ); functional->SetActiveImagesFromTo( 0, NormalGroupFirstN ); functional->SetActiveXformsFromTo( 0, NormalGroupFirstN ); optimizer.Optimize( v, Exploration * templateGrid->GetMinDelta(), Accuracy * templateGrid->GetMinDelta() ); // second: run abnormal group, but keep using normal group's data for reference cmtk::StdErr << "Running diseased subgroup...\n"; functional->SetForceZeroSum( false ); // no point here functional->SetActiveImagesFromTo( 0, imageListOriginal.size() ); functional->SetActiveXformsFromTo( NormalGroupFirstN, imageListOriginal.size() ); optimizer.Optimize( v, Exploration * templateGrid->GetMinDelta(), Accuracy * templateGrid->GetMinDelta() ); } else { optimizer.Optimize( v, Exploration * templateGrid->GetMinDelta(), Accuracy * templateGrid->GetMinDelta() ); } } catch ( cmtk::GroupwiseRegistrationFunctionalBase::BadXform ) { cmtk::StdErr << "FAILED: at least one image has too few pixels in the template area.\n"; return 1; } } } } // determine and print CPU time const double timeElapsedProcess = cmtk::Timers::GetTimeProcess() - timeBaselineProcess; cmtk::StdErr.printf( "Process CPU time [s]: %f\n", timeElapsedProcess ); functional->SetTargetImages( imageListOriginal ); if ( PreDefinedTemplate ) functional->SetTemplateGrid( PreDefinedTemplate ); else functional->CreateTemplateGridFromTargets( imageListOriginal ); cmtk::GroupwiseRegistrationOutput output; output.SetFunctional( functional ); output.SetOutputRootDirectory( OutputRootDirectory ); if ( ! UseTemplateData ) { PreDefinedTemplatePath = AverageImagePath; output.SetExistingTemplatePath( false ); } else { output.SetExistingTemplatePath( true ); } output.WriteGroupwiseArchive( OutputArchive ); output.WriteXformsSeparateArchives( OutputStudyListIndividual, PreDefinedTemplatePath ); output.WriteAverageImage( AverageImagePath, AverageImageInterpolation, cmtk::TYPE_FLOAT, UseTemplateData ); return 0; } #include "cmtkSafeMain" cmtk-3.3.1/apps/groupwise_init.cxx000066400000000000000000000171131276303427400172250ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2012 SRI International // // Copyright 2015 Google, Inc. // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 5394 $ // // $LastChangedDate: 2015-11-07 17:51:53 -0800 (Sat, 07 Nov 2015) $ // // $LastChangedBy: torstenrohlfing $ // */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include const char* PreDefinedTemplatePath = NULL; cmtk::UniformVolume::SmartPtr PreDefinedTemplate; const char* OutputRootDirectory = NULL; const char* OutputArchive = "groupwise.xforms"; const char* OutputStudyListGroup = "groupwise.list"; const char* OutputStudyListIndividual = "pairs"; const char* AverageImagePath = "average.nii"; cmtk::Interpolators::InterpolationEnum AverageImageInterpolation = cmtk::Interpolators::LINEAR; bool AlignCentersOfMass = false; bool InitScales = false; bool CenterTemplate = false; // this vector holds all target image filenames std::vector fileNameList; // this vector holds the original (not downsampled) images. std::vector imageListOriginal; cmtk::ScalarDataType DataType = cmtk::TYPE_FLOAT; int doMain( int argc, const char* argv[] ) { try { cmtk::CommandLine cl; cl.SetProgramInfo( cmtk::CommandLine::PRG_TITLE, "Affine initialization for groupwise registration" ); cl.SetProgramInfo( cmtk::CommandLine::PRG_DESCR, "Compute initial affine alignment for a group of input images, which can be used as an input for groupwise registration" ); cl.SetProgramInfo( cmtk::CommandLine::PRG_SYNTX, "groupwise_init [options] image0 [image1 ...]" ); cl.SetProgramInfo( cmtk::CommandLine::PRG_CATEG, "CMTK.Image Registration" ); typedef cmtk::CommandLine::Key Key; cl.AddOption( Key( 't', "template" ), &PreDefinedTemplatePath, "Input filename for pre-defined template image." ); cl.AddOption( Key( 'O', "output-root" ), &OutputRootDirectory, "Root directory for all output files." ); cl.AddOption( Key( 'o', "output" ), &OutputArchive, "Output filename for groupwise registration archive." ); cl.AddOption( Key( "output-average" ), &AverageImagePath, "Output filename for registered average image." ); cl.AddSwitch( Key( "average-cubic" ), &AverageImageInterpolation, cmtk::Interpolators::CUBIC, "Use cubic (rather than linear) interpolation for average image." ); cl.AddSwitch( Key( "no-output-average" ), &AverageImagePath, (const char*)NULL, "Do not write average image." ); cl.AddSwitch( Key( "align-centers-of-mass" ), &AlignCentersOfMass, true, "Initially align centers of mass rather than centers of bounding boxes." ); cl.AddSwitch( Key( "init-scales" ), &InitScales, true, "Initialize scale factors using first-order moments" ); cl.AddSwitch( Key( "center-template" ), &CenterTemplate, true, "Center aligned images in template grid field of view." ); cmtk::CommandLine::EnumGroup::SmartPtr typeGroup = cl.AddEnum( "outputtype", &DataType, "Scalar data type for the output average image." ); typeGroup->AddSwitch( Key( "char" ), cmtk::TYPE_CHAR, "8 bits, signed" ); typeGroup->AddSwitch( Key( "byte" ), cmtk::TYPE_BYTE, "8 bits, unsigned" ); typeGroup->AddSwitch( Key( "short" ), cmtk::TYPE_SHORT, "16 bits, signed" ); typeGroup->AddSwitch( Key( "ushort" ), cmtk::TYPE_USHORT, "16 bits, unsigned" ); typeGroup->AddSwitch( Key( "int" ), cmtk::TYPE_INT, "32 bits signed" ); typeGroup->AddSwitch( Key( "uint" ), cmtk::TYPE_UINT, "32 bits unsigned" ); typeGroup->AddSwitch( Key( "float" ), cmtk::TYPE_FLOAT, "32 bits floating point" ); typeGroup->AddSwitch( Key( "double" ), cmtk::TYPE_DOUBLE, "64 bits floating point\n" ); cl.Parse( argc, const_cast( argv ) ); const char* next = cl.GetNext(); while ( next ) { fileNameList.push_back( next ); next = cl.GetNextOptional(); } } catch ( const cmtk::CommandLine::Exception& e ) { cmtk::StdErr << e << "\n"; throw cmtk::ExitException( 1 ); } // Make sure we don't exceed maximum number of supported images. This is due to // using, for example, "byte" for pixelwise image counts in averaging. if ( fileNameList.size() > 255 ) { cmtk::StdErr << "ERROR: no more than 255 images are supported.\n"; throw cmtk::ExitException( 1 ); } cmtk::GroupwiseRegistrationFunctionalBase::SmartPtr initializer( new cmtk::GroupwiseRegistrationFunctionalBase ); int idx = 0; for ( std::vector::const_iterator fnIt = fileNameList.begin(); fnIt != fileNameList.end(); ++fnIt, ++idx ) { cmtk::UniformVolume::SmartPtr nextImage; cmtk::UniformVolume::SmartPtr image( cmtk::VolumeIO::ReadOriented( *fnIt ) ); if ( ! image || ! image->GetData() ) { cmtk::StdErr << "ERROR: Could not read image " << *fnIt << "\n"; throw cmtk::ExitException( 1 ); } nextImage = image; imageListOriginal.push_back( nextImage ); } initializer->SetTargetImages( imageListOriginal ); if ( PreDefinedTemplatePath ) { PreDefinedTemplate = cmtk::UniformVolume::SmartPtr( cmtk::VolumeIO::ReadGridOriented( PreDefinedTemplatePath ) ); } if ( PreDefinedTemplate ) initializer->SetTemplateGrid( PreDefinedTemplate ); else initializer->CreateTemplateGridFromTargets( imageListOriginal ); cmtk::UniformVolume::SmartPtr templateGrid = initializer->GetTemplateGrid(); cmtk::DebugOutput( 1 ).GetStream().printf( "Template grid is %d x %d x %d pixels of size %f x %f x %f\n", templateGrid->m_Dims[0], templateGrid->m_Dims[1], templateGrid->m_Dims[2], templateGrid->m_Delta[0], templateGrid->m_Delta[1], templateGrid->m_Delta[2] ); cmtk::GroupwiseRegistrationFunctionalAffineInitializer::InitializeXforms( *initializer, true /*alignCenters*/, AlignCentersOfMass, InitScales, CenterTemplate ); cmtk::GroupwiseRegistrationOutput output; if ( PreDefinedTemplatePath ) { output.SetExistingTemplatePath( true ); } else { PreDefinedTemplatePath = AverageImagePath; } output.SetFunctional( initializer ); output.SetOutputRootDirectory( OutputRootDirectory ); output.WriteGroupwiseArchive( OutputArchive ); output.WriteXformsSeparateArchives( OutputStudyListIndividual, PreDefinedTemplatePath ); output.WriteAverageImage( AverageImagePath, AverageImageInterpolation ); return 0; } #include "cmtkSafeMain" cmtk-3.3.1/apps/groupwise_warp.cxx000066400000000000000000000445121276303427400172360ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2013 SRI International // // Copyright 2015 Google, Inc. // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 2697 $ // // $LastChangedDate: 2011-01-10 16:40:53 -0800 (Mon, 10 Jan 2011) $ // // $LastChangedBy: torstenrohlfing $ // */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include bool OptimizeRMI = false; int DownsampleFrom = 4; int DownsampleTo = 1; const char* PreDefinedTemplatePath = NULL; bool UseTemplateData = false; const char* DisableControlPointsMaskPath = NULL; int RefineTransformationGrid = 0; float JacobianConstraintWeight = 0.0; float BendingEnergyWeight = 0.0; cmtk::Types::Coordinate GridSpacing = 40.0; bool GridSpacingExact = true; bool ForceZeroSum = false; bool ForceZeroSumNoAffine = false; unsigned int ForceZeroSumFirstN = 0; unsigned int NormalGroupFirstN = 0; float SamplingDensity = -1.0; bool DeactivateUninformative = true; float PartialGradientThreshold = 0.0; bool UseNumberOfHistogramBins = false; unsigned int NumberOfHistogramBins = 0; bool CropImageHistograms = false; bool HistogramMatching = false; bool RepeatHistogramMatching = false; bool UseSmoothSigmaFactorPixel = false; cmtk::Types::Coordinate SmoothSigmaFactorPixel = 0.0; bool UseSmoothSigmaFactorControlPointSpacing = false; cmtk::Types::Coordinate SmoothSigmaFactorControlPointSpacing = 0.0; cmtk::Types::Coordinate Accuracy = 0.01; cmtk::Types::Coordinate Exploration = 0.25; cmtk::Types::Coordinate OptimizerStepFactor = 0.5; cmtk::Optimizer::ReturnType OptimizerDeltaFThreshold = 0; bool OptimizerAggressive = true; int OptimizerRepeatLevel = 2; bool DisableOptimization = false; const char* AffineGroupRegistration = NULL; const char* OutputRootDirectory = NULL; const char* OutputArchive = "groupwise.xforms"; const char* OutputStudyListGroup = "groupwise.list"; const char* OutputStudyListIndividual = "pairs"; const char* AverageImagePath = "average.nii"; cmtk::Interpolators::InterpolationEnum AverageImageInterpolation = cmtk::Interpolators::LINEAR; byte UserBackgroundValue = 0; bool UserBackgroundFlag = false; int doMain( int argc, const char* argv[] ) { try { cmtk::CommandLine cl; cl.SetProgramInfo( cmtk::CommandLine::PRG_TITLE, "Nonrigid population registration" ); cl.SetProgramInfo( cmtk::CommandLine::PRG_DESCR, "This tool nonrigidly registers a population of input images simultaneously, without a template, using either the 'congealing' algorithm or a groupwise similarity measure based on " "a continuous approximation of mutual information ('RMI')." ); cl.SetProgramInfo( cmtk::CommandLine::PRG_SYNTX, "groupwise_warp [options] affineGroupRegistration" ); cl.SetProgramInfo( cmtk::CommandLine::PRG_CATEG, "CMTK.Image Registration" ); typedef cmtk::CommandLine::Key Key; cl.BeginGroup( "Metric", "Registration Metric Options" ); cl.AddSwitch( Key( "rmi" ), &OptimizeRMI, true, "Use the RMI (a.k.a. regional mutual information) metric to drive the registration)." ); cl.AddSwitch( Key( "congeal" ), &OptimizeRMI, false, "Use the congealing algorithm using pixelwise stack entropies to drive the registration." ); cl.EndGroup(); cl.BeginGroup( "Template", "Template Image Options" ); cl.AddOption( Key( 't', "template" ), &PreDefinedTemplatePath, "Override template image with given file." ); cl.AddOption( Key( 'T', "template-with-data" ), &PreDefinedTemplatePath, "Use user-supplied template images's pixel data in registration", &UseTemplateData ); cl.EndGroup(); cl.BeginGroup( "Multuresolution", "Multuresolution Parameters" ); cl.AddOption( Key( 'd', "downsample-from" ), &DownsampleFrom, "Initial downsampling factor [4]." ); cl.AddOption( Key( 'D', "downsample-to" ), &DownsampleTo, "Final downsampling factor [1]." ); cl.AddOption( Key( 's', "sampling-density" ), &SamplingDensity, "Probabilistic sampling density [default: off]." ); cl.EndGroup(); cl.BeginGroup( "Output", "Output Options" ); cl.AddOption( Key( 'O', "output-root" ), &OutputRootDirectory, "Root directory for all output files." ); cl.AddOption( Key( 'o', "output" ), &OutputArchive, "Output filename for groupwise registration archive." ); cl.AddOption( Key( "output-average" ), &AverageImagePath, "Output filename for registered average image." ); cl.AddSwitch( Key( "no-output-average" ), &AverageImagePath, (const char*)NULL, "Do not write average image." ); cl.AddSwitch( Key( "average-cubic" ), &AverageImageInterpolation, cmtk::Interpolators::CUBIC, "Use cubic interpolation for average image (default: linear)" ); cl.EndGroup(); cl.BeginGroup( "Image", "Image Options and Operations" ); cl.AddOption( Key( 'B', "force-background" ), &UserBackgroundValue, "Force background pixels (outside FOV) to given (bin) value.", &UserBackgroundFlag ); cl.AddOption( Key( 'H', "histogram-bins" ), &NumberOfHistogramBins, "Set number of histogram bins for entropy evaluation.", &UseNumberOfHistogramBins ); cl.AddSwitch( Key( "crop-histograms" ), &CropImageHistograms, true, "Crop image histograms to make better use of histogram bins." ); cl.AddSwitch( Key( "match-histograms" ), &HistogramMatching, true, "Match all image histograms to template data (or first image, if no template image is given)" ); cl.AddSwitch( Key( "repeat-match-histograms" ), &RepeatHistogramMatching, true, "Frequetly repeat histogram-based intensity matching to account for changing volume proportions." ); cl.AddOption( Key( "smooth-pixels" ), &SmoothSigmaFactorPixel, "Sigma of Gaussian smoothing kernel in multiples of template image pixel size", &UseSmoothSigmaFactorPixel ); cl.AddOption( Key( "smooth-cps" ), &SmoothSigmaFactorControlPointSpacing, "Sigma of Gaussian smoothing kernel in multiples of control point delta", &UseSmoothSigmaFactorControlPointSpacing ); cl.EndGroup(); cl.BeginGroup( "Transformation", "Transformation Parameters" ); cl.AddOption( Key( "grid-spacing" ), &GridSpacing, "Control point grid spacing." ); cl.AddSwitch( Key( "grid-spacing-fit" ), &GridSpacingExact, false, "Use grid spacing that fits volume FOV" ); cl.AddOption( Key( 'r', "refine-grid" ), &RefineTransformationGrid, "Number of times to refine transformation grid [default: 0]." ); cl.EndGroup(); cl.BeginGroup( "Constraints", "Transformation Constraint Options" ); cl.AddSwitch( Key( 'z', "zero-sum" ), &ForceZeroSum, true, "Enforce zero-sum computation." ); cl.AddSwitch( Key( "zero-sum-no-affine" ), &ForceZeroSumNoAffine, true, "Enforce zero-sum computation EXCLUDING affine components." ); cl.AddOption( Key( 'N', "normal-group-first-n" ), &NormalGroupFirstN, "First N images are from the normal group and should be registered unbiased." ); cl.AddOption( Key( 'Z', "zero-sum-first-n" ), &ForceZeroSumFirstN, "Enforce zero-sum computation for first N images.", &ForceZeroSum ); cl.AddOption( Key( 'J', "jacobian-weight" ), &JacobianConstraintWeight, "Weight for Jacobian volume preservation constraint [default: off]" ); cl.AddOption( Key( 'E', "bending-energy-weight" ), &BendingEnergyWeight, "Weight for grid bending energy regularization constraint [default: off]" ); cl.EndGroup(); cl.BeginGroup( "Optimization", "Optimization Parameters" ); cl.AddOption( Key( 'e', "exploration" ), &Exploration, "Exploration of optimization in pixels" ); cl.AddOption( Key( 'a', "accuracy" ), &Accuracy, "Accuracy of optimization in pixels" ); cl.AddOption( Key( 'S', "step-factor" ), &OptimizerStepFactor, "Step factor for successive optimization passes" ); cl.AddOption( Key( "delta-f-threshold" ), &OptimizerDeltaFThreshold, "Optional threshold to terminate optimization (level) if relative change of target function drops below this value." ); cl.AddOption( Key( 'p', "partial-gradient-thresh" ), &PartialGradientThreshold, "Threshold factor for partial gradient zeroing [<0 turn off]" ); cl.AddSwitch( Key( "activate-uninformative" ), &DeactivateUninformative, false, "Activate potentially uninformative control points" ); cl.AddOption( Key( "disable-cp-mask" ), &DisableControlPointsMaskPath, "Path to mask image (matching template grid) defining areas in which control points should be disabled. " "This guarantees that mask foreground areas remain undeformed." ); cl.AddSwitch( Key( "disable-optimization" ), &DisableOptimization, true, "Disable optimization and output initial configuration." ); cl.EndGroup(); cl.Parse( argc, const_cast( argv ) ); AffineGroupRegistration = cl.GetNext(); } catch ( const cmtk::CommandLine::Exception& e ) { cmtk::StdErr << e << "\n"; throw cmtk::ExitException( 1 ); } cmtk::GroupwiseRegistrationFunctionalXformTemplate::SmartPtr functional; if ( OptimizeRMI ) functional = cmtk::SplineWarpGroupwiseRegistrationRMIFunctional::SmartPtr( new cmtk::SplineWarpGroupwiseRegistrationRMIFunctional ); else functional = cmtk::SplineWarpCongealingFunctional::SmartPtr( new cmtk::SplineWarpCongealingFunctional ); functional->SetFreeAndRereadImages( ! HistogramMatching ); // we cannot unload the original images if we still need them for histogram matching functional->SetForceZeroSumFirstN( ForceZeroSumFirstN ); functional->SetForceZeroSum( ForceZeroSum || ForceZeroSumNoAffine ); functional->SetForceZeroSumNoAffine( ForceZeroSumNoAffine ); functional->SetCropImageHistograms( CropImageHistograms ); if ( UserBackgroundFlag ) functional->SetUserBackgroundValue( UserBackgroundValue ); if ( UseNumberOfHistogramBins ) { // must be done IMMEDIATELY after creating the functional! // otherwise, scaling and conversion of input images goes // WRONG! functional->SetNumberOfHistogramBins( NumberOfHistogramBins ); } // disable parameters with less than 1% of maximum contribution functional->SetPartialGradientMode( (PartialGradientThreshold > 0) , PartialGradientThreshold ); functional->SetDeactivateUninformativeMode( DeactivateUninformative ); cmtk::ClassStreamInput stream( AffineGroupRegistration ); if ( ! stream.IsValid() ) { cmtk::StdErr << "Input archive " << AffineGroupRegistration << " could not be opened for reading.\n"; throw cmtk::ExitException( 1 ); } stream >> *functional; stream.Close(); // Make sure we don't exceed maximum number of supported images. This is due to // using, for example, "byte" for pixelwise image counts in averaging. if ( functional->GetNumberOfTargetImages() > 255 ) { cmtk::StdErr << "ERROR: no more than 255 images are supported.\n"; throw cmtk::ExitException( 1 ); } if ( PreDefinedTemplatePath ) { cmtk::UniformVolume::SmartPtr templateImage; if ( UseTemplateData ) { templateImage = cmtk::UniformVolume::SmartPtr( cmtk::VolumeIO::ReadOriented( PreDefinedTemplatePath ) ); } else { templateImage = cmtk::UniformVolume::SmartPtr( cmtk::VolumeIO::ReadGridOriented( PreDefinedTemplatePath ) ); } if ( ! templateImage ) { cmtk::StdErr << "ERROR: could not read template grid/image " << PreDefinedTemplatePath << "\n"; throw cmtk::ExitException( 2 ); } functional->SetTemplateGrid( templateImage, 0 /*downsample*/, false /*useTemplateData: set this later*/ ); } // this vector holds the original (not downsampled) images. std::vector imageListOriginal; functional->GetOriginalTargetImages( imageListOriginal ); cmtk::UniformVolume::SmartPtr originalTemplateGrid = functional->GetTemplateGrid(); if ( HistogramMatching ) { const cmtk::TypedArray* referenceDataForHistogramMatching = NULL; bool useTemplateForHistogramMatching = true; if ( originalTemplateGrid && UseTemplateData ) { referenceDataForHistogramMatching = originalTemplateGrid->GetData(); } if ( !referenceDataForHistogramMatching ) { useTemplateForHistogramMatching = false; referenceDataForHistogramMatching = imageListOriginal[0]->GetData(); } if ( referenceDataForHistogramMatching ) { for ( size_t idx = useTemplateForHistogramMatching?0:1; idx < imageListOriginal.size(); ++idx ) { imageListOriginal[idx]->GetData()->ApplyFunctionObject( cmtk::TypedArrayFunctionHistogramMatching( *(imageListOriginal[idx]->GetData()), *referenceDataForHistogramMatching ) ); } } } functional->InitializeXforms( GridSpacing, GridSpacingExact ); // must do this before downsampling template grid functional->SetRepeatIntensityHistogramMatching( RepeatHistogramMatching ); const cmtk::Types::Coordinate FinestGridSpacing = GridSpacing / (1<SetDisableControlPointsMask( cmtk::UniformVolume::SmartConstPtr( cmtk::VolumeIO::Read( DisableControlPointsMaskPath ) ) ); } const double timeBaselineProcess = cmtk::Timers::GetTimeProcess(); if ( ! DisableOptimization ) { cmtk::CoordinateVector v; const int downsampleFrom = std::max( DownsampleFrom, DownsampleTo ); const int downsampleTo = std::min( DownsampleFrom, DownsampleTo ); for ( int downsample = downsampleFrom; (downsample >= downsampleTo) || RefineTransformationGrid; downsample = downsample?downsample/2:-1 ) { if ( (RefineTransformationGrid > 0) && (downsample != downsampleFrom) ) { functional->RefineTransformationGrids(); --RefineTransformationGrid; } functional->GetParamVector( v ); const int actualDownsample = std::max( downsampleTo, downsample ); functional->SetTemplateGrid( originalTemplateGrid, actualDownsample, UseTemplateData ); cmtk::UniformVolume::SmartPtr templateGrid = functional->GetTemplateGrid(); if ( UseSmoothSigmaFactorPixel ) { functional->SetGaussianSmoothImagesSigma( SmoothSigmaFactorPixel * templateGrid->GetMinDelta() ); functional->SetTargetImages( imageListOriginal ); } else { if ( UseSmoothSigmaFactorControlPointSpacing ) { functional->SetGaussianSmoothImagesSigma( SmoothSigmaFactorControlPointSpacing * FinestGridSpacing * (1<SetTargetImages( imageListOriginal ); } } cmtk::DebugOutput( 1 ).GetStream().printf( "Template grid is %d x %d x %d pixels of size %f x %f x %f\n", templateGrid->m_Dims[0], templateGrid->m_Dims[1], templateGrid->m_Dims[2], templateGrid->m_Delta[0], templateGrid->m_Delta[1], templateGrid->m_Delta[2] ); if ( SamplingDensity > 0 ) { functional->SetProbabilisticSampleDensity( SamplingDensity ); functional->SetProbabilisticSampleUpdatesAfter( 10 ); } functional->AllocateStorage(); cmtk::BestDirectionOptimizer optimizer( OptimizerStepFactor ); optimizer.SetAggressiveMode( OptimizerAggressive ); optimizer.SetRepeatLevelCount( OptimizerRepeatLevel ); optimizer.SetDeltaFThreshold( OptimizerDeltaFThreshold ); optimizer.SetFunctional( functional ); cmtk::Types::Coordinate exploration = Exploration * templateGrid->GetMinDelta(); cmtk::Types::Coordinate accuracy = Accuracy * templateGrid->GetMinDelta(); if ( (downsample > downsampleTo) || RefineTransformationGrid ) accuracy = std::max( accuracy, .25*exploration ); try { // do we have a normal subgroup? if ( NormalGroupFirstN ) { // yes: first run normal group by itself cmtk::StdErr << "Running normal subgroup...\n"; functional->SetForceZeroSum( ForceZeroSum ); functional->SetActiveImagesFromTo( 0, NormalGroupFirstN ); functional->SetActiveXformsFromTo( 0, NormalGroupFirstN ); optimizer.Optimize( v, exploration, accuracy ); // second: run abnormal group, but keep using normal group's data for reference cmtk::StdErr << "Running diseased subgroup...\n"; functional->SetForceZeroSum( false ); // no point here functional->SetActiveImagesFromTo( 0, imageListOriginal.size() ); functional->SetActiveXformsFromTo( NormalGroupFirstN, imageListOriginal.size() ); optimizer.Optimize( v, exploration, accuracy ); } else { optimizer.Optimize( v, exploration, accuracy ); } } catch ( cmtk::GroupwiseRegistrationFunctionalBase::BadXform ) { cmtk::StdErr << "FAILED: at least one image has too few pixels in the template area.\n"; return 1; } } } // determine and print CPU time const double timeElapsedProcess = cmtk::Timers::GetTimeProcess() - timeBaselineProcess; cmtk::StdErr.printf( "Process CPU time [s]: %f\n", timeElapsedProcess ); functional->SetTargetImages( imageListOriginal ); functional->SetTemplateGrid( originalTemplateGrid ); cmtk::GroupwiseRegistrationOutput output; output.SetFunctional( functional ); output.SetOutputRootDirectory( OutputRootDirectory ); output.WriteGroupwiseArchive( OutputArchive ); output.WriteXformsSeparateArchives( OutputStudyListIndividual, AverageImagePath ); output.WriteAverageImage( AverageImagePath, AverageImageInterpolation, cmtk::TYPE_FLOAT, UseTemplateData ); return 0; } #include "cmtkSafeMain" cmtk-3.3.1/apps/hausdorff.cxx000066400000000000000000000051231276303427400161350ustar00rootroot00000000000000/* // // Copyright 2011, 2013 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4834 $ // // $LastChangedDate: 2013-09-12 15:15:51 -0700 (Thu, 12 Sep 2013) $ // // $LastChangedBy: torstenrohlfing $ // */ #include #include #include #include #include #include int doMain ( const int argc, const char *argv[] ) { std::string imagePath0; std::string imagePath1; try { cmtk::CommandLine cl; cl.SetProgramInfo( cmtk::CommandLine::PRG_TITLE, "Hausdorff Distance." ); cl.SetProgramInfo( cmtk::CommandLine::PRG_DESCR, "This tool computes the Hausdorff distance between two label images." ); cl.AddParameter( &imagePath0, "Image0", "First image path." )->SetProperties( cmtk::CommandLine::PROPS_IMAGE ); cl.AddParameter( &imagePath1, "Image1", "Second image path." )->SetProperties( cmtk::CommandLine::PROPS_IMAGE ); cl.Parse( argc, argv ); } catch ( const cmtk::CommandLine::Exception& e ) { cmtk::StdErr << e << "\n"; throw cmtk::ExitException( 1 ); } cmtk::UniformVolume::SmartConstPtr image0 = cmtk::VolumeIO::Read( imagePath0 ); if ( ! image0 || ! image0->GetData() ) { cmtk::StdErr << "ERROR: unable to read image " << imagePath0 << "\n"; throw cmtk::ExitException( 1 ); } cmtk::UniformVolume::SmartConstPtr image1 = cmtk::VolumeIO::Read( imagePath1 ); if ( ! image1 || ! image1->GetData() ) { cmtk::StdErr << "ERROR: unable to read image " << imagePath1 << "\n"; throw cmtk::ExitException( 1 ); } cmtk::StdOut.printf( "%lf\n", static_cast( cmtk::HausdorffDistance( image0, image1 ).GetBinary() ) ); return 0; } #include "cmtkSafeMain" cmtk-3.3.1/apps/histogram.cxx000066400000000000000000000202061276303427400161500ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2012 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4156 $ // // $LastChangedDate: 2012-04-10 10:13:18 -0700 (Tue, 10 Apr 2012) $ // // $LastChangedBy: torstenrohlfing $ // */ #include #include #include #include #include #include #include #include #include #include #include #include #include const char *maskFile = NULL; const char *outFile = NULL; std::list inFileList; size_t NumberOfBins = 0; bool NormalizeBins = false; bool UserMinMaxValue = false; cmtk::Types::DataItem UserMinValue = 0; cmtk::Types::DataItem UserMaxValue = 0; bool Truncate = false; bool PaddingFlag = false; bool PaddingValue = 0; void ComputeHistogram ( std::list& dataList, cmtk::Histogram& histogram ) { const cmtk::Types::DataItemRange range = histogram.GetRange(); std::list::const_iterator it = dataList.begin(); for ( ; it != dataList.end(); ++it ) { const cmtk::TypedArray* data = *it; const size_t size = data->GetDataSize(); cmtk::Types::DataItem value; for ( size_t offset = 0; offset < size; ++offset ) if ( data->Get( value, offset ) ) { if ( !Truncate || ((value >= range.m_LowerBound) && (value <= range.m_UpperBound)) ) histogram.Increment( histogram.ValueToBin( value ) ); } } if ( NormalizeBins ) { histogram.NormalizeMaximum( 1.0 ); } } void ComputeHistogram ( std::list& dataList, const cmtk::TypedArray* mask, const int maskValue, cmtk::Histogram& histogram ) { const cmtk::Types::DataItemRange range = histogram.GetRange(); std::list::const_iterator it = dataList.begin(); for ( ; it != dataList.end(); ++it ) { const cmtk::TypedArray* data = *it; const size_t size = data->GetDataSize(); cmtk::Types::DataItem value, mv; for ( size_t offset = 0; offset < size; ++offset ) if ( data->Get( value, offset ) ) if ( !Truncate || ((value >= range.m_LowerBound) && (value <= range.m_UpperBound)) ) if ( mask->Get( mv, offset ) && ( mv == maskValue ) ) histogram.Increment( histogram.ValueToBin( value ) ); } if ( NormalizeBins ) { histogram.NormalizeMaximum( 1.0 ); } } void WriteHistogram( const cmtk::Histogram& histogram, const char* outfile ) { cmtk::Types::DataItem cumulative = 0; if ( outfile ) { std::ofstream stream( outfile ); for ( unsigned int bin = 0; bin < histogram.GetNumberOfBins(); ++bin ) { cumulative += histogram[bin]; stream << bin << "\t" << histogram.BinToValue( bin ) << "\t" << histogram[bin] << "\t" << cumulative << "\n"; } } else { for ( unsigned int bin = 0; bin < histogram.GetNumberOfBins(); ++bin ) { cumulative += histogram[bin]; std::cout << bin << "\t" << histogram.BinToValue( bin ) << "\t" << histogram[bin] << "\t" << cumulative << "\n"; } } } int doMain ( const int argc, const char* argv[] ) { try { cmtk::CommandLine cl; cl.SetProgramInfo( cmtk::CommandLine::PRG_TITLE, "Image histogram" ); cl.SetProgramInfo( cmtk::CommandLine::PRG_DESCR, "Create a histogram of image intensities and write as tab-separated text file to standard output" ); cl.SetProgramInfo( cmtk::CommandLine::PRG_SYNTX, "histogram [options] image" ); cl.SetProgramInfo( cmtk::CommandLine::PRG_CATEG, "CMTK.Statistics and Modeling" ); typedef cmtk::CommandLine::Key Key; cl.AddOption( Key( 'n', "nbins" ), &NumberOfBins, "Number of histogram bins (default: number of grey levels)" ); cl.AddSwitch( Key( 'N', "normalize" ), &NormalizeBins, true, "Normalize histogram to maximum 1.0" ); cl.AddOption( Key( "min" ), &UserMinValue, "User-defined minimum value", &UserMinMaxValue ); cl.AddOption( Key( "max" ), &UserMaxValue, "User-defined maximum value", &UserMinMaxValue ); cl.AddSwitch( Key( 't', "truncate" ), &Truncate, true, "Truncate histogram (do not enter values outside range into first/last bin)" ); cl.AddOption( Key( "pad" ), &PaddingValue, "Image padding value", &PaddingFlag ); cl.AddOption( Key( 'o', "outfile" ), &outFile, "File name pattern for histograms" ); cl.AddOption( Key( 'm', "mask" ), &maskFile, "File name for multi-valued mask file" ); cl.Parse( argc, argv ); const char* next = cl.GetNext(); while ( next ) { inFileList.push_back( next ); next = cl.GetNextOptional(); } } catch ( const cmtk::CommandLine::Exception& e ) { cmtk::StdErr << e; return 1; } std::list dataList; cmtk::Types::DataItem minData = 0, maxData = 0; std::list::const_iterator it = inFileList.begin(); for ( ; it != inFileList.end(); ++it ) { cmtk::Volume::SmartPtr volume( cmtk::VolumeIO::ReadOriented( *it ) ); if ( ! volume ) { cmtk::StdErr << "Cannot read image " << *it << "\n"; throw cmtk::ExitException( 1 ); } cmtk::TypedArray::SmartPtr data = volume->GetData(); if ( ! data ) { cmtk::StdErr << "Cannot read pixel data for image " << *it << "\n"; throw cmtk::ExitException( 1 ); } if ( PaddingFlag ) { data->SetPaddingValue( PaddingValue ); } dataList.push_back( data ); const cmtk::Types::DataItemRange range = data->GetRange(); if ( it == inFileList.begin() ) { minData = range.m_LowerBound; maxData = range.m_UpperBound; } else { minData = std::min( minData, range.m_LowerBound ); maxData = std::max( maxData, range.m_UpperBound ); } } size_t bins = NumberOfBins; if ( !bins ) { bins = static_cast( maxData - minData + 1 ); if ( bins > 256 ) bins = 256; } cmtk::Histogram histogram( bins ); if ( UserMinMaxValue ) { histogram.SetRange( cmtk::Types::DataItemRange( UserMinValue, UserMaxValue ) ); } else { histogram.SetRange( cmtk::Types::DataItemRange( minData, maxData ) ); } if ( ! maskFile ) { histogram.Reset(); ComputeHistogram( dataList, histogram ); WriteHistogram( histogram, outFile ); } else { cmtk::Volume::SmartPtr mask( cmtk::VolumeIO::ReadOriented( maskFile ) ); if ( mask ) { const cmtk::TypedArray* maskData = mask->GetData(); bool labelFlags[256]; memset( labelFlags, 0, sizeof( labelFlags ) ); for ( size_t i = 0; i < maskData->GetDataSize(); ++i ) { cmtk::Types::DataItem l; if ( maskData->Get( l, i ) ) labelFlags[static_cast( l )] = true; } char fname[PATH_MAX]; for ( int mv = 0; mv < 256; ++mv ) { if ( labelFlags[mv] ) { histogram.Reset(); ComputeHistogram( dataList, mask->GetData(), mv, histogram ); if ( snprintf( fname, PATH_MAX, outFile, mv ) > PATH_MAX ) { cmtk::StdErr << "ERROR: output path exceeds maximum path length\n"; } else { WriteHistogram( histogram, fname ); } } } } } return 0; } #include "cmtkSafeMain" cmtk-3.3.1/apps/imagemath.cxx000066400000000000000000001442561276303427400161230ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2012 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 5307 $ // // $LastChangedDate: 2014-04-10 16:41:12 -0700 (Thu, 10 Apr 2014) $ // // $LastChangedBy: torstenrohlfing $ // */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef CMTK_USE_GCD # include #endif /// The output data type of image operations. cmtk::ScalarDataType ResultType = cmtk::TYPE_FLOAT; /// Value used for padding input data, if PaddingFlag is true. cmtk::Types::DataItem PaddingValue; /// If this flag is set, PaddingValue defines the padding value for images read from files. bool PaddingFlag = false; void CallbackSetPaddingValue( const double paddingValue ) { cmtk::DebugOutput( 2 ) << "CallbackSetPaddingValue\n"; PaddingValue = paddingValue; PaddingFlag = true; } void CallbackUnsetPaddingValue() { cmtk::DebugOutput( 2 ) << "CallbackUnsetPaddingValue\n"; PaddingFlag = false; } /// Type for "stack" of images. typedef std::deque ImageStackType; /// Operating stack of images. ImageStackType ImageStack; /// If this flag is set, the next single-image operation will be applied to all images on the stack. bool ApplyNextToAll = false; void CheckStackOneImage( const char* function ) { if ( ImageStack.empty() ) { cmtk::StdErr << "ERROR: stack is empty in function '" << function << "'\n"; throw cmtk::ExitException( 1 ); } } void CheckStackTwoMatchingImages( const char* function ) { if ( ImageStack.size() < 2 ) { cmtk::StdErr << "ERROR: at least two images are required to perform operation '" << function << "'\n"; throw cmtk::ExitException( 1 ); } cmtk::UniformVolume::SmartPtr top = ImageStack.front(); ImageStack.pop_front(); const bool pixelCountsMatch = (top->GetNumberOfPixels() == ImageStack.front()->GetNumberOfPixels() ); ImageStack.push_front( top ); if ( !pixelCountsMatch ) { cmtk::StdErr << "ERROR: two top images do not have equal pixel counts as required for operation '" << function << "'\n"; throw cmtk::ExitException( 1 ); } } void CallbackIn( const char** argv, int& argsUsed ) { cmtk::DebugOutput( 2 ) << "CallbackIn\n"; argsUsed = 0; while ( argv[argsUsed][0] != '-' ) { cmtk::UniformVolume::SmartPtr volume( cmtk::VolumeIO::ReadOriented( argv[argsUsed] ) ); if ( !volume || !volume->GetData() ) { cmtk::StdErr << "ERROR: could not read input image " << argv[argsUsed] << "\n"; throw cmtk::ExitException( 1 ); } if ( PaddingFlag ) volume->GetData()->SetPaddingValue( PaddingValue ); ImageStack.push_front( volume ); ++argsUsed; } } void CallbackOut( const char* argv ) { cmtk::DebugOutput( 2 ) << "CallbackOut\n"; CheckStackOneImage( "Out" ); cmtk::VolumeIO::Write( *(ImageStack.front()), argv ); } void CallbackPop() { cmtk::DebugOutput( 2 ) << "CallbackPop\n"; CheckStackOneImage( "Pop" ); ImageStack.pop_front(); } void CallbackDup() { cmtk::DebugOutput( 2 ) << "CallbackDup\n"; CheckStackOneImage( "Dup" ); ImageStack.push_front( cmtk::UniformVolume::SmartPtr( ImageStack.front()->Clone() ) ); } void CallbackAll() { ApplyNextToAll = true; } void CallbackFill( const double value) { cmtk::DebugOutput( 2 ) << "CallbackFill\n"; CheckStackOneImage( "Fill" ); ImageStackType::iterator it = ImageStack.begin(); while ( it != ImageStack.end() ) { (*it)->SetData( cmtk::TypedArray::SmartPtr( (*it)->GetData()->Convert( ResultType ) ) ); (*it)->GetData()->Fill( value ); if ( ApplyNextToAll ) ++it; else it = ImageStack.end(); } ApplyNextToAll = false; } void CallbackAbs() { cmtk::DebugOutput( 2 ) << "CallbackAbs\n"; CheckStackOneImage( "Abs" ); ImageStackType::iterator it = ImageStack.begin(); while ( it != ImageStack.end() ) { (*it)->SetData( cmtk::TypedArray::SmartPtr( (*it)->GetData()->Convert( ResultType ) ) ); (*it)->GetData()->ApplyFunctionDouble( cmtk::Wrappers::Abs ); if ( ApplyNextToAll ) ++it; else it = ImageStack.end(); } ApplyNextToAll = false; } void CallbackTrunc() { CheckStackOneImage( "Trunc" ); ImageStackType::iterator it = ImageStack.begin(); while ( it != ImageStack.end() ) { (*it)->SetData( cmtk::TypedArray::SmartPtr( (*it)->GetData()->Convert( ResultType ) ) ); (*it)->GetData()->ApplyFunctionDouble( cmtk::Wrappers::Trunc ); if ( ApplyNextToAll ) ++it; else it = ImageStack.end(); } ApplyNextToAll = false; } void CallbackLog() { CheckStackOneImage( "Log" ); ImageStackType::iterator it = ImageStack.begin(); while ( it != ImageStack.end() ) { (*it)->SetData( cmtk::TypedArray::SmartPtr( (*it)->GetData()->Convert( ResultType ) ) ); (*it)->GetData()->ApplyFunctionDouble( cmtk::Wrappers::Log ); if ( ApplyNextToAll ) ++it; else it = ImageStack.end(); } ApplyNextToAll = false; } void CallbackLogit() { CheckStackOneImage( "Logit" ); ImageStackType::iterator it = ImageStack.begin(); while ( it != ImageStack.end() ) { (*it)->SetData( cmtk::TypedArray::SmartPtr( (*it)->GetData()->Convert( ResultType ) ) ); (*it)->GetData()->ApplyFunctionDouble( cmtk::Wrappers::Logit ); if ( ApplyNextToAll ) ++it; else it = ImageStack.end(); } ApplyNextToAll = false; } void CallbackLogistic() { CheckStackOneImage( "Logistic" ); ImageStackType::iterator it = ImageStack.begin(); while ( it != ImageStack.end() ) { (*it)->SetData( cmtk::TypedArray::SmartPtr( (*it)->GetData()->Convert( ResultType ) ) ); (*it)->GetData()->ApplyFunctionDouble( cmtk::Wrappers::Logistic ); if ( ApplyNextToAll ) ++it; else it = ImageStack.end(); } ApplyNextToAll = false; } void CallbackExp() { CheckStackOneImage( "Exp" ); ImageStackType::iterator it = ImageStack.begin(); while ( it != ImageStack.end() ) { (*it)->SetData( cmtk::TypedArray::SmartPtr( (*it)->GetData()->Convert( ResultType ) ) ); (*it)->GetData()->ApplyFunctionDouble( cmtk::Wrappers::Exp ); if ( ApplyNextToAll ) ++it; else it = ImageStack.end(); } ApplyNextToAll = false; } void CallbackSqr() { CheckStackOneImage( "Sqr" ); ImageStackType::iterator it = ImageStack.begin(); while ( it != ImageStack.end() ) { (*it)->SetData( cmtk::TypedArray::SmartPtr( (*it)->GetData()->Convert( ResultType ) ) ); (*it)->GetData()->ApplyFunctionDouble( cmtk::Wrappers::Square ); if ( ApplyNextToAll ) ++it; else it = ImageStack.end(); } ApplyNextToAll = false; } void CallbackSqrt() { CheckStackOneImage( "Sqrt" ); ImageStackType::iterator it = ImageStack.begin(); while ( it != ImageStack.end() ) { (*it)->SetData( cmtk::TypedArray::SmartPtr( (*it)->GetData()->Convert( ResultType ) ) ); (*it)->GetData()->ApplyFunctionDouble( cmtk::Wrappers::Sqrt ); if ( ApplyNextToAll ) ++it; else it = ImageStack.end(); } ApplyNextToAll = false; } void CallbackThreshBelow( const double threshold ) { CheckStackOneImage( "ThreshBelow" ); ImageStackType::iterator it = ImageStack.begin(); while ( it != ImageStack.end() ) { cmtk::TypedArray::SmartPtr data = (*it)->GetData(); const cmtk::Types::DataItemRange range = data->GetRange(); data->Threshold( cmtk::Types::DataItemRange( threshold, range.m_UpperBound ) ); if ( ApplyNextToAll ) ++it; else it = ImageStack.end(); } ApplyNextToAll = false; } void CallbackThreshAbove( const double threshold ) { CheckStackOneImage( "ThreshAbove" ); ImageStackType::iterator it = ImageStack.begin(); while ( it != ImageStack.end() ) { cmtk::TypedArray::SmartPtr data = (*it)->GetData(); const cmtk::Types::DataItemRange range = data->GetRange(); data->Threshold( cmtk::Types::DataItemRange( range.m_LowerBound, threshold ) ); if ( ApplyNextToAll ) ++it; else it = ImageStack.end(); } ApplyNextToAll = false; } void CallbackScalarMul( const double c ) { CheckStackOneImage( "ScalarMul" ); ImageStackType::iterator it = ImageStack.begin(); while ( it != ImageStack.end() ) { cmtk::UniformVolume& p = **it; const size_t numberOfPixels = p.GetNumberOfPixels(); cmtk::TypedArray::SmartPtr mul( cmtk::TypedArray::Create( ResultType, numberOfPixels ) ); cmtk::TypedArray* mulRef = mul.GetPtr(); // need this to work around GCD / clang limitations #ifdef CMTK_USE_GCD const cmtk::Threads::Stride stride( numberOfPixels ); dispatch_apply( stride.NBlocks(), dispatch_get_global_queue(0, 0), ^(size_t b) { for ( size_t i = stride.From( b ); i < stride.To( b ); ++i ) #else #pragma omp parallel for for ( int i = 0; i < static_cast( numberOfPixels ); ++i ) #endif { cmtk::Types::DataItem pv; if ( p.GetDataAt( pv, i ) ) { mulRef->Set( c * pv, i ); } else { mulRef->SetPaddingAt( i ); } } #ifdef CMTK_USE_GCD } ); #endif p.SetData( mul ); if ( ApplyNextToAll ) ++it; else it = ImageStack.end(); } ApplyNextToAll = false; } void CallbackScalarAdd( const double c ) { CheckStackOneImage( "ScalarAdd" ); ImageStackType::iterator it = ImageStack.begin(); while ( it != ImageStack.end() ) { cmtk::UniformVolume& p = **it; const size_t numberOfPixels = p.GetNumberOfPixels(); cmtk::TypedArray::SmartPtr add( cmtk::TypedArray::Create( ResultType, numberOfPixels ) ); cmtk::TypedArray* addRef = add.GetPtr(); // need this to work around GCD / clang limitations #ifdef CMTK_USE_GCD const cmtk::Threads::Stride stride( numberOfPixels ); dispatch_apply( stride.NBlocks(), dispatch_get_global_queue(0, 0), ^(size_t b) { for ( size_t i = stride.From( b ); i < stride.To( b ); ++i ) #else #pragma omp parallel for for ( int i = 0; i < static_cast( numberOfPixels ); ++i ) #endif { cmtk::Types::DataItem pv; if ( p.GetDataAt( pv, i ) ) { addRef->Set( c + pv, i ); } else { addRef->SetPaddingAt( i ); } } #ifdef CMTK_USE_GCD }); #endif p.SetData( add ); if ( ApplyNextToAll ) ++it; else it = ImageStack.end(); } ApplyNextToAll = false; } void CallbackScalarXor( const long int c ) { CheckStackOneImage( "ScalarXor" ); ImageStackType::iterator it = ImageStack.begin(); while ( it != ImageStack.end() ) { cmtk::UniformVolume& p = **it; const size_t numberOfPixels = p.GetNumberOfPixels(); cmtk::TypedArray::SmartPtr out( cmtk::TypedArray::Create( ResultType, numberOfPixels ) ); cmtk::TypedArray* outRef = out.GetPtr(); // need this to work around GCD / clang limitations #ifdef CMTK_USE_GCD const cmtk::Threads::Stride stride( numberOfPixels ); dispatch_apply( stride.NBlocks(), dispatch_get_global_queue(0, 0), ^(size_t b) { for ( size_t i = stride.From( b ); i < stride.To( b ); ++i ) #else #pragma omp parallel for for ( int i = 0; i < static_cast( numberOfPixels ); ++i ) #endif { cmtk::Types::DataItem pv; if ( p.GetDataAt( pv, i ) ) { const long int iv = static_cast( pv ); outRef->Set( iv ^ c, i ); } else { outRef->SetPaddingAt( i ); } } #ifdef CMTK_USE_GCD }); #endif p.SetData( out ); if ( ApplyNextToAll ) ++it; else it = ImageStack.end(); } ApplyNextToAll = false; } void CallbackScalarAnd( const long int c ) { CheckStackOneImage( "ScalarAnd" ); ImageStackType::iterator it = ImageStack.begin(); while ( it != ImageStack.end() ) { cmtk::UniformVolume& p = **it; const size_t numberOfPixels = p.GetNumberOfPixels(); cmtk::TypedArray::SmartPtr out( cmtk::TypedArray::Create( ResultType, numberOfPixels ) ); cmtk::TypedArray* outRef = out.GetPtr(); // need this to work around GCD / clang limitations #ifdef CMTK_USE_GCD const cmtk::Threads::Stride stride( numberOfPixels ); dispatch_apply( stride.NBlocks(), dispatch_get_global_queue(0, 0), ^(size_t b) { for ( size_t i = stride.From( b ); i < stride.To( b ); ++i ) #else #pragma omp parallel for for ( int i = 0; i < static_cast( numberOfPixels ); ++i ) #endif { cmtk::Types::DataItem pv; if ( p.GetDataAt( pv, i ) ) { const long int iv = static_cast( pv ); outRef->Set( iv & c, i ); } else { outRef->SetPaddingAt( i ); } } #ifdef CMTK_USE_GCD }); #endif p.SetData( out ); if ( ApplyNextToAll ) ++it; else it = ImageStack.end(); } ApplyNextToAll = false; } void CallbackOneOver() { CheckStackOneImage( "OneOver" ); ImageStackType::iterator it = ImageStack.begin(); while ( it != ImageStack.end() ) { cmtk::UniformVolume& p = **it; const size_t numberOfPixels = p.GetNumberOfPixels(); cmtk::TypedArray::SmartPtr inv( cmtk::TypedArray::Create( ResultType, numberOfPixels ) ); cmtk::TypedArray* invRef = inv.GetPtr(); // need this to work around GCD / clang limitations #ifdef CMTK_USE_GCD const cmtk::Threads::Stride stride( numberOfPixels ); dispatch_apply( stride.NBlocks(), dispatch_get_global_queue(0, 0), ^(size_t b) { for ( size_t i = stride.From( b ); i < stride.To( b ); ++i ) #else #pragma omp parallel for for ( int i = 0; i < static_cast( numberOfPixels ); ++i ) #endif { cmtk::Types::DataItem pv; if ( p.GetDataAt( pv, i ) ) { invRef->Set( 1.0 / pv, i ); } else { invRef->SetPaddingAt( i ); } } #ifdef CMTK_USE_GCD }); #endif p.SetData( inv ); if ( ApplyNextToAll ) ++it; else it = ImageStack.end(); } ApplyNextToAll = false; } void CallbackAdd() { CheckStackTwoMatchingImages( "Add" ); cmtk::UniformVolume::SmartPtr p = ImageStack.front(); ImageStack.pop_front(); cmtk::UniformVolume::SmartPtr q = ImageStack.front(); ImageStack.pop_front(); const size_t numberOfPixels = p->GetNumberOfPixels(); cmtk::TypedArray::SmartPtr add( cmtk::TypedArray::Create( ResultType, numberOfPixels ) ); cmtk::TypedArray* addRef = add.GetPtr(); // need this to work around GCD / clang limitations #ifdef CMTK_USE_GCD const cmtk::Threads::Stride stride( numberOfPixels ); dispatch_apply( stride.NBlocks(), dispatch_get_global_queue(0, 0), ^(size_t b) { for ( size_t i = stride.From( b ); i < stride.To( b ); ++i ) #else #pragma omp parallel for for ( int i = 0; i < static_cast( numberOfPixels ); ++i ) #endif { cmtk::Types::DataItem pv, qv; if ( p->GetDataAt( pv, i ) && q->GetDataAt( qv, i ) ) { addRef->Set( pv+qv, i ); } else { addRef->SetPaddingAt( i ); } } #ifdef CMTK_USE_GCD }); #endif p->SetData( add ); ImageStack.push_front( p ); } void CallbackMul() { CheckStackTwoMatchingImages( "Mul" ); cmtk::UniformVolume::SmartPtr p = ImageStack.front(); ImageStack.pop_front(); const size_t numberOfPixels = p->GetNumberOfPixels(); cmtk::TypedArray::SmartPtr mul( cmtk::TypedArray::Create( ResultType, numberOfPixels ) ); cmtk::TypedArray* mulRef = mul.GetPtr(); // need this to work around GCD / clang limitations while ( ! ImageStack.empty() ) { cmtk::UniformVolume::SmartPtr q = ImageStack.front(); ImageStack.pop_front(); #ifdef CMTK_USE_GCD const cmtk::Threads::Stride stride( numberOfPixels ); dispatch_apply( stride.NBlocks(), dispatch_get_global_queue(0, 0), ^(size_t b) { for ( size_t i = stride.From( b ); i < stride.To( b ); ++i ) #else #pragma omp parallel for for ( int i = 0; i < static_cast( numberOfPixels ); ++i ) #endif { cmtk::Types::DataItem pv, qv; if ( p->GetDataAt( pv, i ) && q->GetDataAt( qv, i ) ) { mulRef->Set( pv*qv, i ); } else { mulRef->SetPaddingAt( i ); } } #ifdef CMTK_USE_GCD }); #endif if ( !ApplyNextToAll ) break; } ApplyNextToAll = false; p->SetData( mul ); ImageStack.push_front( p ); } void CallbackDiv() { CheckStackTwoMatchingImages( "Div" ); cmtk::UniformVolume::SmartPtr p = ImageStack.front(); ImageStack.pop_front(); cmtk::UniformVolume::SmartPtr q = ImageStack.front(); ImageStack.pop_front(); const size_t numberOfPixels = p->GetNumberOfPixels(); cmtk::TypedArray::SmartPtr div( cmtk::TypedArray::Create( ResultType, numberOfPixels ) ); cmtk::TypedArray* divRef = div.GetPtr(); // need this to work around GCD / clang limitations #ifdef CMTK_USE_GCD const cmtk::Threads::Stride stride( numberOfPixels ); dispatch_apply( stride.NBlocks(), dispatch_get_global_queue(0, 0), ^(size_t b) { for ( size_t i = stride.From( b ); i < stride.To( b ); ++i ) #else #pragma omp parallel for for ( int i = 0; i < static_cast( numberOfPixels ); ++i ) #endif { cmtk::Types::DataItem pv, qv; if ( p->GetDataAt( pv, i ) && q->GetDataAt( qv, i ) && (qv != 0) ) { divRef->Set( pv/qv, i ); } else { divRef->SetPaddingAt( i ); } } #ifdef CMTK_USE_GCD }); #endif p->SetData( div ); ImageStack.push_front( p ); } void CallbackComplexDiv() { CheckStackTwoMatchingImages( "ComplexDiv" ); cmtk::UniformVolume::SmartPtr q1 = ImageStack.front(); ImageStack.pop_front(); cmtk::UniformVolume::SmartPtr p1 = ImageStack.front(); ImageStack.pop_front(); CheckStackTwoMatchingImages( "ComplexDiv" ); cmtk::UniformVolume::SmartPtr q0 = ImageStack.front(); ImageStack.pop_front(); cmtk::UniformVolume::SmartPtr p0 = ImageStack.front(); ImageStack.pop_front(); const size_t numberOfPixels = p0->GetNumberOfPixels(); cmtk::TypedArray::SmartPtr divP( cmtk::TypedArray::Create( ResultType, numberOfPixels ) ); cmtk::TypedArray::SmartPtr divQ( cmtk::TypedArray::Create( ResultType, numberOfPixels ) ); #pragma omp parallel for for ( int i = 0; i < static_cast( numberOfPixels ); ++i ) { cmtk::Types::DataItem pv0, qv0, pv1, qv1; if ( p0->GetDataAt( pv0, i ) && q0->GetDataAt( qv0, i ) && p1->GetDataAt( pv1, i ) && q1->GetDataAt( qv1, i ) && ((pv1 != 0) || (qv1 != 0)) ) { const cmtk::Types::DataItem denom = pv1*pv1 + qv1*qv1; divP->Set( (pv0*pv1 + qv0*qv1) / denom, i ); divQ->Set( (qv0*pv1 - pv0*qv1) / denom, i ); } else { divP->SetPaddingAt( i ); divQ->SetPaddingAt( i ); } } p0->SetData( divP ); ImageStack.push_front( p0 ); q0->SetData( divQ ); ImageStack.push_front( q0 ); } void CallbackAtan2() { CheckStackTwoMatchingImages( "Atan2" ); cmtk::UniformVolume::SmartPtr p = ImageStack.front(); ImageStack.pop_front(); cmtk::UniformVolume::SmartPtr q = ImageStack.front(); ImageStack.pop_front(); const size_t numberOfPixels = p->GetNumberOfPixels(); cmtk::TypedArray::SmartPtr result( cmtk::TypedArray::Create( ResultType, numberOfPixels ) ); cmtk::TypedArray* resultRef = result.GetPtr(); // need this to work around GCD / clang limitations #ifdef CMTK_USE_GCD const cmtk::Threads::Stride stride( numberOfPixels ); dispatch_apply( stride.NBlocks(), dispatch_get_global_queue(0, 0), ^(size_t b) { for ( size_t i = stride.From( b ); i < stride.To( b ); ++i ) #else #pragma omp parallel for for ( int i = 0; i < static_cast( numberOfPixels ); ++i ) #endif { cmtk::Types::DataItem pv, qv; if ( p->GetDataAt( pv, i ) && q->GetDataAt( qv, i ) && (qv != 0) ) { resultRef->Set( atan2( qv, pv ), i ); } else { resultRef->SetPaddingAt( i ); } } #ifdef CMTK_USE_GCD }); #endif p->SetData( result ); ImageStack.push_front( p ); } void CallbackMatchHistograms() { if ( ImageStack.size() < 2 ) { cmtk::StdErr << "ERROR: need at least two images on stack for histogram intensity matching\n"; return; } cmtk::UniformVolume::SmartPtr ref = ImageStack.front(); ImageStack.pop_front(); cmtk::TypedArray::SmartPtr result( ImageStack.front()->GetData()->Convert( ResultType ) ); result->ApplyFunctionObject( cmtk::TypedArrayFunctionHistogramMatching( *(ImageStack.front()->GetData()), *(ref->GetData()) ) ); ImageStack.front()->SetData( result ); } void CallbackMatchMeanSDev() { if ( ImageStack.size() < 2 ) { cmtk::StdErr << "ERROR: need at least two images on stack for histogram intensity matching\n"; return; } cmtk::UniformVolume::SmartPtr ref = ImageStack.front(); ImageStack.pop_front(); cmtk::Types::DataItem rMean, rVar; ref->GetData()->GetStatistics( rMean, rVar ); cmtk::Types::DataItem mMean, mVar; ImageStack.front()->GetData()->GetStatistics( mMean, mVar ); const cmtk::Types::DataItem scale = sqrt( rVar / mVar ); const cmtk::Types::DataItem offset = rMean - scale * mMean; cmtk::TypedArray::SmartPtr result( ImageStack.front()->GetData()->Convert( ResultType ) ); result->Rescale( scale, offset ); ImageStack.front()->SetData( result ); } void CallbackMatchMeanSDevThree() { if ( ImageStack.size() < 3 ) { cmtk::StdErr << "ERROR: need at least three images on stack for histogram intensity matching using separate reference images\n"; return; } cmtk::UniformVolume::SmartPtr ref = ImageStack.front(); ImageStack.pop_front(); cmtk::UniformVolume::SmartPtr mod = ImageStack.front(); ImageStack.pop_front(); cmtk::Types::DataItem rMean, rVar; ref->GetData()->GetStatistics( rMean, rVar ); cmtk::Types::DataItem mMean, mVar; mod->GetData()->GetStatistics( mMean, mVar ); const cmtk::Types::DataItem scale = sqrt( rVar / mVar ); const cmtk::Types::DataItem offset = rMean - scale * mMean; cmtk::TypedArray::SmartPtr result( ImageStack.front()->GetData()->Convert( ResultType ) ); result->Rescale( scale, offset ); ImageStack.front()->SetData( result ); } void CallbackMaskAverage() { if ( ImageStack.size() < 2 ) { cmtk::StdErr << "ERROR: need at last two images on stack for mask averaging\n"; return; } cmtk::UniformVolume::SmartPtr msk = ImageStack.front(); ImageStack.pop_front(); const cmtk::TypedArray& mskData = *(msk->GetData()); const cmtk::Types::DataItemRange labelRange = mskData.GetRange(); const size_t nLabels = static_cast( labelRange.Width()+1 ); std::vector means( nLabels, 0.0 ); std::vector count( nLabels, 0 ); cmtk::TypedArray::SmartPtr result( ImageStack.front()->GetData()->Convert( ResultType ) ); ImageStack.front()->SetData( result ); cmtk::TypedArray& avgData = *(ImageStack.front()->GetData()); // first pass: compute mean for each labelled ROI const size_t n = mskData.GetDataSize(); for ( size_t idx = 0; idx < n; ++idx ) { cmtk::Types::DataItem l; mskData.Get( l, idx ); cmtk::Types::DataItem v; if ( avgData.Get( v, idx ) ) { const size_t ll = static_cast( l-labelRange.m_LowerBound ); means[ll] += v; ++count[ll]; } } // second pass: replace values with computed ROI means #pragma omp parallel for for ( int idx = 0; idx < static_cast( n ); ++idx ) { cmtk::Types::DataItem l; mskData.Get( l, idx ); const size_t ll = static_cast( l-labelRange.m_LowerBound ); avgData.Set( means[ll] / count[ll], idx ); } } void CallbackSum() { while ( ImageStack.size() > 1 ) { CallbackAdd(); } } void CallbackProduct() { while ( ImageStack.size() > 1 ) { CallbackMul(); } } void CallbackAverage() { CheckStackTwoMatchingImages( "Average" ); std::vector volPtrs; while ( !ImageStack.empty() ) { if ( ImageStack.size() > 1 ) CheckStackTwoMatchingImages( "Average" ); volPtrs.push_back( ImageStack.front() ); ImageStack.pop_front(); } const std::vector& vPtrs = volPtrs; // using const reference, we prevent GCD from segfaulting const size_t numberOfPixels = volPtrs[ 0 ]->GetNumberOfPixels(); cmtk::TypedArray::SmartPtr avgArray( cmtk::TypedArray::Create( ResultType, numberOfPixels ) ); #pragma omp parallel for for ( int i = 0; i < static_cast( numberOfPixels ); ++i ) { double sum = 0; size_t nvalues = 0; for ( size_t curVol = 0; curVol < vPtrs.size(); ++curVol ) { cmtk::Types::DataItem v; if ( vPtrs[ curVol ]->GetDataAt( v, i ) ) { ++nvalues; sum += v; } } if ( nvalues ) { avgArray->Set( sum/nvalues, i ); } else avgArray->SetPaddingAt( i ); } volPtrs[0]->SetData( avgArray ); ImageStack.push_front( volPtrs[0] ); } void CallbackVariance() { CheckStackTwoMatchingImages( "Variance" ); std::vector volPtrs; while ( !ImageStack.empty() ) { if ( ImageStack.size() > 1 ) CheckStackTwoMatchingImages( "Variance" ); volPtrs.push_back( ImageStack.front() ); ImageStack.pop_front(); } const std::vector& vPtrs = volPtrs; // using const reference, we prevent GCD from segfaulting const size_t numberOfPixels = volPtrs[ 0 ]->GetNumberOfPixels(); cmtk::TypedArray::SmartPtr varianceArray( cmtk::TypedArray::Create( ResultType, numberOfPixels ) ); cmtk::TypedArray* varianceArrayRef = varianceArray.GetPtr(); // need this to work around GCD / clang limitations #ifdef CMTK_USE_GCD const cmtk::Threads::Stride stride( numberOfPixels ); dispatch_apply( stride.NBlocks(), dispatch_get_global_queue(0, 0), ^(size_t b) { for ( size_t i = stride.From( b ); i < stride.To( b ); ++i ) #else #pragma omp parallel for for ( int i = 0; i < static_cast( numberOfPixels ); ++i ) #endif { double sum = 0; double sum2 = 0; size_t nvalues = 0; for ( size_t curVol = 0; curVol < vPtrs.size(); ++curVol ) { cmtk::Types::DataItem v; if ( vPtrs[ curVol ]->GetDataAt( v, i ) ) { ++nvalues; sum += v; sum2 += v*v; } } if ( nvalues ) { const double mean = sum / nvalues; varianceArrayRef->Set( (nvalues * mean * mean - 2 * mean * sum + sum2)/nvalues, i ); } else varianceArrayRef->SetPaddingAt( i ); } #ifdef CMTK_USE_GCD }); #endif volPtrs[0]->SetData( varianceArray ); ImageStack.push_front( volPtrs[0] ); } void CallbackVoteCombination() { CheckStackTwoMatchingImages( "Vote" ); cmtk::UniformVolume::SmartPtr grid = ImageStack.front(); std::vector dataPtrs; while ( ! ImageStack.empty() ) { if ( ImageStack.size() > 1 ) CheckStackTwoMatchingImages( "Vote" ); dataPtrs.push_back( ImageStack.front()->GetData() ); ImageStack.pop_front(); } cmtk::LabelCombinationVoting voting( dataPtrs ); grid->SetData( voting.GetResult() ); ImageStack.push_front( grid ); } void CallbackSTAPLE( const long int maxIterations ) { CheckStackTwoMatchingImages( "STAPLE" ); cmtk::UniformVolume::SmartPtr imageGrid = ImageStack.front(); std::vector dataPtrs; while ( ! ImageStack.empty() ) { if ( ImageStack.size() > 1 ) CheckStackTwoMatchingImages( "STAPLE" ); dataPtrs.push_back( ImageStack.front()->GetData() ); ImageStack.pop_front(); } cmtk::LabelCombinationSTAPLE staple( dataPtrs, maxIterations, ResultType ); imageGrid->SetData( staple.GetResult() ); ImageStack.push_front( imageGrid ); if ( cmtk::DebugOutput::GetGlobalLevel() > 0 ) { cmtk::DebugOutput( 1 ) << "p "; for ( size_t i = 0; i < dataPtrs.size(); ++i ) { cmtk::DebugOutput( 1 ).GetStream().printf( "%.3f ", staple.GetPValue( i ) ); } cmtk::DebugOutput( 1 ).GetStream().printf( "\nq " ); for ( size_t i = 0; i < dataPtrs.size(); ++i ) { cmtk::DebugOutput( 1 ).GetStream().printf( "%.3f ", staple.GetQValue( i ) ); } cmtk::DebugOutput( 1 ).GetStream().printf( "\n" ); } } void MultiClassSTAPLE( const long int maxIterations, const bool disputedOnly ) { CheckStackTwoMatchingImages( "MultiClassSTAPLE" ); cmtk::UniformVolume::SmartPtr imageGrid = ImageStack.front(); std::vector dataPtrs; while ( ! ImageStack.empty() ) { if ( ImageStack.size() > 1 ) CheckStackTwoMatchingImages( "MultiClassSTAPLE" ); dataPtrs.push_back( ImageStack.front()->GetData() ); ImageStack.pop_front(); } cmtk::LabelCombinationMultiClassSTAPLE mstaple( dataPtrs, maxIterations, disputedOnly ); imageGrid->SetData( mstaple.GetResult() ); ImageStack.push_front( imageGrid ); } void CallbackMultiClassSTAPLE( const long int maxIterations ) { MultiClassSTAPLE( maxIterations, false /*disputedOnly*/ ); } void CallbackMultiClassSTAPLEDisputed( const long int maxIterations ) { MultiClassSTAPLE( maxIterations, true /*disputedOnly*/ ); } void CallbackStackEntropyLabels() { CheckStackTwoMatchingImages( "StackEntropyLabels" ); std::vector volPtrs; while ( ! ImageStack.empty() ) { if ( ImageStack.size() > 1 ) CheckStackTwoMatchingImages( "StackEntropyLabels" ); volPtrs.push_back( ImageStack.front() ); ImageStack.pop_front(); } const std::vector& vPtrs = volPtrs; // using const reference, we prevent GCD from segfaulting const size_t numberOfPixels = volPtrs[ 0 ]->GetNumberOfPixels(); cmtk::TypedArray::SmartPtr entropyArray( cmtk::TypedArray::Create( ResultType, numberOfPixels ) ); cmtk::TypedArray* entropyArrayRef = entropyArray.GetPtr(); // need this to work around GCD / clang limitations #ifdef CMTK_USE_GCD const cmtk::Threads::Stride stride( numberOfPixels ); dispatch_apply( stride.NBlocks(), dispatch_get_global_queue(0, 0), ^(size_t b) { for ( size_t i = stride.From( b ); i < stride.To( b ); ++i ) #else #pragma omp parallel for for ( int i = 0; i < static_cast( numberOfPixels ); ++i ) #endif { std::map labelCount; size_t totalCount = 0; for ( size_t curVol = 0; curVol < vPtrs.size(); ++curVol ) { cmtk::Types::DataItem v; if ( vPtrs[ curVol ]->GetDataAt( v, i ) ) { ++labelCount[ static_cast( v ) ]; ++totalCount; } } if ( totalCount ) { const double factor = 1.0 / totalCount; double entropy = 0; for ( std::map::const_iterator it = labelCount.begin(); it != labelCount.end(); ++it ) { const double p = factor * it->second; entropy += p * log( p ); } entropyArrayRef->Set( -entropy / log( 2.0 ), i ); } else entropyArrayRef->SetPaddingAt( i ); } #ifdef CMTK_USE_GCD }); #endif volPtrs[0]->SetData( entropyArray ); ImageStack.push_front( volPtrs[0] ); } void CallbackMaxIndex() { CheckStackTwoMatchingImages( "MaxIndex" ); std::vector volPtrs( ImageStack.size() ); while ( ! ImageStack.empty() ) { if ( ImageStack.size() > 1 ) CheckStackTwoMatchingImages( "MaxIndex" ); volPtrs[ImageStack.size()-1] = ImageStack.front(); ImageStack.pop_front(); } const size_t numberOfPixels = volPtrs[ 0 ]->GetNumberOfPixels(); cmtk::TypedArray::SmartPtr maxArray( cmtk::TypedArray::Create( cmtk::TYPE_SHORT, numberOfPixels ) ); cmtk::TypedArray* maxArrayRef = maxArray.GetPtr(); // need this to work around GCD / clang limitations const std::vector& vPtrs = volPtrs; // using const reference, we prevent GCD from segfaulting #ifdef CMTK_USE_GCD const cmtk::Threads::Stride stride( numberOfPixels ); dispatch_apply( stride.NBlocks(), dispatch_get_global_queue(0, 0), ^(size_t b) { for ( size_t i = stride.From( b ); i < stride.To( b ); ++i ) #else #pragma omp parallel for for ( int i = 0; i < static_cast( numberOfPixels ); ++i ) #endif { cmtk::Types::DataItem maxValue = 0; short maxIndex = -2; cmtk::Types::DataItem v; for ( size_t curVol = 0; curVol < vPtrs.size(); ++curVol ) { if ( vPtrs[ curVol ]->GetDataAt( v, i ) ) { if ( maxIndex < -1 ) { maxValue = v; maxIndex = 0; } else { if ( v > maxValue ) { maxValue = v; maxIndex = curVol; } else if ( v == maxValue ) { maxIndex = -1; } } } } maxArrayRef->Set( maxIndex, i ); } #ifdef CMTK_USE_GCD }); #endif volPtrs[0]->SetData( maxArray ); ImageStack.push_front( volPtrs[0] ); } void CallbackMaxValue() { CheckStackTwoMatchingImages( "MaxValue" ); std::vector volPtrs; while ( ! ImageStack.empty() ) { if ( ImageStack.size() > 1 ) CheckStackTwoMatchingImages( "MaxValue" ); volPtrs.push_back( ImageStack.front() ); ImageStack.pop_front(); } const size_t numberOfPixels = volPtrs[ 0 ]->GetNumberOfPixels(); cmtk::TypedArray::SmartPtr maxArray( cmtk::TypedArray::Create( ResultType, numberOfPixels ) ); cmtk::TypedArray* maxArrayRef = maxArray.GetPtr(); // need this to work around GCD / clang limitations const std::vector& vPtrs = volPtrs; // using const reference, we prevent GCD from segfaulting #ifdef CMTK_USE_GCD const cmtk::Threads::Stride stride( numberOfPixels ); dispatch_apply( stride.NBlocks(), dispatch_get_global_queue(0, 0), ^(size_t b) { for ( size_t i = stride.From( b ); i < stride.To( b ); ++i ) #else #pragma omp parallel for for ( int i = 0; i < static_cast( numberOfPixels ); ++i ) #endif { cmtk::Types::DataItem maxValue = 0; bool maxValueValid = false; cmtk::Types::DataItem v; for ( size_t curVol = 0; curVol < vPtrs.size(); ++curVol ) { if ( vPtrs[ curVol ]->GetDataAt( v, i ) ) { if ( maxValueValid ) { maxValue = std::max( maxValue, v ); } else { maxValueValid = true; maxValue = v; } } } maxArrayRef->Set( maxValue, i ); } #ifdef CMTK_USE_GCD }); #endif volPtrs[0]->SetData( maxArray ); ImageStack.push_front( volPtrs[0] ); } void CallbackMinValue() { CheckStackTwoMatchingImages( "MinValue" ); std::vector volPtrs; while ( ! ImageStack.empty() ) { if ( ImageStack.size() > 1 ) CheckStackTwoMatchingImages( "MinValue" ); volPtrs.push_back( ImageStack.front() ); ImageStack.pop_front(); } const size_t numberOfPixels = volPtrs[ 0 ]->GetNumberOfPixels(); cmtk::TypedArray::SmartPtr minArray( cmtk::TypedArray::Create( ResultType, numberOfPixels ) ); cmtk::TypedArray* minArrayRef = minArray.GetPtr(); // need this to work around GCD / clang limitations const std::vector& vPtrs = volPtrs; // using const reference, we prevent GCD from segfaulting #ifdef CMTK_USE_GCD const cmtk::Threads::Stride stride( numberOfPixels ); dispatch_apply( stride.NBlocks(), dispatch_get_global_queue(0, 0), ^(size_t b) { for ( size_t i = stride.From( b ); i < stride.To( b ); ++i ) #else #pragma omp parallel for for ( int i = 0; i < static_cast( numberOfPixels ); ++i ) #endif { cmtk::Types::DataItem minValue = 0; bool minValueValid = false; cmtk::Types::DataItem v; for ( size_t curVol = 0; curVol < vPtrs.size(); ++curVol ) { if ( vPtrs[ curVol ]->GetDataAt( v, i ) ) { if ( minValueValid ) { minValue = std::min( minValue, v ); } else { minValueValid = true; minValue = v; } } } minArrayRef->Set( minValue, i ); } #ifdef CMTK_USE_GCD }); #endif volPtrs[0]->SetData( minArray ); ImageStack.push_front( volPtrs[0] ); } void CallbackContractLabels() { CheckStackTwoMatchingImages( "ContractLabels" ); std::vector volPtrs; while ( ! ImageStack.empty() ) { if ( ImageStack.size() > 1 ) CheckStackTwoMatchingImages( "ContractLabels" ); volPtrs.push_back( ImageStack.front() ); ImageStack.pop_front(); } const size_t numberOfPixels = volPtrs[ 0 ]->GetNumberOfPixels(); cmtk::TypedArray::SmartPtr outArray( cmtk::TypedArray::Create( ResultType, numberOfPixels ) ); cmtk::TypedArray* outArrayRef = outArray.GetPtr(); const std::vector& vPtrs = volPtrs; // using const reference, we prevent GCD from segfaulting #ifdef CMTK_USE_GCD const cmtk::Threads::Stride stride( numberOfPixels ); dispatch_apply( stride.NBlocks(), dispatch_get_global_queue(0, 0), ^(size_t b) { for ( size_t i = stride.From( b ); i < stride.To( b ); ++i ) #else #pragma omp parallel for for ( int i = 0; i < static_cast( numberOfPixels ); ++i ) #endif { cmtk::Types::DataItem v = 0; for ( size_t curVol = 0; curVol < vPtrs.size(); ++curVol ) { if ( vPtrs[ curVol ]->GetDataAt( v, i ) && v ) break; } outArrayRef->Set( v, i ); } #ifdef CMTK_USE_GCD }); #endif volPtrs[0]->SetData( outArray ); ImageStack.push_front( volPtrs[0] ); } void CallbackCombinePCA() { std::vector volPtrs; while ( ! ImageStack.empty() ) { if ( ImageStack.size() > 1 ) CheckStackTwoMatchingImages( "CombinePCA" ); volPtrs.push_back( ImageStack.front() ); ImageStack.pop_front(); } const size_t numberOfPixels = volPtrs[0]->GetNumberOfPixels(); const size_t numberOfImages = volPtrs.size(); cmtk::Vector meanVector( numberOfImages ); for ( size_t image = 0; image < numberOfImages; ++image ) { cmtk::Types::DataItem mean = 0; for ( size_t pixel = 0; pixel < numberOfPixels; ++pixel ) { cmtk::Types::DataItem value; if ( volPtrs[image]->GetDataAt( value, pixel ) ) { mean += value; } } meanVector[image] = mean / numberOfPixels; } cmtk::SymmetricMatrix cc( numberOfImages ); for ( size_t imageY = 0; imageY < numberOfImages; ++imageY ) { for ( size_t imageX = 0; imageX < numberOfImages; ++imageX ) { cmtk::Types::DataItem ccXY = 0; for ( size_t pixel = 0; pixel < numberOfPixels; ++pixel ) { cmtk::Types::DataItem valueX, valueY; if ( volPtrs[imageX]->GetDataAt( valueX, pixel ) && volPtrs[imageY]->GetDataAt( valueY, pixel ) ) { ccXY += ( valueX - meanVector[imageX] ) * ( valueY - meanVector[imageY] ); } } cc(imageX,imageY) = ccXY / numberOfImages; } } const cmtk::EigenSystemSymmetricMatrix eigensystem( cc ); const std::vector eigenvalues = eigensystem.GetEigenvalues(); // find maximum eigenvalue int maxL = 0; cmtk::Types::DataItem maxLambda = fabs( eigenvalues[0] ); for ( size_t l = 1; l < numberOfImages; ++l ) { if ( fabs( eigenvalues[l] ) > maxLambda ) { maxLambda = fabs( eigenvalues[l] ); maxL = l; } } // get normalized pricipal component cmtk::Vector ev( numberOfImages ); for ( size_t l = 0; l < numberOfImages; ++l ) { ev[l] = eigensystem.EigenvectorElement(l,maxL); } ev *= 1.0 / ev.EuclidNorm(); if ( cmtk::DebugOutput::GetGlobalLevel() > 0 ) { cmtk::DebugOutput( 1 ) << "Principal eigenvector (normalized):\n"; for ( size_t l = 0; l < numberOfImages; ++l ) { cmtk::DebugOutput( 1 ).GetStream().printf( "%f\t", ev[l] ); } cmtk::DebugOutput( 1 ) << "\n"; } // project all pixel vectors onto dominant component cmtk::TypedArray::SmartPtr output( cmtk::TypedArray::Create( ResultType, numberOfPixels ) ); cmtk::Vector v( numberOfImages ); for ( size_t pixel = 0; pixel < numberOfPixels; ++pixel ) { for ( size_t image = 0; image < numberOfImages; ++image ) { if ( ! volPtrs[image]->GetDataAt( v[image], pixel ) ) v[image] = 0; v[image] -= meanVector[image]; } output->Set( v * ev, pixel ); } volPtrs[0]->SetData( output ); ImageStack.push_front( volPtrs[0] ); } int doMain( const int argc, const char *argv[] ) { try { cmtk::CommandLine cl; cl.SetProgramInfo( cmtk::CommandLine::PRG_TITLE, "Image operations" ); cl.SetProgramInfo( cmtk::CommandLine::PRG_DESCR, "Perform operations on images using stack-based postfix notation.\n\n" "Images can be read from files and pushed onto the stack. Images on the stack can be processed and combined via different operators. " "Results of all operations are put back onto the stack, where they can be further processed or written back to image files." ); typedef cmtk::CommandLine::Key Key; cl.BeginGroup( "Input/output", "Input/output operations" ); cl.AddCallback( Key( "in" ), CallbackIn, "Read input image(s) to top of stack" ); cl.AddCallback( Key( "out" ), CallbackOut, "Write output image from top of stack (but leave it on the stack)" ); cl.AddCallback( Key( "set-padding-value" ), CallbackSetPaddingValue, "Set the value that is interpreted as padding value in subsequently read images." ); cl.AddCallback( Key( "unset-padding" ), CallbackUnsetPaddingValue, "Disable padding. All values in subsequently read images will be interpreted as actual data." ); cl.EndGroup(); cl.BeginGroup( "Internal", "Internal settings" ); cl.AddSwitch( Key( "float" ), &ResultType, cmtk::TYPE_FLOAT, "Use single precision for computations and results" ); cl.AddSwitch( Key( "double" ), &ResultType, cmtk::TYPE_DOUBLE, "Use double precision for computations and results" ); cl.EndGroup(); cl.BeginGroup( "Stack", "Stack operations" ); cl.AddCallback( Key( "pop" ), CallbackPop, "Pop (discard) top image from stack." ); cl.AddCallback( Key( "dup" ), CallbackDup, "Duplicate image on top of the stack." ); cl.AddCallback( Key( "all" ), CallbackAll, "Apply next single-image operation to all images on the stack." ); cl.EndGroup(); cl.BeginGroup( "Single image", "Single-image operators" ); cl.AddCallback( Key( "fill" ), CallbackFill, "Fill top image with constant value (i.e., assign value to all pixels)" ); cl.AddCallback( Key( "abs" ), CallbackAbs, "Apply abs() function to top image" ); cl.AddCallback( Key( "log" ), CallbackLog, "Apply log() function to top image" ); cl.AddCallback( Key( "logit" ), CallbackLogit, "Apply log(x/(1-x)) function to top image" ); cl.AddCallback( Key( "logistic" ), CallbackLogistic, "Apply 1/(1+exp(-x)) function to top image" ); cl.AddCallback( Key( "exp" ), CallbackExp, "Apply exp() function to top image" ); cl.AddCallback( Key( "sqr" ), CallbackSqr, "Apply square operator to top image" ); cl.AddCallback( Key( "sqrt" ), CallbackSqrt, "Apply square root operator to top image" ); cl.AddCallback( Key( "trunc" ), CallbackTrunc, "Truncate all values in top image to integer" ); cl.AddCallback( Key( "one-over" ), CallbackOneOver, "For each pixel, replace its value x with 1.0/x" ); cl.AddCallback( Key( "scalar-mul" ), CallbackScalarMul, "Multiply top image with a scalar value" ); cl.AddCallback( Key( "scalar-add" ), CallbackScalarAdd, "Add a scalar to each pixel of the top image" ); cl.AddCallback( Key( "scalar-xor" ), CallbackScalarXor, "Bitwise exclusive-or between top level and given scalar value" ); cl.AddCallback( Key( "scalar-and" ), CallbackScalarAnd, "Bitwise and operation between top level and given scalar value" ); cl.AddCallback( Key( "thresh-below" ), CallbackThreshBelow, "Set values below given threshold to threshold." ); cl.AddCallback( Key( "thresh-above" ), CallbackThreshAbove, "Set values above given threshold to threshold." ); cl.EndGroup(); cl.BeginGroup( "Two images", "Image pair operators" ); cl.AddCallback( Key( "add" ), CallbackAdd, "Add top and second image, place result on stack" ); cl.AddCallback( Key( "mul" ), CallbackMul, "Multiply top and second image, place result on stack" ); cl.AddCallback( Key( "div" ), CallbackDiv, "Divide top image by second image, place result on stack" ); cl.AddCallback( Key( "atan2" ), CallbackAtan2, "Compute atan2() function from tup two image pixel pairs, place result on stack" ); cl.AddCallback( Key( "match-histograms" ), CallbackMatchHistograms, "Scale intensities in one image to match intensities of another. The last image pushed onto the stack provides the reference intensity distribution, the preceding image will be modified. Both input images are removed from the stack and the modified image is pushed onto the stack." ); cl.AddCallback( Key( "match-mean-sdev" ), CallbackMatchMeanSDev, "Scale intensities of one image to match mean and standard deviation of another. The last image pushed onto the stack provides the reference intensity distribution, the preceding image will be modified. Both input images are removed from the stack and the modified image is pushed onto the stack." ); cl.AddCallback( Key( "match-mean-sdev3" ), CallbackMatchMeanSDevThree, "Scale intensities of an image by a factor and offset computed from two other images to match their mean and standard deviations. The last image pushed onto the stack provides the reference intensity distribution, the preceding image provides the intensity distribution to match to the reference image's, and the third image on the stack will be modified. All three input images are removed from the stack and the modified image is pushed onto the stack." ); cl.AddCallback( Key( "mask-average" ), CallbackMaskAverage, "Mask averaging: the top image is taken as a multi-label mask. The pixels in the second image are averaged by mask labels, and then replaced with the average value for each mask label." ); cl.EndGroup(); cl.BeginGroup( "Complex", "Complex Arithmetic" ); cl.AddCallback( Key( "complex-div" ), CallbackComplexDiv, "Complex division, (a+ib)/(c+id), assuming four values were put on the stack in order a, b, c, d. Place result on stack, real first, imaginary second (i.e., imaginary is top)." ); cl.EndGroup(); cl.BeginGroup( "Contract multiple images", "Operators that contract the entire stack into a single image" ); cl.AddCallback( Key( "sum" ), CallbackSum, "Sum all images on stack, place result on stack" ); cl.AddCallback( Key( "product" ), CallbackProduct, "Compute product of all images on stack, place result on stack" ); cl.AddCallback( Key( "average" ), CallbackAverage, "Average all images on stack, place result on stack" ); cl.AddCallback( Key( "variance" ), CallbackVariance, "For each pixel, compute variance over all images on stack, place result on stack" ); cl.AddCallback( Key( "combine-pca" ), CallbackCombinePCA, "Combine images using PCA by projecting onto direction of largest correlation" ); cl.AddCallback( Key( "max-value" ), CallbackMaxValue, "For each pixel, compute maximum VALUE over all images, place result on stack" ); cl.AddCallback( Key( "min-value" ), CallbackMinValue, "For each pixel, compute minimum VALUE over all images, place result on stack" ); cl.AddCallback( Key( "max-index" ), CallbackMaxIndex, "For each pixel, compute INDEX of image with maximum value, place result on stack" ); cl.EndGroup(); cl.BeginGroup( "Contract multiple label images", "Operators that contract a stack of label images into a single label image" ); cl.AddCallback( Key( "vote" ), CallbackVoteCombination, "Merge all images on stack with voting, place result on stack" ); cl.AddCallback( Key( "staple" ), CallbackSTAPLE, "Combine binary maps on the stack using [arg] iterations of the STAPLE algorithm. " "The result of this operation is the spatial map of 'weights' W, which are the probabilities of image foreground at each pixel. In 'verbose' " "mode, estimated expert parameters p (sensitivity) and q (specificity) are also written to standard output." ); cl.AddCallback( Key( "contract-labels" ), CallbackContractLabels, "Contract multiple label maps into one by selecting the first (over all images on the stack) non-zero label at each pixel" ); cl.AddCallback( Key( "mstaple" ), CallbackMultiClassSTAPLE, "Combine multi-label maps on the stack using [arg] iterations of the multi-class STAPLE algorithm." "The result of this operation is the combined maximum-likeliood multi-label map." ); cl.AddCallback( Key( "mstaple-disputed" ), CallbackMultiClassSTAPLEDisputed, "Like previous operation, apply multi-class STAPLE algorithm, but restrict computation to 'disputed'" "voxels, i.e., those where the input label maps disagree. This often improves results by reducing, e.g., background effects." ); cl.AddCallback( Key( "stack-entropy-labels" ), CallbackStackEntropyLabels, "Compute stack entropy at each pixel from integer (label) input images" ); cl.EndGroup(); cl.Parse( argc, argv ); } catch ( const cmtk::CommandLine::Exception& e ) { cmtk::StdErr << e << "\n"; throw cmtk::ExitException( 1 ); } if ( ImageStack.size() > 1 ) { cmtk::StdErr << "WARNING: more than one image left on stack. Are you sure you aren't missing something?\n"; } return 0; } #include "cmtkSafeMain" cmtk-3.3.1/apps/interleaved_bad_slices.cxx000066400000000000000000000154301276303427400206300ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2012 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4851 $ // // $LastChangedDate: 2013-09-16 14:41:42 -0700 (Mon, 16 Sep 2013) $ // // $LastChangedBy: torstenrohlfing $ // */ #include #include #include #include #include #include #include #include #include #include int doMain ( const int argc, const char *argv[] ) { std::vector imagePaths; int sliceAxis = cmtk::AXIS_Z; cmtk::TypedArraySimilarity::ID metric = cmtk::TypedArraySimilarity::CC; cmtk::Types::DataItem standardDeviationsThreshold = 3.0; size_t badSlicesThresh = 0; try { cmtk::CommandLine cl; cl.SetProgramInfo( cmtk::CommandLine::PRG_TITLE, "Find bad slices in a time series of interleaved images (e.g., a resting-state fMRI series)." ); cl.SetProgramInfo( cmtk::CommandLine::PRG_DESCR, "This tool reads a time series of 3D images and detects outliers." ); cl.AddParameterVector( &imagePaths, "ImagePaths", "List of file system paths for all images in the time series."); typedef cmtk::CommandLine::Key Key; cl.BeginGroup( "Input", "Input Options" ); cmtk::CommandLine::EnumGroup::SmartPtr sliceGroup = cl.AddEnum( "slice-orientation", &sliceAxis, "Define slice orientation of the diffusion images." ); sliceGroup->AddSwitch( Key( "axial" ), (int)cmtk::AXIS_Z, "Axial slices" ); sliceGroup->AddSwitch( Key( "sagittal" ),(int)cmtk::AXIS_X, "Sagittal slices" ); sliceGroup->AddSwitch( Key( "coronal" ), (int)cmtk::AXIS_Y, "Coronal slices" ); sliceGroup->AddSwitch( Key( "slice-x" ), (int)cmtk::AXIS_X, "X coordinate axis is slice direction" ); sliceGroup->AddSwitch( Key( "slice-y" ), (int)cmtk::AXIS_Y, "Y coordinate axis is slice direction" ); sliceGroup->AddSwitch( Key( "slice-z" ), (int)cmtk::AXIS_Z, "Z coordinate axis is slice direction" ); cl.EndGroup(); cl.BeginGroup( "detection", "Bad Slice Detection" ); cmtk::CommandLine::EnumGroup::SmartPtr metricGroup = cl.AddEnum( "metric", &metric, "Image-to-image similarity metric to compare neighbouring slices." ); metricGroup->AddSwitch( Key( "ncc" ), cmtk::TypedArraySimilarity::CC, "Normalized cross correlation." ); metricGroup->AddSwitch( Key( "rms" ), cmtk::TypedArraySimilarity::MSD, "Root of mean squared differences." ); cl.AddOption( Key( "stdev-thresh" ), &standardDeviationsThreshold, "Threshold for bad slice identification in units of intensity standard deviations over all corresponding slices from the remaining diffusion images." ); cl.EndGroup(); cl.BeginGroup( "output", "Output Options" ); cl.AddOption( Key( "bad-slices-thresh" ), &badSlicesThresh, "Minimum number of detected bad slices before reporting a volume (only number of detected bad slices is reported in this case, rather than each slice separately)." ); cl.EndGroup(); cl.Parse( argc, argv ); } catch ( const cmtk::CommandLine::Exception& e ) { cmtk::StdErr << e << "\n"; throw cmtk::ExitException( 1 ); } // read all diffusion images and make sure their grids match std::vector images( imagePaths.size() ); for ( size_t i = 0; i < imagePaths.size(); ++i ) { images[i] = cmtk::UniformVolume::SmartConstPtr( cmtk::VolumeIO::Read( imagePaths[i] ) ); if ( i && ! images[0]->GridMatches( *images[i] ) ) { cmtk::StdErr << "ERROR: geometry of image '" << imagePaths[i] << "' does not match that of image '" << imagePaths[0] << "'\n"; throw cmtk::ExitException( 1 ); } } // Build slice pair difference tables and statistics std::vector< cmtk::ValueSequence > slicePairStatistics( images[0]->m_Dims[sliceAxis]-1 ); std::vector< std::vector< double> > slicePairSamples( images[0]->m_Dims[sliceAxis]-1 ); for ( int slice = 0; slice < images[0]->m_Dims[sliceAxis]-1; ++slice ) { slicePairSamples[slice].resize( images.size() ); for ( size_t i = 0; i < images.size(); ++i ) { double difference = 0; switch ( metric ) { case cmtk::TypedArraySimilarity::CC: difference = cmtk::TypedArraySimilarity::GetCrossCorrelation( images[i]->ExtractSlice( sliceAxis, slice )->GetData(), images[i]->ExtractSlice( sliceAxis, slice+1 )->GetData() ); break; case cmtk::TypedArraySimilarity::MSD: difference = sqrt( fabs( cmtk::TypedArraySimilarity::GetMinusMeanSquaredDifference( images[i]->ExtractSlice( sliceAxis, slice )->GetData(), images[i]->ExtractSlice( sliceAxis, slice+1 )->GetData() ) ) ); break; default: break; } slicePairSamples[slice][i] = difference; slicePairStatistics[slice].Proceed( difference ); } } // Search for outliers std::vector means( images[0]->m_Dims[sliceAxis]-1 ); std::vector sdevs( images[0]->m_Dims[sliceAxis]-1 ); for ( int slice = 0; slice < images[0]->m_Dims[sliceAxis]-1; ++slice ) { means[slice] = slicePairStatistics[slice].GetAverage(); sdevs[slice] = sqrt( slicePairStatistics[slice].GetVariance() ); } for ( size_t i = 0; i < images.size(); ++i ) { if ( badSlicesThresh ) { size_t badSlices = 0; for ( int slice = 0; slice < images[0]->m_Dims[sliceAxis]-1; ++slice ) { if ( fabs( slicePairSamples[slice][i]-means[slice] ) / sdevs[slice] > standardDeviationsThreshold ) ++badSlices; } if ( badSlices >= badSlicesThresh ) { cmtk::StdOut << imagePaths[i] << "\t" << badSlices << "\n"; } } else { for ( int slice = 0; slice < images[0]->m_Dims[sliceAxis]-1; ++slice ) { if ( fabs( slicePairSamples[slice][i]-means[slice] ) / sdevs[slice] > standardDeviationsThreshold ) cmtk::StdOut << imagePaths[i] << "\t" << slice << "\n"; } } } return 0; } #include "cmtkSafeMain" cmtk-3.3.1/apps/jidb.cxx000066400000000000000000000327321276303427400150720ustar00rootroot00000000000000/* // // Copyright 1997-2010 Torsten Rohlfing // // Copyright 2004-2014 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 5129 $ // // $LastChangedDate: 2014-01-09 13:48:15 -0800 (Thu, 09 Jan 2014) $ // // $LastChangedBy: torstenrohlfing $ // */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include std::string InputFilePath; std::string OutputFilePath; int InterleaveAxis = -1; unsigned int NumberOfPasses = 2; int RegistrationMetric = 4; //MSD double InjectionKernelSigma = 1; int InjectionKernelRadius = 4; enum { DEBLURRING_BOX = 1, DEBLURRING_GAUSSIAN = 2 }; int DeblurringKernel = 0; cmtk::Types::Coordinate PointSpreadFunctionScale = 1.0; bool FourthOrderError = false; int NumberOfIterations = 20; bool RegionalIntensityTruncation = true; double ConstraintWeightLNorm = 0; std::string ReferenceImagePath; std::string InjectedImagePath; std::string ExportXformsPath; std::string ImportXformsPath; std::map PassWeights; bool WriteImagesAsFloat = false; void CallbackSetPassWeight( const char* argv ) { int pass = 0; float weight = 1.0; if ( 2 == sscanf( argv, "%4d:%10f", &pass, &weight ) ) { PassWeights[pass] = weight; } else { cmtk::StdErr << "ERROR: pass weights must be given as 'pass:weight', where 'pass' is an integer and 'weight' is a number between 0 and 1.\n" << " Parameter provided was '" << argv << "'\n"; throw cmtk::ExitException( 1 ); } } template cmtk::UniformVolume::SmartPtr GetReconstructedImage( cmtk::UniformVolume::SmartPtr& volume, cmtk::UniformVolume::SmartPtr& refImage, std::vector& xformsToPassImages ) { if ( InterleaveAxis < 0 ) InterleaveAxis = cmtk::VolumeInjectionReconstruction::GuessInterleaveAxis( volume ); cmtk::DeblurringVolumeReconstruction volRecon( volume, NumberOfPasses, InterleaveAxis, PointSpreadFunctionScale ); for ( std::map::const_iterator it = PassWeights.begin(); it != PassWeights.end(); ++it ) { volRecon.SetPassWeight( it->first, it->second ); } if ( refImage ) volRecon.SetReferenceImage( refImage ); volRecon.SetUseRegionalIntensityTruncation( RegionalIntensityTruncation ); volRecon.SetUseFourthOrderError( FourthOrderError ); volRecon.SetConstraintWeightLNorm( ConstraintWeightLNorm ); if ( xformsToPassImages.size() == NumberOfPasses ) { volRecon.SetTransformationsToPassImages( xformsToPassImages ); } else { cmtk::DebugOutput( 2 ) << "Computing transformations between passes...\n"; volRecon.ComputeTransformationsToPassImages( RegistrationMetric ); xformsToPassImages = volRecon.GetTransformationsToPassImages(); } if ( !ExportXformsPath.empty() ) { cmtk::ClassStreamOutput stream( ExportXformsPath, cmtk::ClassStreamOutput::MODE_WRITE ); if ( stream.IsValid() ) { cmtk::DebugOutput( 2 ) << "Exporting transformations between passes to " << ExportXformsPath << "\n"; for ( unsigned int pass = 0; pass < NumberOfPasses; ++pass ) { stream << dynamic_cast( *xformsToPassImages[pass] ); } } else { cmtk::StdErr << "ERROR: Could not open transformation file" << ExportXformsPath << "\n"; } } cmtk::DebugOutput( 2 ) << "Volume injection...\n"; try { volRecon.VolumeInjectionIsotropic( InjectionKernelSigma, InjectionKernelRadius ); } catch ( const cmtk::AffineXform::MatrixType::SingularMatrixException& ) { cmtk::StdErr << "ERROR: singular coordinate transformation matrix encountered in cmtk::DeblurringVolumeReconstruction::VolumeInjectionIsotropic\n"; throw cmtk::ExitException( 1 ); } if ( !InjectedImagePath.empty() ) { cmtk::UniformVolume::SmartPtr outputImage = volRecon.GetCorrectedImage(); if ( !WriteImagesAsFloat && outputImage->GetData()->GetType() != volume->GetData()->GetType() ) { outputImage = cmtk::UniformVolume::SmartPtr( outputImage->CloneGrid() ); outputImage->SetData( cmtk::TypedArray::SmartPtr( volRecon.GetCorrectedImage()->GetData()->Convert( volume->GetData()->GetType() ) ) ); } cmtk::VolumeIO::Write( *outputImage, InjectedImagePath ); } volRecon.Optimize( NumberOfIterations ); return volRecon.GetCorrectedImage(); } int doMain( const int argc, const char* argv[] ) { try { cmtk::CommandLine cl; cl.SetProgramInfo( cmtk::CommandLine::PRG_TITLE, "Fix interleaved motion using joint iterative deblurring" ); cl.SetProgramInfo( cmtk::CommandLine::PRG_DESCR, "This tool splits an interleaved input image into the pass images, co-registers them, and reconstructs a motion-corrected image" ); cl.SetProgramInfo( cmtk::CommandLine::PRG_SYNTX, "jidb [options] inImage outImage" ); typedef cmtk::CommandLine::Key Key; cl.BeginGroup( "interleave", "Interleaving Options" ); cmtk::CommandLine::EnumGroup::SmartPtr interleaveGroup = cl.AddEnum( "interleave-axis", &InterleaveAxis, "Define interleave axis: this is the through-slice direction of the acquisition." ); interleaveGroup->AddSwitch( Key( "guess-from-input" ), -1, "Guess from input image" ); interleaveGroup->AddSwitch( Key( 'a', "axial" ), (int)cmtk::AXIS_Z, "Interleaved axial images" ); interleaveGroup->AddSwitch( Key( 's', "sagittal" ),(int)cmtk::AXIS_X, "Interleaved sagittal images" ); interleaveGroup->AddSwitch( Key( 'c', "coronal" ), (int)cmtk::AXIS_Y, "Interleaved coronal images" ); interleaveGroup->AddSwitch( Key( 'x', "interleave-x" ), (int)cmtk::AXIS_X, "Interleaved along x axis" ); interleaveGroup->AddSwitch( Key( 'y', "interleave-y" ), (int)cmtk::AXIS_Y, "Interleaved along y axis" ); interleaveGroup->AddSwitch( Key( 'z', "interleave-z" ), (int)cmtk::AXIS_Z, "Interleaved along z axis" ); cl.AddOption( Key( 'p', "passes" ), &NumberOfPasses, "Number of interleaved passes" ); cl.AddCallback( Key( 'W', "pass-weight" ), CallbackSetPassWeight, "Set contribution weight for a pass in the form 'pass:weight'" ); cl.EndGroup(); cl.BeginGroup( "motion", "Motion Correction / Registration Options" ); cl.AddOption( Key( 'R', "reference-image" ), &ReferenceImagePath, "Use a separate high-resolution reference image for registration" )->SetProperties( cmtk::CommandLine::PROPS_IMAGE ); cmtk::CommandLine::EnumGroup::SmartPtr metricGroup = cl.AddEnum( "registration-metric", &RegistrationMetric, "Registration metric for motion estimation by image-to-image registration." ); metricGroup->AddSwitch( Key( "nmi" ), 0, "Use Normalized Mutual Information for pass-to-refereence registration" ); metricGroup->AddSwitch( Key( "mi" ), 1, "Use standard Mutual Information for pass-to-refereence registration" ); metricGroup->AddSwitch( Key( "cr" ), 2, "Use Correlation Ratio for pass-to-refereence registration" ); metricGroup->AddSwitch( Key( "msd" ), 4, "Use Mean Squared Differences for pass-to-refereence registration" ); metricGroup->AddSwitch( Key( "cc" ), 5, "Use Cross-Correlation for pass-to-refereence registration" ); cl.AddOption( Key( "import-xforms-path" ), &ImportXformsPath, "Path of file from which to import transformations between passes." ) ->SetProperties( cmtk::CommandLine::PROPS_FILENAME ); cl.AddOption( Key( "export-xforms-path" ), &ExportXformsPath, "Path of file to which to export transformations between passes." ) ->SetProperties( cmtk::CommandLine::PROPS_FILENAME | cmtk::CommandLine::PROPS_OUTPUT ); cl.BeginGroup( "inject", "Volume Injection Options" ); cl.AddOption( Key( 'S', "injection-kernel-sigma" ), &InjectionKernelSigma, "Standard deviation of Gaussian kernel for volume injection" ); cl.AddOption( Key( 'r', "injection-kernel-radius" ), &InjectionKernelRadius, "Truncation radius of injection kernel" ); cl.BeginGroup( "deblur", "Deblurring Options" ); cmtk::CommandLine::EnumGroup::SmartPtr psfGroup = cl.AddEnum( "psf", &DeblurringKernel, "Kernel for the inverse interpolation reconstruction" ); psfGroup->AddSwitch( Key( "box" ), (int)DEBLURRING_BOX, "Box-shaped PSF" ); psfGroup->AddSwitch( Key( "gaussian" ), (int)DEBLURRING_GAUSSIAN, "Gaussian-shaped PSF" ); cl.AddOption( Key( "psf-scale" ), &PointSpreadFunctionScale, "Set global scale factor for point spread function size, which itself is derived from the input image." ); cl.EndGroup(); cl.BeginGroup( "optimize", "Optimization Options" ); cl.AddSwitch( Key( 'f', "fourth-order-error" ), &FourthOrderError, true, "Use fourth-order (rather than second-order) error for optimization." ); cl.AddOption( Key( 'n', "num-iterations" ), &NumberOfIterations, "Maximum number of inverse interpolation iterations" ); cl.AddSwitch( Key( 'T', "no-truncation" ), &RegionalIntensityTruncation, false, "Turn off regional intensity truncatrion" ); cl.AddOption( Key( "l-norm-weight" ), &ConstraintWeightLNorm, "Set constraint weight for Tikhonov-type L-Norm regularization (0 disables constraint)" ); cl.EndGroup(); cl.BeginGroup( "output", "Output Options" ); cl.AddOption( Key( "write-injected-image" ), &InjectedImagePath, "Write initial volume injection image to path" )->SetProperties( cmtk::CommandLine::PROPS_IMAGE | cmtk::CommandLine::PROPS_OUTPUT );; cl.AddSwitch( Key( 'F', "write-images-as-float" ), &WriteImagesAsFloat, true, "Write output images as floating point [default: same as input]" ); cl.AddParameter( &InputFilePath, "InputImage", "Input image path" )->SetProperties( cmtk::CommandLine::PROPS_IMAGE ); cl.AddParameter( &OutputFilePath, "OutputImage", "Output image path" )->SetProperties( cmtk::CommandLine::PROPS_IMAGE | cmtk::CommandLine::PROPS_OUTPUT ); cl.Parse( argc, argv ); } catch ( const cmtk::CommandLine::Exception& e ) { cmtk::StdErr << e << "\n"; throw cmtk::ExitException( 1 ); } /* // Read input image */ cmtk::UniformVolume::SmartPtr volume( cmtk::VolumeIO::ReadOriented( InputFilePath ) ); if ( ! volume || ! volume->GetData() ) { cmtk::StdErr << "ERROR: Could not read image " << InputFilePath << "\n"; throw cmtk::ExitException( 1 ); } cmtk::UniformVolume::SmartPtr refImage; if ( !ReferenceImagePath.empty() ) { refImage = cmtk::UniformVolume::SmartPtr( cmtk::VolumeIO::ReadOriented( ReferenceImagePath ) ); if ( ! refImage || ! refImage->GetData() ) { cmtk::StdErr << "ERROR: Could not read image " << ReferenceImagePath << "\n"; throw cmtk::ExitException( 1 ); } } std::vector xformsToPassImages; if ( !ImportXformsPath.empty() ) { cmtk::ClassStreamInput stream( ImportXformsPath ); if ( stream.IsValid() ) { cmtk::DebugOutput( 1 ) << "Importing transformations between passes from " << ImportXformsPath << "\n"; cmtk::AffineXform xform; for ( unsigned int pass = 0; pass < NumberOfPasses; ++pass ) { try { stream >> xform; } catch ( const cmtk::Exception& ex ) { cmtk::StdErr << "ERROR: " << ex.what() << "\n"; throw cmtk::ExitException( 1 ); } xformsToPassImages.push_back( cmtk::Xform::SmartPtr( xform.Clone() ) ); } } else { cmtk::StdErr << "ERROR: Could not open transformation file" << ImportXformsPath << "\n"; throw cmtk::ExitException( 1 ); } } cmtk::UniformVolume::SmartPtr correctedVolume; switch ( DeblurringKernel ) { case DEBLURRING_BOX: default: correctedVolume = GetReconstructedImage( volume, refImage, xformsToPassImages ); break; case DEBLURRING_GAUSSIAN: correctedVolume = GetReconstructedImage( volume, refImage, xformsToPassImages ); break; } cmtk::UniformVolume::SmartPtr outputImage = correctedVolume; if ( !WriteImagesAsFloat && outputImage->GetData()->GetType() != volume->GetData()->GetType() ) { outputImage = cmtk::UniformVolume::SmartPtr( outputImage->CloneGrid() ); outputImage->SetData( cmtk::TypedArray::SmartPtr( correctedVolume->GetData()->Convert( volume->GetData()->GetType() ) ) ); } cmtk::VolumeIO::Write( *outputImage, OutputFilePath ); return 0; } #include "cmtkSafeMain" cmtk-3.3.1/apps/levelset.cxx000066400000000000000000000027461276303427400160070ustar00rootroot00000000000000/* // // Copyright 1997-2010 Torsten Rohlfing // // Copyright 2004-2011 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 2765 $ // // $LastChangedDate: 2011-01-18 16:17:56 -0800 (Tue, 18 Jan 2011) $ // // $LastChangedBy: torstenrohlfing $ // */ #include #include #include #include int doMain( const int argc, const char* argv[] ) { cmtk::SimpleLevelsetCommandLine levelset; const int init = levelset.Init( argc, argv ); if ( init ) return init; levelset.Execute(); return 0; } #include "cmtkSafeMain" cmtk-3.3.1/apps/levelset_cuda.cxx000066400000000000000000000031151276303427400167720ustar00rootroot00000000000000/* // // Copyright 1997-2010 Torsten Rohlfing // // Copyright 2004-2011 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 2765 $ // // $LastChangedDate: 2011-01-18 16:17:56 -0800 (Tue, 18 Jan 2011) $ // // $LastChangedBy: torstenrohlfing $ // */ #include #include #include #include int doMain( const int argc, const char* argv[] ) { cmtk::SimpleLevelsetCommandLine levelset; levelset.GetCommandLine().SetProgramInfo( cmtk::CommandLine::PRG_CATEG, "CMTK.Segmentation.GPU" ); const int init = levelset.Init( argc, argv ); if ( init ) return init; levelset.Execute(); return 0; } #include "cmtkSafeMain" cmtk-3.3.1/apps/lmsba.cxx000066400000000000000000000126451276303427400152610ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2013 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4834 $ // // $LastChangedDate: 2013-09-12 15:15:51 -0700 (Thu, 12 Sep 2013) $ // // $LastChangedBy: torstenrohlfing $ // */ #include #include #include #include #include #include #include #include int doMain ( const int argc, const char *argv[] ) { std::string targetImagePath; std::vector atlasImagesLabels; bool detectLocalOutliers = false; bool detectGlobalOutliers = false; size_t patchRadius = 5; size_t searchRadius = 0; std::string outputImagePath = "lsbo.nii"; cmtk::Types::DataItem paddingValue = 0; bool paddingFlag = false; try { cmtk::CommandLine cl; cl.SetProgramInfo( cmtk::CommandLine::PRG_TITLE, "Local voting." ); cl.SetProgramInfo( cmtk::CommandLine::PRG_DESCR, "This tool combines multiple multi-class segmentations from co-registered and reformatted atlases using locally-weighted Shape-Based Averaging." ); cl.SetProgramInfo( cmtk::CommandLine::PRG_SYNTX, "lsba [options] targetImage atlasIntensity1 atlasLabels1 [atlasIntensity2 atlasLabels2 [...]]" ); cl.AddParameter( &targetImagePath, "TargetImage", "Target image path. This is the image to be segmented." )->SetProperties( cmtk::CommandLine::PROPS_IMAGE ); cl.AddParameterVector( &atlasImagesLabels, "AtlasImagesLabels", "List of reformatted atlas intensity and label images. This must be an even number of paths, where the first path within each pair is the intensity channel of" "an atlas, and the second a label map channel of the same atlas, each reformatted into the space of the target image via an appropriate registration."); typedef cmtk::CommandLine::Key Key; cl.BeginGroup( "input", "Input Options" ); cl.AddOption( Key( "set-padding-value" ), &paddingValue, "Set padding value for input intensity images. Pixels with this value will be ignored.", &paddingFlag ); cl.EndGroup(); cl.BeginGroup( "combine", "Label Combination Options" ); cl.AddOption( Key( "patch-radius" ), &patchRadius, "Radius of image patch (in pixels) used for local similarity computation." ); cl.AddOption( Key( "search-radius" ), &searchRadius, "Search radius for local image patch matching. The algorithm finds the best-matching patch within this radius by exhaustive search." ); cl.AddSwitch( Key( "no-local-outliers" ), &detectLocalOutliers, true, "Detect and exclude local outliers in the Shape Based Averaging procedure." ); cl.AddSwitch( Key( "no-global-outliers" ), &detectGlobalOutliers, true, "Detect and exclude global outliers by removing poorly correlated atlases prior to local SBA procedure." ); cl.EndGroup(); cl.BeginGroup( "output", "Output Options" ); cl.AddOption( Key( 'o', "output" ), &outputImagePath, "File system path for the output image." ); cl.EndGroup(); cl.Parse( argc, argv ); if ( atlasImagesLabels.size() < 2 ) { throw cmtk::CommandLine::Exception( "List of atlas intensity and label images must have at least two entries (one image and one label map file)" ); } if ( atlasImagesLabels.size() % 2 ) { throw cmtk::CommandLine::Exception( "List of atlas intensity and label images must have an even number of entries (one image and one label map file per atlas)" ); } } catch ( const cmtk::CommandLine::Exception& e ) { cmtk::StdErr << e << "\n"; throw cmtk::ExitException( 1 ); } cmtk::UniformVolume::SmartPtr targetImage = cmtk::VolumeIO::Read( targetImagePath ); cmtk::LabelCombinationLocalShapeBasedAveraging lsba( targetImage ); lsba.SetPatchRadius( patchRadius ); lsba.SetSearchRadius( searchRadius ); lsba.SetDetectLocalOutliers( detectLocalOutliers ); for ( size_t atlasIdx = 0; atlasIdx < atlasImagesLabels.size(); atlasIdx += 2 ) { cmtk::UniformVolume::SmartPtr atlasImage = cmtk::VolumeIO::Read( atlasImagesLabels[atlasIdx] ); cmtk::UniformVolume::SmartPtr atlasLabels = cmtk::VolumeIO::Read( atlasImagesLabels[atlasIdx+1] ); if ( paddingFlag ) { atlasImage->GetData()->SetPaddingValue( paddingValue ); } lsba.AddAtlas( atlasImage, atlasLabels ); } if ( detectGlobalOutliers ) lsba.ExcludeGlobalOutliers(); targetImage->SetData( lsba.GetResult() ); if ( !outputImagePath.empty() ) { cmtk::VolumeIO::Write( *targetImage, outputImagePath ); } return 0; } #include "cmtkSafeMain" cmtk-3.3.1/apps/lsba.cxx000066400000000000000000000126541276303427400151040ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2013 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4834 $ // // $LastChangedDate: 2013-09-12 15:15:51 -0700 (Thu, 12 Sep 2013) $ // // $LastChangedBy: torstenrohlfing $ // */ #include #include #include #include #include #include #include #include int doMain ( const int argc, const char *argv[] ) { std::string targetImagePath; std::vector atlasImagesLabels; bool detectLocalOutliers = false; bool detectGlobalOutliers = false; size_t patchRadius = 5; size_t searchRadius = 0; std::string outputImagePath = "lsbo.nii"; cmtk::Types::DataItem paddingValue = 0; bool paddingFlag = false; try { cmtk::CommandLine cl; cl.SetProgramInfo( cmtk::CommandLine::PRG_TITLE, "Local voting." ); cl.SetProgramInfo( cmtk::CommandLine::PRG_DESCR, "This tool combines multiple binary segmentations from co-registered and reformatted atlases using locally-weighted Shape-Based Averaging." ); cl.SetProgramInfo( cmtk::CommandLine::PRG_SYNTX, "lsba [options] targetImage atlasIntensity1 atlasLabels1 [atlasIntensity2 atlasLabels2 [...]]" ); cl.AddParameter( &targetImagePath, "TargetImage", "Target image path. This is the image to be segmented." )->SetProperties( cmtk::CommandLine::PROPS_IMAGE ); cl.AddParameterVector( &atlasImagesLabels, "AtlasImagesLabels", "List of reformatted atlas intensity and label images. This must be an even number of paths, where the first path within each pair is the intensity channel of" "an atlas, and the second a label map channel of the same atlas, each reformatted into the space of the target image via an appropriate registration."); typedef cmtk::CommandLine::Key Key; cl.BeginGroup( "input", "Input Options" ); cl.AddOption( Key( "set-padding-value" ), &paddingValue, "Set padding value for input intensity images. Pixels with this value will be ignored.", &paddingFlag ); cl.EndGroup(); cl.BeginGroup( "combine", "Label Combination Options" ); cl.AddOption( Key( "patch-radius" ), &patchRadius, "Radius of image patch (in pixels) used for local similarity computation." ); cl.AddOption( Key( "search-radius" ), &searchRadius, "Search radius for local image patch matching. The algorithm finds the best-matching patch within this radius by exhaustive search." ); cl.AddSwitch( Key( "no-local-outliers" ), &detectLocalOutliers, true, "Detect and exclude local outliers in the Shape Based Averaging procedure." ); cl.AddSwitch( Key( "no-global-outliers" ), &detectGlobalOutliers, true, "Detect and exclude global outliers by removing poorly correlated atlases prior to local SBA procedure." ); cl.EndGroup(); cl.BeginGroup( "output", "Output Options" ); cl.AddOption( Key( 'o', "output" ), &outputImagePath, "File system path for the output image." ); cl.EndGroup(); cl.Parse( argc, argv ); if ( atlasImagesLabels.size() < 2 ) { throw cmtk::CommandLine::Exception( "List of atlas intensity and label images must have at least two entries (one image and one label map file)" ); } if ( atlasImagesLabels.size() % 2 ) { throw cmtk::CommandLine::Exception( "List of atlas intensity and label images must have an even number of entries (one image and one label map file per atlas)" ); } } catch ( const cmtk::CommandLine::Exception& e ) { cmtk::StdErr << e << "\n"; throw cmtk::ExitException( 1 ); } cmtk::UniformVolume::SmartPtr targetImage = cmtk::VolumeIO::Read( targetImagePath ); cmtk::LabelCombinationLocalBinaryShapeBasedAveraging lsba( targetImage ); lsba.SetPatchRadius( patchRadius ); lsba.SetSearchRadius( searchRadius ); lsba.SetDetectLocalOutliers( detectLocalOutliers ); for ( size_t atlasIdx = 0; atlasIdx < atlasImagesLabels.size(); atlasIdx += 2 ) { cmtk::UniformVolume::SmartPtr atlasImage = cmtk::VolumeIO::Read( atlasImagesLabels[atlasIdx] ); cmtk::UniformVolume::SmartPtr atlasLabels = cmtk::VolumeIO::Read( atlasImagesLabels[atlasIdx+1] ); if ( paddingFlag ) { atlasImage->GetData()->SetPaddingValue( paddingValue ); } lsba.AddAtlas( atlasImage, atlasLabels ); } if ( detectGlobalOutliers ) lsba.ExcludeGlobalOutliers(); targetImage->SetData( lsba.GetResult() ); if ( !outputImagePath.empty() ) { cmtk::VolumeIO::Write( *targetImage, outputImagePath ); } return 0; } #include "cmtkSafeMain" cmtk-3.3.1/apps/lvote.cxx000066400000000000000000000127761276303427400153210ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2013 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4834 $ // // $LastChangedDate: 2013-09-12 15:15:51 -0700 (Thu, 12 Sep 2013) $ // // $LastChangedBy: torstenrohlfing $ // */ #include #include #include #include #include #include #include #include int doMain ( const int argc, const char *argv[] ) { std::string targetImagePath; std::vector atlasImagesLabels; bool detectGlobalOutliers = false; size_t patchRadius = 5; bool useGlobalWeights = false; std::string outputImagePath = "lvote.nii"; cmtk::Types::DataItem paddingValue = 0; bool paddingFlag = false; try { cmtk::CommandLine cl; cl.SetProgramInfo( cmtk::CommandLine::PRG_TITLE, "Local voting." ); cl.SetProgramInfo( cmtk::CommandLine::PRG_DESCR, "This tool combines multiple segmentations fro co-registered and reformatted atlases using locally-weighted voting." ); cl.SetProgramInfo( cmtk::CommandLine::PRG_SYNTX, "lvote [options] targetImage atlasIntensity1 atlasLabels1 [atlasIntensity2 atlasLabels2 [...]]" ); cl.AddParameter( &targetImagePath, "TargetImage", "Target image path. This is the image to be segmented." )->SetProperties( cmtk::CommandLine::PROPS_IMAGE ); cl.AddParameterVector( &atlasImagesLabels, "AtlasImagesLabels", "List of reformatted atlas intensity and label images. This must be an even number of paths, where the first path within each pair is the intensity channel of" "an atlas, and the second a label map channel of the same atlas, each reformatted into the space of the target image via an appropriate registration."); typedef cmtk::CommandLine::Key Key; cl.BeginGroup( "input", "Input Options" ); cl.AddOption( Key( "set-padding-value" ), &paddingValue, "Set padding value for input intensity images. Pixels with this value will be ignored.", &paddingFlag ); cl.EndGroup(); cl.BeginGroup( "combine", "Label Combination Options" ); cl.AddOption( Key( "patch-radius" ), &patchRadius, "Radius of image patch (in pixels) used for local similarity computation." ); cl.AddSwitch( Key( "use-global-weights" ), &useGlobalWeights, true, "Use global weights per atlas to normalize the patch-pased local similarity scores." ); cl.AddSwitch( Key( "no-global-outliers" ), &detectGlobalOutliers, true, "Detect and exclude global outliers by removing poorly correlated atlases prior to local voting procedure." ); cl.EndGroup(); cl.BeginGroup( "output", "Output Options" ); cl.AddOption( Key( 'o', "output" ), &outputImagePath, "File system path for the output image." ); cl.EndGroup(); cl.Parse( argc, argv ); if ( atlasImagesLabels.size() < 2 ) { throw cmtk::CommandLine::Exception( "List of atlas intensity and label images must have at least two entries (one image and one label map file)" ); } if ( atlasImagesLabels.size() % 2 ) { throw cmtk::CommandLine::Exception( "List of atlas intensity and label images must have an even number of entries (one image and one label map file per atlas)" ); } } catch ( const cmtk::CommandLine::Exception& e ) { cmtk::StdErr << e << "\n"; throw cmtk::ExitException( 1 ); } cmtk::UniformVolume::SmartPtr targetImage = cmtk::VolumeIO::Read( targetImagePath ); cmtk::LabelCombinationLocalVoting lvote( targetImage ); lvote.SetPatchRadius( patchRadius ); lvote.SetUseGlobalAtlasWeights( useGlobalWeights ); for ( size_t atlasIdx = 0; atlasIdx < atlasImagesLabels.size(); atlasIdx += 2 ) { cmtk::UniformVolume::SmartPtr atlasImage = cmtk::VolumeIO::Read( atlasImagesLabels[atlasIdx] ); if ( !atlasImage ) { cmtk::StdErr << "ERROR: could not read atlas intensity image " << atlasImagesLabels[atlasIdx] << "\n"; throw cmtk::ExitException( 1 ); } cmtk::UniformVolume::SmartPtr atlasLabels = cmtk::VolumeIO::Read( atlasImagesLabels[atlasIdx+1] ); if ( !atlasLabels ) { cmtk::StdErr << "ERROR: could not read atlas label image " << atlasImagesLabels[atlasIdx+1] << "\n"; throw cmtk::ExitException( 1 ); } if ( paddingFlag ) { atlasImage->GetData()->SetPaddingValue( paddingValue ); } lvote.AddAtlas( atlasImage, atlasLabels ); } if ( detectGlobalOutliers ) lvote.ExcludeGlobalOutliers(); targetImage->SetData( lvote.GetResult() ); if ( !outputImagePath.empty() ) { cmtk::VolumeIO::Write( *targetImage, outputImagePath ); } return 0; } #include "cmtkSafeMain" cmtk-3.3.1/apps/make_initial_affine.cxx000066400000000000000000000157361276303427400201250ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2011, 2013-2014 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 5129 $ // // $LastChangedDate: 2014-01-09 13:48:15 -0800 (Thu, 09 Jan 2014) $ // // $LastChangedBy: torstenrohlfing $ // */ #include #include #include #include #include #include #include #include #include #include #include #ifdef CMTK_USE_SQLITE # include #endif #include int doMain( const int argc, const char* argv[] ) { std::string referenceImagePath; std::string floatingImagePath; std::string outputXformPath; bool centerXform = false; bool writeXformNativeSpaces = false; if ( getenv( "Slicer3_HOME" ) != NULL ) { writeXformNativeSpaces = true; } int mode = 0; #ifdef CMTK_USE_SQLITE std::string updateDB; #endif try { cmtk::CommandLine cl( cmtk::CommandLine::PROPS_XML ); cl.SetProgramInfo( cmtk::CommandLine::PRG_TITLE, "Initialize affine transformation" ); cl.SetProgramInfo( cmtk::CommandLine::PRG_DESCR, "Compute initial affine transformation by aligning centers of mass or principal axes" ); cl.SetProgramInfo( cmtk::CommandLine::PRG_CATEG, "CMTK.Registration" ); typedef cmtk::CommandLine::Key Key; cl.BeginGroup( "Transformation", "Transformation construction control" ); cmtk::CommandLine::EnumGroup::SmartPtr modeGroup = cl.AddEnum( "mode", &mode, "Mode selection for initialization" ); modeGroup->AddSwitch( Key( "direction-vectors" ), 0, "Alignment based on image direction vectors" ); modeGroup->AddSwitch( Key( "centers-of-mass" ), 1, "Alignment based on centers of mass (translation only)" ); modeGroup->AddSwitch( Key( "principal-axes" ), 2, "Alignment based on principal axes" ); modeGroup->AddSwitch( Key( "identity" ), -1, "Create only an identity transformation" ); cl.AddSwitch( Key( 'C', "center-xform" ), ¢erXform, true, "Set transformation center (for rotation, scale) to center of reference image." ); cl.AddSwitch( Key( "native-space" ), &writeXformNativeSpaces, true, "Write transformation between the native image spaces, rather than in CMTK standard RAS space. This is the default when running this tool as a Slicer plugin." ) ->SetProperties( cmtk::CommandLine::PROPS_NOXML );; cl.EndGroup(); #ifdef CMTK_USE_SQLITE cl.BeginGroup( "Database", "Image/Transformation Database" ); cl.AddOption( Key( "db" ), &updateDB, "Path to image/transformation database that should be updated with the newly transformation from reference to floating image." ); cl.EndGroup(); #endif cl.AddParameter( &referenceImagePath, "ReferenceImage", "Reference (fixed) image path" )->SetProperties( cmtk::CommandLine::PROPS_IMAGE ); cl.AddParameter( &floatingImagePath, "FloatingImage", "Floating (moving) image path" )->SetProperties( cmtk::CommandLine::PROPS_IMAGE ); cl.AddParameter( &outputXformPath, "OutputXform", "Output transformation path" ) ->SetProperties( cmtk::CommandLine::PROPS_XFORM | cmtk::CommandLine::PROPS_OUTPUT ) ->SetAttribute( "reference", "FloatingImage" ); cl.Parse( argc, argv ); } catch ( const cmtk::CommandLine::Exception& ex ) { cmtk::StdErr << ex << "\n"; return 1; } cmtk::UniformVolume::SmartPtr referenceImage( cmtk::VolumeIO::ReadOriented( referenceImagePath ) ); if ( ! referenceImage ) { cmtk::StdErr << "ERROR: could not read image " << referenceImagePath << "\n"; throw cmtk::ExitException( 1 ); } cmtk::UniformVolume::SmartPtr floatingImage( cmtk::VolumeIO::ReadOriented( floatingImagePath ) ); if ( ! floatingImage ) { cmtk::StdErr << "ERROR: could not read image " << floatingImagePath << "\n"; throw cmtk::ExitException( 1 ); } cmtk::AffineXform::SmartPtr xform; switch ( mode ) { case -1: xform = cmtk::AffineXform::SmartPtr( new cmtk::AffineXform ); break; case 0: { try { xform = cmtk::AffineXform::SmartPtr( cmtk::MakeInitialAffineTransformation::AlignDirectionVectors( *referenceImage, *floatingImage, centerXform ) ); } catch ( const cmtk::AffineXform::MatrixType::SingularMatrixException& ) { cmtk::StdErr << "ERROR: singular matrix encountered in call to cmtk::MakeInitialAffineTransformation::AlignDirectionVectors()\n"; throw cmtk::ExitException( 1 ); } } break; case 1: xform = cmtk::AffineXform::SmartPtr( cmtk::MakeInitialAffineTransformation::AlignCentersOfMass( *referenceImage, *floatingImage ) ); break; case 2: { try { xform = cmtk::AffineXform::SmartPtr( cmtk::MakeInitialAffineTransformation::AlignPrincipalAxes( *referenceImage, *floatingImage ) ); } catch ( const cmtk::AffineXform::MatrixType::SingularMatrixException& ) { cmtk::StdErr << "ERROR: singular matrix encountered in call to cmtk::MakeInitialAffineTransformation::AlignPrincipalAxes()\n"; throw cmtk::ExitException( 1 ); } } break; } if ( xform ) { if ( writeXformNativeSpaces ) { cmtk::TransformChangeToSpaceAffine toNative( *xform, *referenceImage, *floatingImage ); cmtk::XformIO::Write( &toNative.GetTransformation(), outputXformPath ); } else { cmtk::XformIO::Write( xform, outputXformPath ); } #ifdef CMTK_USE_SQLITE if ( !updateDB.empty() ) { try { cmtk::ImageXformDB db( updateDB ); db.AddImagePairXform( outputXformPath, true /*always affine*/, referenceImagePath, floatingImagePath ); } catch ( const cmtk::SQLite::Exception& ex ) { cmtk::StdErr << "ERROR adding transformation to database: " << ex.what() << "\n"; } } #endif } else { cmtk::StdErr << "ERROR: at least one of the two images does not have a grid-to-physical space coordinate transformation.\n"; throw cmtk::ExitException( 1 ); } return 0; } #include "cmtkSafeMain" cmtk-3.3.1/apps/mat2dof.cxx000066400000000000000000000201221276303427400155040ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2014 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 5293 $ // // $LastChangedDate: 2014-04-07 10:51:58 -0700 (Mon, 07 Apr 2014) $ // // $LastChangedBy: torstenrohlfing $ // */ #include #include #include #include #include #include #include #include #include #include #include #include #include void ReadMatrix( cmtk::Types::Coordinate (&matrix)[4][4], std::istream& stream, const bool matrix3x3 ) { memset( matrix, 0 , sizeof( matrix ) ); if ( matrix3x3 ) { for ( int j = 0; j < 3; ++j ) { for ( int i = 0; i < 3; ++i ) { stream >> matrix[j][i]; } } matrix[3][3] = 1.0; } else { for ( int j = 0; j < 4; ++j ) { for ( int i = 0; i < 4; ++i ) { stream >> matrix[j][i]; } } } } int doMain( const int argc, const char* argv[] ) { const char* CenterStr = NULL; const char* OffsetStr = NULL; const char* TranslateStr = NULL; const char* PixelSizeStr = NULL; const char* OutList = NULL; const char* OutXform = NULL; bool AppendToOutput = false; bool Matrix3x3 = false; bool Transpose = false; bool Inverse = false; const char* InputFileName = NULL; try { cmtk::CommandLine cl; cl.SetProgramInfo( cmtk::CommandLine::PRG_TITLE, "Matrix to degrees of freedom" ); cl.SetProgramInfo( cmtk::CommandLine::PRG_DESCR, "Convert transformation matrix to degrees of freedom" ); cl.SetProgramInfo( cmtk::CommandLine::PRG_SYNTX, "mat2dof [options] < matrix" ); typedef cmtk::CommandLine::Key Key; cl.BeginGroup( "Input", "Input Options" ); cl.AddSwitch( Key( '3', "matrix3x3" ), &Matrix3x3, true, "Only input upper left 3x3 submatrix." ); cl.AddSwitch( Key( 't', "transpose" ), &Transpose, true, "Transpose input matrix." ); cl.AddOption( Key( "pixel-size" ), &PixelSizeStr, "For matrices in pixel space (i.e., mapping from grid index to physical coordinate), set image grid pixel size." ); cl.EndGroup(); cl.BeginGroup( "Modifiers", "Transformation Modifiers" ); cl.AddOption( Key( "center" ), &CenterStr, "Set center x,y,z for rotation and scaling." ); cl.AddOption( Key( "offset" ), &OffsetStr, "Set offset dx,dy,dz for translation." ); cl.AddOption( Key( "xlate" ), &TranslateStr, "Translate result relative by given vector." ); cl.AddSwitch( Key( "inverse" ), &Inverse, true, "Output inverse transformation. Inversion happens after all other modifiers have been applied." ); cl.EndGroup(); cl.BeginGroup( "Output", "Output Options" ); cl.AddOption( Key( 'o', "output" ), &OutXform, "Write output transformation to this file" ); cl.AddOption( Key( "list" ), &OutList, "Write output in list format to this archive (directory including 'registration' and 'studylist' files)." ); cl.AddSwitch( Key( "append" ), &AppendToOutput, true, "Append to output file rather than create a new one and overwrite exisiting files." ); cl.EndGroup(); cl.Parse( argc, argv ); InputFileName = cl.GetNextOptional(); } catch ( const cmtk::CommandLine::Exception& e ) { cmtk::StdErr << e; throw cmtk::ExitException( 1 ); } cmtk::Types::Coordinate matrix[4][4]; if ( InputFileName ) { std::ifstream stream( InputFileName ); if ( stream.good() ) { ReadMatrix( matrix, stream, Matrix3x3 ); } else { cmtk::StdErr << "ERROR: cannot open input file " << InputFileName << "\n"; } } else { ReadMatrix( matrix, std::cin, Matrix3x3 ); } if ( Transpose ) { for ( int row = 0; row < 4; ++row ) for ( int col = 0; col < row; ++col ) { const cmtk::Types::Coordinate tmp = matrix[col][row]; matrix[col][row] = matrix[row][col]; matrix[row][col] = tmp; } } if ( PixelSizeStr ) { float pixel[4] = { 1.0, 1.0, 1.0, 1.0 }; if ( 3 == sscanf( PixelSizeStr, "%15f,%15f,%15f", pixel+0, pixel+1, pixel+2 ) ) { for ( int row = 0; row < 4; ++row ) for ( int col = 0; col < 4; ++col ) matrix[col][row] /= pixel[row]; } } // Create transformation from imported matrix cmtk::AffineXform::SmartPtr xform; try { xform = cmtk::AffineXform::SmartPtr( new cmtk::AffineXform( matrix ) ); } catch ( const cmtk::AffineXform::MatrixType::SingularMatrixException& ) { cmtk::StdErr << "ERROR: singular matrix encountered in cmtk::AffineXform constructor.\n"; throw cmtk::ExitException( 1 ); } if ( CenterStr ) { float center[3]; if ( 3 == sscanf( CenterStr, "%15f,%15f,%15f", center+0, center+1, center+2 ) ) { xform->ChangeCenter( cmtk::FixedVector<3,cmtk::Types::Coordinate>::FromPointer( center ) ); } } if ( OffsetStr ) { float offset[3]; if ( 3 == sscanf( OffsetStr, "%15f,%15f,%15f", offset+0, offset+1, offset+2 ) ) { xform->Translate( xform->RotateScaleShear( cmtk::FixedVector<3,cmtk::Types::Coordinate>::FromPointer( offset ) ) ); } } if ( TranslateStr ) { float xlate[3]; if ( 3 == sscanf( TranslateStr, "%15f,%15f,%15f", xlate+0, xlate+1, xlate+2 ) ) { xform->Translate( cmtk::FixedVector<3,cmtk::Types::Coordinate>::FromPointer( xlate ) ); } } // Invert transformation if ( Inverse ) { try { xform = xform->GetInverse(); } catch ( const cmtk::AffineXform::MatrixType::SingularMatrixException& ) { cmtk::StdErr << "ERROR: affine transformation with singular matrix cannot be inverted\n"; throw cmtk::ExitException( 1 ); } } // Are we writing to a list archive? if ( OutList ) { if ( AppendToOutput ) { cmtk::ClassStreamOutput outStream( OutList, "registration", cmtk::ClassStreamOutput::MODE_APPEND ); outStream << *xform; } else { cmtk::StudyList studyList; const char* strReference = "reference"; const char* strFloating = "floating"; studyList.AddStudy( strReference ); studyList.AddStudy( strFloating ); studyList.AddXform( strReference, strFloating, xform ); cmtk::ClassStreamStudyList::Write( OutList, &studyList ); } } // Are we writing to a single file? if ( OutXform ) { cmtk::ClassStreamOutput outStream( OutXform, AppendToOutput ? cmtk::ClassStreamOutput::MODE_APPEND : cmtk::ClassStreamOutput::MODE_WRITE ); outStream << *xform; } // No output files given: just dump parameters to console if ( !OutList && !OutXform ) { cmtk::CoordinateVector v; if ( Inverse ) { try { xform->GetInverse()->GetParamVector( v ); } catch ( const cmtk::AffineXform::MatrixType::SingularMatrixException& ) { cmtk::StdErr << "ERROR: affine transformation with singular matrix cannot be inverted\n"; throw cmtk::ExitException( 1 ); } } else { xform->GetParamVector( v ); } for ( unsigned int idx = 0; idx < v.Dim; ++idx ) std::cout << "#" << idx << "\t" << v.Elements[idx] << "\n"; } return 0; } #include "cmtkSafeMain" cmtk-3.3.1/apps/mcaffine.cxx000066400000000000000000000323231276303427400157260ustar00rootroot00000000000000/* // // Copyright 1997-2010 Torsten Rohlfing // // Copyright 2004-2013 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4885 $ // // $LastChangedDate: 2013-09-27 21:04:59 -0700 (Fri, 27 Sep 2013) $ // // $LastChangedBy: torsten_at_home $ // */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if defined(HAVE_STDINT_H) # include #else typedef long int uint64_t; #endif std::list fileListRef; std::list fileListFlt; const char* initialXformPath = NULL; const char* outArchive = NULL; std::list refChannelList; std::list fltChannelList; std::vector numberDOFs; cmtk::Types::Coordinate initialStepSize = 1.0; cmtk::Types::Coordinate finalStepSize = 0.125; cmtk::Optimizer::ReturnType optimizerDeltaFThreshold = 0; bool alignCenters = true; bool metricNMI = true; bool useHistograms = true; bool useCubicInterpolation = false; int downsampleFrom = 1; int downsampleTo = 1; bool downsampleWithAverage = false; float smoothSigmaFactor = 0.0; cmtk::Types::Coordinate minPixelSize = FLT_MAX; const char* cropReferenceFromIndex = NULL; const char* cropReferenceToIndex = NULL; cmtk::UniformVolume::SmartPtr MakeDownsampled( cmtk::UniformVolume::SmartConstPtr& image, const int downsample ) { if ( downsampleWithAverage ) return cmtk::UniformVolume::SmartPtr( image->GetDownsampled( downsample * image->GetMinDelta() ) ); cmtk::UniformVolume::SmartPtr result( image->CloneGrid() ); if ( (smoothSigmaFactor > 0) && downsample ) { const cmtk::Units::GaussianSigma sigma( smoothSigmaFactor * downsample * image->GetMinDelta() ); result->SetData( cmtk::UniformVolumeGaussianFilter( image ).GetFiltered3D( sigma ) ); } else { result->SetData( image->GetData() ); } if ( downsample > 1 ) result = cmtk::UniformVolume::SmartPtr( result->GetDownsampledAndAveraged( downsample, true /*approxIsotropic*/ ) ); return result; } template void DoRegistration() { typedef cmtk::AffineMultiChannelRegistrationFunctional FunctionalType; typename FunctionalType::SmartPtr functional( new FunctionalType ); functional->SetNormalizedMI( metricNMI ); // determine initial transformation parameters based on bounding boxes. cmtk::CoordinateVector params; if ( initialXformPath ) { cmtk::Xform::SmartPtr xform( cmtk::XformIO::Read( initialXformPath ) ); if ( xform ) { const cmtk::AffineXform::SmartPtr affine = cmtk::AffineXform::SmartPtr::DynamicCastFrom( xform ); if ( affine ) { affine->GetParamVector( params ); } else { const cmtk::WarpXform::SmartPtr warp = cmtk::WarpXform::SmartPtr::DynamicCastFrom( xform ); if ( warp ) { warp->GetInitialAffineXform()->GetParamVector( params ); } } functional->SetParamVector( params ); } else { cmtk::StdErr << "ERROR: unable to read initial transformation from " << initialXformPath << "\n"; throw cmtk::ExitException( 2 ); } } else { functional->AddReferenceChannel( *(refChannelList.begin()) ); functional->AddFloatingChannel( *(fltChannelList.begin()) ); functional->InitTransformation( alignCenters ); functional->GetParamVector( params ); } cmtk::BestNeighbourOptimizer optimizer; optimizer.SetDeltaFThreshold( optimizerDeltaFThreshold ); optimizer.SetCallback( cmtk::RegistrationCallback::SmartPtr( new cmtk::RegistrationCallback ) ); optimizer.SetFunctional( functional ); for ( int downsample = std::max(downsampleFrom, downsampleTo); downsample >= std::min(downsampleFrom, downsampleTo); --downsample ) { cmtk::DebugOutput( 1 ).GetStream().printf( "Downsampling stage 1:%d\n", downsample ); functional->ClearAllChannels(); for ( std::list::iterator it = refChannelList.begin(); it != refChannelList.end(); ++it ) { cmtk::UniformVolume::SmartPtr image = MakeDownsampled( (*it), downsample ); image->CopyMetaInfo( **it, cmtk::META_FS_PATH ); functional->AddReferenceChannel( image ); } for ( std::list::iterator it = fltChannelList.begin(); it != fltChannelList.end(); ++it ) { cmtk::UniformVolume::SmartPtr image = MakeDownsampled( (*it), downsample ); image->CopyMetaInfo( **it, cmtk::META_FS_PATH ); functional->AddFloatingChannel( image ); } for ( std::vector::const_iterator itDOF = numberDOFs.begin(); itDOF != numberDOFs.end(); ++itDOF ) { cmtk::DebugOutput( 1 ).GetStream().printf( "Setting number of DOFs to %d\n", *itDOF ); functional->SetNumberDOFs( *itDOF ); const cmtk::Types::Coordinate effectiveMinPixelSize = std::max( 1, downsample ) * minPixelSize; optimizer.Optimize( params, initialStepSize * effectiveMinPixelSize, finalStepSize * effectiveMinPixelSize ); } } if ( outArchive ) { cmtk::ClassStreamOutput stream( outArchive, cmtk::ClassStreamOutput::MODE_WRITE ); if ( stream.IsValid() ) { stream << *functional; stream.Close(); } else { cmtk::StdErr << "ERROR: could not open archive " << outArchive << " for writing.\n"; } } } int doMain( const int argc, const char* argv[] ) { try { cmtk::CommandLine cl; cl.SetProgramInfo( cmtk::CommandLine::PRG_TITLE, "Multi-channel affine registration" ); cl.SetProgramInfo( cmtk::CommandLine::PRG_DESCR, "Multi-channel affine image registration using histogram-based or covariance-based joint entropy measures" ); cl.SetProgramInfo( cmtk::CommandLine::PRG_SYNTX, "mcaffine [options] refChannel0 [refChannel1 ...] -- fltChannel0 [fltChannel1 ...]" ); cl.SetProgramInfo( cmtk::CommandLine::PRG_CATEG, "CMTK.Image Registration" ); typedef cmtk::CommandLine::Key Key; cl.AddOption( Key( 'o', "out-archive" ), &outArchive, "Output archive path." ); cl.AddOption( Key( "initial-xform" ), &initialXformPath, "Optional path of a file with the initial transformation." ); cl.AddOption( Key( 'd', "downsample-from" ), &downsampleFrom, "Initial downsampling factor [1]." ); cl.AddOption( Key( 'D', "downsample-to" ), &downsampleTo, "Final downsampling factor [1]. Factor 0 is full resolution with smoothing turned off" ); cl.AddOption( Key( "smooth" ), &smoothSigmaFactor, "Sigma of Gaussian smoothing kernel in multiples of template image pixel size [default: off] )" ); cl.AddOption( Key( "downsample-average" ), &downsampleWithAverage, "Downsample using sliding-window averaging [default: off] )" ); cl.AddVector( Key( "dofs" ), numberDOFs, "Set sequence of numbers of DOFs for optimization schedule [can be repeated]. Supported values are: 0, 3, 6, 7, 9, 12." ); cl.AddSwitch( Key( "nmi" ), &metricNMI, true, "Use normalized mutual information metric [default]" ); cl.AddSwitch( Key( "mi" ), &metricNMI, false, "Use standard mutual information metric" ); cl.AddSwitch( Key( 'H', "histograms" ), &useHistograms, true, "Use multi-dimensional histograms to compute entropies [default]" ); cl.AddSwitch( Key( 'C', "covariance" ), &useHistograms, false, "Use covariance matrix determinants to compute entropies" ); cl.AddSwitch( Key( 'c', "cubic" ), &useCubicInterpolation, true, "Use cubic interpolation [default: linear]" ); cl.AddOption( Key( "initial-step-size" ), &initialStepSize, "Initial optimizer step size in pixels." ); cl.AddOption( Key( "final-step-size" ), &finalStepSize, "Initial optimizer step size in pixels." ); cl.AddOption( Key( "delta-f-threshold" ), &optimizerDeltaFThreshold, "Optional threshold to terminate optimization (level) if relative change of target function drops below this value." ); cl.AddOption( Key( "crop-reference-from-index" ), &cropReferenceFromIndex, "Crop reference image from index x,y,z." ); cl.AddOption( Key( "crop-reference-to-index" ), &cropReferenceToIndex, "Crop reference image to index x,y,z." ); cl.Parse( argc, argv ); const char* next = cl.GetNext(); while ( next && strcmp( next, "--" ) ) { fileListRef.push_back( next ); next = cl.GetNextOptional(); } next = cl.GetNext(); while ( next ) { fileListFlt.push_back( next ); next = cl.GetNextOptional(); } } catch ( const cmtk::CommandLine::Exception& e ) { cmtk::StdErr << e << "\n"; throw cmtk::ExitException( 1 ); } if ( numberDOFs.empty() ) numberDOFs.push_back( 6 ); for ( std::list::const_iterator refIt = fileListRef.begin(); refIt != fileListRef.end(); ++refIt ) { cmtk::UniformVolume::SmartPtr volume( cmtk::VolumeIO::ReadOriented( *refIt ) ); if ( !volume || !volume->GetData() ) { cmtk::StdErr << "ERROR: Cannot read image " << *refIt << "\n"; throw cmtk::ExitException( 1 ); } minPixelSize = std::min( volume->GetMinDelta(), minPixelSize ); refChannelList.push_back( volume ); } if ( cropReferenceFromIndex ) { int xyz[3]; if ( 3 != sscanf( cropReferenceFromIndex, "%6d,%6d,%6d", &xyz[0], &xyz[1], &xyz[2] ) ) { cmtk::StdErr << "ERROR: reference crop from index could not parse index '" << cropReferenceFromIndex << "' as valid x,y,z index.\n"; throw cmtk::ExitException( 1 ); } for ( std::list::iterator refIt = refChannelList.begin(); refIt != refChannelList.end(); ++refIt ) { (*refIt)->CropRegion() = cmtk::DataGrid::RegionType( cmtk::DataGrid::IndexType::FromPointer( xyz ), (*refIt)->CropRegion().To() ); } } if ( cropReferenceToIndex ) { int xyz[3]; if ( 3 != sscanf( cropReferenceToIndex, "%6d,%6d,%6d", &xyz[0], &xyz[1], &xyz[2] ) ) { cmtk::StdErr << "ERROR: reference crop to index could not parse index '" << cropReferenceToIndex << "' as valid x,y,z index.\n"; throw cmtk::ExitException( 1 ); } for ( std::list::iterator refIt = refChannelList.begin(); refIt != refChannelList.end(); ++refIt ) { (*refIt)->CropRegion() = cmtk::DataGrid::RegionType( (*refIt)->CropRegion().From(), cmtk::DataGrid::IndexType::FromPointer( xyz ) ); } } for ( std::list::const_iterator fltIt = fileListFlt.begin(); fltIt != fileListFlt.end(); ++fltIt ) { cmtk::UniformVolume::SmartPtr volume( cmtk::VolumeIO::ReadOriented( *fltIt ) ); if ( !volume || !volume->GetData() ) { cmtk::StdErr << "ERROR: Cannot read image " << *fltIt << "\n"; throw cmtk::ExitException( 1 ); } minPixelSize = std::min( volume->GetMinDelta(), minPixelSize ); fltChannelList.push_back( volume ); } if ( useCubicInterpolation ) { typedef cmtk::UniformVolumeInterpolator InterpolatorType; if ( useHistograms ) { typedef cmtk::MultiChannelHistogramRegistrationFunctional MetricFunctionalType; DoRegistration(); } else { typedef cmtk::MultiChannelRMIRegistrationFunctional MetricFunctionalType; DoRegistration(); } } else { typedef cmtk::UniformVolumeInterpolator InterpolatorType; if ( useHistograms ) { typedef cmtk::MultiChannelHistogramRegistrationFunctional MetricFunctionalType; DoRegistration(); } else { typedef cmtk::MultiChannelRMIRegistrationFunctional MetricFunctionalType; DoRegistration(); } } return 0; } #include "cmtkSafeMain" cmtk-3.3.1/apps/mcmrbias.cxx000066400000000000000000000317261276303427400157610ustar00rootroot00000000000000/* // // Copyright 1997-2012 Torsten Rohlfing // // Copyright 2004-2013 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 3977 $ // // $LastChangedDate: 2012-03-06 15:30:28 -0800 (Tue, 06 Mar 2012) $ // // $LastChangedBy: torstenrohlfing $ // */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef CMTK_USE_SQLITE # include #endif const char* ImportBiasFieldAdd = NULL; const char* ImportBiasFieldMul = NULL; const char* FNameBiasFieldAdd = NULL; const char* FNameBiasFieldMul = NULL; cmtk::Types::DataItem PaddingValue = 0; bool PaddingFlag = false; float ThresholdForegroundMin = -FLT_MAX; float ThresholdForegroundMax = FLT_MAX; bool ThresholdForegroundFlag = false; bool ThresholdAuto = false; const char* FNameMaskImage = NULL; bool UseSamplingDensity = false; float SamplingDensity = 1.0; unsigned int NumberOfHistogramBins = 256; const char* FNameInputImage = NULL; const char* FNameOutputImage = NULL; bool OutputFloatImage = false; int PolynomialDegreeAdd = 0; int PolynomialDegreeMul = 2; bool IncrementalPolynomials = false; cmtk::Types::Coordinate StepMax = 1.0; cmtk::Types::Coordinate StepMin = 0.1; bool LogIntensities = false; #ifdef CMTK_USE_SQLITE const char* updateDB = NULL; #endif int doMain( const int argc, const char *argv[] ) { try { cmtk::CommandLine cl( cmtk::CommandLine::PROPS_XML ); cl.SetProgramInfo( cmtk::CommandLine::PRG_TITLE, "Multi-Channel MR Image Intensity Bias Field Correction" ); cl.SetProgramInfo( cmtk::CommandLine::PRG_DESCR, "This program corrects intensity inhomogeneity artifacts in multiple-channel MR images simultaneously using a bias field estimated via joint and marginal entropy minimization." ); cl.SetProgramInfo( cmtk::CommandLine::PRG_CATEG, "CMTK.Artifact Correction" ); typedef cmtk::CommandLine::Key Key; cl.BeginGroup( "Bias Field", "Bias Field Parameterization" ); cl.AddOption( Key( 'A', "degree-add" ), &PolynomialDegreeAdd, "Polynomial degree for additive correction." ); cl.AddOption( Key( 'M', "degree-mul" ), &PolynomialDegreeMul, "Polynomial degree for multiplicative correction." ); cl.AddSwitch( Key( 'I', "incremental" ), &IncrementalPolynomials, true, "Incrementally increase polynomial degrees." ); cl.EndGroup(); cl.BeginGroup( "Preprocessing", "Input Image Preprocessing" ); cl.AddOption( Key( "set-padding-value" ), &PaddingValue, "Set padding value for input intensity image. Pixels with this value will be ignored.", &PaddingFlag ); cl.AddOption( Key( 'm', "mask" ), &FNameMaskImage, "Binary mask image filename." )->SetProperties( cmtk::CommandLine::PROPS_IMAGE | cmtk::CommandLine::PROPS_LABELS ); cl.AddOption( Key( 't', "thresh-min" ), &ThresholdForegroundMin, "Minimum intensity threshold for image foreground.", &ThresholdForegroundFlag ); cl.AddOption( Key( 'T', "thresh-max" ), &ThresholdForegroundMax, "Minimum intensity threshold for image foreground.", &ThresholdForegroundFlag ); cl.AddSwitch( Key( "thresh-auto" ), &ThresholdAuto, true, "Automatic minimum intensity threshold selection for image foreground using an estimate of image noise level." ); cl.EndGroup(); cl.BeginGroup( "Entropy Estimation", "Entropy Estimation Settings" )->SetProperties( cmtk::CommandLine::PROPS_ADVANCED ); cl.AddSwitch( Key( 'L', "log-intensities" ), &LogIntensities, true, "Use log intensities for entropy estimation." ); cl.AddOption( Key( 's', "sampling-density" ), &SamplingDensity, "Image sampling density to use only subset of image pixels", &UseSamplingDensity ); cl.AddOption( Key( 'n', "num-bins" ), &NumberOfHistogramBins, "Number of histogram bins for entropy estimation" ); cl.EndGroup(); cl.BeginGroup( "Optimization", "Optimization Algorithm Settings" )->SetProperties( cmtk::CommandLine::PROPS_ADVANCED );; cl.AddOption( Key( "step-max" ), &StepMax, "Maximum (initial) search step size." ); cl.AddOption( Key( "step-min" ), &StepMin, "Minimum (final) search step size." ); cl.EndGroup(); cl.BeginGroup( "I/O", "Input and Output Options" )->SetProperties( cmtk::CommandLine::PROPS_ADVANCED );; cl.AddOption( Key( "import-bias-add" ), &ImportBiasFieldAdd, "Import additive bias field (disables optimization)." )->SetProperties( cmtk::CommandLine::PROPS_IMAGE ); cl.AddOption( Key( "import-bias-mul" ), &ImportBiasFieldMul, "Import multiplicative bias field (disables optimization)." )->SetProperties( cmtk::CommandLine::PROPS_IMAGE ); cl.AddOption( Key( "write-bias-add" ), &FNameBiasFieldAdd, "File name for output of additive bias field." )->SetProperties( cmtk::CommandLine::PROPS_IMAGE | cmtk::CommandLine::PROPS_OUTPUT ); cl.AddOption( Key( "write-bias-mul" ), &FNameBiasFieldMul, "File name for output of multiplicative bias field." )->SetProperties( cmtk::CommandLine::PROPS_IMAGE | cmtk::CommandLine::PROPS_OUTPUT ); cl.AddSwitch( Key( 'F', "write-float" ), &OutputFloatImage, true, "Write output image with floating point pixel data. If this is not given, the input data type is used." ); cl.EndGroup(); #ifdef CMTK_USE_SQLITE cl.BeginGroup( "Database", "Image/Transformation Database" ); cl.AddOption( Key( "db" ), &updateDB, "Path to image/transformation database that should be updated with the newly created image." ); cl.EndGroup(); #endif cl.AddParameter( &FNameInputImage, "InputImage", "Input image path" )->SetProperties( cmtk::CommandLine::PROPS_IMAGE ); cl.AddParameter( &FNameOutputImage, "OutputImage", "Output image path" )->SetProperties( cmtk::CommandLine::PROPS_IMAGE | cmtk::CommandLine::PROPS_OUTPUT ); cl.Parse( argc, argv ); } catch ( const cmtk::CommandLine::Exception& e ) { cmtk::StdErr << e << "\n"; throw cmtk::ExitException( 1 ); } // Instantiate programm progress indicator. cmtk::ProgressConsole progressIndicator( "Intensity Bias Field Correction" ); cmtk::UniformVolume::SmartPtr inputImage( cmtk::VolumeIO::ReadOriented( FNameInputImage ) ); if ( PaddingFlag ) { inputImage->GetData()->SetPaddingValue( PaddingValue ); } cmtk::UniformVolume::SmartPtr maskImage; if ( FNameMaskImage ) { maskImage = cmtk::UniformVolume::SmartPtr( cmtk::VolumeIO::ReadOriented( FNameMaskImage ) ); if ( ! maskImage || ! maskImage->GetData() ) { cmtk::StdErr << "ERROR: Could not read mask image " << FNameMaskImage << "\n"; throw cmtk::ExitException( 1 ); } } else { if ( ThresholdAuto ) { ThresholdForegroundMin = static_cast( cmtk::TypedArrayNoiseEstimatorNaiveGaussian( *(inputImage->GetData()) ).GetNoiseThreshold() ); ThresholdForegroundFlag = true; cmtk::DebugOutput( 1 ) << "INFO: estimated foreground threshold from noise level as " << ThresholdForegroundMin << "\n"; } if ( ThresholdForegroundFlag ) { maskImage = cmtk::UniformVolume::SmartPtr( inputImage->Clone( true /*copyData*/ ) ); maskImage->GetData()->SetPaddingValue( 0.0 ); maskImage->GetData()->ThresholdToPadding( cmtk::Types::DataItemRange( ThresholdForegroundMin, ThresholdForegroundMax ) ); } } typedef cmtk::EntropyMinimizationIntensityCorrectionFunctionalBase::SmartPtr FunctionalPointer; FunctionalPointer functional( NULL ); cmtk::UniformVolume::SmartPtr outputImage; if ( ImportBiasFieldAdd || ImportBiasFieldMul ) { functional = cmtk::CreateEntropyMinimizationIntensityCorrectionFunctional( 1, 1 ); if ( SamplingDensity > 0 ) functional->SetSamplingDensity( SamplingDensity ); if ( NumberOfHistogramBins ) functional->SetNumberOfHistogramBins( NumberOfHistogramBins ); functional->SetInputImage( inputImage ); if ( ImportBiasFieldAdd ) { cmtk::UniformVolume::SmartPtr biasAdd( cmtk::VolumeIO::ReadOriented( ImportBiasFieldAdd ) ); if ( ! biasAdd || ! biasAdd->GetData() ) { cmtk::StdErr << "ERROR: Could not read additive bias field image " << ImportBiasFieldAdd << "\n"; throw cmtk::ExitException( 1 ); } functional->SetBiasFieldAdd( *biasAdd ); } if ( ImportBiasFieldMul ) { cmtk::UniformVolume::SmartPtr biasMul( cmtk::VolumeIO::ReadOriented( ImportBiasFieldMul ) ); if ( ! biasMul || ! biasMul->GetData() ) { cmtk::StdErr << "ERROR: Could not read multiplicative bias field image " << ImportBiasFieldMul << "\n"; throw cmtk::ExitException( 1 ); } functional->SetBiasFieldMul( *biasMul ); } outputImage = functional->GetOutputImage( true /*update*/ ); } else { cmtk::CoordinateVector v; size_t polynomialDegreeFrom = std::max( PolynomialDegreeAdd, PolynomialDegreeMul ); const size_t polynomialDegreeTo = polynomialDegreeFrom + 1; if ( IncrementalPolynomials ) polynomialDegreeFrom = 1; for ( size_t polynomialDegree = polynomialDegreeFrom; polynomialDegree < polynomialDegreeTo; ++polynomialDegree ) { const size_t degreeAdd = std::min( polynomialDegree, PolynomialDegreeAdd ); const size_t degreeMul = std::min( polynomialDegree, PolynomialDegreeMul ); functional = cmtk::CreateEntropyMinimizationIntensityCorrectionFunctional( degreeAdd, degreeMul, functional ); functional->SetUseLogIntensities( LogIntensities ); if ( SamplingDensity > 0 ) functional->SetSamplingDensity( SamplingDensity ); if ( NumberOfHistogramBins ) functional->SetNumberOfHistogramBins( NumberOfHistogramBins ); functional->SetInputImage( inputImage ); if ( maskImage && maskImage->GetData() ) { functional->SetForegroundMask( *maskImage ); } else { cmtk::StdErr << "ERROR: please use a mask image. Seriously.\n"; throw cmtk::ExitException( 1 ); } functional->GetParamVector( v ); cmtk::DebugOutput( 1 ).GetStream().printf( "Estimating bias field with order %u multiplicative / %u additive polynomials.\nNumber of parameters: %d\n", degreeMul, degreeAdd, v.Dim ); if ( (PolynomialDegreeAdd > 0) || (PolynomialDegreeMul > 0) ) { try { cmtk::Optimizer::SmartPtr optimizer; optimizer = cmtk::Optimizer::SmartPtr( new cmtk::BestDirectionOptimizer ); optimizer->SetFunctional( functional ); optimizer->Optimize( v, StepMax, StepMin ); } catch ( const char* cp ) { cmtk::StdErr << "EXCEPTION: " << cp << "\n"; } v.Print(); } } // make sure everything is according to optimum parameters outputImage = functional->GetOutputImage( v, false/*foregroundOnly*/ ); } if ( FNameOutputImage ) { if ( ! OutputFloatImage ) { outputImage->GetData()->ReplacePaddingData( 0.0 ); cmtk::TypedArray::SmartPtr convertedData( outputImage->GetData()->Convert( inputImage->GetData()->GetType() ) ); outputImage->SetData( convertedData ); } cmtk::VolumeIO::Write( *outputImage, FNameOutputImage ); #ifdef CMTK_USE_SQLITE if ( updateDB ) { cmtk::ImageXformDB db( updateDB ); db.AddImage( FNameOutputImage, FNameInputImage ); } #endif } if ( FNameBiasFieldAdd && PolynomialDegreeAdd ) { cmtk::UniformVolume::SmartPtr biasField( functional->GetBiasFieldAdd( true /*completeImage*/ ) ); cmtk::VolumeIO::Write( *biasField, FNameBiasFieldAdd ); #ifdef CMTK_USE_SQLITE if ( updateDB ) { cmtk::ImageXformDB db( updateDB ); db.AddImage( FNameBiasFieldAdd, FNameInputImage ); } #endif } if ( FNameBiasFieldMul && PolynomialDegreeMul ) { cmtk::UniformVolume::SmartPtr biasField( functional->GetBiasFieldMul( true /*completeImage*/ ) ); cmtk::VolumeIO::Write( *biasField, FNameBiasFieldMul ); #ifdef CMTK_USE_SQLITE if ( updateDB ) { cmtk::ImageXformDB db( updateDB ); db.AddImage( FNameBiasFieldMul, FNameInputImage ); } #endif } return 0; } #include "cmtkSafeMain" cmtk-3.3.1/apps/mcwarp.cxx000066400000000000000000000344361276303427400154560ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2012 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4426 $ // // $LastChangedDate: 2012-06-12 10:30:56 -0700 (Tue, 12 Jun 2012) $ // // $LastChangedBy: torstenrohlfing $ // */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef HAVE_STDINT_H # include #else typedef long int uint64_t; #endif std::list fileListRef; std::list fileListFlt; const char* mcaffineOutput = NULL; const char* outArchive = NULL; std::list refChannelList; std::list fltChannelList; cmtk::Types::Coordinate gridSpacing = 40; bool exactGridSpacing = false; int gridRefinements = 0; bool delayGridRefine = false; bool adaptiveFixEntropy = false; float adaptiveFixThreshFactor = 0.0; bool fixWarpX = false; bool fixWarpY = false; bool fixWarpZ = false; float jacobianConstraintWeight = 0.0; cmtk::Types::Coordinate initialStepSize = 1.0; cmtk::Types::Coordinate finalStepSize = 0.125; cmtk::Optimizer::ReturnType optimizerDeltaFThreshold = 0; bool metricNMI = false; bool intensityCorrection = false; bool useHistograms = true; bool useCubicInterpolation = false; int downsampleFrom = 1; int downsampleTo = 1; bool downsampleWithAverage = false; float smoothSigmaFactor = 0.0; cmtk::UniformVolume::SmartPtr MakeDownsampled( cmtk::UniformVolume::SmartConstPtr& image, const int downsample, const cmtk::Types::Coordinate smoothSigmaFactor ) { if ( downsampleWithAverage ) return cmtk::UniformVolume::SmartPtr( image->GetDownsampled( downsample * image->GetMinDelta() ) ); cmtk::UniformVolume::SmartPtr result( image->CloneGrid() ); if ( (smoothSigmaFactor > 0) && downsample ) { const cmtk::Units::GaussianSigma sigma( smoothSigmaFactor * downsample * image->GetMinDelta() ); result->SetData( cmtk::UniformVolumeGaussianFilter( image ).GetFiltered3D( sigma ) ); } else { result->SetData( image->GetData() ); } if ( downsample > 1 ) result = cmtk::UniformVolume::SmartPtr( result->GetDownsampledAndAveraged( downsample, true /*approxIsotropic*/ ) ); return result; } template void DoRegistration() { typedef cmtk::SplineWarpMultiChannelRegistrationFunctional FunctionalType; typedef cmtk::SplineWarpMultiChannelIntensityCorrectionRegistrationFunctional ICFunctionalType; typename FunctionalType::SmartPtr functional; if ( intensityCorrection ) { functional = typename FunctionalType::SmartPtr( new ICFunctionalType ); } else { functional = typename FunctionalType::SmartPtr( new FunctionalType ); } functional->SetNormalizedMI( metricNMI ); functional->SetAdaptiveFixEntropyThreshold( adaptiveFixEntropy ); functional->SetAdaptiveFixThreshFactor( adaptiveFixThreshFactor ); functional->SetJacobianConstraintWeight( jacobianConstraintWeight ); if ( fixWarpX ) functional->AddFixedCoordinateDimension( 0 ); if ( fixWarpY ) functional->AddFixedCoordinateDimension( 1 ); if ( fixWarpZ ) functional->AddFixedCoordinateDimension( 2 ); if ( mcaffineOutput ) { cmtk::AffineMultiChannelRegistrationFunctional affineFunctional; cmtk::ClassStreamInput inStream( mcaffineOutput ); if ( !inStream.IsValid() ) { cmtk::StdErr << "ERROR: could not open '" << mcaffineOutput << "' for reading.\n"; throw cmtk::ExitException( 1 ); } inStream >> affineFunctional; inStream.Close(); functional->SetInitialAffineTransformation( affineFunctional.GetTransformation() ); for ( size_t idx = 0; idx < affineFunctional.GetNumberOfReferenceChannels(); ++idx ) { cmtk::UniformVolume::SmartPtr channel = affineFunctional.GetReferenceChannel(idx); refChannelList.push_back( channel ); } for ( size_t idx = 0; idx < affineFunctional.GetNumberOfFloatingChannels(); ++idx ) { cmtk::UniformVolume::SmartPtr channel = affineFunctional.GetFloatingChannel(idx); fltChannelList.push_back( channel ); } } const cmtk::Vector3D referenceImageDomain = (*refChannelList.begin())->m_Size; cmtk::Types::Coordinate minPixelSize = FLT_MAX; for ( std::list::const_iterator it = refChannelList.begin(); it != refChannelList.end(); ++it ) { minPixelSize = std::min( (*it)->GetMinDelta(), minPixelSize ); } for ( std::list::const_iterator it = fltChannelList.begin(); it != fltChannelList.end(); ++it ) { minPixelSize = std::min( (*it)->GetMinDelta(), minPixelSize ); } cmtk::BestDirectionOptimizer optimizer; optimizer.SetDeltaFThreshold( optimizerDeltaFThreshold ); optimizer.SetCallback( cmtk::RegistrationCallback::SmartPtr( new cmtk::RegistrationCallback ) ); optimizer.SetFunctional( functional ); int gridRefinementsLeft = gridRefinements; std::queue refinementSchedule; refinementSchedule.push( -1 ); // init marker for ( int downsample = std::max(downsampleFrom, downsampleTo); (downsample >= std::min(downsampleFrom,downsampleTo)) || gridRefinementsLeft; ) { int refinementFlags = 0; if ( gridRefinementsLeft ) { refinementFlags |= 2; --gridRefinementsLeft; } if ( downsample > downsampleTo ) { refinementFlags |= 1; --downsample; } if ( ! refinementFlags ) break; if ( delayGridRefine && (refinementFlags == 3) ) { refinementSchedule.push( 1 ); refinementSchedule.push( 2 ); } else { refinementSchedule.push( refinementFlags ); } } refinementSchedule.push( 0 ); // last iteration marker cmtk::CoordinateVector params; for ( int downsample = downsampleFrom; !refinementSchedule.empty(); refinementSchedule.pop() ) { cmtk::DebugOutput( 1 ) << "Downsampling stage 1:" << downsample << "\n"; functional->ClearAllChannels(); if ( (downsample == 0) || ( (downsample==1) && (smoothSigmaFactor==0) ) ) { functional->AddReferenceChannels( refChannelList.begin(), refChannelList.end() ); functional->AddFloatingChannels( fltChannelList.begin(), fltChannelList.end() ); } else { for ( std::list::iterator it = refChannelList.begin(); it != refChannelList.end(); ++it ) { cmtk::UniformVolume::SmartPtr image = MakeDownsampled( (*it), downsample, smoothSigmaFactor ); image->CopyMetaInfo( **it, cmtk::META_FS_PATH ); functional->AddReferenceChannel( image ); } for ( std::list::iterator it = fltChannelList.begin(); it != fltChannelList.end(); ++it ) { cmtk::UniformVolume::SmartPtr image = MakeDownsampled( (*it), downsample, smoothSigmaFactor ); image->CopyMetaInfo( **it, cmtk::META_FS_PATH ); functional->AddFloatingChannel( image ); } } if ( refinementSchedule.front() == -1 ) { functional->InitTransformation( referenceImageDomain, gridSpacing, exactGridSpacing ); functional->GetParamVector( params ); refinementSchedule.pop(); } cmtk::DebugOutput( 1 ) << "Number of parameters is" << functional->VariableParamVectorDim() << "\n"; if ( optimizer.Optimize( params, initialStepSize * downsample * minPixelSize, finalStepSize * downsample * minPixelSize )!= cmtk::CALLBACK_OK ) break; if ( refinementSchedule.front() & 1 ) { --downsample; } if ( refinementSchedule.front() & 2 ) { functional->RefineTransformation(); functional->GetParamVector( params ); } } if ( outArchive ) { cmtk::ClassStreamOutput stream( outArchive, cmtk::ClassStreamOutput::MODE_WRITE_ZLIB ); stream << *functional; stream.Close(); } } int doMain( const int argc, const char* argv[] ) { try { cmtk::CommandLine cl; cl.SetProgramInfo( cmtk::CommandLine::PRG_TITLE, "Multi-channel nonrigid registration" ); cl.SetProgramInfo( cmtk::CommandLine::PRG_DESCR, "Multi-channel nonrigid B-spline image registration using histogram-based or covariance-based joint entropy measures" ); cl.SetProgramInfo( cmtk::CommandLine::PRG_SYNTX, "mcwarp [options] mcaffineOutput" ); cl.SetProgramInfo( cmtk::CommandLine::PRG_CATEG, "CMTK.Image Registration" ); typedef cmtk::CommandLine::Key Key; cl.AddOption( Key( 'o', "out-archive" ), &outArchive, "Output archive path." ); cl.AddOption( Key( 'd', "downsample-from" ), &downsampleFrom, "Initial downsampling factor [1]." ); cl.AddOption( Key( 'D', "downsample-to" ), &downsampleTo, "Final downsampling factor [1]." ); cl.AddOption( Key( "downsample-average" ), &downsampleWithAverage, "Downsample using sliding-window averaging [default: off] )" ); cl.AddOption( Key( "smooth" ), &smoothSigmaFactor, "Sigma of Gaussian smoothing kernel in multiples of template image pixel size [default: off] )" ); cl.AddOption( Key( "grid-spacing" ), &gridSpacing, "Initial control point grid spacing in mm." ); cl.AddOption( Key( "grid-spacing-exact" ), &gridSpacing, "Exact initial grid spacing, even if it doesn't match image FOV.", &exactGridSpacing ); cl.AddOption( Key( "refine-grid" ), &gridRefinements, "Number of control point grid refinements." ); cl.AddSwitch( Key( "delay-refine-grid" ), &delayGridRefine, true, "Delay control point grid refinement until after pixel refinement." ); cl.AddOption( Key( "adaptive-fix-thresh-factor" ), &adaptiveFixThreshFactor, "Intensity threshold factor [0..1] for adaptive parameter fixing. [default: 0 -- no fixing]" ); cl.AddOption( Key( "adaptive-fix-thresh-factor-entropy" ), &adaptiveFixThreshFactor, "Entropy threshold factor [0..1] for adaptive parameter fixing. [default: 0 -- no fixing]", &adaptiveFixEntropy ); cl.AddSwitch( Key( "fix-warp-x" ), &fixWarpX, true, "Fix transformation (do not warp) in x-direction." ); cl.AddSwitch( Key( "fix-warp-y" ), &fixWarpY, true, "Fix transformation (do not warp) in y-direction." ); cl.AddSwitch( Key( "fix-warp-z" ), &fixWarpZ, true, "Fix transformation (do not warp) in z-direction." ); cl.AddOption( Key( "jacobian-constraint-weight" ), &jacobianConstraintWeight, "Weight for Jacobian volume preservation constraint" ); cl.AddSwitch( Key( 'H', "histograms" ), &useHistograms, true, "Use multi-dimensional histograms to compute entropies [default." ); cl.AddSwitch( Key( 'C', "covariance" ), &useHistograms, false, "Use covariance matrix determinants to compute entropies." ); cl.AddSwitch( Key( 'c', "cubic" ), &useCubicInterpolation, true, "Use cubic interpolation [default: linear]" ); cl.AddSwitch( Key( "mi" ), &metricNMI, false, "Use standard mutual information metric [default]" ); cl.AddSwitch( Key( "nmi" ), &metricNMI, true, "Use normalized mutual information metric" ); cl.AddSwitch( Key( 'I', "intensity-correction" ), &intensityCorrection, true, "Correct image intensities using local transformation Jacobian to preserve total signal" ); cl.AddOption( Key( "initial-step-size" ), &initialStepSize, "Initial optimizer step size in pixels." ); cl.AddOption( Key( "final-step-size" ), &finalStepSize, "Initial optimizer step size in pixels." ); cl.AddOption( Key( "delta-f-threshold" ), &optimizerDeltaFThreshold, "Optional threshold to terminate optimization (level) if relative change of target function drops below this value." ); cl.Parse( argc, argv ); mcaffineOutput = cl.GetNext(); } catch ( const cmtk::CommandLine::Exception& e ) { cmtk::StdErr << e << "\n"; throw cmtk::ExitException( 1 ); } if ( useCubicInterpolation ) { typedef cmtk::UniformVolumeInterpolator InterpolatorType; if ( useHistograms ) { typedef cmtk::MultiChannelHistogramRegistrationFunctional MetricFunctionalType; DoRegistration(); } else { typedef cmtk::MultiChannelRMIRegistrationFunctional MetricFunctionalType; DoRegistration(); } } else { typedef cmtk::UniformVolumeInterpolator InterpolatorType; if ( useHistograms ) { typedef cmtk::MultiChannelHistogramRegistrationFunctional MetricFunctionalType; DoRegistration(); } else { typedef cmtk::MultiChannelRMIRegistrationFunctional MetricFunctionalType; DoRegistration(); } } return 0; } #include "cmtkSafeMain" cmtk-3.3.1/apps/mk_adni_phantom.cxx000066400000000000000000000073151276303427400173110ustar00rootroot00000000000000/* // // Copyright 1997-2011 Torsten Rohlfing // // Copyright 2004-2011, 2013 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4834 $ // // $LastChangedDate: 2013-09-12 15:15:51 -0700 (Thu, 12 Sep 2013) $ // // $LastChangedBy: torstenrohlfing $ // */ #include #include #include #include #include #include #include #include int doMain( const int argc, const char* argv[] ) { cmtk::Types::Coordinate resolution = 1.0; bool labels = false; std::string outputFileName = "phantom.nii"; std::string outputLabelsName; std::string outputLandmarksName; try { cmtk::CommandLine cl; cl.SetProgramInfo( cmtk::CommandLine::PRG_TITLE, "Generate ADNI phantom image" ); cl.SetProgramInfo( cmtk::CommandLine::PRG_DESCR, "Generate image of the ADNI structural imaging calibration phantom (a.k.a. Magphan EMR051)." ); typedef cmtk::CommandLine::Key Key; cl.AddOption( Key( "resolution" ), &resolution, "Set output image resolution in [mm]" ); cl.AddSwitch( Key( "labels" ), &labels, true, "Draw each marker sphere with a label value defined by its index in the marker table. Otherwise, estimated T1 is used." ); cl.AddOption( Key( "write-labels" ), &outputLabelsName, "Optional path to write text file with label names." )->SetProperties( cmtk::CommandLine::PROPS_OUTPUT ); cl.AddOption( Key( "write-landmarks" ), &outputLandmarksName, "Optional path to write text file with landmark locations." )->SetProperties( cmtk::CommandLine::PROPS_OUTPUT ); cl.AddParameter( &outputFileName, "OutputImage", "Output image path" )->SetProperties( cmtk::CommandLine::PROPS_IMAGE | cmtk::CommandLine::PROPS_OUTPUT ); cl.Parse( argc, argv ); } catch ( const cmtk::CommandLine::Exception& e ) { cmtk::StdErr << e; return 1; } cmtk::VolumeIO::Write( *(cmtk::MagphanEMR051::GetPhantomImage( resolution, labels )), outputFileName ); // write optional labels file if ( !outputLabelsName.empty() ) { std::ofstream stream( outputLabelsName.c_str() ); if ( stream.good() ) { for ( size_t i = 0; i < cmtk::MagphanEMR051::NumberOfSpheres; ++i ) { stream << i+1 << "\t" << cmtk::MagphanEMR051::SphereName( i ) << std::endl; } } } // write optional landmarks file if ( !outputLandmarksName.empty() ) { std::ofstream stream( outputLandmarksName.c_str() ); if ( stream.good() ) { for ( size_t i = 0; i < cmtk::MagphanEMR051::NumberOfSpheres; ++i ) { stream << cmtk::MagphanEMR051::SphereCenter( i )[0] << "\t" << cmtk::MagphanEMR051::SphereCenter( i )[1] << "\t" << cmtk::MagphanEMR051::SphereCenter( i )[2] << "\t" << cmtk::MagphanEMR051::SphereName( i ) << "\t" << std::endl; } } } return 0; } #include "cmtkSafeMain" cmtk-3.3.1/apps/mk_analyze_hdr.cxx000066400000000000000000000223611276303427400171460ustar00rootroot00000000000000/* // // Copyright 2004-2011, 2013 SRI International // // Copyright 1997-2009 Torsten Rohlfing // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4959 $ // // $LastChangedDate: 2013-10-10 16:59:19 -0700 (Thu, 10 Oct 2013) $ // // $LastChangedBy: torstenrohlfing $ // */ #include #include #include #include #include #include #include #include #include int DimsX = 0; int DimsY = 0; int DimsZ = 0; bool PutDims = false; int LegacyMode = 0; void SetDims( const char* arg ) { if ( 3 == sscanf( arg, "%6d,%6d,%6d", &DimsX, &DimsY, &DimsZ ) ) { PutDims = true; } else { cmtk::StdErr << "ERROR: could not parse X,Y,Z dimensions from argument '" << arg << "'\n"; throw cmtk::ExitException( 1 ); } } float DeltaX = 1.0; float DeltaY = 1.0; float DeltaZ = 1.0; bool PutDeltas = false; void SetDeltas( const char* arg ) { if ( 3 == sscanf( arg, "%15f,%15f,%15f", &DeltaX, &DeltaY, &DeltaZ ) ) { PutDeltas = true; } else { cmtk::StdErr << "ERROR: could not parse X,Y,Z pixel size from argument '" << arg << "'\n"; throw cmtk::ExitException( 1 ); } } bool LittleEndian = false; cmtk::ScalarDataType DataType = cmtk::TYPE_NONE; cmtk::AnalyzeOrientation Orientation = cmtk::ANALYZE_UNKNOWN; float Offset = 0; bool PutOffset = false; const char* HdrFileName = "header.nii"; const char* ImportHdrFile = NULL; const char* Description = NULL; int doMain( const int argc, const char* argv[] ) { try { cmtk::CommandLine cl; cl.SetProgramInfo( cmtk::CommandLine::PRG_TITLE, "Make Analyze header file" ); cl.SetProgramInfo( cmtk::CommandLine::PRG_DESCR, "Make header file according Analzye 7.5 format based on user-supplied parameters for geometry, data type, orientation, etc." ); cl.SetProgramInfo( cmtk::CommandLine::PRG_SYNTX, "mk_analyze_hdr [options] [output.nii]" ); typedef cmtk::CommandLine::Key Key; cl.AddCallback( Key( 'D', "dims" ), SetDims, "Set dimensions in voxels" ); cl.AddCallback( Key( 'V', "voxel" ), SetDeltas, "Set voxel size in [mm]" ); cl.AddOption( Key( 'O', "offset" ), &Offset, "Binary data file offset", &PutOffset ); cl.AddSwitch( Key( 'b', "byte" ), &DataType, cmtk::TYPE_BYTE, "8 bits, unsigned (this is the default)" ); cl.AddSwitch( Key( 'c', "char" ), &DataType, cmtk::TYPE_CHAR, "8 bits, signed" ); cl.AddSwitch( Key( 's', "short" ), &DataType, cmtk::TYPE_SHORT, "16 bits, signed" ); cl.AddSwitch( Key( 'u', "ushort" ), &DataType, cmtk::TYPE_USHORT, "16 bits, unsigned" ); cl.AddSwitch( Key( 'i', "int" ), &DataType, cmtk::TYPE_INT, "32 bits signed" ); cl.AddSwitch( Key( 'f', "float" ), &DataType, cmtk::TYPE_FLOAT, "32 bits floating point" ); cl.AddSwitch( Key( 'd', "double" ), &DataType, cmtk::TYPE_DOUBLE, "64 bits floating point\n" ); cl.AddSwitch( Key( 'B', "big-endian" ), &LittleEndian, false, "Big endian data" ); cl.AddSwitch( Key( 'L', "little-endian" ), &LittleEndian, true, "Little endian data" ); cl.AddSwitch( Key( "axial" ), &Orientation, cmtk::ANALYZE_AXIAL, "Set slice orientation to axial (this is the default)" ); cl.AddSwitch( Key( "axial-flip" ), &Orientation, cmtk::ANALYZE_AXIAL_FLIP, "Set slice orientation to reverse axial" ); cl.AddSwitch( Key( "sagittal" ), &Orientation, cmtk::ANALYZE_SAGITTAL, "Set slice orientation to sagittal" ); cl.AddSwitch( Key( "sagittal-flip" ), &Orientation, cmtk::ANALYZE_SAGITTAL_FLIP, "Set slice orientation to reverse sagittal" ); cl.AddSwitch( Key( "coronal" ), &Orientation, cmtk::ANALYZE_CORONAL, "Set slice orientation to corona;" ); cl.AddSwitch( Key( "coronal-flip" ), &Orientation, cmtk::ANALYZE_CORONAL_FLIP, "Set slice orientation to reverse coronal" ); cl.AddSwitch( Key( "legacy" ), &LegacyMode, 1, "Legacy mode; do not write 'SRI1' tag into header" ); cl.AddSwitch( Key( "SRI1" ), &LegacyMode, -1, "Force 'SRI1' tag even if not present in imported header" ); cl.AddOption( Key( 'I', "import" ), &ImportHdrFile, "Import data from given header file." ); cl.AddOption( Key( "description" ), &Description, "Set description string [max. 80 characters]" ); cl.Parse( argc, argv ); HdrFileName = cl.GetNext(); } catch ( const cmtk::CommandLine::Exception& e ) { cmtk::StdErr << e << "\n"; throw cmtk::ExitException( 1 ); } char buffer[348]; memset( buffer, 0, sizeof( buffer ) ); if ( ImportHdrFile ) { FILE *hdrIn = fopen( ImportHdrFile, "r" ); if ( hdrIn ) { if ( 1 != fread( buffer, sizeof( buffer ), 1, hdrIn ) ) { cmtk::StdErr << "ERROR: could not read " << sizeof( buffer ) << " bytes from file " << ImportHdrFile << "\n"; throw cmtk::ExitException( 1 ); } fclose( hdrIn ); cmtk::DebugOutput( 1 ) << "Imported header file " << ImportHdrFile << "\n"; } else { cmtk::StdErr << "ERROR: Could not open file " << ImportHdrFile << " for import.\n"; throw cmtk::ExitException( 1 ); } LittleEndian = (buffer[0] != 0x00); } cmtk::FileHeader header( buffer, !LittleEndian ); // ID header.StoreField( 0, 0x0000015C ); // ndims if ( ! ImportHdrFile ) { header.StoreField( 40, 3 ); } // dimensions if ( !ImportHdrFile || PutDims ) { cmtk::DebugOutput( 1 ) << "Setting image dimensions\n"; header.StoreField( 42, DimsX ); header.StoreField( 44, DimsY ); header.StoreField( 46, DimsZ ); header.StoreField( 48, 0 ); // write dims 3 and 4 header.StoreField( 50, 0 ); // just for safety } if ( !ImportHdrFile || DataType != cmtk::TYPE_NONE ) { cmtk::DebugOutput( 1 ) << "Setting data type\n"; switch ( DataType ) { default: case cmtk::TYPE_BYTE: header.StoreField( 70, cmtk::ANALYZE_TYPE_UNSIGNED_CHAR ); header.StoreField( 72, 8 * sizeof( byte ) ); break; case cmtk::TYPE_SHORT: case cmtk::TYPE_USHORT: header.StoreField( 70, cmtk::ANALYZE_TYPE_SIGNED_SHORT ); header.StoreField( 72, 8 * sizeof( short ) ); break; case cmtk::TYPE_INT: header.StoreField( 70, cmtk::ANALYZE_TYPE_SIGNED_INT ); header.StoreField( 72, 8 * sizeof( signed int ) ); break; case cmtk::TYPE_FLOAT: header.StoreField( 70, cmtk::ANALYZE_TYPE_FLOAT ); header.StoreField( 72, 8 * sizeof( float ) ); break; case cmtk::TYPE_DOUBLE: header.StoreField( 70, cmtk::ANALYZE_TYPE_DOUBLE ); header.StoreField( 72, 8 * sizeof( double ) ); break; } } if ( !ImportHdrFile || PutDeltas ) { cmtk::DebugOutput( 1 ) << "Setting pixel size\n"; header.StoreField( 80, (float)DeltaX ); header.StoreField( 84, (float)DeltaY ); header.StoreField( 88, (float)DeltaZ ); } if ( !ImportHdrFile ) { header.StoreField( 92, 1.0f ); // write sizes in dims 3 and header.StoreField( 96, 1.0f ); // 4 just to be safe } // set offset for binary file. if ( !ImportHdrFile || PutOffset ) { cmtk::DebugOutput( 1 ) << "Setting image file offset\n"; header.StoreField( 108, Offset ); } // slice orientation if ( !ImportHdrFile || Orientation != cmtk::ANALYZE_UNKNOWN ) { cmtk::DebugOutput( 1 ) << "Setting image orientation\n"; if ( Orientation == cmtk::ANALYZE_UNKNOWN ) header.StoreField( 252, cmtk::ANALYZE_AXIAL ); else header.StoreField( 252, Orientation ); } if ( (!ImportHdrFile && LegacyMode == 0) || (LegacyMode < 0) ) header.StoreFieldString( 344, "SRI1", 4 ); else { // in "legacy" mode, remove "SRI1" tag even if present in imported header if ( LegacyMode > 0 ) { header.StoreFieldString( 344, "\0x\0x\0x\0x", 4 ); } } if ( Description ) { cmtk::DebugOutput( 1 ) << "Setting image description\n"; header.StoreFieldString( 148, Description, std::min( strlen( Description )+1, 80 ) ); } // write header info #ifdef _MSC_VER FILE *hdrFile = fopen( HdrFileName, "wb" ); #else FILE *hdrFile = fopen( HdrFileName, "w" ); #endif if ( hdrFile ) { if ( 348 != fwrite( buffer, 1, 348, hdrFile ) ) { cmtk::StdErr.printf( "ERROR: could not write 348 bytes to header file %s\n", HdrFileName ); } fclose( hdrFile ); } return 0; } #include "cmtkSafeMain" cmtk-3.3.1/apps/mk_nifti_hdr.cxx000066400000000000000000000205571276303427400166210ustar00rootroot00000000000000/* // // Copyright 2004-2011, 2013 SRI International // // Copyright 1997-2011 Torsten Rohlfing // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4923 $ // // $LastChangedDate: 2013-10-04 10:01:17 -0700 (Fri, 04 Oct 2013) $ // // $LastChangedBy: torstenrohlfing $ // */ #include #include #include #include #include #include #include #include #include #include #include int DimsX = 0; int DimsY = 0; int DimsZ = 0; bool PutDims = false; bool DetachedHeader = true; void SetDims( const char* arg ) { if ( 3 == sscanf( arg, "%6d,%6d,%6d", &DimsX, &DimsY, &DimsZ ) ) { PutDims = true; } else { cmtk::StdErr << "ERROR: could not parse X,Y,Z dimensions from argument '" << arg << "'\n"; throw cmtk::ExitException( 1 ); } } float DeltaX = 1.0; float DeltaY = 1.0; float DeltaZ = 1.0; bool PutDeltas = false; void SetDeltas( const char* arg ) { if ( 3 == sscanf( arg, "%15f,%15f,%15f", &DeltaX, &DeltaY, &DeltaZ ) ) { PutDeltas = true; } else { cmtk::StdErr << "ERROR: could not parse X,Y,Z pixel size from argument '" << arg << "'\n"; throw cmtk::ExitException( 1 ); } } cmtk::ScalarDataType DataType = cmtk::TYPE_NONE; float Offset = 0; bool PutOffset = false; bool ResetCoordinates = false; const char* HdrFileName = "header.nii"; const char* ImportHdrFile = NULL; const char* Description = NULL; int doMain( const int argc, const char* argv[] ) { try { cmtk::CommandLine cl; cl.SetProgramInfo( cmtk::CommandLine::PRG_TITLE, "Make NIFTI header file" ); cl.SetProgramInfo( cmtk::CommandLine::PRG_DESCR, "Make header file according to NIFTI file format based on user-supplied parameters for geometry, data type, orientation, etc." ); cl.SetProgramInfo( cmtk::CommandLine::PRG_SYNTX, "mk_nifti_hdr [options] [output.nii]" ); typedef cmtk::CommandLine::Key Key; cl.AddCallback( Key( 'D', "dims" ), SetDims, "Set dimensions in voxels. Provided as 'DimsX,DimsY,DimsZ'" ); cl.AddCallback( Key( 'V', "voxel" ), SetDeltas, "Set voxel size. Provided as 'PixX,PixY,PixZ' [in mm]" ); cl.AddOption( Key( 'O', "offset" ), &Offset, "Binary data file offset", &PutOffset ); cl.AddSwitch( Key( 'b', "byte" ), &DataType, cmtk::TYPE_BYTE, "8 bits, unsigned (this is the default)" ); cl.AddSwitch( Key( 'c', "char" ), &DataType, cmtk::TYPE_CHAR, "8 bits, signed" ); cl.AddSwitch( Key( 's', "short" ), &DataType, cmtk::TYPE_SHORT, "16 bits, signed" ); cl.AddSwitch( Key( 'u', "ushort" ), &DataType, cmtk::TYPE_USHORT, "16 bits, unsigned" ); cl.AddSwitch( Key( 'i', "int" ), &DataType, cmtk::TYPE_INT, "32 bits signed" ); cl.AddSwitch( Key( 'f', "float" ), &DataType, cmtk::TYPE_FLOAT, "32 bits floating point" ); cl.AddSwitch( Key( 'd', "double" ), &DataType, cmtk::TYPE_DOUBLE, "64 bits floating point\n" ); cl.AddSwitch( Key( "reset-coordinates" ), &ResetCoordinates, true, "In an imported header, reset the image-to-physical coordinate mapping to an identity matrix." ); cl.AddSwitch( Key( "attached" ), &DetachedHeader, false, "Create attached header for use in single-file images. Image data must be concatenated directly onto the created header file." ); cl.AddOption( Key( 'I', "import" ), &ImportHdrFile, "Import data from given header file." ); cl.AddOption( Key( "description" ), &Description, "Set description string [max. 80 characters]" ); cl.Parse( argc, argv ); HdrFileName = cl.GetNext(); } catch ( const cmtk::CommandLine::Exception& e ) { cmtk::StdErr << e << "\n"; throw cmtk::ExitException( 1 ); } nifti_1_header header; memset( &header, 0, sizeof( header ) ); if ( ImportHdrFile ) { cmtk::CompressedStream hdrStream( ImportHdrFile ); if ( !hdrStream.IsValid() ) { cmtk::StdErr.printf( "ERROR: could not file %s\n", ImportHdrFile ); throw cmtk::ExitException( 1 ); } if ( sizeof(header) != hdrStream.Read( &header, 1, sizeof(header) ) ) { cmtk::StdErr.printf( "ERROR: could not read %d bytes from header file %s\n", int( sizeof( header ) ), ImportHdrFile ); throw cmtk::ExitException( 1 ); } hdrStream.Close(); } // set offset for binary file. if ( !ImportHdrFile || PutOffset ) { cmtk::DebugOutput( 1 ) << "Setting image file offset\n"; header.vox_offset = Offset; } header.sizeof_hdr = 348; // header size header.dim_info = 0; if ( ! ImportHdrFile || PutDims ) { // ndims header.dim[0] = 4; // dimensions header.dim[1] = DimsX; header.dim[2] = DimsY; header.dim[3] = DimsZ; header.dim[4] = 1; header.dim[5] = 0; header.dim[6] = 0; header.dim[7] = 0; } if ( ! ImportHdrFile || PutDeltas ) { header.pixdim[0] = 1; header.pixdim[1] = DeltaX; header.pixdim[2] = DeltaY; header.pixdim[3] = DeltaZ; header.pixdim[4] = 0.0; header.pixdim[5] = 0.0; } if ( ! ImportHdrFile || ResetCoordinates ) { header.qform_code = 0; header.sform_code = 1; cmtk::AffineXform::MatrixType m4 = cmtk::AffineXform::MatrixType::Identity(); for ( int i = 0; i < 4; ++i ) { header.srow_x[i] = header.pixdim[1+i] * static_cast( m4[i][0] ); header.srow_y[i] = header.pixdim[1+i] * static_cast( m4[i][1] ); header.srow_z[i] = header.pixdim[1+i] * static_cast( m4[i][2] ); } } if ( !ImportHdrFile || DataType != cmtk::TYPE_NONE ) { cmtk::DebugOutput( 1 ) << "Setting data type\n"; switch ( DataType ) { default: case cmtk::TYPE_BYTE: header.datatype = DT_UNSIGNED_CHAR; header.bitpix = 8 * sizeof(byte); break; case cmtk::TYPE_CHAR: header.datatype = DT_INT8; header.bitpix = 8 * sizeof(char); break; case cmtk::TYPE_SHORT: header.datatype = DT_INT16; header.bitpix = 8 * sizeof(short); break; case cmtk::TYPE_USHORT: header.datatype = DT_UINT16; header.bitpix = 8 * sizeof(unsigned short); break; case cmtk::TYPE_INT: header.datatype = DT_INT32; header.bitpix = 8 * sizeof(int); break; case cmtk::TYPE_FLOAT: header.datatype = DT_FLOAT; header.bitpix = 8 * sizeof(float); break; case cmtk::TYPE_DOUBLE: header.datatype = DT_DOUBLE; header.bitpix = 8 * sizeof(double); break; } } if ( Description ) { cmtk::DebugOutput( 1 ) << "Setting image description\n"; memcpy( header.descrip, Description, std::min( strlen( Description )+1, 80 ) ); } #ifdef _MSC_VER const char *const modestr = "wb"; #else const char *const modestr = "w"; #endif if ( DetachedHeader ) { memcpy( &header.magic, "ni1\x00", 4 ); header.vox_offset = 0; FILE *hdrFile = fopen( HdrFileName, modestr ); if ( hdrFile ) { fwrite( &header, 1, sizeof( header ), hdrFile ); const int extension = 0; fwrite( &extension, 1, 4, hdrFile ); fclose( hdrFile ); } else { cmtk::StdErr << "ERROR: file '" << HdrFileName << "' could not be opened for writing!\n"; } } else { memcpy( &header.magic, "n+1\x00", 4 ); header.vox_offset = 352; FILE *hdrFile = fopen( HdrFileName, modestr ); if ( hdrFile ) { fwrite( &header, 1, sizeof( header ), hdrFile ); const int extension = 0; fwrite( &extension, 1, 4, hdrFile ); fclose( hdrFile ); } } return 0; } #include "cmtkSafeMain" cmtk-3.3.1/apps/mk_phantom_3d.cxx000066400000000000000000000234231276303427400167020ustar00rootroot00000000000000/* // // Copyright 1997-2011 Torsten Rohlfing // // Copyright 2004-2013 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4949 $ // // $LastChangedDate: 2013-10-08 13:51:34 -0700 (Tue, 08 Oct 2013) $ // // $LastChangedBy: torstenrohlfing $ // */ #include #include #include #include #include #include #include #include #include #include #ifdef CMTK_USE_DCMTK # include #endif int Dims[3] = { 256, 256, 256 }; void SetDims( const char* arg ) { if ( 3 != sscanf( arg, "%6d,%6d,%6d", &Dims[0], &Dims[1], &Dims[2] ) ) { cmtk::StdErr << "ERROR: cannot extract grid dimensions X,Y,Z from argument '" << arg << "'\n"; throw cmtk::ExitException( 1 ); } } float Delta[3] = { 1.0, 1.0, 1.0 }; void SetDeltas( const char* arg ) { if ( 3 != sscanf( arg, "%15f,%15f,%15f", &Delta[0], &Delta[1], &Delta[2] ) ) { cmtk::StdErr << "ERROR: cannot extract pixel size dX,dY,dZ from argument '" << arg << "'\n"; throw cmtk::ExitException( 1 ); } } cmtk::ScalarDataType DataType = cmtk::TYPE_USHORT; cmtk::Types::DataItem Background = 0; const char* InputImageName = NULL; bool InputImageGridOnly = false; const char* OutputFileName = "phantom.nii"; cmtk::UniformVolumePainter::CoordinateModeEnum CoordinateMode = cmtk::UniformVolumePainter::COORDINATES_INDEXED; int doMain( const int argc, const char* argv[] ) { try { cmtk::CommandLine cl; cl.SetProgramInfo( cmtk::CommandLine::PRG_TITLE, "Generate phantom image" ); cl.SetProgramInfo( cmtk::CommandLine::PRG_DESCR, "Generate 3D digital phantom images using a selection of drawing commands" ); cl.SetProgramInfo( cmtk::CommandLine::PRG_SYNTX, "mk_phantom_3d [options] command0 [command1 ...]\n" "\t sphere x,y,z radius value\n" "\t box x0,y0,z0 x1,y1,z1 value\n"); typedef cmtk::CommandLine::Key Key; cl.AddCallback( Key( 'D', "dims" ), SetDims, "Set dimensions in voxels" ); cl.AddCallback( Key( 'V', "voxel" ), SetDeltas, "Set voxel size in [mm]" ); cmtk::CommandLine::EnumGroup::SmartPtr modeGroup = cl.AddEnum( "coordinates", &CoordinateMode, "Coordinate specification mode." ); modeGroup->AddSwitch( Key( "indexed" ), cmtk::UniformVolumePainter::COORDINATES_INDEXED, "Use grid indexes to specify coordinates. For each dimension, the valid value range is [0,Dims-1]." ); modeGroup->AddSwitch( Key( "absolute" ), cmtk::UniformVolumePainter::COORDINATES_ABSOLUTE, "Use absolute volume coordinates. For each dimension, the valid range is [0,FOV]." ); modeGroup->AddSwitch( Key( "relative" ), cmtk::UniformVolumePainter::COORDINATES_RELATIVE, "Use relative volume coordinates. For each dimension, the valid range is [0,1]." ); cl.AddSwitch( Key( 'c', "char" ), &DataType, cmtk::TYPE_CHAR, "8 bits, signed" ); cl.AddSwitch( Key( 'b', "byte" ), &DataType, cmtk::TYPE_BYTE, "8 bits, unsigned" ); cl.AddSwitch( Key( 's', "short" ), &DataType, cmtk::TYPE_SHORT, "16 bits, signed" ); cl.AddSwitch( Key( 'u', "ushort" ), &DataType, cmtk::TYPE_USHORT, "16 bits, unsigned" ); cl.AddSwitch( Key( 'i', "int" ), &DataType, cmtk::TYPE_INT, "32 bits signed" ); cl.AddSwitch( Key( 'f', "float" ), &DataType, cmtk::TYPE_FLOAT, "32 bits floating point" ); cl.AddSwitch( Key( 'd', "double" ), &DataType, cmtk::TYPE_DOUBLE, "64 bits floating point\n" ); cl.AddOption( Key( 'B', "bg" ), &Background, "Set the image background value (use to initialize newly created image)." ); cl.AddOption( Key( 'I', "import" ), &InputImageName, "Import image" ); cl.AddOption( Key( "import-grid" ), &InputImageName, "Import image grid only, ignore data", &InputImageGridOnly ); cl.AddOption( Key( 'o', "outfile" ), &OutputFileName, "File name for output image" ); cl.Parse( argc, argv ); cmtk::UniformVolume::SmartPtr volume; if ( InputImageName ) { if ( InputImageGridOnly ) { volume = cmtk::UniformVolume::SmartPtr( cmtk::VolumeIO::ReadGridOriented( InputImageName ) ); volume->CreateDataArray( DataType ); volume->GetData()->Fill( Background ); } else volume = cmtk::UniformVolume::SmartPtr( cmtk::VolumeIO::ReadOriented( InputImageName ) ); } else { const cmtk::Types::Coordinate size[3] = { Delta[0] * (Dims[0]-1), Delta[1] * (Dims[1]-1), Delta[2] * (Dims[2]-1) }; volume = cmtk::UniformVolume::SmartPtr( new cmtk::UniformVolume( cmtk::UniformVolume::IndexType::FromPointer( Dims ), cmtk::FixedVector<3,cmtk::Types::Coordinate>::FromPointer( size ) ) ); volume->SetMetaInfo( cmtk::META_SPACE, cmtk::AnatomicalOrientation::ORIENTATION_STANDARD ); volume->SetMetaInfo( cmtk::META_SPACE_ORIGINAL, cmtk::AnatomicalOrientation::ORIENTATION_STANDARD ); cmtk::TypedArray::SmartPtr data( cmtk::TypedArray::Create( DataType, volume->GetNumberOfPixels() ) ); volume->SetData( data ); data->Fill( Background ); } cmtk::UniformVolumePainter painter( volume, CoordinateMode ); const char* nextCmd = cl.GetNextOptional(); while ( nextCmd ) { if ( ! strcmp( nextCmd, "sphere" ) ) { const char* center = cl.GetNext(); const char* radius = cl.GetNext(); const char* value = cl.GetNext(); float cc[3]; if ( sscanf( center, "%15f,%15f,%15f", &cc[0], &cc[1], &cc[2] ) != 3 ) { cmtk::StdErr << "Parameter 'center' of 'sphere' command must be x,y,z\n"; return 1; } painter.DrawSphere( cmtk::FixedVector<3,cmtk::Types::Coordinate>::FromPointer( cc ), atof( radius ), atof( value ) ); } if ( ! strcmp( nextCmd, "box" ) ) { const char* fromCorner = cl.GetNext(); const char* toCorner = cl.GetNext(); const char* value = cl.GetNext(); float boxFrom[3], boxTo[3]; if ( sscanf( fromCorner, "%15f,%15f,%15f", &boxFrom[0], &boxFrom[1], &boxFrom[2] ) != 3 ) { cmtk::StdErr << "Parameter 'corner0' of 'box' command must be three number x,y,z\n"; return 1; } if ( sscanf( toCorner, "%15f,%15f,%15f", &boxTo[0], &boxTo[1], &boxTo[2] ) != 3 ) { cmtk::StdErr << "Parameter 'corner1' of 'box' command must be three numbers x,y,z\n"; return 1; } painter.DrawBox( cmtk::FixedVector<3,cmtk::Types::Coordinate>::FromPointer( boxFrom ), cmtk::FixedVector<3,cmtk::Types::Coordinate>::FromPointer( boxTo ), atof( value ) ); } #ifdef CMTK_USE_DCMTK if ( ! strcmp( nextCmd, "mrs-voxel" ) ) { const char* dicom = cl.GetNext(); const char* value = cl.GetNext(); std::auto_ptr fileformat( new DcmFileFormat ); fileformat->transferInit(); OFCondition status = fileformat->loadFile( dicom ); fileformat->transferEnd(); if ( !status.good() ) { cmtk::StdErr << "Error: cannot read DICOM file " << dicom << " (" << status.text() << ")\n"; exit( 1 ); } DcmDataset *dataset = fileformat->getDataset(); if ( ! dataset ) { exit( 1 ); } Float64 cntr[3]; Float64 size[3]; const DcmTagKey keyVoxelLocation( 0x0043, 0x108c); // private GE tag "Voxel Location" if ( dataset->findAndGetFloat64( keyVoxelLocation, cntr[0], 0 ).good() ) { cntr[0] *= -1; // convert RL to LR dataset->findAndGetFloat64( keyVoxelLocation, cntr[1], 1 ); cntr[1] *= -1; // convert AP to PA dataset->findAndGetFloat64( keyVoxelLocation, cntr[2], 2 ); dataset->findAndGetFloat64( keyVoxelLocation, size[0], 3 ); dataset->findAndGetFloat64( keyVoxelLocation, size[1], 4 ); dataset->findAndGetFloat64( keyVoxelLocation, size[2], 5 ); } else { // Some image GE MRS DICOM files do not have "Voxel Location" tag, but store location in different undocumented tags. // Order of x/y is switched, and some values have negative signs. dataset->findAndGetFloat64( DcmTagKey( 0x0019, 0x10b3 ), cntr[0] ); // already * -1 dataset->findAndGetFloat64( DcmTagKey( 0x0019, 0x10b2 ), cntr[1] ); cntr[1] *= -1; dataset->findAndGetFloat64( DcmTagKey( 0x0019, 0x10b4 ), cntr[2] ); dataset->findAndGetFloat64( DcmTagKey( 0x0019, 0x10b0 ), size[0] ); dataset->findAndGetFloat64( DcmTagKey( 0x0019, 0x10af ), size[1] ); dataset->findAndGetFloat64( DcmTagKey( 0x0019, 0x10b1 ), size[2] ); } cmtk::UniformVolumePainter roiPainter( volume, cmtk::UniformVolumePainter::COORDINATES_INDEXED ); cmtk::FixedVector<3,cmtk::Types::Coordinate> roiCntr = cmtk::FixedVector<3,cmtk::Types::Coordinate>::FromPointer( cntr ); cmtk::FixedVector<3,cmtk::Types::Coordinate> roiSize = cmtk::FixedVector<3,cmtk::Types::Coordinate>::FromPointer( size ); roiSize *= 0.5; roiPainter.DrawBox( volume->PhysicalToIndex( roiCntr - roiSize ), volume->PhysicalToIndex( roiCntr + roiSize ), atof( value ) ); } #endif // #ifdef CMTK_USE_DCMTK nextCmd = cl.GetNextOptional(); } cmtk::VolumeIO::Write( *volume, OutputFileName ); } catch ( const cmtk::CommandLine::Exception& e ) { cmtk::StdErr << e; return 1; } return 0; } #include "cmtkSafeMain" cmtk-3.3.1/apps/mrbias.cxx000066400000000000000000000340371276303427400154370ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2013 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4986 $ // // $LastChangedDate: 2013-10-18 10:30:23 -0700 (Fri, 18 Oct 2013) $ // // $LastChangedBy: torstenrohlfing $ // */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef CMTK_USE_SQLITE # include #endif std::string ImportBiasFieldAdd; std::string ImportBiasFieldMul; std::string FNameBiasFieldAdd; std::string FNameBiasFieldMul; cmtk::Types::DataItem PaddingValue = 0; bool PaddingFlag = false; float ThresholdForegroundMin = -FLT_MAX; float ThresholdForegroundMax = FLT_MAX; bool ThresholdForegroundFlag = false; bool ThresholdAuto = false; int ThresholdOtsuNBins = 0; std::string FNameMaskImage; bool UseSamplingDensity = false; float SamplingDensity = 1.0; unsigned int NumberOfHistogramBins = 256; std::string FNameInputImage; std::string FNameOutputImage; bool OutputFloatImage = false; int PolynomialDegreeAdd = 0; int PolynomialDegreeMul = 2; bool IncrementalPolynomials = false; cmtk::Types::Coordinate StepMax = 1.0; cmtk::Types::Coordinate StepMin = 0.1; bool LogIntensities = false; #ifdef CMTK_USE_SQLITE std::string updateDB; #endif int doMain( const int argc, const char *argv[] ) { try { cmtk::CommandLine cl( cmtk::CommandLine::PROPS_XML ); cl.SetProgramInfo( cmtk::CommandLine::PRG_TITLE, "MR Image Intensity Bias Field Correction" ); cl.SetProgramInfo( cmtk::CommandLine::PRG_DESCR, "This program corrects intensity inhomogeneity artifacts in MR images using a bias field estimated via entropy minimization." ); cl.SetProgramInfo( cmtk::CommandLine::PRG_CATEG, "CMTK.Artifact Correction" ); typedef cmtk::CommandLine::Key Key; cl.BeginGroup( "Bias Field", "Bias Field Parameterization" ); cl.AddOption( Key( 'A', "degree-add" ), &PolynomialDegreeAdd, "Polynomial degree for additive correction." ); cl.AddOption( Key( 'M', "degree-mul" ), &PolynomialDegreeMul, "Polynomial degree for multiplicative correction." ); cl.AddSwitch( Key( 'I', "incremental" ), &IncrementalPolynomials, true, "Incrementally increase polynomial degrees." ); cl.EndGroup(); cl.BeginGroup( "Preprocessing", "Input Image Preprocessing" ); cl.AddOption( Key( "set-padding-value" ), &PaddingValue, "Set padding value for input intensity image. Pixels with this value will be ignored.", &PaddingFlag ); cl.AddOption( Key( 'm', "mask" ), &FNameMaskImage, "Binary mask image filename." )->SetProperties( cmtk::CommandLine::PROPS_IMAGE | cmtk::CommandLine::PROPS_LABELS ); cl.AddOption( Key( 't', "thresh-min" ), &ThresholdForegroundMin, "Minimum intensity threshold for image foreground.", &ThresholdForegroundFlag ); cl.AddOption( Key( 'T', "thresh-max" ), &ThresholdForegroundMax, "Minimum intensity threshold for image foreground.", &ThresholdForegroundFlag ); cl.AddSwitch( Key( "thresh-auto" ), &ThresholdAuto, true, "Automatic minimum intensity threshold selection for image foreground using an estimate of image noise level." ); cl.AddOption( Key( "thresh-otsu-nbins" ), &ThresholdOtsuNBins, "If this is a positive integer, use automatic minimum intensity threshold selection for image foreground by Otsu thresholding with given number of histogram bins." ); cl.EndGroup(); cl.BeginGroup( "Entropy Estimation", "Entropy Estimation Settings" )->SetProperties( cmtk::CommandLine::PROPS_ADVANCED ); cl.AddSwitch( Key( 'L', "log-intensities" ), &LogIntensities, true, "Use log intensities for entropy estimation." ); cl.AddOption( Key( 's', "sampling-density" ), &SamplingDensity, "Image sampling density to use only subset of image pixels", &UseSamplingDensity ); cl.AddOption( Key( 'n', "num-bins" ), &NumberOfHistogramBins, "Number of histogram bins for entropy estimation" ); cl.EndGroup(); cl.BeginGroup( "Optimization", "Optimization Algorithm Settings" )->SetProperties( cmtk::CommandLine::PROPS_ADVANCED );; cl.AddOption( Key( "step-max" ), &StepMax, "Maximum (initial) search step size." ); cl.AddOption( Key( "step-min" ), &StepMin, "Minimum (final) search step size." ); cl.EndGroup(); cl.BeginGroup( "I/O", "Input and Output Options" )->SetProperties( cmtk::CommandLine::PROPS_ADVANCED );; cl.AddOption( Key( "import-bias-add" ), &ImportBiasFieldAdd, "Import additive bias field (disables optimization)." )->SetProperties( cmtk::CommandLine::PROPS_IMAGE ); cl.AddOption( Key( "import-bias-mul" ), &ImportBiasFieldMul, "Import multiplicative bias field (disables optimization)." )->SetProperties( cmtk::CommandLine::PROPS_IMAGE ); cl.AddOption( Key( "write-bias-add" ), &FNameBiasFieldAdd, "File name for output of additive bias field." )->SetProperties( cmtk::CommandLine::PROPS_IMAGE | cmtk::CommandLine::PROPS_OUTPUT ); cl.AddOption( Key( "write-bias-mul" ), &FNameBiasFieldMul, "File name for output of multiplicative bias field." )->SetProperties( cmtk::CommandLine::PROPS_IMAGE | cmtk::CommandLine::PROPS_OUTPUT ); cl.AddSwitch( Key( 'F', "write-float" ), &OutputFloatImage, true, "Write output image with floating point pixel data. If this is not given, the input data type is used." ); cl.EndGroup(); #ifdef CMTK_USE_SQLITE cl.BeginGroup( "Database", "Image/Transformation Database" ); cl.AddOption( Key( "db" ), &updateDB, "Path to image/transformation database that should be updated with the newly created image." ); cl.EndGroup(); #endif cl.AddParameter( &FNameInputImage, "InputImage", "Input image path" )->SetProperties( cmtk::CommandLine::PROPS_IMAGE ); cl.AddParameter( &FNameOutputImage, "OutputImage", "Output image path" )->SetProperties( cmtk::CommandLine::PROPS_IMAGE | cmtk::CommandLine::PROPS_OUTPUT ); cl.Parse( argc, argv ); } catch ( const cmtk::CommandLine::Exception& e ) { cmtk::StdErr << e << "\n"; throw cmtk::ExitException( 1 ); } // Instantiate programm progress indicator. cmtk::ProgressConsole progressIndicator( "Intensity Bias Field Correction" ); cmtk::UniformVolume::SmartPtr inputImage( cmtk::VolumeIO::ReadOriented( FNameInputImage ) ); if ( PaddingFlag ) { inputImage->GetData()->SetPaddingValue( PaddingValue ); } cmtk::UniformVolume::SmartPtr maskImage; if ( !FNameMaskImage.empty() ) { maskImage = cmtk::UniformVolume::SmartPtr( cmtk::VolumeIO::ReadOriented( FNameMaskImage ) ); if ( ! maskImage || ! maskImage->GetData() ) { cmtk::StdErr << "ERROR: Could not read mask image " << FNameMaskImage << "\n"; throw cmtk::ExitException( 1 ); } } else { if ( ThresholdAuto ) { ThresholdForegroundMin = static_cast( cmtk::TypedArrayNoiseEstimatorNaiveGaussian( *(inputImage->GetData()) ).GetNoiseThreshold() ); ThresholdForegroundFlag = true; cmtk::DebugOutput( 1 ) << "INFO: estimated foreground threshold from noise level as " << ThresholdForegroundMin << "\n"; } if ( ThresholdOtsuNBins > 0 ) { ThresholdForegroundMin = static_cast( cmtk::HistogramOtsuThreshold< cmtk::Histogram >( *(inputImage->GetData()->GetHistogram( ThresholdOtsuNBins )) ).Get() ); ThresholdForegroundFlag = true; cmtk::DebugOutput( 1 ) << "INFO: Otsu thresholding at " << ThresholdForegroundMin << "\n"; } if ( ThresholdForegroundFlag ) { maskImage = cmtk::UniformVolume::SmartPtr( inputImage->Clone( true /*copyData*/ ) ); maskImage->GetData()->SetPaddingValue( 0.0 ); maskImage->GetData()->ThresholdToPadding( cmtk::Types::DataItemRange( ThresholdForegroundMin, ThresholdForegroundMax ) ); } } typedef cmtk::EntropyMinimizationIntensityCorrectionFunctionalBase::SmartPtr FunctionalPointer; FunctionalPointer functional( NULL ); cmtk::UniformVolume::SmartPtr outputImage; if ( ! (ImportBiasFieldAdd.empty() && ImportBiasFieldMul.empty()) ) { functional = cmtk::CreateEntropyMinimizationIntensityCorrectionFunctional( 1, 1 ); if ( SamplingDensity > 0 ) functional->SetSamplingDensity( SamplingDensity ); if ( NumberOfHistogramBins ) functional->SetNumberOfHistogramBins( NumberOfHistogramBins ); functional->SetInputImage( inputImage ); if ( !ImportBiasFieldAdd.empty() ) { cmtk::UniformVolume::SmartPtr biasAdd( cmtk::VolumeIO::ReadOriented( ImportBiasFieldAdd ) ); if ( ! biasAdd || ! biasAdd->GetData() ) { cmtk::StdErr << "ERROR: Could not read additive bias field image " << ImportBiasFieldAdd << "\n"; throw cmtk::ExitException( 1 ); } functional->SetBiasFieldAdd( *biasAdd ); } if ( !ImportBiasFieldMul.empty() ) { cmtk::UniformVolume::SmartPtr biasMul( cmtk::VolumeIO::ReadOriented( ImportBiasFieldMul ) ); if ( ! biasMul || ! biasMul->GetData() ) { cmtk::StdErr << "ERROR: Could not read multiplicative bias field image " << ImportBiasFieldMul << "\n"; throw cmtk::ExitException( 1 ); } functional->SetBiasFieldMul( *biasMul ); } outputImage = functional->GetOutputImage( true /*update*/ ); } else { cmtk::CoordinateVector v; size_t polynomialDegreeFrom = std::max( PolynomialDegreeAdd, PolynomialDegreeMul ); const size_t polynomialDegreeTo = polynomialDegreeFrom + 1; if ( IncrementalPolynomials ) polynomialDegreeFrom = 1; for ( size_t polynomialDegree = polynomialDegreeFrom; polynomialDegree < polynomialDegreeTo; ++polynomialDegree ) { const size_t degreeAdd = std::min( polynomialDegree, PolynomialDegreeAdd ); const size_t degreeMul = std::min( polynomialDegree, PolynomialDegreeMul ); functional = cmtk::CreateEntropyMinimizationIntensityCorrectionFunctional( degreeAdd, degreeMul, functional ); functional->SetUseLogIntensities( LogIntensities ); if ( SamplingDensity > 0 ) functional->SetSamplingDensity( SamplingDensity ); if ( NumberOfHistogramBins ) functional->SetNumberOfHistogramBins( NumberOfHistogramBins ); functional->SetInputImage( inputImage ); if ( maskImage && maskImage->GetData() ) { functional->SetForegroundMask( *maskImage ); } else { cmtk::StdErr << "ERROR: please use a mask image. Seriously.\n"; throw cmtk::ExitException( 1 ); } functional->GetParamVector( v ); cmtk::DebugOutput( 1 ).GetStream().printf( "Estimating bias field with order %u multiplicative / %u additive polynomials.\nNumber of parameters: %d\n", degreeMul, degreeAdd, v.Dim ); if ( (PolynomialDegreeAdd > 0) || (PolynomialDegreeMul > 0) ) { try { cmtk::Optimizer::SmartPtr optimizer; optimizer = cmtk::Optimizer::SmartPtr( new cmtk::BestDirectionOptimizer ); optimizer->SetFunctional( functional ); optimizer->Optimize( v, StepMax, StepMin ); } catch ( const char* cp ) { cmtk::StdErr << "EXCEPTION: " << cp << "\n"; } v.Print(); } } // make sure everything is according to optimum parameters outputImage = functional->GetOutputImage( v, false/*foregroundOnly*/ ); } if ( !FNameOutputImage.empty() ) { if ( ! OutputFloatImage ) { outputImage->GetData()->ReplacePaddingData( 0.0 ); cmtk::TypedArray::SmartPtr convertedData( outputImage->GetData()->Convert( inputImage->GetData()->GetType() ) ); outputImage->SetData( convertedData ); } cmtk::VolumeIO::Write( *outputImage, FNameOutputImage ); #ifdef CMTK_USE_SQLITE if ( !updateDB.empty() ) { try { cmtk::ImageXformDB db( updateDB ); db.AddImage( FNameOutputImage, FNameInputImage ); } catch ( const cmtk::SQLite::Exception& ex ) { cmtk::StdErr << "ERROR: updating SQLite database failed - " << ex.what() << "\n"; } } #endif } if ( PolynomialDegreeAdd && !FNameBiasFieldAdd.empty() ) { cmtk::UniformVolume::SmartPtr biasField( functional->GetBiasFieldAdd( true /*completeImage*/ ) ); cmtk::VolumeIO::Write( *biasField, FNameBiasFieldAdd ); #ifdef CMTK_USE_SQLITE if ( !updateDB.empty() ) { try { cmtk::ImageXformDB db( updateDB ); db.AddImage( FNameBiasFieldAdd, FNameInputImage ); } catch ( const cmtk::SQLite::Exception& ex ) { cmtk::StdErr << "ERROR: updating SQLite database failed - " << ex.what() << "\n"; } } #endif } if ( PolynomialDegreeMul && !FNameBiasFieldMul.empty() ) { cmtk::UniformVolume::SmartPtr biasField( functional->GetBiasFieldMul( true /*completeImage*/ ) ); cmtk::VolumeIO::Write( *biasField, FNameBiasFieldMul ); #ifdef CMTK_USE_SQLITE if ( !updateDB.empty() ) { try { cmtk::ImageXformDB db( updateDB ); db.AddImage( FNameBiasFieldMul, FNameInputImage ); } catch ( const cmtk::SQLite::Exception& ex ) { cmtk::StdErr << "ERROR: updating SQLite database failed - " << ex.what() << "\n"; } } #endif } return 0; } #include "cmtkSafeMain" cmtk-3.3.1/apps/mrbias_cuda.cxx000066400000000000000000000311631276303427400164300ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2013 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4986 $ // // $LastChangedDate: 2013-10-18 10:30:23 -0700 (Fri, 18 Oct 2013) $ // // $LastChangedBy: torstenrohlfing $ // */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef CMTK_USE_SQLITE # include #endif std::string FNameBiasFieldAdd; std::string FNameBiasFieldMul; cmtk::Types::DataItem PaddingValue = 0; bool PaddingFlag = false; float ThresholdForegroundMin = -FLT_MAX; float ThresholdForegroundMax = FLT_MAX; bool ThresholdForegroundFlag = false; bool ThresholdAuto = false; int ThresholdOtsuNBins = 0; std::string FNameMaskImage; bool UseSamplingDensity = false; float SamplingDensity = 1.0; unsigned int NumberOfHistogramBins = 256; std::string FNameInputImage; std::string FNameOutputImage; bool OutputFloatImage = false; int PolynomialDegreeAdd = 0; int PolynomialDegreeMul = 2; bool IncrementalPolynomials = false; cmtk::Types::Coordinate StepMax = 1.0; cmtk::Types::Coordinate StepMin = 0.1; bool LogIntensities = false; #ifdef CMTK_USE_SQLITE std::string updateDB; #endif int doMain( const int argc, const char *argv[] ) { try { cmtk::CommandLine cl( cmtk::CommandLine::PROPS_XML ); cl.SetProgramInfo( cmtk::CommandLine::PRG_TITLE, "MR Image Intensity Bias Field Correction on GPU using CUDA" ); cl.SetProgramInfo( cmtk::CommandLine::PRG_DESCR, "This program corrects intensity inhomogeneity artifacts in MR images using a bias field estimated via entropy minimization using GPU-accelerated computation." ); cl.SetProgramInfo( cmtk::CommandLine::PRG_CATEG, "CMTK.Artifact Correction" ); typedef cmtk::CommandLine::Key Key; cl.BeginGroup( "Bias Field", "Bias Field Parameterization" ); cl.AddOption( Key( 'A', "degree-add" ), &PolynomialDegreeAdd, "Polynomial degree for additive correction." ); cl.AddOption( Key( 'M', "degree-mul" ), &PolynomialDegreeMul, "Polynomial degree for multiplicative correction." ); cl.AddSwitch( Key( 'I', "incremental" ), &IncrementalPolynomials, true, "Incrementally increase polynomial degrees." ); cl.EndGroup(); cl.BeginGroup( "Preprocessing", "Input Image Preprocessing" ); cl.AddOption( Key( "set-padding-value" ), &PaddingValue, "Set padding value for input intensity image. Pixels with this value will be ignored.", &PaddingFlag ); cl.AddOption( Key( 'm', "mask" ), &FNameMaskImage, "Binary mask image filename." )->SetProperties( cmtk::CommandLine::PROPS_IMAGE | cmtk::CommandLine::PROPS_LABELS ); cl.AddOption( Key( 't', "thresh-min" ), &ThresholdForegroundMin, "Minimum intensity threshold for image foreground.", &ThresholdForegroundFlag ); cl.AddOption( Key( 'T', "thresh-max" ), &ThresholdForegroundMax, "Minimum intensity threshold for image foreground.", &ThresholdForegroundFlag ); cl.AddSwitch( Key( "thresh-auto" ), &ThresholdAuto, true, "Automatic minimum intensity threshold selection for image foreground using an estimate of image noise level." ); cl.AddOption( Key( "thresh-otsu-nbins" ), &ThresholdOtsuNBins, "If this is a positive integer, use automatic minimum intensity threshold selection for image foreground by Otsu thresholding with given number of histogram bins." ); cl.EndGroup(); cl.BeginGroup( "Entropy Estimation", "Entropy Estimation Settings" )->SetProperties( cmtk::CommandLine::PROPS_ADVANCED ); cl.AddSwitch( Key( 'L', "log-intensities" ), &LogIntensities, true, "Use log intensities for entropy estimation." ); cl.AddOption( Key( 's', "sampling-density" ), &SamplingDensity, "Image sampling density to use only subset of image pixels", &UseSamplingDensity ); cl.AddOption( Key( 'n', "num-bins" ), &NumberOfHistogramBins, "Number of histogram bins for entropy estimation" ); cl.EndGroup(); cl.BeginGroup( "Optimization", "Optimization Algorithm Settings" )->SetProperties( cmtk::CommandLine::PROPS_ADVANCED );; cl.AddOption( Key( "step-max" ), &StepMax, "Maximum (initial) search step size." ); cl.AddOption( Key( "step-min" ), &StepMin, "Minimum (final) search step size." ); cl.EndGroup(); cl.BeginGroup( "Output", "Output Options" )->SetProperties( cmtk::CommandLine::PROPS_ADVANCED );; cl.AddOption( Key( "write-bias-add" ), &FNameBiasFieldAdd, "File name for output of additive bias field." )->SetProperties( cmtk::CommandLine::PROPS_IMAGE | cmtk::CommandLine::PROPS_OUTPUT ); cl.AddOption( Key( "write-bias-mul" ), &FNameBiasFieldMul, "File name for output of multiplicative bias field." )->SetProperties( cmtk::CommandLine::PROPS_IMAGE | cmtk::CommandLine::PROPS_OUTPUT ); cl.AddSwitch( Key( 'F', "write-float" ), &OutputFloatImage, true, "Write output image with floating point pixel data. If this is not given, the input data type is used." ); cl.EndGroup(); #ifdef CMTK_USE_SQLITE cl.BeginGroup( "Database", "Image/Transformation Database" ); cl.AddOption( Key( "db" ), &updateDB, "Path to image/transformation database that should be updated with the newly created image." ); cl.EndGroup(); #endif cl.AddParameter( &FNameInputImage, "InputImage", "Input image path" )->SetProperties( cmtk::CommandLine::PROPS_IMAGE ); cl.AddParameter( &FNameOutputImage, "OutputImage", "Output image path" )->SetProperties( cmtk::CommandLine::PROPS_IMAGE | cmtk::CommandLine::PROPS_OUTPUT ); cl.Parse( argc, argv ); } catch ( const cmtk::CommandLine::Exception& e ) { cmtk::StdErr << e << "\n"; throw cmtk::ExitException( 1 ); } // Instantiate programm progress indicator. cmtk::ProgressConsole progressIndicator( "Intensity Bias Field Correction" ); cmtk::UniformVolume::SmartPtr inputImage( cmtk::VolumeIO::ReadOriented( FNameInputImage ) ); if ( ! inputImage || ! inputImage->GetData() ) { cmtk::StdErr << "ERROR: Could not read input image " << FNameInputImage << "\n"; throw cmtk::ExitException( 1 ); } if ( PaddingFlag ) { inputImage->GetData()->SetPaddingValue( PaddingValue ); } cmtk::UniformVolume::SmartPtr maskImage; if ( !FNameMaskImage.empty() ) { maskImage = cmtk::UniformVolume::SmartPtr( cmtk::VolumeIO::ReadOriented( FNameMaskImage ) ); if ( ! maskImage || ! maskImage->GetData() ) { cmtk::StdErr << "ERROR: Could not read mask image " << FNameMaskImage << "\n"; throw cmtk::ExitException( 1 ); } } else { if ( ThresholdAuto ) { ThresholdForegroundMin = static_cast( cmtk::TypedArrayNoiseEstimatorNaiveGaussian( *(inputImage->GetData()) ).GetNoiseThreshold() ); ThresholdForegroundFlag = true; cmtk::DebugOutput( 1 ) << "INFO: estimated foreground threshold from noise level as " << ThresholdForegroundMin << "\n"; } if ( ThresholdOtsuNBins > 0 ) { ThresholdForegroundMin = static_cast( cmtk::HistogramOtsuThreshold< cmtk::Histogram >( *(inputImage->GetData()->GetHistogram( ThresholdOtsuNBins )) ).Get() ); ThresholdForegroundFlag = true; cmtk::DebugOutput( 1 ) << "INFO: Otsu thresholding at " << ThresholdForegroundMin << "\n"; } if ( ThresholdForegroundFlag ) { maskImage = cmtk::UniformVolume::SmartPtr( inputImage->Clone( true /*copyData*/ ) ); maskImage->GetData()->SetPaddingValue( 0.0 ); maskImage->GetData()->ThresholdToPadding( cmtk::Types::DataItemRange( ThresholdForegroundMin, ThresholdForegroundMax ) ); } } typedef cmtk::EntropyMinimizationIntensityCorrectionFunctionalBase::SmartPtr FunctionalPointer; FunctionalPointer functional( NULL ); cmtk::UniformVolume::SmartPtr outputImage; cmtk::CoordinateVector v; size_t polynomialDegreeFrom = std::max( PolynomialDegreeAdd, PolynomialDegreeMul ); const size_t polynomialDegreeTo = polynomialDegreeFrom + 1; if ( IncrementalPolynomials ) polynomialDegreeFrom = 1; for ( size_t polynomialDegree = polynomialDegreeFrom; polynomialDegree < polynomialDegreeTo; ++polynomialDegree ) { const size_t degreeAdd = std::min( polynomialDegree, PolynomialDegreeAdd ); const size_t degreeMul = std::min( polynomialDegree, PolynomialDegreeMul ); functional = cmtk::CreateEntropyMinimizationIntensityCorrectionFunctionalDevice( degreeAdd, degreeMul, functional ); functional->SetUseLogIntensities( LogIntensities ); if ( SamplingDensity > 0 ) functional->SetSamplingDensity( SamplingDensity ); if ( NumberOfHistogramBins ) functional->SetNumberOfHistogramBins( NumberOfHistogramBins ); functional->SetInputImage( inputImage ); if ( maskImage && maskImage->GetData() ) { functional->SetForegroundMask( *maskImage ); } else { cmtk::StdErr << "ERROR: please use a mask image. Seriously.\n"; throw cmtk::ExitException( 1 ); } functional->GetParamVector( v ); cmtk::DebugOutput( 1 ).GetStream().printf( "Estimating bias field with order %u multiplicative / %u additive polynomials.\nNumber of parameters: %d\n", degreeMul, degreeAdd, v.Dim ); if ( (PolynomialDegreeAdd > 0) || (PolynomialDegreeMul > 0) ) { try { cmtk::Optimizer::SmartPtr optimizer; optimizer = cmtk::Optimizer::SmartPtr( new cmtk::BestDirectionOptimizer ); optimizer->SetFunctional( functional ); optimizer->Optimize( v, StepMax, StepMin ); } catch ( const char* cp ) { cmtk::StdErr << "EXCEPTION: " << cp << "\n"; } v.Print(); } } // make sure everything is according to optimum parameters outputImage = functional->GetOutputImage( v, false/*foregroundOnly*/ ); if ( !FNameOutputImage.empty() ) { if ( ! OutputFloatImage ) { outputImage->GetData()->ReplacePaddingData( 0.0 ); cmtk::TypedArray::SmartPtr convertedData( outputImage->GetData()->Convert( inputImage->GetData()->GetType() ) ); outputImage->SetData( convertedData ); } cmtk::VolumeIO::Write( *outputImage, FNameOutputImage ); #ifdef CMTK_USE_SQLITE if ( !updateDB.empty() ) { try { cmtk::ImageXformDB db( updateDB ); db.AddImage( FNameOutputImage, FNameInputImage ); } catch ( const cmtk::SQLite::Exception& ex ) { cmtk::StdErr << "ERROR: updating SQLite database failed - " << ex.what() << "\n"; } } #endif } if ( PolynomialDegreeAdd && !FNameBiasFieldAdd.empty() ) { cmtk::UniformVolume::SmartPtr biasField( functional->GetBiasFieldAdd( true /*completeImage*/ ) ); cmtk::VolumeIO::Write( *biasField, FNameBiasFieldAdd ); #ifdef CMTK_USE_SQLITE if ( !updateDB.empty() ) { try { cmtk::ImageXformDB db( updateDB ); db.AddImage( FNameBiasFieldAdd, FNameInputImage ); } catch ( const cmtk::SQLite::Exception& ex ) { cmtk::StdErr << "ERROR: updating SQLite database failed - " << ex.what() << "\n"; } } #endif } if ( PolynomialDegreeMul && !FNameBiasFieldMul.empty() ) { cmtk::UniformVolume::SmartPtr biasField( functional->GetBiasFieldMul( true /*completeImage*/ ) ); cmtk::VolumeIO::Write( *biasField, FNameBiasFieldMul ); #ifdef CMTK_USE_SQLITE if ( !updateDB.empty() ) { try { cmtk::ImageXformDB db( updateDB ); db.AddImage( FNameBiasFieldMul, FNameInputImage ); } catch ( const cmtk::SQLite::Exception& ex ) { cmtk::StdErr << "ERROR: updating SQLite database failed - " << ex.what() << "\n"; } } #endif } return 0; } #include "cmtkSafeMain" cmtk-3.3.1/apps/overlap.cxx000066400000000000000000000121301276303427400156200ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2011, 2013 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4869 $ // // $LastChangedDate: 2013-09-24 10:42:40 -0700 (Tue, 24 Sep 2013) $ // // $LastChangedBy: torstenrohlfing $ // */ #include #include #include #include #include #include #include #include #include #include bool PaddingInFlag = false; cmtk::Types::DataItem PaddingInValue = 0; std::vector vectorOfDataArrays; const char* MaskFileName = NULL; unsigned int NumberOfLabels = 0; unsigned int FirstLabel = 0; bool ByLabel = false; int doMain ( const int argc, const char* argv[] ) { try { cmtk::CommandLine cl; cl.SetProgramInfo( cmtk::CommandLine::PRG_TITLE, "Overlap computation" ); cl.SetProgramInfo( cmtk::CommandLine::PRG_DESCR, "Compute overlap measures between two or more images" ); cl.SetProgramInfo( cmtk::CommandLine::PRG_SYNTX, "overlap [options] image0 image1 [image2 ...]" ); cl.SetProgramInfo( cmtk::CommandLine::PRG_CATEG, "CMTK.Statistics and Modeling" ); typedef cmtk::CommandLine::Key Key; cl.AddOption( Key( 'p', "pad-in" ), &PaddingInValue, "Define padding value for input images", &PaddingInFlag ); cl.AddOption( Key( 'm', "mask" ), &MaskFileName, "Mask file for optional region-based analysis" ); cl.AddOption( Key( 'N', "num-labels" ), &NumberOfLabels, "Number of label values [default: autodetect]" ); cl.AddOption( Key( 'f', "first-label" ), &FirstLabel, "Index of first label value [default: 0]; labels below this one are ignored" ); cl.AddSwitch( Key( "by-label" ), &ByLabel, true, "Analysis by label, i.e., separately for each label in given (detected) range" ); if ( !cl.Parse( argc, argv ) ) { throw cmtk::ExitException( 1 ); } const char* next = cl.GetNext(); while ( next ) { cmtk::UniformVolume::SmartPtr volume( cmtk::VolumeIO::ReadOriented( next ) ); if ( ! volume || ! volume->GetData() ) { cmtk::StdErr << "ERROR: Could not read image " << next << "\n"; throw cmtk::ExitException( 1 ); } if ( PaddingInFlag ) { volume->GetData()->SetPaddingValue( PaddingInValue ); } if ( !vectorOfDataArrays.empty() ) { if ( vectorOfDataArrays[0]->GetDataSize() != volume->GetNumberOfPixels() ) { cmtk::StdErr << "ERROR: pixel count of image " << next << " does not match other images.\n"; throw cmtk::ExitException( 1 ); } } vectorOfDataArrays.push_back( volume->GetData() ); next = cl.GetNextOptional(); } } catch ( const cmtk::CommandLine::Exception& ex ) { cmtk::StdErr << ex; return false; } cmtk::TypedArray::SmartPtr maskData; if ( MaskFileName ) { cmtk::UniformVolume::SmartPtr maskVolume( cmtk::VolumeIO::ReadOriented( MaskFileName ) ); if ( ! maskVolume || ! maskVolume->GetData() ) { cmtk::StdErr << "ERROR: Could not read mask image " << MaskFileName << "\n"; throw cmtk::ExitException( 1 ); } maskData = maskVolume->GetData(); } cmtk::ProgressConsole progressIndicator; double overlapEqual = 0, overlapVolume = 0, overlapInverse = 0; const cmtk::OverlapMeasures overlapMeasures( vectorOfDataArrays ); if ( !NumberOfLabels ) { NumberOfLabels = 1 + overlapMeasures.GetMaxLabelValue() - FirstLabel; } if ( ByLabel ) { fprintf( stdout, "Label\tO[eq]\tO[vol]\tO[invvol]\n" ); for ( unsigned int label = FirstLabel; label < FirstLabel+NumberOfLabels; ++label ) { if ( overlapMeasures.ComputeGroupwiseOverlap( label, 1 /*numberOfLabels*/, overlapEqual, overlapVolume, overlapInverse ) > 0 ) { fprintf( stdout, "%u\t%lf\t%lf\t %lf\n", label, overlapEqual, overlapVolume, overlapInverse ); } } } else { overlapMeasures.ComputeGroupwiseOverlap( FirstLabel, NumberOfLabels, overlapEqual, overlapVolume, overlapInverse ); fprintf( stdout, "O[equal] = %lf\nO[volume] = %lf\nO[inverse_volume] = %lf\n", overlapEqual, overlapVolume, overlapInverse ); } return 0; } #include "cmtkSafeMain" cmtk-3.3.1/apps/probe.cxx000066400000000000000000000225371276303427400152730ustar00rootroot00000000000000/* // // Copyright 1997-2010 Torsten Rohlfing // // Copyright 2004-2014 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 3003 $ // // $LastChangedDate: 2011-03-17 11:18:47 -0700 (Thu, 17 Mar 2011) $ // // $LastChangedBy: torstenrohlfing $ // */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include cmtk::UniformVolumeInterpolatorBase* MakeInterpolator( const cmtk::UniformVolume& volume, const cmtk::Interpolators::InterpolationEnum interpolation, const int interpolationWindowRadius ) { switch ( interpolation ) { default: case cmtk::Interpolators::LINEAR: { typedef cmtk::UniformVolumeInterpolator TInterpolator; return new TInterpolator( volume ); } case cmtk::Interpolators::CUBIC: { typedef cmtk::UniformVolumeInterpolator TInterpolator; return new TInterpolator( volume ); } case cmtk::Interpolators::NEAREST_NEIGHBOR: { typedef cmtk::UniformVolumeInterpolator TInterpolator; return new TInterpolator( volume ); } case cmtk::Interpolators::COSINE_SINC: { switch ( interpolationWindowRadius ) { case 2: { typedef cmtk::UniformVolumeInterpolator< cmtk::Interpolators::CosineSinc<2> > TInterpolator; return new TInterpolator( volume ); } case 3: { typedef cmtk::UniformVolumeInterpolator< cmtk::Interpolators::CosineSinc<3> > TInterpolator; return new TInterpolator( volume ); } case 4: { typedef cmtk::UniformVolumeInterpolator< cmtk::Interpolators::CosineSinc<4> > TInterpolator; return new TInterpolator( volume ); } case 5: { typedef cmtk::UniformVolumeInterpolator< cmtk::Interpolators::CosineSinc<5> > TInterpolator; return new TInterpolator( volume ); } default: cmtk::StdErr.printf( "ERROR: Sinc window radius %d is not supported.\n", (int)interpolationWindowRadius ); } } break; case cmtk::Interpolators::HAMMING_SINC: { switch ( interpolationWindowRadius ) { case 2: { typedef cmtk::UniformVolumeInterpolator< cmtk::Interpolators::HammingSinc<2> > TInterpolator; return new TInterpolator( volume ); } case 3: { typedef cmtk::UniformVolumeInterpolator< cmtk::Interpolators::HammingSinc<3> > TInterpolator; return new TInterpolator( volume ); } case 4: { typedef cmtk::UniformVolumeInterpolator< cmtk::Interpolators::HammingSinc<4> > TInterpolator; return new TInterpolator( volume ); } case 5: { typedef cmtk::UniformVolumeInterpolator< cmtk::Interpolators::HammingSinc<5> > TInterpolator; return new TInterpolator( volume ); } default: cmtk::StdErr.printf( "ERROR: Sinc window radius %d is not supported.\n", (int)interpolationWindowRadius ); } } break; case cmtk::Interpolators::PARTIALVOLUME: { typedef cmtk::UniformVolumeInterpolatorPartialVolume TInterpolator; return new TInterpolator( volume ); } } return NULL; } typedef enum { COORDINATES_INDEXED, COORDINATES_ABSOLUTE, COORDINATES_RELATIVE, COORDINATES_PHYSICAL } CoordinateModeEnum; int doMain( const int argc, const char *argv[] ) { std::string inputImagePath; const char* readOrientation = "RAS"; CoordinateModeEnum mode = COORDINATES_ABSOLUTE; cmtk::Interpolators::InterpolationEnum interpolation = cmtk::Interpolators::NEAREST_NEIGHBOR; int interpolationWindowRadius = 3; try { cmtk::CommandLine cl; cl.SetProgramInfo( cmtk::CommandLine::PRG_TITLE, "Probe image data." ); cl.SetProgramInfo( cmtk::CommandLine::PRG_DESCR, "This tool prints pixel values or symbolic labels at a list of user-provided image coordinates." ); typedef cmtk::CommandLine::Key Key; cmtk::CommandLine::EnumGroup::SmartPtr modeGroup = cl.AddEnum( "coordinates", &mode, "Coordinate specification mode." ); modeGroup->AddSwitch( Key( "absolute" ), COORDINATES_ABSOLUTE, "Use absolute volume coordinates. For each dimension, the valid range is [0,FOV]." ); modeGroup->AddSwitch( Key( "indexed" ), COORDINATES_INDEXED, "Use grid indexes to specify coordinates. For each dimension, the valid value range is [0,Dims-1]." ); modeGroup->AddSwitch( Key( "relative" ), COORDINATES_RELATIVE, "Use relative volume coordinates. For each dimension, the valid range is [0,1]." ); modeGroup->AddSwitch( Key( "physical" ), COORDINATES_PHYSICAL, "Use physical volume coordinates. " "Each given location is transformed into image coordinates via the inverse of the images's index-to-physical space matrix." ); cmtk::CommandLine::EnumGroup::SmartPtr interpolationGroup = cl.AddEnum( "interpolation", &interpolation, "Image interpolation method." ); interpolationGroup->AddSwitch( Key( "nn" ), cmtk::Interpolators::NEAREST_NEIGHBOR, "Nearest neighbor interpolation" ); interpolationGroup->AddSwitch( Key( "linear" ), cmtk::Interpolators::LINEAR, "Trilinear interpolation" ); interpolationGroup->AddSwitch( Key( "cubic" ), cmtk::Interpolators::CUBIC, "Tricubic interpolation" ); interpolationGroup->AddSwitch( Key( "pv" ), cmtk::Interpolators::PARTIALVOLUME, "Partial volume interpolation" ); interpolationGroup->AddSwitch( Key( "sinc-cosine" ), cmtk::Interpolators::COSINE_SINC, "Sinc interpolation with cosine window" ); interpolationGroup->AddSwitch( Key( "sinc-hamming" ), cmtk::Interpolators::HAMMING_SINC, "Sinc interpolation with Hamming window" ); cl.AddOption( Key( "sinc-window-radius" ), &interpolationWindowRadius, "Window radius for Sinc interpolation" ); cl.AddSwitch( Key( "no-reorient" ), &readOrientation, static_cast( NULL ), "Disable image reorientation into RAS alignment." ); cl.AddParameter( &inputImagePath, "InputImage", "Input image path" )->SetProperties( cmtk::CommandLine::PROPS_IMAGE ); cl.Parse( argc, argv ); } catch ( const cmtk::CommandLine::Exception& e ) { cmtk::StdErr << e << "\n"; throw cmtk::ExitException( 1 ); } cmtk::UniformVolume::SmartPtr volume; if ( readOrientation ) volume = cmtk::VolumeIO::ReadOriented( inputImagePath, readOrientation ); else volume = cmtk::VolumeIO::Read( inputImagePath ); if ( ! volume || ! volume->GetData() ) { cmtk::StdErr << "ERROR: could not read image " << inputImagePath << "\n"; throw cmtk::ExitException( 1 ); } cmtk::UniformVolumeInterpolatorBase::SmartPtr interpolator( MakeInterpolator( *volume, interpolation, interpolationWindowRadius ) ); FILE *infile = stdin; FILE *outfile = stdout; cmtk::AffineXform::MatrixType physicalToImageMatrix; try { physicalToImageMatrix = volume->GetImageToPhysicalMatrix().GetInverse(); } catch ( const cmtk::AffineXform::MatrixType::SingularMatrixException& ) { cmtk::StdErr << "ERROR: singular image-to-physical space matrix encountered\n"; throw cmtk::ExitException( 1 ); } while ( ! feof( infile ) ) { double xyz[3]; if ( 3 == fscanf( infile, "%20lf %20lf %20lf", xyz, xyz+1, xyz+2 ) ) { cmtk::UniformVolume::SpaceVectorType v = cmtk::UniformVolume::SpaceVectorType::FromPointer( xyz ); switch ( mode ) { case COORDINATES_INDEXED: // absolute image coordinate is index times pixel size v *= volume->m_Delta; break; case COORDINATES_ABSOLUTE: // nothing to do - lookup will be done by absolute coordinate break; case COORDINATES_RELATIVE: // absolute image coordinate is relative times volume size v *= volume->m_Size; break; case COORDINATES_PHYSICAL: // absolute image coordinate is physical transformed by inverse image-to-physical matrix v *= physicalToImageMatrix; break; } cmtk::Types::DataItem value; if ( interpolator->GetDataAt( v, value ) ) { fprintf( outfile, "%lf\n", static_cast( value ) ); } else { fprintf( outfile, "NAN\n" ); } } } return 0; } #include "cmtkSafeMain" cmtk-3.3.1/apps/pxsearch.cxx000066400000000000000000000200371276303427400157720ustar00rootroot00000000000000/* // // Copyright 2016 Google, Inc. // // Copyright 1997-2010 Torsten Rohlfing // // Copyright 2004-2014 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 5403 $ // // $LastChangedDate: 2016-01-14 20:59:04 -0800 (Thu, 14 Jan 2016) $ // // $LastChangedBy: torstenrohlfing $ // */ #include #include #include #include #include #include #include #include typedef enum { COORDINATES_INDEXED, COORDINATES_ABSOLUTE, COORDINATES_RELATIVE, COORDINATES_PHYSICAL } CoordinateModeEnum; int doMain( const int argc, const char *argv[] ) { std::string inputImagePath; const char* readOrientation = "RAS"; CoordinateModeEnum inputMode = COORDINATES_ABSOLUTE; CoordinateModeEnum outputMode = COORDINATES_ABSOLUTE; const char* radiusStr = "1"; try { cmtk::CommandLine cl; cl.SetProgramInfo( cmtk::CommandLine::PRG_TITLE, "Search image neighborhoods for pixels." ); cl.SetProgramInfo( cmtk::CommandLine::PRG_DESCR, "This tool reads an image file, as well as a list of pixel coordinates from standard input. For each pixel, a local neighbourhood in the image is searched for the maximum " "value. The location of the maximum is then written to standard output." ); typedef cmtk::CommandLine::Key Key; cmtk::CommandLine::EnumGroup::SmartPtr inputModeGroup = cl.AddEnum( "input-coordinates", &inputMode, "Coordinate specification mode for program input." ); inputModeGroup->AddSwitch( Key( "absolute" ), COORDINATES_ABSOLUTE, "Use absolute volume coordinates. For each dimension, the valid range is [0,FOV]." ); inputModeGroup->AddSwitch( Key( "indexed" ), COORDINATES_INDEXED, "Use grid indexes to specify coordinates. For each dimension, the valid value range is [0,Dims-1]." ); inputModeGroup->AddSwitch( Key( "relative" ), COORDINATES_RELATIVE, "Use relative volume coordinates. For each dimension, the valid range is [0,1]." ); inputModeGroup->AddSwitch( Key( "physical" ), COORDINATES_PHYSICAL, "Use physical volume coordinates. " "Each given location is transformed into image coordinates via the inverse of the images's index-to-physical space matrix." ); cmtk::CommandLine::EnumGroup::SmartPtr outputModeGroup = cl.AddEnum( "output-coordinates", &outputMode, "Coordinate specification mode for program output." ); outputModeGroup->AddSwitch( Key( "absolute" ), COORDINATES_ABSOLUTE, "Use absolute volume coordinates. For each dimension, the valid range is [0,FOV]." ); outputModeGroup->AddSwitch( Key( "indexed" ), COORDINATES_INDEXED, "Use grid indexes to specify coordinates. For each dimension, the valid value range is [0,Dims-1]." ); outputModeGroup->AddSwitch( Key( "relative" ), COORDINATES_RELATIVE, "Use relative volume coordinates. For each dimension, the valid range is [0,1]." ); outputModeGroup->AddSwitch( Key( "physical" ), COORDINATES_PHYSICAL, "Use physical volume coordinates. " "Each given location is transformed into image coordinates via the inverse of the images's index-to-physical space matrix." ); cl.AddOption( Key( "radius" ), &radiusStr, "Radius of the search region in pixels (specified either as triple \"rX,rY,rZ\", or a single value, \"rXYZ\"). " "The region searched is [2*rX+1,2*rY+1,2*rZ+1] pixels large, centered at the input location (but cropped at the image boundary)." ); cl.AddSwitch( Key( "no-reorient" ), &readOrientation, static_cast( NULL ), "Disable image reorientation into RAS alignment." ); cl.AddParameter( &inputImagePath, "InputImage", "Input image path" )->SetProperties( cmtk::CommandLine::PROPS_IMAGE ); cl.Parse( argc, argv ); } catch ( const cmtk::CommandLine::Exception& e ) { cmtk::StdErr << e << "\n"; throw cmtk::ExitException( 1 ); } cmtk::UniformVolume::SmartPtr volume; if ( readOrientation ) volume = cmtk::VolumeIO::ReadOriented( inputImagePath, readOrientation ); else volume = cmtk::VolumeIO::Read( inputImagePath ); if ( ! volume || ! volume->GetData() ) { cmtk::StdErr << "ERROR: could not read image " << inputImagePath << "\n"; throw cmtk::ExitException( 1 ); } cmtk::FixedVector<3,int> radius; if ( 3 != sscanf( radiusStr, "%6d,%6d,%6d", &radius[0], &radius[1], &radius[2] ) ) { radius[0] = radius[1] = radius[2] = atof( radiusStr ); } cmtk::UniformVolume::SpaceVectorType v; std::string restOfLine; cmtk::AffineXform::MatrixType physicalToImageMatrix; try { physicalToImageMatrix = volume->GetImageToPhysicalMatrix().GetInverse(); } catch ( const cmtk::AffineXform::MatrixType::SingularMatrixException& ) { cmtk::StdErr << "ERROR: singular image-to-physical matrix encountered\n"; throw cmtk::ExitException( 1 ); } while ( ! std::cin.eof() ) { std::cin >> v; std::getline( std::cin, restOfLine ); if ( std::cin.eof() ) break; switch ( inputMode ) { case COORDINATES_INDEXED: // nothing to do - lookup will be done by absolute coordinate break; case COORDINATES_ABSOLUTE: // index is absolute image coordinate divided (component-wise) by pixel size v /= volume->m_Delta; break; case COORDINATES_RELATIVE: // absolute image coordinate is relative times volume size (v *= volume->m_Size) /= volume->m_Delta; break; case COORDINATES_PHYSICAL: // absolute image coordinate is physical transformed by inverse image-to-physical matrix (v *= physicalToImageMatrix) /= volume->m_Delta; break; } // convert to discrete index by rounding (not simple truncation) const cmtk::UniformVolume::IndexType ijk = v.AddScalar( 0.5 ); cmtk::DataGrid::IndexType maxIndex = ijk; cmtk::Types::DataItem maxValue = volume->GetDataAt( volume->GetOffsetFromIndex( ijk ) ); cmtk::DataGrid::IndexType probe = ijk; for ( probe[2] = std::max( 0, ijk[2]-radius[2] ); probe[2] < std::min( volume->m_Dims[2], ijk[2]+radius[2]+1 ); ++probe[2] ) for ( probe[1] = std::max( 0, ijk[1]-radius[1] ); probe[1] < std::min( volume->m_Dims[1], ijk[1]+radius[1]+1 ); ++probe[1] ) for ( probe[0] = std::max( 0, ijk[0]-radius[0] ); probe[0] < std::min( volume->m_Dims[0], ijk[0]+radius[0]+1 ); ++probe[0] ) { const cmtk::Types::DataItem value = volume->GetDataAt( volume->GetOffsetFromIndex( probe ) ); if ( value > maxValue ) { maxValue = value; maxIndex = probe; } } v = maxIndex; switch ( outputMode ) { case COORDINATES_INDEXED: // nothing to do - already indexed break; case COORDINATES_ABSOLUTE: // absolute image coordinate is index times pixel size v *= volume->m_Delta; break; case COORDINATES_RELATIVE: // absolute image coordinate is relative times volume size (v *= volume->m_Delta) /= volume->m_Size; break; case COORDINATES_PHYSICAL: // absolute image coordinate is obtained using image-to-physical matrix (v *= volume->m_Delta) *= volume->GetImageToPhysicalMatrix(); break; } std::cout << v << restOfLine << "\n"; } return 0; } #include "cmtkSafeMain" cmtk-3.3.1/apps/reformatx.cxx000066400000000000000000000502171276303427400161670ustar00rootroot00000000000000/* // // Copyright 1997-2010 Torsten Rohlfing // // Copyright 2004-2013 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 5157 $ // // $LastChangedDate: 2014-01-12 14:04:53 -0800 (Sun, 12 Jan 2014) $ // // $LastChangedBy: torsten_at_home $ // */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef CMTK_USE_SQLITE # include #endif bool JacobianCorrectGlobal = false; bool MassPreservingReformat = false; bool TargetMask = false; const char* TargetVolumeName = NULL; const char* FloatingVolumeName = NULL; cmtk::UniformVolume::SmartPtr UserDefinedTargetVolume; #ifdef CMTK_USE_SQLITE const char* updateDB = NULL; #endif void CallbackTargetVolume( const char* arg ) { int gridDims[3] = { 0, 0, 0 }; float gridDelta[3] = { 0, 0, 0 }; float gridOffset[3] = { 0, 0, 0 }; const size_t numArgs = sscanf( arg, "%6d,%6d,%6d:%15f,%15f,%15f:%15f,%15f,%15f", gridDims, gridDims+1, gridDims+2, gridDelta, gridDelta+1, gridDelta+2, gridOffset, gridOffset+1, gridOffset+2 ); if ( (numArgs != 6) && (numArgs != 9) ) { cmtk::StdErr << "ERROR: target volume definition must be int,int,int:float,float,float or int,int,int:float,float,float:float,float,float\n"; throw cmtk::ExitException( 1 ); } UserDefinedTargetVolume = cmtk::UniformVolume::SmartPtr( new cmtk::UniformVolume( cmtk::UniformVolume::IndexType::FromPointer( gridDims ), gridDelta[0], gridDelta[1], gridDelta[2] ) ); UserDefinedTargetVolume->SetMetaInfo( cmtk::META_SPACE, cmtk::AnatomicalOrientation::ORIENTATION_STANDARD ); UserDefinedTargetVolume->SetMetaInfo( cmtk::META_SPACE_ORIGINAL, cmtk::AnatomicalOrientation::ORIENTATION_STANDARD ); UserDefinedTargetVolume->SetMetaInfo( cmtk::META_IMAGE_ORIENTATION, cmtk::AnatomicalOrientation::ORIENTATION_STANDARD ); UserDefinedTargetVolume->SetMetaInfo( cmtk::META_IMAGE_ORIENTATION_ORIGINAL, cmtk::AnatomicalOrientation::ORIENTATION_STANDARD ); if ( numArgs == 9 ) { UserDefinedTargetVolume->SetOffset( cmtk::UniformVolume::CoordinateVectorType::FromPointer( gridOffset ) ); } } cmtk::XformList TargetToReference; cmtk::XformList ReferenceToFloating; cmtk::ReformatVolume::Mode Mode = cmtk::ReformatVolume::REFORMAT_PLAIN; cmtk::Interpolators::InterpolationEnum Interpolation = cmtk::Interpolators::LINEAR; int InterpolatorWindowRadius = 3; // default to auto-selection of data type cmtk::ScalarDataType DataType = cmtk::TYPE_NONE; cmtk::Types::DataItem OutPaddingValue = 0; bool OutPaddingValueFlag = true; cmtk::Types::DataItem FltPaddingValue = 0; bool FltPaddingValueFlag = false; const char* OutputImageName = "reformat.nii"; bool CropImages = false; cmtk::DataGrid::RegionType CropImagesRegion; void CallbackCropImages( const char* arg ) { int cropFrom[3], cropTo[3]; CropImages = (6 == sscanf( arg, "%6d,%6d,%6d,%6d,%6d,%6d", cropFrom, cropFrom+1, cropFrom+2, cropTo,cropTo+1,cropTo+2 ) ); if ( CropImages ) { CropImagesRegion = cmtk::DataGrid::RegionType( cmtk::DataGrid::IndexType::FromPointer( cropFrom ), cmtk::DataGrid::IndexType::FromPointer( cropTo ) ); } } bool TargetImageOffsetReal = false; bool TargetImageOffsetPixels = false; cmtk::UniformVolume::CoordinateVectorType TargetImageOffset( 0.0 ); void CallbackTargetImageOffset( const char* arg ) { float x, y, z; if ( 3 != sscanf( arg, "%15f,%15f,%15f", &x, &y, &z ) ) { x = y = z = 0; } TargetImageOffset[0] = x; TargetImageOffset[1] = y; TargetImageOffset[2] = z; TargetImageOffsetReal = true; TargetImageOffsetPixels = false; } void CallbackTargetImageOffsetPixels( const char* arg ) { float x, y, z; if ( 3 != sscanf( arg, "%15f,%15f,%15f", &x, &y, &z ) ) { x = y = z = 0; } TargetImageOffset[0] = x; TargetImageOffset[1] = y; TargetImageOffset[2] = z; TargetImageOffsetReal = false; TargetImageOffsetPixels = true; } template void InitializeReformatVolume( cmtk::TypedArray::SmartPtr& reformatData, cmtk::UniformVolume::SmartPtr& targetVolume, cmtk::UniformVolume::SmartPtr& floatingVolume ) { switch ( Mode ) { default: case cmtk::ReformatVolume::REFORMAT_PLAIN: { if ( ! floatingVolume ) { cmtk::StdErr << "ERROR: floating volume must be defined in plain reformat mode; use '--floating' command line option\n"; throw cmtk::ExitException( 1 ); } cmtk::ReformatVolume::Plain plain( DataType ); typename TInterpolator::SmartPtr interpolator ( new TInterpolator(*floatingVolume) ); if ( OutPaddingValueFlag ) plain.SetPaddingValue( OutPaddingValue ); reformatData = cmtk::TypedArray::SmartPtr( cmtk::ReformatVolume::ReformatMasked( targetVolume, TargetToReference, ReferenceToFloating, plain, floatingVolume, interpolator ) ); if ( MassPreservingReformat ) { cmtk::ReformatVolume::Jacobian jacobian( cmtk::TYPE_DOUBLE, false /*correctGlobalScale*/ ); cmtk::XformList emptyXformList; cmtk::TypedArray::SmartPtr jacobianData( cmtk::ReformatVolume::ReformatMasked( targetVolume, emptyXformList, TargetToReference, jacobian, NULL, TInterpolator::SmartConstPtr::Null ) ); const size_t nPixels = reformatData->GetDataSize(); for ( size_t i = 0; i < nPixels; ++i ) { cmtk::Types::DataItem v, j; if ( reformatData->Get( v, i ) && jacobianData->Get( j, i ) ) reformatData->Set( v*j, i ); else reformatData->SetPaddingAt( i ); } } break; } case cmtk::ReformatVolume::REFORMAT_JACOBIAN: { cmtk::ReformatVolume::Jacobian jacobian( DataType, JacobianCorrectGlobal ); if ( OutPaddingValueFlag ) jacobian.SetPaddingValue( OutPaddingValue ); reformatData = cmtk::TypedArray::SmartPtr( cmtk::ReformatVolume::ReformatMasked( targetVolume, TargetToReference, ReferenceToFloating, jacobian, NULL, TInterpolator::SmartConstPtr::Null ) ); break; } } } void ReformatPullback() { cmtk::UniformVolume::SmartPtr targetVolume = UserDefinedTargetVolume; if ( TargetVolumeName ) { if ( TargetMask ) targetVolume = cmtk::UniformVolume::SmartPtr( cmtk::VolumeIO::ReadOriented( TargetVolumeName ) ); else targetVolume = cmtk::UniformVolume::SmartPtr( cmtk::VolumeIO::ReadGridOriented( TargetVolumeName ) ); if ( ! targetVolume ) { cmtk::StdErr << "ERROR: could not read target volume " << TargetVolumeName << "\n"; throw cmtk::ExitException( 1 ); } } if ( CropImages ) { targetVolume->CropRegion() = CropImagesRegion; targetVolume = cmtk::UniformVolume::SmartPtr( targetVolume->GetCroppedVolume() ); } TargetToReference.SetEpsilon( 0.1 * targetVolume->GetMinDelta() ); ReferenceToFloating.SetEpsilon( 0.1 * targetVolume->GetMinDelta() ); if ( TargetImageOffsetPixels ) { for ( int dim = 0; dim < 3; ++dim ) TargetImageOffset[dim] *= targetVolume->m_Delta[dim]; targetVolume->SetOffset( TargetImageOffset ); cmtk::DebugOutput( 1 ) << "INFO: setting reference image offset to " << TargetImageOffset[0] << "/" << TargetImageOffset[1] << "/" << TargetImageOffset[2] << "\n"; } if ( TargetImageOffsetReal ) { targetVolume->SetOffset( TargetImageOffset ); cmtk::DebugOutput( 1 ) << "INFO: setting reference image offset to " << TargetImageOffset[0] << "/" << TargetImageOffset[1] << "/" << TargetImageOffset[2] << "\n"; } cmtk::UniformVolume::SmartPtr floatingVolume; cmtk::TypedArray::SmartPtr floatingData( NULL ); if ( FloatingVolumeName ) { floatingVolume = cmtk::UniformVolume::SmartPtr( cmtk::VolumeIO::ReadOriented( FloatingVolumeName ) ); if ( floatingVolume ) { floatingData = floatingVolume->GetData(); if ( ! floatingData ) { cmtk::StdErr << "ERROR: floating volume " << FloatingVolumeName << " seems to have no data\n"; throw cmtk::ExitException( 1 ); } else { if ( FltPaddingValueFlag ) floatingData->SetPaddingValue( FltPaddingValue ); } } else { cmtk::StdErr << "ERROR: floating volume " << FloatingVolumeName << " could not be read\n"; throw cmtk::ExitException( 1 ); } } if ( !TargetMask ) { // unless we're in mask mode, remove target pixel data. cmtk::TypedArray::SmartPtr nullData( NULL ); targetVolume->SetData( nullData ); } else { cmtk::DebugOutput( 1 ) << "INFO: Using target data as binary mask.\n"; } cmtk::ProgressConsole progressIndicator; cmtk::TypedArray::SmartPtr reformatData; switch ( Interpolation ) { default: case cmtk::Interpolators::LINEAR: { typedef cmtk::UniformVolumeInterpolator TInterpolator; InitializeReformatVolume( reformatData, targetVolume, floatingVolume ); break; } case cmtk::Interpolators::CUBIC: { typedef cmtk::UniformVolumeInterpolator TInterpolator; InitializeReformatVolume( reformatData, targetVolume, floatingVolume ); break; } case cmtk::Interpolators::NEAREST_NEIGHBOR: { typedef cmtk::UniformVolumeInterpolator TInterpolator; InitializeReformatVolume( reformatData, targetVolume, floatingVolume ); break; } case cmtk::Interpolators::COSINE_SINC: { switch ( InterpolatorWindowRadius ) { case 2: { typedef cmtk::UniformVolumeInterpolator< cmtk::Interpolators::CosineSinc<2> > TInterpolator; InitializeReformatVolume( reformatData, targetVolume, floatingVolume ); break; } case 3: { typedef cmtk::UniformVolumeInterpolator< cmtk::Interpolators::CosineSinc<3> > TInterpolator; InitializeReformatVolume( reformatData, targetVolume, floatingVolume ); break; } case 4: { typedef cmtk::UniformVolumeInterpolator< cmtk::Interpolators::CosineSinc<4> > TInterpolator; InitializeReformatVolume( reformatData, targetVolume, floatingVolume ); break; } case 5: { typedef cmtk::UniformVolumeInterpolator< cmtk::Interpolators::CosineSinc<5> > TInterpolator; InitializeReformatVolume( reformatData, targetVolume, floatingVolume ); break; } default: cmtk::StdErr.printf( "ERROR: Sinc window radius %d is not supported.\n", (int)InterpolatorWindowRadius ); } } break; case cmtk::Interpolators::HAMMING_SINC: { switch ( InterpolatorWindowRadius ) { case 2: { typedef cmtk::UniformVolumeInterpolator< cmtk::Interpolators::HammingSinc<2> > TInterpolator; InitializeReformatVolume( reformatData, targetVolume, floatingVolume ); break; } case 3: { typedef cmtk::UniformVolumeInterpolator< cmtk::Interpolators::HammingSinc<3> > TInterpolator; InitializeReformatVolume( reformatData, targetVolume, floatingVolume ); break; } case 4: { typedef cmtk::UniformVolumeInterpolator< cmtk::Interpolators::HammingSinc<4> > TInterpolator; InitializeReformatVolume( reformatData, targetVolume, floatingVolume ); break; } case 5: { typedef cmtk::UniformVolumeInterpolator< cmtk::Interpolators::HammingSinc<5> > TInterpolator; InitializeReformatVolume( reformatData, targetVolume, floatingVolume ); break; } default: cmtk::StdErr.printf( "ERROR: Sinc window radius %d is not supported.\n", (int)InterpolatorWindowRadius ); } } break; case cmtk::Interpolators::PARTIALVOLUME: { typedef cmtk::UniformVolumeInterpolatorPartialVolume TInterpolator; InitializeReformatVolume( reformatData, targetVolume, floatingVolume ); break; } } if ( reformatData ) { targetVolume->SetData( reformatData ); cmtk::VolumeIO::Write( *targetVolume, OutputImageName ); #ifdef CMTK_USE_SQLITE if ( updateDB ) { if ( TargetVolumeName ) { try { cmtk::ImageXformDB db( updateDB ); db.AddImage( OutputImageName, TargetVolumeName ); } catch ( const cmtk::SQLite::Exception& ex ) { cmtk::StdErr << "ERROR: updating SQLite database failed - " << ex.what() << "\n"; } } } #endif } else { cmtk::StdErr << "ERROR: no reformatted data was generated.\n"; } } int doMain( const int argc, const char* argv[] ) { try { cmtk::CommandLine cl; cl.SetProgramInfo( cmtk::CommandLine::PRG_TITLE, "Volume reformatter" ); cl.SetProgramInfo( cmtk::CommandLine::PRG_DESCR, "Extended volume reformatter tool to compute reformatted images and Jacobian maps from arbitrary sequences of concatenated transformations" ); cl.SetProgramInfo( cmtk::CommandLine::PRG_SYNTX, "reformatx [options] --floating floatingImg target x0 [x1 ...]\n OR\n" "reformatx [options] target [x0 x1 ...] {-j,--jacobian} xx0 [xx1 ...]\n OR\n" "WHERE x0 ... xN and xx0 ... xxN is [{-i,--inverse}] transformation##. " "(If the first transformation in the sequence is inverted, then '--inverse' must be preceded by '--', i.e., use '-- --inverse xform.path')." ); typedef cmtk::CommandLine::Key Key; cl.BeginGroup( "PlainOptions", "Options for Plain Reformatting" ); cmtk::CommandLine::EnumGroup::SmartPtr interpolationGroup = cl.AddEnum( "interpolation", &Interpolation, "Image interpolation method." ); interpolationGroup->AddSwitch( Key( "linear" ), cmtk::Interpolators::LINEAR, "Trilinear interpolation" ); interpolationGroup->AddSwitch( Key( "nn" ), cmtk::Interpolators::NEAREST_NEIGHBOR, "Nearest neighbor interpolation" ); interpolationGroup->AddSwitch( Key( "cubic" ), cmtk::Interpolators::CUBIC, "Tricubic interpolation" ); interpolationGroup->AddSwitch( Key( "pv" ), cmtk::Interpolators::PARTIALVOLUME, "Partial volume interpolation" ); interpolationGroup->AddSwitch( Key( "sinc-cosine" ), cmtk::Interpolators::COSINE_SINC, "Sinc interpolation with cosine window" ); interpolationGroup->AddSwitch( Key( "sinc-hamming" ), cmtk::Interpolators::HAMMING_SINC, "Sinc interpolation with Hamming window" ); cl.AddOption( Key( "sinc-window-radius" ), &InterpolatorWindowRadius, "Window radius for Sinc interpolation" ); cl.AddSwitch( Key( "preserve-mass" ), &MassPreservingReformat, true, "Mass-preserving reformatting: multiply every reformatted value with the Jacobian determinant of the applied transformation." ); cl.EndGroup(); cl.BeginGroup( "JacobianOptions", "Options for Jacobian Map Reformatting" ); cl.AddSwitch( Key( "jacobian-correct-global" ), &JacobianCorrectGlobal, true, "Correct Jacobian maps for global scale." ); cl.AddSwitch( Key( "no-jacobian-correct-global" ), &JacobianCorrectGlobal, false, "Do not correct Jacobian maps for global scale." ); cl.EndGroup(); cl.BeginGroup( "Input", "Input Options" ); cl.AddCallback( Key( "target-grid" ), CallbackTargetVolume, "Define target grid for reformating as Nx,Ny,Nz:dX,dY,dZ[:Ox,Oy,Oz] (dims:pixel:offset)" ); cl.AddSwitch( Key( 'm', "mask" ), &TargetMask, true, "Use target pixel data as binary mask." ); cl.AddCallback( Key( "crop" ), CallbackCropImages, "Crop target image: x0,y0,z0,x1,y1,z2" ); cl.AddCallback( Key( 'O', "target-offset" ), CallbackTargetImageOffset, "Override target image offset and set to x,y,z mm" ); cl.AddCallback( Key( "target-offset-pixels" ), CallbackTargetImageOffsetPixels, "Override target image offset and set to dx,dy,dz pixels" ); cl.AddOption( Key( 'F', "floating" ), &FloatingVolumeName, "Format and path of floating image." ); cl.AddOption( Key( "pad-floating" ), &FltPaddingValue, "Padding value for the floating image", &FltPaddingValueFlag ); cl.BeginGroup( "Output", "Output Options" ); cl.AddOption( Key( 'o', "outfile" ), &OutputImageName, "Format and path of output image." ); cl.EndGroup(); cmtk::CommandLine::EnumGroup::SmartPtr typeGroup = cl.AddEnum( "outputtype", &DataType, "Scalar data type for the output image." ); typeGroup->AddSwitch( Key( "char" ), cmtk::TYPE_CHAR, "8 bits, signed" ); typeGroup->AddSwitch( Key( "byte" ), cmtk::TYPE_BYTE, "8 bits, unsigned" ); typeGroup->AddSwitch( Key( "short" ), cmtk::TYPE_SHORT, "16 bits, signed" ); typeGroup->AddSwitch( Key( "ushort" ), cmtk::TYPE_USHORT, "16 bits, unsigned" ); typeGroup->AddSwitch( Key( "int" ), cmtk::TYPE_INT, "32 bits signed" ); typeGroup->AddSwitch( Key( "uint" ), cmtk::TYPE_UINT, "32 bits unsigned" ); typeGroup->AddSwitch( Key( "float" ), cmtk::TYPE_FLOAT, "32 bits floating point" ); typeGroup->AddSwitch( Key( "double" ), cmtk::TYPE_DOUBLE, "64 bits floating point\n" ); cl.AddOption( Key( 'P', "pad-out" ), &OutPaddingValue, "Padding value for output image", &OutPaddingValueFlag ); cl.EndGroup(); #ifdef CMTK_USE_SQLITE cl.BeginGroup( "Database", "Image/Transformation Database" ); cl.AddOption( Key( "db" ), &updateDB, "Path to image/transformation database that should be updated with the newly created image." ); cl.EndGroup(); #endif cl.Parse( argc, argv ); if ( ! UserDefinedTargetVolume ) TargetVolumeName = cl.GetNext(); const char* next = cl.GetNextOptional(); while ( next ) { if ( ! strcmp( next, "-j" ) || ! strcmp( next, "--jacobian" ) ) break; const bool inverse = ! strcmp( next, "-i" ) || ! strcmp( next, "--inverse" ); if ( inverse ) next = cl.GetNext(); cmtk::Xform::SmartPtr xform( cmtk::XformIO::Read( next ) ); if ( ! xform ) { cmtk::StdErr << "ERROR: could not read target-to-reference transformation from " << next << "\n"; throw cmtk::ExitException( 1 ); } TargetToReference.Add( xform, inverse, xform->GetGlobalScaling() ); next = cl.GetNextOptional(); } if ( next ) { if ( ! strcmp( next, "-j" ) || ! strcmp( next, "--jacobian" ) ) { Mode = cmtk::ReformatVolume::REFORMAT_JACOBIAN; next = cl.GetNext(); } switch ( Mode ) { case cmtk::ReformatVolume::REFORMAT_JACOBIAN: while ( next ) { const bool inverse = ! strcmp( next, "-i" ) || ! strcmp( next, "--inverse" ); if ( inverse ) next = cl.GetNext(); cmtk::Xform::SmartPtr xform( cmtk::XformIO::Read( next ) ); if ( ! xform ) { cmtk::StdErr << "ERROR: could not read target-to-floating transformation from " << next << "\n"; throw cmtk::ExitException( 1 ); } ReferenceToFloating.Add( xform, inverse, xform->GetGlobalScaling() ); next = cl.GetNextOptional(); } break; case cmtk::ReformatVolume::REFORMAT_PLAIN: default: break; } } } catch ( const cmtk::CommandLine::Exception& e ) { cmtk::StdErr << e << "\n"; throw cmtk::ExitException( 1 ); } ReformatPullback(); return 0; } #include "cmtkSafeMain" cmtk-3.3.1/apps/registration.cxx000066400000000000000000000035531276303427400166730ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2011 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 2765 $ // // $LastChangedDate: 2011-01-18 16:17:56 -0800 (Tue, 18 Jan 2011) $ // // $LastChangedBy: torstenrohlfing $ // */ #include #include #include #include #include #include #include #include int doMain ( const int argc, const char *argv[] ) { #ifdef DEBUG int entry_mem = cmtk::Memory::Used(); #endif try { cmtk::AffineRegistrationCommandLine Registration( argc, argv ); // set up console progress reporting cmtk::ProgressConsole progressInstance( "AffineImageRegistration" ); Registration.Register(); } catch ( const cmtk::VoxelRegistration::ConstructorFailed& ) { return 1; } #ifdef DEBUG cmtk::Memory::Diff(entry_mem,"AffineRegistration"); #endif return 0; } #include "cmtkSafeMain" cmtk-3.3.1/apps/registrationx.cxx000066400000000000000000000036011276303427400170550ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2011 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 2765 $ // // $LastChangedDate: 2011-01-18 16:17:56 -0800 (Tue, 18 Jan 2011) $ // // $LastChangedBy: torstenrohlfing $ // */ #include #include #include #include #include #include #include #include int doMain ( const int argc, const char *argv[] ) { #ifdef DEBUG int entry_mem = cmtk::Memory::Used(); #endif try { cmtk::ImagePairAffineRegistrationCommandLine Registration( argc, argv ); // set up console progress reporting cmtk::ProgressConsole progressInstance( "AffineImageRegistration" ); Registration.Register(); } catch ( const cmtk::ImagePairRegistration::ConstructorFailed& ) { return 1; } #ifdef DEBUG cmtk::Memory::Diff(entry_mem,"AffineRegistration"); #endif return 0; } #include "cmtkSafeMain" cmtk-3.3.1/apps/regress.cxx000066400000000000000000000243561276303427400156370ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2011, 2013, 2014 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 5129 $ // // $LastChangedDate: 2014-01-09 13:48:15 -0800 (Thu, 09 Jan 2014) $ // // $LastChangedBy: torstenrohlfing $ // */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include void ParseArgsString( const std::string& in, std::vector& args ) { std::string arg = in; while ( arg.length() ) { const size_t colon = arg.find( ':' ); if ( colon == std::string::npos ) { args.push_back( atof( arg.c_str() ) ); arg.clear(); } else { const std::string next = arg.substr( 0, colon ); args.push_back( atof( next.c_str() ) ); arg.erase( 0, colon+1 ); } } } void ParseArgsWarpString( const std::string& in, std::string& fname, std::vector& args ) { size_t colon = in.find( ':' ); if ( colon == std::string::npos ) { cmtk::StdErr << "ERROR: could not parse argument string '" << in << "'; must be ID:x0:[x1...]\n"; throw cmtk::ExitException( 1 ); } fname = in.substr( 0, colon ); ParseArgsString( in.substr( colon+1 ), args ); } int doMain( const int argc, const char* argv[] ) { std::list pathList; const char* outFileName = NULL; const char* controlFileName = NULL; int order = 1; size_t nParameters = 0; size_t nActualParameters = 0; std::vector design; std::vector meanParam; const char* sParameters = NULL; std::vector param; const char* pathPrintf = "%s"; try { cmtk::CommandLine cl; cl.SetProgramInfo( cmtk::CommandLine::PRG_TITLE, "Regression" ); cl.SetProgramInfo( cmtk::CommandLine::PRG_DESCR, "Linear (and higher-order polynomial) regression of deformation fields and images." ); cl.SetProgramInfo( cmtk::CommandLine::PRG_SYNTX, "regress [options] controlFile" ); typedef cmtk::CommandLine::Key Key; cl.AddOption( Key( 'o', "output" ), &outFileName, "File name for output." ); cl.AddOption( Key( 'p', "parameters" ), &sParameters, "Model parameters for regression instantiation" ); cl.AddOption( Key( 's', "substitution" ), &pathPrintf, "Printf format string for ID-to-path substitition" ); cl.AddOption( Key( 'O', "order" ), &order, "Polynonial order of the regression [default: 1=linear]" ); cl.Parse( argc, argv ); controlFileName = cl.GetNext(); } catch ( const cmtk::CommandLine::Exception& ex ) { cmtk::StdErr << ex << "\n"; } std::ifstream controlStream( controlFileName ); if ( controlStream.fail() ) { cmtk::StdErr << "ERROR: could not open control file " << controlFileName << "\n"; throw cmtk::ExitException( 1 ); } while ( controlStream.good() ) { std::string line; controlStream >> line; cmtk::DebugOutput( 7 ) << line << "\n"; if ( line.length() ) { std::string fname; std::vector args; ParseArgsWarpString( line, fname, args ); if ( nActualParameters ) { if ( nActualParameters != args.size() ) { cmtk::StdErr << "ERROR: number of model parameters changed from " << nActualParameters << " to " << args.size() << " in argument " << line << "\n"; throw cmtk::ExitException( 1 ); } } else { nActualParameters = args.size(); nParameters = nActualParameters; if ( order > 1 ) nParameters += nActualParameters * (nActualParameters+1) / 2; meanParam.resize( nParameters ); } size_t ofs = 0; for ( size_t i = 0; i < args.size(); ++i ) { design.push_back( args[i] ); meanParam[ofs++] += args[i]; } if ( order > 1 ) { for ( size_t j = 0; j < nActualParameters; ++j ) { for ( size_t i = j; i < nActualParameters; ++i ) { design.push_back( args[i] * args[j] ); meanParam[ofs++] += args[i] * args[j]; } } } design.push_back( 1.0 ); char path[PATH_MAX]; snprintf( path, PATH_MAX, pathPrintf, fname.c_str() ); pathList.push_back( std::string( path ) ); } } const size_t nData = pathList.size(); if ( ! nData ) { cmtk::StdErr << "ERROR: control file must contain at least one valid entry.\n"; throw cmtk::ExitException( 1 ); } cmtk::GeneralLinearModel glm( 1+nParameters, nData, &(design[0]) ); for ( size_t i = 0; i < meanParam.size(); ++i ) { meanParam[i] /= nData; } if ( sParameters ) { ParseArgsString( std::string( sParameters ), param ); if ( order > 1 ) { size_t ofs = nActualParameters; param.resize( nParameters ); for ( size_t j = 0; j < nActualParameters; ++j ) { for ( size_t i = j; i < nActualParameters; ++i ) { param[ofs++] += param[i] * param[j]; } } } } else { param = meanParam; } if ( cmtk::FileFormat::Identify( pathList.begin()->c_str() ) == cmtk::FILEFORMAT_TYPEDSTREAM ) // xform mode { cmtk::DebugOutput( 1 ) << "INFO: operating in XFORM mode\n"; std::vector vWarpXform; for ( std::list::const_iterator it = pathList.begin(); it != pathList.end(); ++it ) { cmtk::SplineWarpXform::SmartPtr xform = cmtk::SplineWarpXform::SmartPtr::DynamicCastFrom( cmtk::Xform::SmartPtr( cmtk::XformIO::Read( *it ) ) ); if ( ! xform ) { cmtk::StdErr << "ERROR: transformation '" << *it << "' is either invalid or not a spline warp xform\n"; throw cmtk::ExitException( 1 ); } vWarpXform.push_back( xform ); } cmtk::SplineWarpXform::SmartPtr referenceWarp = vWarpXform[0]; for ( size_t i = 0; i < vWarpXform.size(); ++i ) { try { vWarpXform[i]->ReplaceInitialAffine(); } catch ( const cmtk::AffineXform::MatrixType::SingularMatrixException& ) { cmtk::StdErr << "ERROR: encountered singular matrix replacing warp initial affine transformation with identity.\n"; throw cmtk::ExitException( 1 ); } } const size_t nWarpParameters = vWarpXform[0]->m_NumberOfParameters; std::vector mean( nWarpParameters ); std::fill( mean.begin(), mean.end(), 0 ); std::vector vData; for ( size_t i = 0; i GetPureDeformation(); cmtk::TypedArray::SmartPtr data( cmtk::TypedArray::Create( cmtk::TYPE_COORDINATE, deformation, nWarpParameters ) ); for ( size_t n = 0; n < nWarpParameters; ++n ) { mean[n] += deformation[n]; } vData.push_back( data ); vWarpXform[i] = cmtk::SplineWarpXform::SmartPtr::Null(); // no longer needed } for ( size_t n = 0; n < mean.size(); ++n ) mean[n] /= nData; glm.FitModel( vData, false /*normalizeParameters*/ ); for ( size_t p = 0; p < nParameters; ++p ) { cmtk::TypedArray::SmartPtr mode = glm.GetModel( p ); for ( size_t n = 0; n < nWarpParameters; ++n ) { cmtk::Types::DataItem value; if ( mode->Get( value, n ) ) { mean[n] += value * (param[p] - meanParam[p]); } } } for ( size_t n = 0; n < nWarpParameters; ++n ) { referenceWarp->SetParameter( n, mean[n] ); } if ( outFileName ) cmtk::XformIO::Write( referenceWarp, outFileName ); } else // image mode { cmtk::DebugOutput( 1 ) << "INFO: operating in IMAGE mode\n"; std::vector vVolume; for ( std::list::const_iterator it = pathList.begin(); it != pathList.end(); ++it ) { cmtk::UniformVolume::SmartPtr volume( cmtk::VolumeIO::ReadOriented( *it ) ); if ( ! volume ) { cmtk::StdErr << "ERROR: image '" << *it << "' could not be read\n"; throw cmtk::ExitException( 1 ); } vVolume.push_back( volume ); } cmtk::UniformVolume::SmartPtr referenceVolume = vVolume[0]; const size_t nVolumeParameters = referenceVolume->GetNumberOfPixels(); std::vector mean( nVolumeParameters ); std::fill( mean.begin(), mean.end(), 0.0 ); std::vector vData; for ( size_t i = 0; i GetData() ); } glm.FitModel( vData, false /*normalizeParameters*/ ); for ( size_t p = 0; p < nParameters; ++p ) { cmtk::TypedArray::SmartPtr mode = glm.GetModel( p ); for ( size_t n = 0; n < nVolumeParameters; ++n ) { cmtk::Types::DataItem value; if ( mode->Get( value, n ) ) { mean[n] += value * param[p]; } } } cmtk::TypedArray::SmartPtr constant = glm.GetModel( nParameters ); for ( size_t n = 0; n < nVolumeParameters; ++n ) { cmtk::Types::DataItem value; if ( constant->Get( value, n ) ) { mean[n] += value; } } referenceVolume->CreateDataArray( cmtk::TYPE_FLOAT ); for ( size_t n = 0; n < nVolumeParameters; ++n ) { referenceVolume->SetDataAt( mean[n], n ); } if ( outFileName ) cmtk::VolumeIO::Write( *referenceVolume, outFileName ); } return 0; } #include "cmtkSafeMain" cmtk-3.3.1/apps/reorient.cxx000066400000000000000000000100501276303427400157760ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2012 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4420 $ // // $LastChangedDate: 2012-06-05 14:43:08 -0700 (Tue, 05 Jun 2012) $ // // $LastChangedBy: torstenrohlfing $ // */ #include #include #include #include #include #include #include const char* InFileName = NULL; const char* OutFileName = NULL; const char* OldOrientation = NULL; const char* NewOrientation = NULL; const char* NewSpace = NULL; int doMain( const int argc, const char* argv[] ) { try { cmtk::CommandLine cl; cl.SetProgramInfo( cmtk::CommandLine::PRG_TITLE, "Reorientation" ); cl.SetProgramInfo( cmtk::CommandLine::PRG_DESCR, "Convert between image orientations, i.e., physically re-order pixel array and adapt stored anatomical orientation information" ); cl.SetProgramInfo( cmtk::CommandLine::PRG_SYNTX, "reorient [options] new-orientation infile outfile" ); typedef cmtk::CommandLine::Key Key; cl.AddOption( Key( 'i', "input-orientation" ), &OldOrientation, "Override input orientation. This is a three-letter code, e.g., 'RAS', 'LPI', etc." ); cl.AddOption( Key( 'o', "output-orientation" ), &NewOrientation, "Override output orientation. Default is 'RAS', or the closest match supported by the output image file format" ); cl.AddOption( Key( "output-space" ), &NewSpace, "Override output coordinate space (e.g., 'RAS', 'LAS', 'LPS'). This does not affect the array order. Default is to write image in the input image space." ); if ( ! cl.Parse( argc, argv ) ) return 1; InFileName = cl.GetNext(); OutFileName = cl.GetNext(); } catch ( const cmtk::CommandLine::Exception& ex ) { cmtk::StdErr << ex << "\n"; return false; } cmtk::UniformVolume::SmartPtr volume( cmtk::VolumeIO::Read( InFileName ) ); if ( ! volume ) { cmtk::StdErr << "ERROR: could not read image " << InFileName << "\n"; exit( 1 ); } if ( OldOrientation ) { volume->SetMetaInfo( cmtk::META_IMAGE_ORIENTATION, OldOrientation ); volume->SetMetaInfo( cmtk::META_IMAGE_ORIENTATION_ORIGINAL, OldOrientation ); volume->SetMetaInfo( cmtk::META_SPACE, OldOrientation ); volume->SetMetaInfo( cmtk::META_SPACE_ORIGINAL, OldOrientation ); volume->CreateDefaultIndexToPhysicalMatrix(); } else { OldOrientation = volume->GetMetaInfo( cmtk::META_IMAGE_ORIENTATION ).c_str(); } if ( NewOrientation ) { cmtk::DebugOutput( 1 ) << "Reorienting from '" << OldOrientation << "' to '" << NewOrientation << "'\n"; // now reorient here in case the writer function doesn't try to write original orientation volume = cmtk::UniformVolume::SmartPtr( volume->GetReoriented( NewOrientation ) ); // override original orientation to force output with desired output orientation volume->SetMetaInfo( cmtk::META_IMAGE_ORIENTATION_ORIGINAL, NewOrientation ); } if ( NewSpace ) { volume->SetMetaInfo( cmtk::META_SPACE_ORIGINAL, NewSpace ); } cmtk::VolumeIO::Write( *volume, OutFileName ); return 0; } #include "cmtkSafeMain" cmtk-3.3.1/apps/sba.cxx000066400000000000000000000114211276303427400147170ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2012 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 3933 $ // // $LastChangedDate: 2012-02-28 14:37:33 -0800 (Tue, 28 Feb 2012) $ // // $LastChangedBy: torstenrohlfing $ // */ #include #include #include #include #include #include #include #include #include #include #ifdef CMTK_USE_GCD # include #endif const char *OutputFileName = "sba.nii"; std::vector InputFileVector; unsigned short NumberOfLabels = 0; bool PaddingFlag = false; float PaddingValue = 0; bool ExcludeLocalOutliers = false; void AddVolumeFile ( const char* fileName, std::vector& volumeVector ) { cmtk::DebugOutput( 1 ) << "Opening image " << fileName << ".\n"; cmtk::UniformVolume::SmartPtr nextVolume( cmtk::VolumeIO::ReadOriented( fileName ) ); if ( PaddingFlag ) { nextVolume->GetData()->SetPaddingValue( PaddingValue ); } if ( nextVolume->GetData()->GetType() != cmtk::TYPE_USHORT ) { cmtk::StdErr << "WARNING: converting data to 'unsigned short'\n"; nextVolume->SetData( cmtk::TypedArray::SmartPtr( nextVolume->GetData()->Convert( cmtk::TYPE_USHORT ) ) ); } volumeVector.push_back( nextVolume ); } int doMain ( const int argc, const char* argv[] ) { try { cmtk::CommandLine cl; cl.SetProgramInfo( cmtk::CommandLine::PRG_TITLE, "Shape-based Averaging of label images" ); cl.SetProgramInfo( cmtk::CommandLine::PRG_DESCR, "Average segmentations (label fields) using the Euclidean Distance Transform. All input images must be in the same space. EDT is computed in this space also. " "See http://dx.doi.org/10.1109/TIP.2006.884936 for details of the underlying algorithm." ); cl.AddParameterVector( &InputFileVector, "InputImageVector", "Input image file names." ); typedef cmtk::CommandLine::Key Key; cl.BeginGroup( "Input", "Input Options" ); cl.AddOption( Key( 'n', "num-labels" ), &NumberOfLabels, "Number of labels. It is assumed that only values [0..num] occur in the images" ); cl.AddOption( Key( 'p', "padding" ), &PaddingValue, "Padding value in input image", &PaddingFlag ); cl.EndGroup(); cl.BeginGroup( "Combination", "Label Combination Options" ); cl.AddSwitch( Key( 'x', "exclude-outliers" ), &ExcludeLocalOutliers, true, "Exclude local outliers in the shape-based averaging algorithm. Outliers at each pixel are defined as those input images for which the distance from " "the nearest pixel with the current label exceeds thresholds computed from the set of distances over all inputs." ); cl.EndGroup(); cl.BeginGroup( "Output", "Output Options" ); cl.AddOption( Key( 'o', "output" ), &OutputFileName, "File name for output segmentation file." ); cl.EndGroup(); cl.Parse( argc, argv ); } catch ( const cmtk::CommandLine::Exception& e ) { cmtk::StdErr << e; throw cmtk::ExitException( 1 ); } std::vector volumeVector; for ( std::vector::const_iterator it = InputFileVector.begin(); it != InputFileVector.end(); ++it ) { AddVolumeFile( it->c_str(), volumeVector ); } const double timeBaseline = cmtk::Timers::GetTimeProcess(); cmtk::TypedArray::SmartPtr avgArray = cmtk::TypedArray::SmartPtr( cmtk::LabelCombinationShapeBasedAveraging( volumeVector, NumberOfLabels ).GetResult( ExcludeLocalOutliers ) ); cmtk::DebugOutput( 1 ).GetStream().printf( "Time %f sec\n", cmtk::Timers::GetTimeProcess() - timeBaseline ); cmtk::UniformVolume::SmartPtr volume = volumeVector[0]->CloneGrid(); volume->SetData( avgArray ); cmtk::VolumeIO::Write( *volume, OutputFileName ); return 0; } #include "cmtkSafeMain" cmtk-3.3.1/apps/sbai.cxx000066400000000000000000000150621276303427400150750ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2013 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4834 $ // // $LastChangedDate: 2013-09-12 15:15:51 -0700 (Thu, 12 Sep 2013) $ // // $LastChangedBy: torstenrohlfing $ // */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef CMTK_USE_GCD # include #endif const char *OutputFileName = "sbai.nii"; std::vector InputFileVector; unsigned short NumberOfLabels = 0; const char* ReplaceFrom; std::map ReplaceMap; bool PaddingFlag = false; float PaddingValue = 0; void AddReplaceFrom( const char* arg ) { ReplaceFrom = arg; } void AddReplaceTo( const char* arg ) { ReplaceMap[std::string(ReplaceFrom)] = std::string( arg ); } void AddVolumeStudyVector ( const char* listName, std::vector& volumeVector, std::vector& xformVector, cmtk::UniformVolume::SmartConstPtr& referenceVolume ) { cmtk::DebugOutput( 1 ) << "Opening studylist " << listName << ".\n"; cmtk::TypedStreamStudylist studylist; studylist.Read( listName ); if ( ! referenceVolume ) { const std::string actualPath = cmtk::StrReplaceByRules( studylist.GetReferenceStudyPath(), ReplaceMap ); referenceVolume = cmtk::UniformVolume::SmartPtr( cmtk::VolumeIO::ReadGridOriented( actualPath ) ); if ( ! referenceVolume ) { cmtk::StdErr << "WARNING: could not read reference volume " << actualPath.c_str() << "\n"; return; } } const std::string actualPath = cmtk::StrReplaceByRules( studylist.GetFloatingStudyPath(), ReplaceMap ); cmtk::UniformVolume::SmartPtr floatingVolume( cmtk::VolumeIO::ReadOriented( actualPath ) ); if ( PaddingFlag ) { floatingVolume->GetData()->SetPaddingValue( PaddingValue ); } volumeVector.push_back( floatingVolume ); cmtk::SplineWarpXform::SmartConstPtr splinexform( cmtk::SplineWarpXform::SmartConstPtr::DynamicCastFrom( studylist.GetWarpXform() ) ); if ( !splinexform ) { cmtk::AffineXform::SmartConstPtr affinexform( studylist.GetAffineXform() ); if ( !affinexform ) { cmtk::StdErr << "WARNING: no transformation in studylist " << listName << "\n"; return; } else { xformVector.push_back( cmtk::XformUniformVolume::SmartConstPtr( new cmtk::AffineXformUniformVolume( *(referenceVolume), *(affinexform) ) ) ); } } else { xformVector.push_back( cmtk::XformUniformVolume::SmartConstPtr( new cmtk::SplineWarpXformUniformVolume( *(referenceVolume), splinexform ) ) ); } } int doMain ( const int argc, const char* argv[] ) { try { cmtk::CommandLine cl; cl.SetProgramInfo( cmtk::CommandLine::PRG_TITLE, "Shape-Based Averaging and interpolation of label images" ); cl.SetProgramInfo( cmtk::CommandLine::PRG_DESCR, "Average segmentations (label fields) using the Euclidean Distance Transform. This tool performs joint interpolation and averaging by interpolating from the EDT. " "This requires that the inputs are transformations from the same fixed to (not necessarily) different moving images. EDT computation is done in the space of each moving image. " "See http://dx.doi.org/10.1109/TIP.2006.884936 for details of the underlying algorithm." ); cl.AddParameterVector( &InputFileVector, "InputImageVector", "Input transformation file names." ); typedef cmtk::CommandLine::Key Key; cl.BeginGroup( "Input", "Input Options" ); cl.AddOption( Key( 'n', "num-labels" ), &NumberOfLabels, "Number of labels. It is assumed that only values [0..num] occur in the images" ); cl.AddOption( Key( 'p', "padding" ), &PaddingValue, "Padding value in input image", &PaddingFlag ); cl.AddCallback( Key( "replace-from" ), AddReplaceFrom, "Replace from pattern" ); cl.AddCallback( Key( "replace-to" ), AddReplaceTo, "Replace to pattern" ); cl.EndGroup(); cl.BeginGroup( "Output", "Output Options" ); cl.AddOption( Key( 'o', "output" ), &OutputFileName, "File name for output segmentation file." ); cl.EndGroup(); cl.Parse( argc, argv ); } catch ( const cmtk::CommandLine::Exception& e ) { cmtk::StdErr << e; throw cmtk::ExitException( 1 ); } std::vector volumeVector; cmtk::UniformVolume::SmartConstPtr referenceVolume; std::vector xformVector; for ( std::vector::const_iterator it = InputFileVector.begin(); it != InputFileVector.end(); ++it ) { AddVolumeStudyVector( it->c_str(), volumeVector, xformVector, referenceVolume ); } const double timeBaseline = cmtk::Timers::GetTimeProcess(); cmtk::TypedArray::SmartPtr avgArray = cmtk::TypedArray::SmartPtr( cmtk::LabelCombinationShapeBasedAveragingInterpolation( volumeVector, xformVector, referenceVolume, NumberOfLabels ).GetResult() ); cmtk::DebugOutput( 1 ).GetStream().printf( "Time %f sec\n", cmtk::Timers::GetTimeProcess() - timeBaseline ); cmtk::UniformVolume::SmartPtr volume = referenceVolume->CloneGrid(); volume->SetData( avgArray ); cmtk::VolumeIO::Write( *volume, OutputFileName ); return 0; } #include "cmtkSafeMain" cmtk-3.3.1/apps/sequence.cxx000066400000000000000000000153441276303427400157720ustar00rootroot00000000000000/* // // Copyright 1997-2011 Torsten Rohlfing // // Copyright 2004-2014 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 5283 $ // // $LastChangedDate: 2014-04-04 18:30:46 -0700 (Fri, 04 Apr 2014) $ // // $LastChangedBy: torsten_at_home $ // */ #include #include #include #include #include #include #ifdef HAVE_IEEEFP_H # include #endif #include #include #include #include #include #include #include #include double MaxThreshold = 0; bool UseMaxThreshold = false; bool AbsoluteValues = false; const char* OutputFormat = "%.6f"; const char* WriteHistogram = NULL; double HistogramMin = 0; bool HistogramMinSet = false; double HistogramMax = 100; bool HistogramMaxSet = false; int HistogramBins = 1000; int doMain( const int argc, const char* argv[] ) { try { cmtk::CommandLine cl; cl.SetProgramInfo( cmtk::CommandLine::PRG_TITLE, "Value sequence" ); cl.SetProgramInfo( cmtk::CommandLine::PRG_DESCR, "Analyze sequence of numerical values, which is read from standard input" ); typedef cmtk::CommandLine::Key Key; cl.AddOption( Key( 't', "thresh" ), &MaxThreshold, "Maximum value threshold. All values above are ignored.", &UseMaxThreshold ); cl.AddSwitch( Key( 'a', "abs" ), &AbsoluteValues, true, "Use absolute values." ); cl.BeginGroup( "Histogram", "Histogram Options" ); cl.AddOption( Key( "histogram-min" ), &HistogramMin, "Minimum of the histogram value range. All values below this will be counted in the first histogram bin.", &HistogramMinSet ); cl.AddOption( Key( "histogram-max" ), &HistogramMax, "Maximum of the histogram value range. All values above this will be counted in the last histogram bin.", &HistogramMaxSet ); cl.AddOption( Key( "histogram-bins" ), &HistogramBins, "Number of histogram bins." ); cl.EndGroup(); cl.BeginGroup( "Output", "Output Options" ); cl.AddOption( Key( 'f', "format" ), &OutputFormat, "Output number format in printf() style." ); cl.AddOption( Key( "write-histogram" ), &WriteHistogram, "Path for optional histogram output in comma-separated (CSV) format." ); cl.EndGroup(); cl.Parse( argc, argv ); } catch ( const cmtk::CommandLine::Exception& e ) { cmtk::StdErr << "Command line parse error: " << e << "\n"; return 1; } cmtk::ValueSequence seq; std::list list; unsigned int countOverThreshold = 0; double f; while ( ! std::cin.eof() ) { std::cin >> f; if ( ! finite( f ) ) break; if ( AbsoluteValues ) f = fabs( f ); if ( UseMaxThreshold && (f > MaxThreshold) ) ++countOverThreshold; else { seq.Proceed( f ); list.push_back( f ); } f = std::numeric_limits::signaling_NaN(); } // Check for empty input and simply exit in that case if ( list.empty() ) { return 0; } // Print value counts const size_t totalNumberOfValues = seq.GetNValues() + countOverThreshold; printf( "Number of Values:\t%d\n", (int)totalNumberOfValues ); printf( "Values over Threshold:\t%u (%.2f%%)\n", countOverThreshold, 100.0 * countOverThreshold / totalNumberOfValues ); // Format and print simple statistics char format[120]; snprintf( format, sizeof( format ), "\nSTAT\tMin\tMax\tMean\tStdDev\nSTATval\t%s\t%s\t%s\t%s\n", OutputFormat, OutputFormat, OutputFormat, OutputFormat ); printf( format, seq.GetMinimum(), seq.GetMaximum(), seq.GetAverage(), sqrt( seq.GetVariance() ) ); // Create sorted list for percentile computation std::vector sorted( list.begin(), list.end() ); std::sort( sorted.begin(), sorted.end() ); printf( "\nPERC" ); const int percentiles[] = { 5, 10, 25, 50, 75, 90, 95, -1 }; for ( size_t idx = 0; percentiles[idx] > 0; ++idx ) { printf( "\t%d", percentiles[idx] ); } snprintf( format, sizeof( format ), "\t%s", OutputFormat ); printf( "\nPERCval" ); for ( size_t idx = 0; percentiles[idx] > 0; ++idx ) { if ( percentiles[idx] == 50 ) { const size_t medianIdx = sorted.size() / 2; if ( sorted.size() & 1 ) { printf( format, sorted[medianIdx] ); } else { printf( format, 0.5*(sorted[medianIdx] + sorted[1+medianIdx]) ); } } else { printf( format, sorted[ (size_t)( sorted.size() * 0.01 * percentiles[idx]) ] ); } } printf( "\n" ); // Create histogram is desired if ( WriteHistogram ) { // If no histogram minimum given, use minimum data value (first in sorted vector) if ( ! HistogramMinSet ) HistogramMin = sorted[0]; // If no histogram maximum given, use maximum data value (last in sorted vector) if ( ! HistogramMaxSet ) HistogramMax = sorted[sorted.size()-1]; std::ofstream hstream( WriteHistogram ); if ( ! hstream.good() ) { cmtk::StdErr << "ERROR: could not open file " << WriteHistogram << " for writing the histogram.\n"; throw cmtk::ExitException( 1 ); } hstream << "bin_min,bin_max,bin_count\n"; const double binWidth = (HistogramMax - HistogramMin) / HistogramBins; std::vector::const_iterator sample = sorted.begin(); for ( int bin = 0; bin < HistogramBins; ++bin ) { const double binFrom = HistogramMin + bin*binWidth; const double binTo = HistogramMin + (1+bin)*binWidth; // go through sorted vector and add to this bin whatever belongs int countBin = 0; for ( ; (sample != sorted.end()) && (*sample < binTo); ++countBin, ++sample ) {} // last bin -- add whatever is left if ( bin == HistogramBins-1 ) for ( ; (sample != sorted.end()); ++countBin, ++sample ) {} hstream << binFrom << "," << binTo << "," << countBin << "\n"; } } return 0; } #include "cmtkSafeMain" cmtk-3.3.1/apps/similarity.cxx000066400000000000000000000306301276303427400163430ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2011, 2013 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 5074 $ // // $LastChangedDate: 2013-12-02 14:26:35 -0800 (Mon, 02 Dec 2013) $ // // $LastChangedBy: torstenrohlfing $ // */ #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef HAVE_SYS_TYPES_H # include #endif #ifdef HAVE_SYS_STAT_H # include #endif #ifdef HAVE_IEEEFP_H # include #endif #include bool Padding0 = false; bool Padding1 = false; cmtk::Types::DataItem PaddingValue0 = 0; cmtk::Types::DataItem PaddingValue1 = 0; bool LabelMode = false; cmtk::UniformVolume::SmartPtr Volume0; cmtk::UniformVolume::SmartPtr Volume1; bool OutsideBG = false; std::string imagePath0; std::string imagePath1; std::string MaskFileName; unsigned int ForceMaxLabel = 0; std::string HistogramTextFileName; bool ParseCommandLine( const int argc, const char* argv[] ) { try { cmtk::CommandLine cl; cl.SetProgramInfo( cmtk::CommandLine::PRG_TITLE, "Image similarity" ); cl.SetProgramInfo( cmtk::CommandLine::PRG_DESCR, "Compute similarity measures such as intensity difference or label overlaps between two images." ); typedef cmtk::CommandLine::Key Key; cl.AddSwitch( Key( "outside-bg" ), &OutsideBG, true, "Assume voxels outside floating image to be background" ); cl.AddOption( Key( "pad0" ), &PaddingValue0, "Define padding value for reference image", &Padding0 ); cl.AddOption( Key( "pad1" ), &PaddingValue1, "Define padding value for floating image", &Padding1 ); cl.AddSwitch( Key( 'g', "grey" ), &LabelMode, false, "Image pixels are intensities" ); cl.AddSwitch( Key( 'l', "labels" ), &LabelMode, true, "Image pixels are labels" ); cl.AddOption( Key( 'm', "mask" ), &MaskFileName, "Mask file for optional region-based analysis" ); cl.AddOption( Key( "force-max" ), &ForceMaxLabel, "Force maximum label value" ); cl.AddOption( Key( "histogram-text-file" ), &HistogramTextFileName, "Output file name for histogram (plain text format)." ); cl.AddParameter( &imagePath0, "Image0", "First input image path. When applicable, this provides the 'ground truth' image." )->SetProperties( cmtk::CommandLine::PROPS_IMAGE ); cl.AddParameter( &imagePath1, "Image1", "Second input image path. When applicable, this provides the 'test' image." )->SetProperties( cmtk::CommandLine::PROPS_IMAGE ); if ( !cl.Parse( argc, argv ) ) return false; } catch ( const cmtk::CommandLine::Exception& ex ) { cmtk::StdErr << ex << "\n"; return false; } return true; } cmtk::JointHistogram* AnalyseStudies ( unsigned int& voxelCount, double& sumAbs, double& sumSq, double& sumMult, unsigned int& countVoxelsUnequal, double& maxDifference, double& maxRelDifference, cmtk::VoxelMatchingCrossCorrelation& ccMetric, const cmtk::TypedArray* mask = NULL ) { sumAbs = sumSq = sumMult = 0; voxelCount = countVoxelsUnequal = 0; maxDifference = maxRelDifference = 0; ccMetric.Reset(); const cmtk::TypedArray *data0 = Volume0->GetData(); const cmtk::TypedArray *data1 = Volume1->GetData(); cmtk::JointHistogram *histogram = NULL; if ( LabelMode ) { const cmtk::Types::DataItemRange range0 = data0->GetRange(); const cmtk::Types::DataItemRange range1 = data1->GetRange(); histogram = new cmtk::JointHistogram( 1+static_cast( range0.Width() ), 1+static_cast( range1.Width() ) ); histogram->SetRangeCenteredX( range0 ); histogram->SetRangeCenteredY( range1 ); } else { histogram = new cmtk::JointHistogram( 256, 256 ); histogram->SetRangeCenteredX( data0->GetRange() ); histogram->SetRangeCenteredY( data1->GetRange() ); } unsigned int recog = 0, subst = 0, reject = 0; unsigned int recogFG = 0, substFG = 0, rejectFG = 0, voxelCountFG = 0; unsigned int recogM = 0, substM = 0, rejectM = 0, voxelCountM = 0; cmtk::Types::DataItem value0, value1, maskValue; unsigned int numberOfVoxels = std::min( data0->GetDataSize(), data1->GetDataSize() ); bool insideMask = true; for ( size_t r = 0; r < numberOfVoxels; ++r ) { const bool dataExist0 = data0->Get( value0, r ) && finite( value0 ); bool dataExist1 = data1->Get( value1, r ) && finite( value1 ); if ( ! dataExist1 && OutsideBG ) { value1 = 0; dataExist1 = true; } if ( dataExist0 && dataExist1 ) { insideMask = !mask || (( mask->Get( maskValue, r ) && maskValue )); if ( insideMask ) { const double d = fabs( value0 - value1 ); sumAbs += d; maxDifference = std::max( maxDifference, d ); maxRelDifference = std::max( maxRelDifference, d / std::max( fabs(value0) , fabs(value1)) ); sumSq += d*d; sumMult += value0 * value1; } voxelCount++; if ( value0 != 0 ) ++voxelCountFG; if ( insideMask ) { ++voxelCountM; histogram->Increment( histogram->ValueToBinX( value0 ), histogram->ValueToBinY( value1 ) ); ccMetric.Increment( value0, value1 ); } if ( value0 == value1 ) { ++recog; if ( value0 != 0 ) ++recogFG; if ( insideMask ) ++recogM; } else { ++subst; if ( value0 != 0 ) ++substFG; if ( insideMask ) ++substM; countVoxelsUnequal++; } } else { if ( dataExist0 ) { ++reject; if ( value0 != 0 ) { ++voxelCountFG; ++rejectFG; } if ( insideMask ) { ++voxelCountM; ++rejectM; } } if ( dataExist0 || dataExist1 ) countVoxelsUnequal++; } } if ( numberOfVoxels && (numberOfVoxels != reject) ) { fprintf( stdout, "\nCL\trecog\tsubst\treject\treliab\nCL-all\t%.4f\t%.4f\t%.4f\t%.4f\n", 1.0*recog / numberOfVoxels, 1.0*subst / numberOfVoxels, 1.0*reject / numberOfVoxels, 1.0 * recog / (numberOfVoxels - reject) ); } if ( voxelCountFG && (voxelCountFG != rejectFG ) ) { fprintf( stdout, "CL-fg\t%.4f\t%.4f\t%.4f\t%.4f\n", 1.0 * recogFG / voxelCountFG, 1.0 * substFG / voxelCountFG, 1.0 * rejectFG / voxelCountFG, 1.0 * recogFG / (voxelCountFG - rejectFG) ); } if ( mask ) { if ( voxelCountM && (voxelCountM != rejectM ) ) { fprintf( stdout, "CL-mask\t%.4f\t%.4f\t%.4f\t%.4f\n", 1.0 * recogM / voxelCountM, 1.0 * substM / voxelCountM, 1.0 * rejectM / voxelCountM, 1.0 * recogM / (voxelCountM - rejectM) ); } } fputs( "\n", stdout ); return histogram; } int doMain ( const int argc, const char* argv[] ) { if ( ! ParseCommandLine( argc, argv ) ) return 1; cmtk::UniformVolume::SmartPtr volume( cmtk::VolumeIO::ReadOriented( imagePath0 ) ); if ( ! volume ) { cmtk::StdErr << "ERROR: could not read image " << imagePath0 << "\n"; throw cmtk::ExitException( 1 ); } Volume0 = volume; if ( Padding0 ) { Volume0->GetData()->SetPaddingValue( PaddingValue0 ); } cmtk::TypedArray::SmartPtr mask; if ( !MaskFileName.empty() ) { cmtk::UniformVolume::SmartPtr maskVolume( cmtk::VolumeIO::ReadOriented( MaskFileName ) ); if ( maskVolume ) mask = maskVolume->GetData(); } volume = cmtk::UniformVolume::SmartPtr( cmtk::VolumeIO::ReadOriented( imagePath1 ) ); if ( ! volume ) { cmtk::StdErr << "ERROR: could not read image " << imagePath1 << "\n"; throw cmtk::ExitException( 1 ); } Volume1 = volume; if ( Padding1 ) { Volume1->GetData()->SetPaddingValue( PaddingValue1 ); } unsigned int voxelCount; double sumAbs; double sumSq; double sumMult; double maxDifference; double maxRelDifference; unsigned int countVoxelsUnequal; cmtk::VoxelMatchingCrossCorrelation ccMetric; cmtk::JointHistogram::SmartPtr histogram( AnalyseStudies( voxelCount, sumAbs, sumSq, sumMult, countVoxelsUnequal, maxDifference, maxRelDifference, ccMetric, mask ) ); double hX, hY; histogram->GetMarginalEntropies( hX, hY ); const double hXY = histogram->GetJointEntropy(); fprintf( stdout, "STAT\tN\tHX\tHY\nSTATval\t%u\t%.5f\t%.5f\n\n", voxelCount, hX, hY ); if ( voxelCount ) { fprintf( stdout, "SIM\tNDIFF\tDMAX\trDMAX\tMSD\tMAD\tNCC\tHXY\tMI\tNMI\nSIMval\t%u\t%.5g\t%.5g\t%.5g\t%.5g\t%.5g\t%.5g\t%.5g\t%.5g\n", countVoxelsUnequal, maxDifference, maxRelDifference, sumSq / voxelCount, sumAbs / voxelCount, ccMetric.Get(), hXY, hX + hY - hXY, ( hX + hY ) / hXY ); } if ( !HistogramTextFileName.empty() ) { FILE *file = fopen( HistogramTextFileName.c_str(), "w" ); if ( file ) { unsigned int binsX = histogram->GetNumBinsX(); unsigned int binsY = histogram->GetNumBinsY(); for ( unsigned int i = 0; i < binsX; ++i ) { for ( unsigned int j = 0; j < binsY; ++j ) { fprintf( file, "%d\t", histogram->GetBin( i, j ) ); } fputs( "\n", file ); } fclose( file ); } } if ( LabelMode ) { unsigned int numberLabelsRef = histogram->GetNumBinsX(); unsigned int numberLabelsFlt = histogram->GetNumBinsY(); // SI : Similarity Index as defined by Dawant et al., TMI 18(10), 1999 fputs( "\nLabel\tTotal\tCorrect\tCorr%\tFalseN\tFN%\tFalseP\tFP%\tSI\tJ\n", stdout ); unsigned int sumTotal = 0, sumCorrect = 0, countLabels = 0; double sumSI = 0; double sumSIweighted = 0; double avgRecog = 0; for ( unsigned int i = 0; i < numberLabelsRef; ++i ) { unsigned int totalRef = histogram->ProjectToX( i ); if ( totalRef ) { const unsigned int correct = ( i < numberLabelsFlt ) ? histogram->GetBin( i, i ) : 0; avgRecog += static_cast( (1.0 * correct) / totalRef ); unsigned int falseNeg = 0, falsePos = 0; for ( unsigned int j = 0; j < numberLabelsFlt; ++j ) { if ( i != j ) { if ( (i < numberLabelsFlt) && (j < numberLabelsRef) ) { falseNeg += histogram->GetBin( i, j ); falsePos += histogram->GetBin( j, i ); } } } const unsigned int totalFlt = ( i < numberLabelsFlt ) ? histogram->ProjectToY( i ) : 0; const double SI = static_cast( 2.0 * correct / (totalRef + totalFlt ) ); // Similarity Index / Dice score const unsigned int totalRefFlt = totalRef + totalFlt - correct; const double J = static_cast( 1.0 * correct / totalRefFlt ); // Jaccard index if ( totalRef ) { fprintf( stdout, "\n%06u\t%u\t%u\t%.5lf\t%u\t%.5lf\t%u\t%.5lf\t%.5lf\t%.5lf", i, totalRef, correct, 1.0 * correct / totalRef, falseNeg, 1.0 * falseNeg / totalRef, falsePos, 1.0 * falsePos / totalRef, SI, J ); } if ( i ) { // assume background is label #0 and exclude this ++countLabels; sumTotal += totalRef; sumCorrect += correct; sumSI += SI; sumSIweighted += SI * totalRef; } } else { // this label does not exist in the reference image if ( i <= ForceMaxLabel ) fprintf( stdout, "\nLabel #%u\t", i ); } } avgRecog /= (countLabels+1); fputs( "\n\n\tsumTot\tsumCorr\t%corr\tavgSI\tWmeanSI\n", stdout ); if ( sumTotal && countLabels ) { fprintf( stdout, "Total:\t%u\t%u\t%.2lf\t%.4lf\t%.4lf\n\n", sumTotal, sumCorrect, 100.0 * sumCorrect / sumTotal, sumSI / countLabels, sumSIweighted / sumTotal ); } fprintf( stdout, "AvgRecog:\t%.6f\n", avgRecog ); } return 0; } #include "cmtkSafeMain" cmtk-3.3.1/apps/split.cxx000066400000000000000000000124141276303427400153100ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2013 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4975 $ // // $LastChangedDate: 2013-10-11 13:53:00 -0700 (Fri, 11 Oct 2013) $ // // $LastChangedBy: torstenrohlfing $ // */ #include #include #include #include #include #include #include #include std::string InputFilePath; std::string OutputFilePath; std::string OutputXformPath; int Axis = cmtk::AXIS_Z; int Factor = 2; bool Padded = false; int doMain( const int argc, const char* argv[] ) { try { cmtk::CommandLine cl; cl.SetProgramInfo( cmtk::CommandLine::PRG_TITLE, "Split images" ); cl.SetProgramInfo( cmtk::CommandLine::PRG_DESCR, "Split volume image into sub-images, i.e., to separate interleaved images into passes" ); typedef cmtk::CommandLine::Key Key; cl.BeginGroup( "Orientation", "Orientation Options" ); cmtk::CommandLine::EnumGroup::SmartPtr interleaveGroup = cl.AddEnum( "slice-orientation", &Axis, "Define slice axis for splitting." ); interleaveGroup->AddSwitch( Key( 'a', "axial" ), (int)cmtk::AXIS_Z, "Interleaved axial images" ); interleaveGroup->AddSwitch( Key( 's', "sagittal" ),(int)cmtk::AXIS_X, "Interleaved sagittal images" ); interleaveGroup->AddSwitch( Key( 'c', "coronal" ), (int)cmtk::AXIS_Y, "Interleaved coronal images" ); interleaveGroup->AddSwitch( Key( 'x', "interleave-x" ), (int)cmtk::AXIS_X, "Interleaved along x axis" ); interleaveGroup->AddSwitch( Key( 'y', "interleave-y" ), (int)cmtk::AXIS_Y, "Interleaved along y axis" ); interleaveGroup->AddSwitch( Key( 'z', "interleave-z" ), (int)cmtk::AXIS_Z, "Interleaved along z axis" ); cl.EndGroup(); cl.BeginGroup( "Splitting", "Splitting Options" ); cl.AddOption( Key( 'f', "factor" ), &Factor, "Interleave factor. This is the number of subimages generated. If this is set to zero, a separate 2D output image is generated for each slice in the input " "(in the given slice orientation)." ); cl.EndGroup(); cl.BeginGroup( "Output", "Output Options" ); cl.AddSwitch( Key( 'p', "padded" ), &Padded, true, "Padded output, i.e., fill in removed slices" ); cl.AddOption( Key( "output-xform-path" ), &OutputXformPath, "Optional path template (fprintf-style) for output affine transformation that maps input image coordinates to each output image." ); cl.EndGroup(); cl.AddParameter( &InputFilePath, "InputImage", "Input image path" )->SetProperties( cmtk::CommandLine::PROPS_IMAGE ); cl.AddParameter( &OutputFilePath, "OutputImagePattern", "Output image path pattern. Use '%d' to substitute subimage index." )->SetProperties( cmtk::CommandLine::PROPS_IMAGE | cmtk::CommandLine::PROPS_OUTPUT ); cl.Parse( argc, argv ); } catch ( const cmtk::CommandLine::Exception& e ) { cmtk::StdErr << e << "\n"; return 1; } cmtk::UniformVolume::SmartPtr volume( cmtk::VolumeIO::ReadOriented( InputFilePath ) ); // if Factor is zero, set to number of slices and generate 2D output images. if ( Factor == 0 ) Factor = volume->m_Dims[Axis]; for ( int i = 0; i < Factor; ++i ) { cmtk::UniformVolume::SmartPtr subvolume( Padded ? volume->GetInterleavedPaddedSubVolume( Axis, Factor, i ) : volume->GetInterleavedSubVolume( Axis, Factor, i ) ); char path[PATH_MAX]; if ( snprintf( path, PATH_MAX, OutputFilePath.c_str(), i ) > PATH_MAX ) { cmtk::StdErr << "ERROR: output path exceeds maximum path length\n"; } else { cmtk::VolumeIO::Write( *subvolume, path ); } if ( !OutputXformPath.empty() ) { if ( snprintf( path, PATH_MAX, OutputXformPath.c_str(), i ) > PATH_MAX ) { cmtk::StdErr << "ERROR: output path exceeds maximum path length\n"; } cmtk::AffineXform xform; cmtk::Types::Coordinate xlate[3] = {0,0,0}; xlate[Axis] = -i * volume->m_Delta[Axis]; try { xform.SetXlate( xlate ); } catch ( const cmtk::AffineXform::MatrixType::SingularMatrixException& ex ) { cmtk::StdErr << "ERROR: singular matrix in cmtk::AffineXform::SetXlate()\n"; throw cmtk::ExitException( 1 ); } cmtk::ClassStreamOutput stream( path, cmtk::ClassStreamOutput::MODE_WRITE ); if ( stream.IsValid() ) { stream << xform; stream.Close(); } } } return 0; } #include "cmtkSafeMain" cmtk-3.3.1/apps/statistics.cxx000066400000000000000000000334521276303427400163540ustar00rootroot00000000000000/* // // Copyright 1997-2011 Torsten Rohlfing // // Copyright 2004-2013 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 5051 $ // // $LastChangedDate: 2013-12-01 10:43:19 -0800 (Sun, 01 Dec 2013) $ // // $LastChangedBy: torsten_at_home $ // */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include bool Label = false; bool LogData = false; bool ExpData = false; bool WriteAsColumn = false; bool OutputExpNotation = false; const char *MaskFileName = NULL; bool MaskIsBinary = false; int MaskOutputAllUpTo = 0; std::list ImageFileNames; int NumberOfHistogramBins = 256; std::list percentiles; void CallbackAddPercentile( const double arg ) { percentiles.push_back( static_cast( arg ) ); } void AnalyzeLabels( const cmtk::UniformVolume& volume, const cmtk::TypedArray* maskData ) { const cmtk::TypedArray* data = volume.GetData(); cmtk::Types::DataItemRange range = data->GetRange(); if ( MaskOutputAllUpTo ) range.m_UpperBound = MaskOutputAllUpTo; const unsigned int numberOfLabels = static_cast( range.m_UpperBound - range.m_LowerBound + 1 ); // Number of label voxels. std::vector count( numberOfLabels ); std::fill( count.begin(), count.end(), 0 ); // Number of label surface voxels. std::vector countSurface( numberOfLabels ); std::fill( countSurface.begin(), countSurface.end(), 0 ); // Centers-of-mass for each label std::vector centerOfMass( numberOfLabels ); std::fill( centerOfMass.begin(), centerOfMass.end(), cmtk::UniformVolume::CoordinateVectorType( 0.0 ) ); int index = 0; cmtk::Types::DataItem value, neighbor, maskValue; for ( int z = 0; z < volume.GetDims()[cmtk::AXIS_Z]; ++z ) { for ( int y = 0; y < volume.GetDims()[cmtk::AXIS_Y]; ++y ) { for ( int x = 0; x < volume.GetDims()[cmtk::AXIS_X]; ++x, ++index ) { if ( maskData && !(maskData->Get( maskValue, index ) && (maskValue != 0) ) ) continue; if ( data->Get( value, index ) && range.InRange( value ) ) { const int labelIdx = static_cast( value - range.m_LowerBound ); ++count[labelIdx]; centerOfMass[labelIdx] += volume.GetGridLocation( x, y, z ); bool isSurface = false; for ( int dz = -1; (dz < 2) && !isSurface; ++dz ) for ( int dy = -1; (dy < 2) && !isSurface; ++dy ) for ( int dx = -1; (dx < 2) && !isSurface; ++dx ) if ( dx || dy || dz ) if ( (dx+x)>=0 && (dx+x)=0 && (dy+y)=0 && (dz+z)Get( neighbor, offset ) && ( neighbor != value ) ) isSurface = true; } if ( isSurface ) ++countSurface[labelIdx]; } } } } cmtk::DebugOutput( 1 ) << "idx\t\tcount\t\tsurface\t\tvolume\tCenterOfMass\n"; const cmtk::Types::Coordinate voxelVolume = volume.m_Delta[0] * volume.m_Delta[1] * volume.m_Delta[2]; size_t totalCount = 0; for ( unsigned int idx = 0; idx < numberOfLabels; ++idx ) { if ( count[idx] || MaskOutputAllUpTo ) { if ( count[idx] ) centerOfMass[idx] *= (1.0 / count[idx]); if ( OutputExpNotation ) fprintf( stdout, "%03d\t%14d\t%14d\t%.4e\t(%e,%e,%e)\n", (int)(idx+range.m_LowerBound), count[idx], countSurface[idx], count[idx] * voxelVolume, centerOfMass[idx][0], centerOfMass[idx][1], centerOfMass[idx][2] ); else fprintf( stdout, "%03d\t%14d\t%14d\t%.4f\t(%f,%f,%f)\n", (int)(idx+range.m_LowerBound), count[idx], countSurface[idx], count[idx] * voxelVolume, centerOfMass[idx][0], centerOfMass[idx][1], centerOfMass[idx][2] ); totalCount += count[idx]; } } cmtk::Types::DataItem entropy = 0; if ( totalCount ) { for ( unsigned int idx=0; idx < numberOfLabels; ++idx ) { if ( count[idx] ) { cmtk::Types::DataItem p = ((cmtk::Types::DataItem) count[idx]) / totalCount; entropy += p * log( p ); } } } if ( OutputExpNotation ) fprintf( stdout, "\nEntropy:\t%.5e\n", entropy ); else fprintf( stdout, "\nEntropy:\t%.5f\n", entropy ); } void AnalyzeGrey( const cmtk::UniformVolume& volume, const cmtk::TypedArray& maskData ) { const cmtk::TypedArray* data = volume.GetData(); cmtk::Histogram histogram( NumberOfHistogramBins ); histogram.SetRange( data->GetRange() ); int maxLabel = static_cast( maskData.GetRange().m_UpperBound ); if ( MaskOutputAllUpTo ) maxLabel = MaskOutputAllUpTo; std::vector maskFlags( maxLabel+1 ); std::fill( maskFlags.begin(), maskFlags.end(), false ); for ( size_t i = 0; i < maskData.GetDataSize(); ++i ) { cmtk::Types::DataItem l; if ( maskData.Get( l, i ) && (l <= maxLabel) ) maskFlags[static_cast( l )] = true; } if ( ! WriteAsColumn ) fprintf( stdout, "#M\tmin\tmax\tmean\tsdev\tn\tEntropy\tsum\n" ); for ( int maskSelect = 0; maskSelect <= maxLabel; ++maskSelect ) { cmtk::ValueSequence seq; if ( maskFlags[maskSelect] ) { histogram.Reset(); cmtk::Types::DataItem value, maskValue; size_t index = 0; for ( int z = 0; z < volume.GetDims()[cmtk::AXIS_Z]; ++z ) { for ( int y = 0; y < volume.GetDims()[cmtk::AXIS_Y]; ++y ) { for ( int x = 0; x < volume.GetDims()[cmtk::AXIS_X]; ++x, ++index ) { if ( maskData.Get( maskValue, index ) && (maskValue == maskSelect) ) { if ( data->Get( value, index ) ) { seq.Proceed( value ); histogram.Increment( histogram.ValueToBin( value ) ); } } } } } } if ( seq.GetNValues() || MaskOutputAllUpTo ) { if ( ! WriteAsColumn ) { if ( OutputExpNotation ) fprintf( stdout, "%03d\t%.5f\t%.5e\t%.5e\t%.5e\t%d\t%.5e\t%f\n", maskSelect, seq.GetMinimum(), seq.GetMaximum(), seq.GetAverage(), sqrt( seq.GetVariance() ), seq.GetNValues(), histogram.GetEntropy(), seq.GetSum() ); else fprintf( stdout, "%03d\t%.5f\t%.5f\t%.5f\t%.5f\t%d\t%.5f\t%f\n", maskSelect, seq.GetMinimum(), seq.GetMaximum(), seq.GetAverage(), sqrt( seq.GetVariance() ), seq.GetNValues(), histogram.GetEntropy(), seq.GetSum() ); } } } } void AnalyzeGrey( const cmtk::UniformVolume& volume ) { const cmtk::TypedArray* data = volume.GetData(); if ( ! WriteAsColumn ) fprintf( stdout, "min\tmax\tmean\tsdev\tn\tEntropy\tsum\n" ); cmtk::Types::DataItem value; cmtk::ValueSequence seq; size_t index = 0; for ( int z = 0; z < volume.GetDims()[cmtk::AXIS_Z]; ++z ) { for ( int y = 0; y < volume.GetDims()[cmtk::AXIS_Y]; ++y ) { for ( int x = 0; x < volume.GetDims()[cmtk::AXIS_X]; ++x, ++index ) { if ( data->Get( value, index ) ) { seq.Proceed( value ); } } } } if ( seq.GetNValues() ) { if ( WriteAsColumn ) { if ( OutputExpNotation ) fprintf( stdout, "min\t%.5e\nmax\t%.5e\nmean\t%.5e\nsdev\t%.5e\nn\t%d\nEntropy\t%.5e\nsum\t%.5e\n", seq.GetMinimum(), seq.GetMaximum(), seq.GetAverage(), sqrt( seq.GetVariance() ), seq.GetNValues(), data->GetEntropy( true /*fractional*/, NumberOfHistogramBins ), seq.GetSum()); else fprintf( stdout, "min\t%.5f\nmax\t%.5f\nmean\t%.5f\nsdev\t%.5f\nn\t%d\nEntropy\t%.5f\nsum\t%f\n", seq.GetMinimum(), seq.GetMaximum(), seq.GetAverage(), sqrt( seq.GetVariance() ), seq.GetNValues(), data->GetEntropy( true /*fractional*/, NumberOfHistogramBins ), seq.GetSum()); } else { if ( OutputExpNotation ) fprintf( stdout, "%.5e\t%.5e\t%.5e\t%.5e\t%d\t%.5e\t%e\n", seq.GetMinimum(), seq.GetMaximum(), seq.GetAverage(), sqrt( seq.GetVariance() ), seq.GetNValues(), data->GetEntropy( true /*fractional*/, NumberOfHistogramBins ), seq.GetSum() ); else fprintf( stdout, "%.5f\t%.5f\t%.5f\t%.5f\t%d\t%.5f\t%f\n", seq.GetMinimum(), seq.GetMaximum(), seq.GetAverage(), sqrt( seq.GetVariance() ), seq.GetNValues(), data->GetEntropy( true /*fractional*/, NumberOfHistogramBins ), seq.GetSum() ); } } if ( !percentiles.empty() ) { fprintf( stdout, "PERC\tVALUE\n" ); for ( std::list::const_iterator it = percentiles.begin(); it != percentiles.end(); ++it ) { fprintf( stdout, "%.5f\t%.5f\n", (cmtk::Types::DataItem)(*it), (cmtk::Types::DataItem)data->GetPercentile( *it, NumberOfHistogramBins ) ); } } } int doMain ( const int argc, const char* argv[] ) { try { cmtk::CommandLine cl; cl.SetProgramInfo( cmtk::CommandLine::PRG_TITLE, "Image statistics" ); cl.SetProgramInfo( cmtk::CommandLine::PRG_DESCR, "Statistical computations on image pixel intensities, i.e., means and standard deviations" ); cl.SetProgramInfo( cmtk::CommandLine::PRG_SYNTX, "statistics [options] ImageFile0 [ImageFile1 ...]" ); cl.SetProgramInfo( cmtk::CommandLine::PRG_CATEG, "CMTK.Statistics and Modeling" ); typedef cmtk::CommandLine::Key Key; cl.AddSwitch( Key( 'l', "label" ), &Label, true, "Interpret voxel values as labels" ); cl.AddSwitch( Key( 'g', "gray" ), &Label, false, "Interpret voxel values as gray values (default)" ); cl.AddSwitch( Key( 'L', "log" ), &LogData, true, "Apply log() function to data" ); cl.AddSwitch( Key( 'e', "exp" ), &ExpData, true, "Apply exp() function to data" ); cl.AddOption( Key( 'n', "num-bins" ), &NumberOfHistogramBins, "Number of histogram bins." ); cl.AddSwitch( Key( 'C', "column" ), &WriteAsColumn, true, "Write statistics in column format rather than line format." ); cl.AddSwitch( Key( 'E', "e-notation" ), &OutputExpNotation, true, "Write floating point numbers in #e# notation (i.e., 1e-3 instead of 0.001)" ); cl.AddOption( Key( 'm', "mask" ), &MaskFileName, "Analyze region based on binary mask file", &MaskIsBinary ); cl.AddOption( Key( 'M', "Mask" ), &MaskFileName, "Analyze regions separately based on mask file (label field)." ); cl.AddOption( Key( "mask-output-all-up-to" ), &MaskOutputAllUpTo, "Output results for all mask values up to given value, even such that do not actually appear in the mask." ); cl.AddCallback( Key( 'p', "percentile" ), CallbackAddPercentile, "Add value to list of percentile to compute." ); cl.Parse( argc, argv ); const char* next = cl.GetNext(); while ( next ) { ImageFileNames.push_back( next ); next = cl.GetNextOptional(); } } catch ( const cmtk::CommandLine::Exception& e ) { cmtk::StdErr << e; throw cmtk::ExitException( 1 ); } cmtk::UniformVolume::SmartPtr maskVolume( NULL ); cmtk::TypedArray::SmartPtr maskData( NULL ); if ( MaskFileName ) { maskVolume = cmtk::UniformVolume::SmartPtr( cmtk::VolumeIO::ReadOriented( MaskFileName ) ); if ( ! maskVolume ) { cmtk::StdErr << "ERROR: could not read mask file " << MaskFileName << "\n"; throw cmtk::ExitException( 1 ); } maskData = maskVolume->GetData(); if ( ! maskData ) { cmtk::StdErr << "ERROR: could not read data from mask file " << MaskFileName << "\n"; throw cmtk::ExitException( 1 ); } if ( MaskIsBinary ) { maskData->Binarize(); } } std::list::const_iterator it = ImageFileNames.begin(); for ( ; it != ImageFileNames.end(); ++it ) { const char* imageFileName = *it; cmtk::UniformVolume::SmartPtr volume( cmtk::VolumeIO::ReadOriented( imageFileName ) ); if ( ! volume ) { cmtk::StdErr << "ERROR: could not read image file " << imageFileName << "\n"; continue; } cmtk::TypedArray::SmartPtr volumeData = volume->GetData(); if ( ! volumeData ) { cmtk::StdErr << "ERROR: could not read data from image file " << imageFileName << "\n"; continue; } if ( maskVolume && !volume->GridMatches( *maskVolume ) ) { cmtk::StdErr << "ERROR: mask grid does not match grid of image " << imageFileName << "\n"; continue; } cmtk::StdOut.printf( "Statistics for image %s\n", imageFileName ); if ( Label ) { AnalyzeLabels( *volume, maskData ); } else { if ( LogData ) volumeData->ApplyFunctionDouble( cmtk::Wrappers::Log ); if ( ExpData ) volumeData->ApplyFunctionDouble( cmtk::Wrappers::Exp ); if ( maskData ) AnalyzeGrey( *volume, *maskData ); else AnalyzeGrey( *volume ); } } return 0; } #include "cmtkSafeMain" cmtk-3.3.1/apps/stream_pixels.cxx000066400000000000000000000112171276303427400170340ustar00rootroot00000000000000/* // // Copyright 1997-2010 Torsten Rohlfing // // Copyright 2004-2013 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4983 $ // // $LastChangedDate: 2013-10-15 15:08:33 -0700 (Tue, 15 Oct 2013) $ // // $LastChangedBy: torstenrohlfing $ // */ #include #include #include #include #include #include #include #include #include #include #include int doMain( int argc, const char *argv[] ) { const char* readOrientation = NULL; std::vector imagePaths; cmtk::ScalarDataType convertToType = cmtk::TYPE_NONE; bool changeEndian = false; try { cmtk::CommandLine cl; cl.SetProgramInfo( cmtk::CommandLine::PRG_TITLE, "Stream pixel data from one or more images to Standard Output." ); cl.SetProgramInfo( cmtk::CommandLine::PRG_DESCR, "This tool reads one or more images and writes all their pixels to standard output in binary form. Optionally, each image can be reoriented to a specified anatomical orientation " "and/or converted to a different data type. This is useful for piping image data through a pipeline, e.g., the Camino DTI toolkit." ); typedef cmtk::CommandLine::Key Key; cl.AddOption( Key( "reorient" ), &readOrientation, "Reorient all images to anatomy-based standard orientation, e.g., RAS or LPS for axial. Default: no reorientation." ); cl.AddSwitch( Key( "change-endian" ), &changeEndian, true, "Change endianness. Default: native endianness." ); cmtk::CommandLine::EnumGroup::SmartPtr typeGroup = cl.AddEnum( "convert", &convertToType, "Convert data to new scalar type. Default: no conversion." ); typeGroup->AddSwitch( Key( "char" ), cmtk::TYPE_CHAR, "8 bits, signed integer" ); typeGroup->AddSwitch( Key( "byte" ), cmtk::TYPE_BYTE, "8 bits, unsigned integer" ); typeGroup->AddSwitch( Key( "short" ), cmtk::TYPE_SHORT, "16 bits, signed integer" ); typeGroup->AddSwitch( Key( "ushort" ), cmtk::TYPE_USHORT, "16 bits, unsigned integer" ); typeGroup->AddSwitch( Key( "int" ), cmtk::TYPE_INT, "32 bits, signed integer" ); typeGroup->AddSwitch( Key( "uint" ), cmtk::TYPE_UINT, "32 bits, unsigned integer" ); typeGroup->AddSwitch( Key( "float" ), cmtk::TYPE_FLOAT, "32-bits, single-precision floating point" ); typeGroup->AddSwitch( Key( "double" ), cmtk::TYPE_DOUBLE, "64 bits, double-precision floating point" ); cl.AddParameterVector( &imagePaths, "ImagePaths", "List of paths to one or more image files. These will be read and processed in the given order." ); cl.Parse( argc, argv ); } catch ( const cmtk::CommandLine::Exception& e ) { cmtk::StdErr << e << "\n"; throw cmtk::ExitException( 1 ); } cmtk::UniformVolume::SmartPtr volume; for ( size_t idx = 0; idx < imagePaths.size(); ++idx ) { if ( readOrientation ) volume = cmtk::VolumeIO::ReadOriented( imagePaths[idx], readOrientation ); else volume = cmtk::VolumeIO::Read( imagePaths[idx] ); if ( ! volume ) { cmtk::StdErr << "ERROR: failed to read image " << imagePaths[idx] << "\n"; throw cmtk::ExitException( 1 ); } cmtk::TypedArray::SmartPtr dataArray = volume->GetData(); if ( ! dataArray ) { cmtk::StdErr << "ERROR: image " << imagePaths[idx] << " does not have any valid pixel data\n"; throw cmtk::ExitException( 1 ); } if ( convertToType != cmtk::TYPE_NONE ) { dataArray = dataArray->Convert( convertToType ); } if ( changeEndian ) { dataArray->ChangeEndianness(); } std::cout.write( reinterpret_cast( dataArray->GetDataPtr() ), dataArray->GetDataSizeBytes() ); } return 0; } #include "cmtkSafeMain" cmtk-3.3.1/apps/streamxform.cxx000066400000000000000000000146161276303427400165320ustar00rootroot00000000000000/* // // Copyright 1997-2010 Torsten Rohlfing // // Copyright 2004-2014 SRI International // // Copyright 2014 Greg Jefferis // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 1652 $ // // $LastChangedDate: 2010-05-14 14:45:52 -0700 (Fri, 14 May 2010) $ // // $LastChangedBy: torstenrohlfing $ // */ #include #include #include #include #include #include #include #include #include #include #include #include #include int doMain( const int argc, const char* argv[] ) { bool affineOnlyGlobal = false; cmtk::Types::Coordinate inversionTolerance = 1e-8; std::vector inputXformPaths; const char* sourceImagePath = NULL; const char* targetImagePath = NULL; const char* separator = " "; int precision = 9; try { cmtk::CommandLine cl; cl.SetProgramInfo( cmtk::CommandLine::PRG_TITLE, "Apply coordinate transformation to point coordinates from text stream." ); cl.SetProgramInfo( cmtk::CommandLine::PRG_DESCR, "An ASCII-format list of point coordinates is read from standard input and a user-provided sequence of coordinate transformations (each optionally inverted) is applied to them. " "The transformed points are then written to standard output." ); typedef cmtk::CommandLine::Key Key; cl.BeginGroup( "Xform", "Transformation Options" ); cl.AddSwitch( Key( "affine-only" ), &affineOnlyGlobal, true, "Apply only the affine component of each transformation (or its inverse, if specified) in the given series, even if the actual transformation is nonrigid." ); cl.AddOption( Key( "inversion-tolerance" ), &inversionTolerance, "Numerical tolerance of B-spline inversion in mm. Smaller values will lead to more accurate inversion, but may increase failure rate." ); cl.EndGroup(); cl.BeginGroup( "Output", "Output Options" ); cl.AddOption( Key( "separator" ), &separator, "Field separator - this string or character is used to separate x, y, and z coordinates from each other, and z from the remainder of every line." ); cl.AddOption( Key( "precision" ), &precision, "Floating point precision for output file." ); cl.EndGroup(); cl.BeginGroup( "Spaces", "Image Space Options" ); cl.AddOption( Key( "source-image" ), &sourceImagePath, "Set source image of the transformation (i.e., the image that the transformation maps points FROM) to correct for differences in orientation and coordinate space." ); cl.AddOption( Key( "target-image" ), &targetImagePath, "Set target image of the transformation (i.e., the image that the transformation maps points TO) to correct for differences in orientation and coordinate space." ); cl.EndGroup(); cl.AddParameterVector( &inputXformPaths, "XformList", "List of concatenated transformations. Insert '--inverse' to use the inverse of the transformation listed next. " "(If the first transformation in the sequence is inverted, then '--inverse' must be preceded by '--', i.e., use '-- --inverse xform.path')." )->SetProperties( cmtk::CommandLine::PROPS_XFORM ); cl.Parse( argc, argv ); } catch ( cmtk::CommandLine::Exception& ex ) { cmtk::StdErr << ex << "\n"; throw cmtk::ExitException( 1 ); } cmtk::XformList xformList = cmtk::XformListIO::MakeFromStringList( inputXformPaths ); xformList.SetEpsilon( inversionTolerance ); if ( affineOnlyGlobal ) { xformList = xformList.MakeAllAffine(); } if ( sourceImagePath ) { cmtk::UniformVolume::SmartConstPtr sourceImage( cmtk::VolumeIO::ReadOriented( sourceImagePath ) ); if ( ! sourceImage ) { cmtk::StdErr << "ERROR: could not read source image '" << sourceImagePath << "'\n"; throw cmtk::ExitException( 1 ); } try { xformList.AddToFront( cmtk::AffineXform::SmartPtr( new cmtk::AffineXform( sourceImage->GetImageToPhysicalMatrix() ) )->GetInverse() ); } catch ( const cmtk::AffineXform::MatrixType::SingularMatrixException& ) { cmtk::StdErr << "ERROR: singular source image-to-physical space matrix.\n"; throw cmtk::ExitException( 1 ); } } if ( targetImagePath ) { cmtk::UniformVolume::SmartConstPtr targetImage( cmtk::VolumeIO::ReadOriented( targetImagePath ) ); if ( ! targetImage ) { cmtk::StdErr << "ERROR: could not read target image '" << targetImagePath << "'\n"; throw cmtk::ExitException( 1 ); } try { xformList.Add( cmtk::AffineXform::SmartPtr( new cmtk::AffineXform( targetImage->GetImageToPhysicalMatrix() ) ) ); } catch ( const cmtk::AffineXform::MatrixType::SingularMatrixException& ) { cmtk::StdErr << "ERROR: singular target image-to-physical space matrix.\n"; throw cmtk::ExitException( 1 ); } } const std::streamsize originalPrecision = std::cout.precision( precision ); cmtk::Xform::SpaceVectorType xyz; std::string restOfLine; while ( std::cin ) { std::cin >> xyz[0] >> xyz[1] >> xyz[2]; if ( std::cin ) { std::getline( std::cin, restOfLine ); // Apply transformation sequence const bool valid = xformList.ApplyInPlace( xyz ); std::cout << xyz[0] << separator << xyz[1] << separator << xyz[2]; if ( ! valid ) { std::cout << " FAILED"; } std::cout << separator << restOfLine << std::endl; } } std::cout.precision( originalPrecision ); // if we got here, the program probably ran return 0; } #include "cmtkSafeMain" cmtk-3.3.1/apps/sympl.cxx000066400000000000000000000446261276303427400153330ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2013 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4869 $ // // $LastChangedDate: 2013-09-24 10:42:40 -0700 (Tue, 24 Sep 2013) $ // // $LastChangedBy: torstenrohlfing $ // */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include float MinValue = -1e5; bool MinValueSet = false; float MaxValue = 1e5; bool MaxValueSet = false; cmtk::Types::Coordinate Sampling = 1.0; cmtk::Types::Coordinate Accuracy = 0.1; cmtk::Interpolators::InterpolationEnum Interpolation = cmtk::Interpolators::LINEAR; int Levels = 4; bool OutputOnly = false; cmtk::Types::Coordinate Rho; cmtk::Units::Degrees Theta; cmtk::Units::Degrees Phi; std::string MirrorOutFile; std::string AlignedOutFile; bool MarkPlaneAligned = false; std::string MarkedOutFile; std::string DifferenceOutFile; std::string WriteXformPath; cmtk::Types::DataItem MarkPlaneValue = 4095; bool PadOutValueSet = false; cmtk::Types::DataItem PadOutValue = 0; std::string SymmetryOutFileName; std::string SymmetryParameters; std::string SymmetryParametersFile; std::string InFileName; /// Constants for initial plane orientation. typedef enum { /// XY plane (axial) SYMPL_INIT_XY, /// XZ plane (coronal) SYMPL_INIT_XZ, /// YZ plane (sagittal) SYMPL_INIT_YZ } InitialPlaneEnum; /// Initial plane orientation: default to sagittal for human images. InitialPlaneEnum InitialPlane = SYMPL_INIT_YZ; bool ParseCommandLine ( const int argc, const char* argv[] ) { try { cmtk::CommandLine cl( cmtk::CommandLine::PROPS_XML ); cl.SetProgramInfo( cmtk::CommandLine::PRG_TITLE, "Symmetry plane computation" ); cl.SetProgramInfo( cmtk::CommandLine::PRG_DESCR, "Compute the approximate symmetry plane of an image to determine, for example, the mid-sagittal plane in human brain images. " "Various forms of output are supported, e.g., writing the input image with the symmetry plane drawn into it, or the input image realigned along the symmetry plane." ); cl.SetProgramInfo( cmtk::CommandLine::PRG_CATEG, "CMTK.Registration" ); typedef cmtk::CommandLine::Key Key; cl.BeginGroup( "Optimization", "Optimization" ); cl.AddOption( Key( 'a', "accuracy" ), &Accuracy, "Accuracy (final optimization step size in [mm]." ); cl.AddOption( Key( 's', "sampling" ), &Sampling, "Resampled image resolution. This is the resolution [in mm] of the first (finest) resampled image in the multi-scale pyramid, " "which is derived directly from the original full-resolution images."); cl.AddOption( Key( 'l', "levels" ), &Levels, "Number of resolution levels. The algorithm will create (levels-1) resampled images with increasingly coarse resolution and use these " "in successive order of increasing resolution before using the original images at the final level." ); cl.EndGroup(); cl.BeginGroup( "Initial", "Initial approximate symmetry plane orientation" ); cmtk::CommandLine::EnumGroup::SmartPtr initialPlane = cl.AddEnum( "initial-plane", &InitialPlane, "Initial orientation of symmetry plane. This should be the closest orthogonal plane to the expected actual symmetry plane." ); initialPlane->AddSwitch( Key( "initial-axial" ), SYMPL_INIT_XY, "Approximately axial symmetry" ); initialPlane->AddSwitch( Key( "initial-coronal" ), SYMPL_INIT_XZ, "Approximately coronal symmetry" ); initialPlane->AddSwitch( Key( "initial-sagittal" ), SYMPL_INIT_YZ, "Approximately sagittal symmetry" ); initialPlane->AddSwitch( Key( "initial-xy" ), SYMPL_INIT_XY, "Approximately XY plane symmetry" ); initialPlane->AddSwitch( Key( "initial-xz" ), SYMPL_INIT_XZ, "Approximately XZ plane symmetry" ); initialPlane->AddSwitch( Key( "initial-yz" ), SYMPL_INIT_YZ, "Approximately YZ plane symmetry" ); cl.EndGroup(); cl.BeginGroup( "Pre-computed", "Pre-computed symmetry" )->SetProperties( cmtk::CommandLine::PROPS_ADVANCED | cmtk::CommandLine::PROPS_NOXML ); cl.AddOption( Key( "output-only" ), &SymmetryParameters, "Give symmetry parameters [Rho Theta Phi] as option, skip search.", &OutputOnly ); cl.AddOption( Key( "output-only-file" ), &SymmetryParametersFile, "Read symmetry parameters from file, skip search.", &OutputOnly ); cl.EndGroup(); cl.BeginGroup( "Preprocessing", "Data pre-processing" )->SetProperties( cmtk::CommandLine::PROPS_ADVANCED ); cl.AddOption( Key( "min-value" ), &MinValue, "Force minumum data value.", &MinValueSet ); cl.AddOption( Key( "max-value" ), &MaxValue, "Force maximum data value.", &MaxValueSet ); cl.EndGroup(); cl.BeginGroup( "OutputImages", "Output of Images" ); cmtk::CommandLine::EnumGroup::SmartPtr interpGroup = cl.AddEnum( "interpolation", &Interpolation, "Interpolation method used for reformatted output data" ); interpGroup->AddSwitch( Key( 'L', "linear" ), cmtk::Interpolators::LINEAR, "Use linear image interpolation for output." ); interpGroup->AddSwitch( Key( 'C', "cubic" ), cmtk::Interpolators::CUBIC, "Use cubic image interpolation for output." ); interpGroup->AddSwitch( Key( 'S', "sinc" ), cmtk::Interpolators::COSINE_SINC, "Use cosine-windowed sinc image interpolation for output." ); cl.AddOption( Key( 'P', "pad-out" ), &PadOutValue, "Padding value for output images.", &PadOutValueSet )->SetProperties( cmtk::CommandLine::PROPS_ADVANCED ); cl.AddOption( Key( "mark-value" ), &MarkPlaneValue, "Data value to mark (draw) symmetry plane." ); cl.AddOption( Key( "write-marked" ), &MarkedOutFile, "File name for output image with marked symmetry plane." ) ->SetProperties( cmtk::CommandLine::PROPS_IMAGE | cmtk::CommandLine::PROPS_OUTPUT ); cl.AddOption( Key( "write-aligned" ), &AlignedOutFile, "File name for symmetry plane-aligned output image." ) ->SetProperties( cmtk::CommandLine::PROPS_IMAGE | cmtk::CommandLine::PROPS_OUTPUT ); cl.AddSwitch( Key( "mark-aligned" ), &MarkPlaneAligned, true, "Mark symmetry plane in aligned output image." ); cl.AddOption( Key( "write-subtract" ), &DifferenceOutFile, "File name for mirror subtraction image." ) ->SetProperties( cmtk::CommandLine::PROPS_IMAGE | cmtk::CommandLine::PROPS_OUTPUT ); cl.AddOption( Key( "write-mirror" ), &MirrorOutFile, "File name for image mirrored w.r.t. symmetry plane." ) ->SetProperties( cmtk::CommandLine::PROPS_IMAGE | cmtk::CommandLine::PROPS_OUTPUT ); cl.EndGroup(); cl.BeginGroup( "OutputParameters", "Output of Parameters" )->SetProperties( cmtk::CommandLine::PROPS_ADVANCED ); cl.AddOption( Key( 'o', "outfile" ), &SymmetryOutFileName, "File name for symmetry plane parameter output." )->SetProperties( cmtk::CommandLine::PROPS_FILENAME | cmtk::CommandLine::PROPS_OUTPUT ); cl.AddOption( Key( "write-xform" ), &WriteXformPath, "Write affine alignment transformation to file" ) ->SetProperties( cmtk::CommandLine::PROPS_XFORM | cmtk::CommandLine::PROPS_OUTPUT ) ->SetAttribute( "reference", "InputImage" ); cl.EndGroup(); cl.AddParameter( &InFileName, "InputImage", "Input image path" )->SetProperties( cmtk::CommandLine::PROPS_IMAGE ); if ( ! cl.Parse( argc, argv ) ) return false; if ( !SymmetryParameters.empty() ) { double rho, theta, phi; if ( 3 == sscanf( SymmetryParameters.c_str(), "%20lf %20lf %20lf", &rho, &theta, &phi ) ) { Rho = rho; Theta = cmtk::Units::Degrees( theta ); Phi = cmtk::Units::Degrees( phi ); } } if ( !SymmetryParametersFile.empty() ) { cmtk::ClassStreamInput inStream( SymmetryParametersFile ); if ( inStream.IsValid() ) { cmtk::ParametricPlane *plane = NULL; inStream >> plane; Rho = plane->GetRho(); Theta = plane->GetTheta(); Phi = plane->GetPhi(); delete plane; } else { cmtk::StdErr.printf( "ERROR: Could not open symmetry parameter file %s\n", SymmetryParametersFile.c_str() ); } } } catch ( const cmtk::CommandLine::Exception& ex ) { cmtk::StdErr << ex << "\n"; return false; } return true; } void WriteDifference ( const cmtk::UniformVolume* originalVolume, const cmtk::UniformVolumeInterpolatorBase* interpolator, const cmtk::ParametricPlane& parametricPlane, const std::string& outFileName ) { cmtk::UniformVolume::SmartPtr diffVolume( originalVolume->CloneGrid() ); const cmtk::TypedArray* originalData = originalVolume->GetData(); cmtk::TypedArray::SmartPtr diffData = cmtk::TypedArray::SmartPtr( cmtk::TypedArray::Create( GetSignedDataType( originalData->GetType() ), originalData->GetDataSize() ) ); diffVolume->SetData( diffData ); cmtk::Types::DataItem dataV, dataW; int offset = 0; for ( int z = 0; z < originalVolume->GetDims()[2]; ++z ) for ( int y = 0; y < originalVolume->GetDims()[1]; ++y ) for ( int x = 0; x < originalVolume->GetDims()[0]; ++x, ++offset ) { if ( ! originalData->Get( dataV, offset ) ) { diffData->SetPaddingAt( offset ); continue; } cmtk::UniformVolume::CoordinateVectorType w = originalVolume->GetGridLocation( x, y, z ); parametricPlane.MirrorInPlace( w ); if ( interpolator->GetDataAt( w, dataW ) ) { diffData->Set( fabs( dataV - dataW ), offset ); } else { diffData->SetPaddingAt( offset ); } } cmtk::VolumeIO::Write( *diffVolume, outFileName ); } void WriteMirror ( const cmtk::UniformVolume* originalVolume, const cmtk::UniformVolumeInterpolatorBase* interpolator, const cmtk::ParametricPlane& parametricPlane, const std::string& outFileName ) { cmtk::TypedArray::SmartPtr mirrorData = cmtk::TypedArray::Create( originalVolume->GetData()->GetType(), originalVolume->GetData()->GetDataSize() ); cmtk::Types::DataItem data; int offset = 0; for ( int z = 0; z < originalVolume->GetDims()[2]; ++z ) { for ( int y = 0; y < originalVolume->GetDims()[1]; ++y ) for ( int x = 0; x < originalVolume->GetDims()[0]; ++x, ++offset ) { cmtk::UniformVolume::CoordinateVectorType v = originalVolume->GetGridLocation( x, y, z ); parametricPlane.MirrorInPlace( v ); if ( interpolator->GetDataAt( v, data ) ) { mirrorData->Set( data, offset ); } else { mirrorData->SetPaddingAt( offset ); } } } cmtk::UniformVolume::SmartPtr mirrorVolume( originalVolume->CloneGrid() ); mirrorVolume->SetData( mirrorData ); cmtk::VolumeIO::Write( *mirrorVolume, outFileName ); } void WriteMarkPlane ( const cmtk::UniformVolume* originalVolume, const cmtk::ParametricPlane& parametricPlane, const cmtk::Types::DataItem markPlaneValue, const std::string& outFileName ) { cmtk::UniformVolume::SmartPtr markVolume( originalVolume->CloneGrid() ); cmtk::TypedArray::SmartPtr markData( originalVolume->GetData()->Clone() ); markVolume->SetData( markData ); int offset = 0; for ( int z = 0; z < originalVolume->GetDims()[2]; ++z ) { for ( int y = 0; y < originalVolume->GetDims()[1]; ++y ) { int currentSideOfPlane = 0; for ( int x = 0; x < originalVolume->GetDims()[0]; ++x, ++offset ) { int newSideOfPlane = parametricPlane.GetWhichSide( originalVolume->GetGridLocation( x, y, z ) ); if ( ( newSideOfPlane != currentSideOfPlane ) && x ) markData->Set( markPlaneValue, offset ); currentSideOfPlane = newSideOfPlane; } } } cmtk::VolumeIO::Write( *markVolume, outFileName ); } void WriteAligned ( const cmtk::UniformVolume* originalVolume, const cmtk::UniformVolumeInterpolatorBase* interpolator, const cmtk::ParametricPlane& parametricPlane, const InitialPlaneEnum initialPlane, const std::string& outFileName ) { const cmtk::TypedArray* originalData = originalVolume->GetData(); cmtk::TypedArray::SmartPtr alignData = cmtk::TypedArray::Create( originalData->GetType(), originalData->GetDataSize() ); if ( PadOutValueSet ) { alignData->SetPaddingValue( PadOutValue ); } cmtk::UniformVolume::SmartPtr alignVolume( originalVolume->CloneGrid() ); alignVolume->SetData( alignData ); const cmtk::Types::DataItem maxData = originalData->GetRange().m_UpperBound; cmtk::Types::DataItem data; int normalAxis = 0; switch ( initialPlane ) { case SYMPL_INIT_XY: normalAxis = 2; break; case SYMPL_INIT_XZ: normalAxis = 1; break; case SYMPL_INIT_YZ: normalAxis = 0; break; } cmtk::AffineXform::SmartPtr alignment( parametricPlane.GetAlignmentXform( normalAxis ) ); int offset = 0; for ( int z = 0; z < originalVolume->GetDims()[2]; ++z ) { for ( int y = 0; y < originalVolume->GetDims()[1]; ++y ) { for ( int x = 0; x < originalVolume->GetDims()[0]; ++x, ++offset ) { const cmtk::UniformVolume::CoordinateVectorType v = alignment->Apply( originalVolume->GetGridLocation( x, y, z ) ); if ( interpolator->GetDataAt( v, data ) ) { if ( MarkPlaneAligned && (x == ( originalVolume->GetDims()[0] / 2 )) ) alignData->Set( 2 * maxData, offset ); else alignData->Set( data, offset ); } else { alignData->SetPaddingAt( offset ); } } } } cmtk::VolumeIO::Write( *alignVolume, outFileName ); } int doMain ( const int argc, const char* argv[] ) { if ( ! ParseCommandLine( argc, argv ) ) return 1; cmtk::UniformVolume::SmartPtr originalVolume( cmtk::VolumeIO::ReadOriented( InFileName ) ); if ( !originalVolume ) { cmtk::StdErr.printf( "Could not read image file %s\n", InFileName.c_str() ); throw cmtk::ExitException(1); } cmtk::CoordinateVector v( 6 ); // initialize plane as the mid-sagittal with respect to image orientation -- // distance from coordinate origin (image center) is 0: v[0] = 0; // and angles are chosen so that the plane normal is (1,0,0) switch ( InitialPlane ) { case SYMPL_INIT_XY: v[1] = 0; v[2] = 0; break; case SYMPL_INIT_XZ: v[1] = 90; v[2] = 90; break; default: case SYMPL_INIT_YZ: v[1] = 0; v[2] = 90; break; } // set center of volume (crop region) as coordinate origin. cmtk::Vector3D center = originalVolume->GetCenterCropRegion(); v[3] = center[0]; v[4] = center[1]; v[5] = center[2]; if ( OutputOnly ) { v[0] = Rho; v[1] = Theta.Value(); v[2] = Phi.Value(); } else { cmtk::BestNeighbourOptimizer optimizer; cmtk::ProgressConsole progressIndicator( "Symmetry Plane Computation" ); cmtk::Progress::Begin( 0, Levels, 1, "Symmetry Plane Computation" ); for ( int level = 0; level < Levels; ++level ) { cmtk::UniformVolume::SmartPtr volume; if ( level < Levels-1 ) { cmtk::Types::Coordinate voxelSize = Sampling * pow( 2.0, (Levels-level-2) ); volume = cmtk::UniformVolume::SmartPtr( originalVolume->GetResampled( voxelSize ) ); cmtk::DebugOutput( 1 ).GetStream().printf( "Entering level %d out of %d (%.2f mm voxel size)\n", level+1, Levels, voxelSize ); } else { volume = originalVolume; cmtk::DebugOutput( 1 ).GetStream().printf( "Entering level %d out of %d (original voxel size)\n", level+1, Levels ); } cmtk::SmartPointer functional( NULL ); if ( MinValueSet || MaxValueSet ) { cmtk::Types::DataItemRange valueRange = volume->GetData()->GetRange(); if ( MinValueSet ) valueRange.m_LowerBound = MinValue; if ( MaxValueSet ) valueRange.m_UpperBound = MaxValue; functional = cmtk::SmartPointer( new cmtk::SymmetryPlaneFunctional( volume, valueRange ) ); } else { functional = cmtk::SmartPointer( new cmtk::SymmetryPlaneFunctional( volume ) ); } optimizer.SetFunctional( cmtk::Functional::SmartPtr::DynamicCastFrom( functional ) ); optimizer.Optimize( v, pow( 2.0, Levels-level-1 ), Accuracy * pow( 2.0, Levels-level-1 ) ); cmtk::Progress::SetProgress( level ); } cmtk::Progress::Done(); cmtk::DebugOutput( 1 ).GetStream().printf( "rho=%f, theta=%f, phi=%f\n", v[0], v[1], v[2] ); } cmtk::ParametricPlane parametricPlane; parametricPlane.SetParameters( v ); if ( !SymmetryOutFileName.empty() ) { cmtk::ClassStreamOutput stream( SymmetryOutFileName, cmtk::ClassStreamOutput::MODE_WRITE ); stream << parametricPlane; stream.Close(); } const cmtk::UniformVolumeInterpolatorBase::SmartPtr interpolator( cmtk::ReformatVolume::CreateInterpolator( Interpolation, originalVolume ) );; if ( !AlignedOutFile.empty() ) WriteAligned( originalVolume, interpolator, parametricPlane, InitialPlane, AlignedOutFile ); if ( !MarkedOutFile.empty() ) WriteMarkPlane( originalVolume, parametricPlane, MarkPlaneValue, MarkedOutFile ); if ( !DifferenceOutFile.empty() ) WriteDifference( originalVolume, interpolator, parametricPlane, DifferenceOutFile ); if ( !MirrorOutFile.empty() ) WriteMirror( originalVolume, interpolator, parametricPlane, MirrorOutFile ); if ( !WriteXformPath.empty() ) { cmtk::AffineXform::SmartPtr alignment( parametricPlane.GetAlignmentXform( 0 ) ); cmtk::XformIO::Write( alignment, WriteXformPath ); } return 0; } #include "cmtkSafeMain" cmtk-3.3.1/apps/symplx.cxx000066400000000000000000000027041276303427400155120ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2010 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 2535 $ // // $LastChangedDate: 2010-11-01 16:22:31 -0700 (Mon, 01 Nov 2010) $ // // $LastChangedBy: torstenrohlfing $ // */ #include #include #include #include int doMain ( const int argc, const char* argv[] ) { return cmtk::ImageSymmetryPlaneCommandLine().Run( argc, argv ); } #include "cmtkSafeMain" cmtk-3.3.1/apps/symplx_cuda.cxx000066400000000000000000000030701276303427400165030ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2010 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 2535 $ // // $LastChangedDate: 2010-11-01 16:22:31 -0700 (Mon, 01 Nov 2010) $ // // $LastChangedBy: torstenrohlfing $ // */ #include #include #include #include int doMain ( const int argc, const char* argv[] ) { cmtk::ImageSymmetryPlaneCommandLine sympl; sympl.GetCommandLine().SetProgramInfo( cmtk::CommandLine::PRG_CATEG, "CMTK.Registration.GPU" ); return sympl.Run( argc, argv ); } #include "cmtkSafeMain" cmtk-3.3.1/apps/ttest.cxx000066400000000000000000000316461276303427400153300ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2011, 2013 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4954 $ // // $LastChangedDate: 2013-10-10 16:08:08 -0700 (Thu, 10 Oct 2013) $ // // $LastChangedBy: torstenrohlfing $ // */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include typedef enum { TTEST, TTEST_PAIRED, CORRELATION_PAIRED, ZSCORES } ModeEnum; int doMain ( const int argc, const char* argv[] ) { ModeEnum Mode = TTEST; bool UseLogData = false; bool UseAbsData = false; bool ConvertToShort = false; bool AbsoluteOutput = false; bool Invert = false; bool TextFileMode = false; const char* OutFileName = "ttest.nii"; const char* TStatFileName = NULL; const char* MaskFileName = NULL; std::list FileListX; std::list FileListY; try { cmtk::CommandLine cl; cl.SetProgramInfo( cmtk::CommandLine::PRG_TITLE, "T-tests" ); cl.SetProgramInfo( cmtk::CommandLine::PRG_DESCR, "Pixelwise tests of statistical significance. Also compute correlations and z-scores" ); cl.SetProgramInfo( cmtk::CommandLine::PRG_SYNTX, "ttest [options] imageX0 [imageX1 ...] -- imageY0 [imageY1] ...\n" "ttest [options] --symmetric imageX0 [imageX1 ...]"); cl.SetProgramInfo( cmtk::CommandLine::PRG_CATEG, "CMTK.Statistics and Modeling" ); typedef cmtk::CommandLine::Key Key; cl.AddSwitch( Key( 'l', "log" ), &UseLogData, true, "Use log data for testing" ); cl.AddSwitch( Key( 'a', "abs" ), &UseAbsData, true, "Use absolute data for testing" ); cl.AddSwitch( Key( 'A', "abs-out" ), &AbsoluteOutput, true, "Generate absolute (unsigned) data" ); cl.AddSwitch( Key( 'i', "invert" ), &Invert, true, "Invert data, i.e., subtract from 1.0" ); cl.AddSwitch( Key( 's', "short" ), &ConvertToShort, true, "Convert probabilities to short (and scale by 1000)" ); cl.AddSwitch( Key( 'p', "paired" ), &Mode, TTEST_PAIRED, "Compute paired t-test [default: groupwise]" ); cl.AddSwitch( Key( 'c', "cross-correlation" ), &Mode, CORRELATION_PAIRED, "Compute paired cross-correlation" ); cl.AddSwitch( Key( 'Z', "zscores" ), &Mode, ZSCORES, "Compute z-scores of test (y) distribution averages" ); cl.AddSwitch( Key( 't', "text" ), &TextFileMode, true, "Text file input and output rather than image files." ); cl.AddOption( Key( 'm', "mask" ), &MaskFileName, "Mask file name" ); cl.AddOption( Key( 'o', "outfile" ), &OutFileName, "Output file name" ); cl.AddOption( Key( "tstats-file" ), &TStatFileName, "T-statistics output file name (for correlation: p-value output file)" ); cl.Parse( argc, argv ); const char* next = cl.GetNext(); while ( next && strcmp( next, "--" ) ) { FileListX.push_back( next ); next = cl.GetNextOptional(); } next = cl.GetNextOptional(); while ( next ) { FileListY.push_back( next ); next = cl.GetNextOptional(); } } catch ( const cmtk::CommandLine::Exception& e ) { cmtk::StdErr << e << "\n"; throw cmtk::ExitException( 1 ); } cmtk::UniformVolume::SmartPtr refVolume; std::vector dataX; std::vector dataY; std::list::const_iterator fnameIt; if ( TextFileMode ) { fnameIt = FileListX.begin(); for ( ; fnameIt != FileListX.end(); ++fnameIt ) { cmtk::DebugOutput( 1 ) << "Reading X data file " << *fnameIt << "...\n"; std::ifstream stream( *fnameIt ); std::vector data; while ( ! stream.eof() ) { std::string line; getline( stream, line ); if ( line[0] != '#' ) { float value; if ( 1 == sscanf( line.c_str(), "%10f", &value ) ) { data.push_back( value ); } } } cmtk::TypedArray::SmartPtr array( cmtk::TypedArray::Create( cmtk::TYPE_ITEM, data.size() ) ); for ( size_t i = 0; i < data.size(); ++i ) array->Set( data[i], i ); dataX.push_back( array ); } fnameIt = FileListY.begin(); for ( ; fnameIt != FileListY.end(); ++fnameIt ) { cmtk::DebugOutput( 1 ) << "Reading Y data file " << *fnameIt << "...\n"; std::ifstream stream( *fnameIt ); std::vector data; while ( ! stream.eof() ) { std::string line; getline( stream, line ); if ( line[0] != '#' ) { float value; if ( 1 == sscanf( line.c_str(), "%10f", &value ) ) { data.push_back( value ); } } } cmtk::TypedArray::SmartPtr array( cmtk::TypedArray::Create( cmtk::TYPE_ITEM, data.size() ) ); for ( size_t i = 0; i < data.size(); ++i ) array->Set( data[i], i ); dataY.push_back( array ); } } else { fnameIt = FileListX.begin(); for ( ; fnameIt != FileListX.end(); ++fnameIt ) { std::cerr << "Reading X volume " << *fnameIt << "...\n"; cmtk::UniformVolume::SmartPtr volume( cmtk::VolumeIO::ReadOriented( *fnameIt ) ); if ( volume ) { if ( ! refVolume ) refVolume = volume; if ( volume->GetData() ) { dataX.push_back( volume->GetData() ); } } } fnameIt = FileListY.begin(); for ( ; fnameIt != FileListY.end(); ++fnameIt ) { std::cerr << "Reading Y volume " << *fnameIt << "...\n"; cmtk::UniformVolume::SmartPtr volume( cmtk::VolumeIO::ReadOriented( *fnameIt ) ); if ( volume ) { if ( ! refVolume ) refVolume = volume; if ( volume->GetData() ) { dataY.push_back( volume->GetData() ); } } } } std::vector::iterator dataIt; if ( UseLogData ) { for ( dataIt = dataX.begin(); dataIt != dataX.end(); ++dataIt ) (*dataIt)->ApplyFunctionDouble( cmtk::Wrappers::Log ); for ( dataIt = dataY.begin(); dataIt != dataY.end(); ++dataIt ) (*dataIt)->ApplyFunctionDouble( cmtk::Wrappers::Log ); } if ( UseAbsData ) { for ( dataIt = dataX.begin(); dataIt != dataX.end(); ++dataIt ) (*dataIt)->ApplyFunctionDouble( cmtk::Wrappers::Abs ); for ( dataIt = dataY.begin(); dataIt != dataY.end(); ++dataIt ) (*dataIt)->ApplyFunctionDouble( cmtk::Wrappers::Abs ); } cmtk::TypedArray::SmartPtr maskData( NULL ); if ( MaskFileName ) { if ( TextFileMode ) { cmtk::StdErr << "WARNING: mask image not supported in text file mode\n"; } else { cmtk::UniformVolume::SmartPtr volume( cmtk::VolumeIO::ReadOriented( MaskFileName ) ); if ( volume ) maskData = volume->GetData(); if ( ! maskData ) { cmtk::StdErr << "WARNING: could not read mask image " << MaskFileName << "\n"; } } } if ( TextFileMode || refVolume ) { cmtk::TypedArray::SmartPtr probData; switch ( Mode ) { case TTEST: { // allocated by GetUnpairedTTest; freed by SP: cmtk::TypedArray::SmartPtr tstatsData, avgXData, avgYData; if ( TextFileMode ) { if ( dataY.empty() ) { probData = cmtk::TypedArray::SmartPtr( cmtk::HypothesisTests::GetOneSampleTTest( dataX, &tstatsData, &avgXData, maskData ) ); } else { probData = cmtk::TypedArray::SmartPtr( cmtk::HypothesisTests::GetUnpairedTwoTailedTTest( dataX, dataY, &tstatsData, &avgXData, &avgYData, maskData ) ); } } else { if ( dataY.empty() ) { probData = cmtk::TypedArray::SmartPtr( cmtk::HypothesisTests::GetOneSampleTTest( dataX, &tstatsData, NULL /*avgXData*/, maskData ) ); } else { probData = cmtk::TypedArray::SmartPtr( cmtk::HypothesisTests::GetUnpairedTwoTailedTTest( dataX, dataY, &tstatsData, NULL /*avgXData*/, NULL /*avgYData*/, maskData ) ); } } if ( refVolume && TStatFileName ) { cmtk::DebugOutput( 1 ) << "Writing T-statistics to file " << TStatFileName << "\n"; refVolume->SetData( cmtk::TypedArray::SmartPtr( tstatsData ) ); cmtk::VolumeIO::Write( *refVolume, TStatFileName ); } if ( AbsoluteOutput ) probData->ApplyFunctionDouble( cmtk::Wrappers::Abs ); if ( Invert ) probData->Rescale( -1.0, 1.0 ); if ( ConvertToShort ) { probData->Rescale( 1000, 0 ); probData = cmtk::TypedArray::SmartPtr( probData->Convert( cmtk::TYPE_SHORT ) ); } if ( TextFileMode ) { fprintf( stdout, "#M\tp\t\tT\t\tavgX\t\tavgY\n" ); for ( size_t i = 0; i < probData->GetDataSize(); ++i ) { cmtk::Types::DataItem p = 0, t = 0, avgX = 0, avgY = 0; probData->Get( p, i ); tstatsData->Get( t, i ); avgXData->Get( avgX, i ); if ( avgYData ) avgYData->Get( avgY, i ); fprintf( stdout, "%d\t%+-15g\t%+-15g\t%+-15g\t%+-15g\n", (int)i, p, t, avgX, avgY ); } } else { cmtk::DebugOutput( 1 ) << "Writing T-probablities to file " << OutFileName << "\n"; refVolume->SetData( probData ); cmtk::VolumeIO::Write( *refVolume, OutFileName ); } break; } case TTEST_PAIRED: { cmtk::TypedArray::SmartPtr tstatsData; if ( !dataY.empty() ) { try { probData = cmtk::TypedArray::SmartPtr( cmtk::HypothesisTests::GetPairedTwoTailedTTest( dataX, dataY, &tstatsData, NULL /*avgXData*/, NULL /*avgYData*/, maskData ) ); } catch ( const cmtk::Exception& ex ) { cmtk::StdErr << "ERROR: " << ex.what() << "\n"; throw cmtk::ExitException( 1 ); } } if ( refVolume && TStatFileName ) { cmtk::DebugOutput( 1 ) << "Writing T-statistics to file " << TStatFileName << "\n"; refVolume->SetData( cmtk::TypedArray::SmartPtr( tstatsData ) ); cmtk::VolumeIO::Write( *refVolume, TStatFileName ); } if ( AbsoluteOutput ) probData->ApplyFunctionDouble( cmtk::Wrappers::Abs ); if ( Invert ) probData->Rescale( -1.0, 1.0 ); if ( ConvertToShort ) { probData->Rescale( 1000, 0 ); probData = cmtk::TypedArray::SmartPtr( probData->Convert( cmtk::TYPE_SHORT ) ); } cmtk::DebugOutput( 1 ) << "Writing T-probablities to file " << OutFileName << "\n"; refVolume->SetData( probData ); cmtk::VolumeIO::Write( *refVolume, OutFileName ); break; } case CORRELATION_PAIRED: { cmtk::TypedArray::SmartPtr pData; if ( !dataY.empty() ) { try { probData = cmtk::TypedArray::SmartPtr( cmtk::HypothesisTests::GetPairedCorrelation( dataX, dataY, &pData, maskData ) ); } catch ( const cmtk::Exception& ex ) { cmtk::StdErr << "ERROR: " << ex.what() << "\n"; throw cmtk::ExitException( 1 ); } } if ( AbsoluteOutput ) probData->ApplyFunctionDouble( cmtk::Wrappers::Abs ); if ( Invert ) probData->Rescale( -1.0, 1.0 ); if ( ConvertToShort ) { probData->Rescale( 1000, 0 ); probData = cmtk::TypedArray::SmartPtr( probData->Convert( cmtk::TYPE_SHORT ) ); } if ( refVolume && TStatFileName ) { cmtk::DebugOutput( 1 ) << "Writing probabilities to file " << TStatFileName << "\n"; refVolume->SetData( cmtk::TypedArray::SmartPtr( pData ) ); cmtk::VolumeIO::Write( *refVolume, TStatFileName ); } cmtk::DebugOutput( 1) << "Writing correlations to file " << OutFileName << "\n"; refVolume->SetData( probData ); cmtk::VolumeIO::Write( *refVolume, OutFileName ); break; } case ZSCORES: { cmtk::TypedArray::SmartPtr zscoreData = cmtk::TypedArray::SmartPtr( cmtk::HypothesisTests::GetZScores( dataX, dataY, maskData ) ); if ( AbsoluteOutput ) zscoreData->ApplyFunctionDouble( cmtk::Wrappers::Abs ); if ( Invert ) zscoreData->Rescale( -1.0, 1.0 ); if ( ConvertToShort ) { zscoreData->Rescale( 1000, 0 ); zscoreData = cmtk::TypedArray::SmartPtr( zscoreData->Convert( cmtk::TYPE_SHORT ) ); } cmtk::DebugOutput( 1 ) << "Writing z-scores map to file " << OutFileName << "\n"; refVolume->SetData( zscoreData ); cmtk::VolumeIO::Write( *refVolume, OutFileName ); } break; } } return 0; } #include "cmtkSafeMain" cmtk-3.3.1/apps/unsplit.cxx000066400000000000000000000143241276303427400156550ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2013 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 5021 $ // // $LastChangedDate: 2013-11-23 09:17:13 -0800 (Sat, 23 Nov 2013) $ // // $LastChangedBy: torstenrohlfing $ // */ #include #include #include #include #include #include #include #include int doMain( const int argc, const char* argv[] ) { std::list inputFilePaths; const char* outputFilePath = NULL; int axis = 2; cmtk::Types::Coordinate forceSpacing = 0; try { cmtk::CommandLine cl; cl.SetProgramInfo( cmtk::CommandLine::PRG_TITLE, "Unsplit images" ); cl.SetProgramInfo( cmtk::CommandLine::PRG_DESCR, "Join separate image stacks into a single interleaved image volume" ); cl.SetProgramInfo( cmtk::CommandLine::PRG_SYNTX, "unsplit [options] inImage0 inImage1 ..." ); typedef cmtk::CommandLine::Key Key; cl.BeginGroup( "Stacking", "Stacking Options" ); cmtk::CommandLine::EnumGroup::SmartPtr interleaveGroup = cl.AddEnum( "stacking", &axis, "Define slice axis for stacking." ); interleaveGroup->AddSwitch( Key( 'a', "axial" ), (int)cmtk::AXIS_Z, "Interleaved axial images" ); interleaveGroup->AddSwitch( Key( 's', "sagittal" ),(int)cmtk::AXIS_X, "Interleaved sagittal images" ); interleaveGroup->AddSwitch( Key( 'c', "coronal" ), (int)cmtk::AXIS_Y, "Interleaved coronal images" ); interleaveGroup->AddSwitch( Key( 'x', "interleave-x" ), (int)cmtk::AXIS_X, "Interleaved along x axis" ); interleaveGroup->AddSwitch( Key( 'y', "interleave-y" ), (int)cmtk::AXIS_Y, "Interleaved along y axis" ); interleaveGroup->AddSwitch( Key( 'z', "interleave-z" ), (int)cmtk::AXIS_Z, "Interleaved along z axis" ); cl.AddOption( Key( "spacing" ), &forceSpacing, "If non-zero, force slice spacing in the output image to given value. This is required when stacking single-slice images." ); cl.EndGroup(); cl.AddOption( Key( 'o', "output" ), &outputFilePath, "Path for output image." ); cl.Parse( argc, argv ); const char *next = cl.GetNext(); while ( next ) { inputFilePaths.push_back( next ); next = cl.GetNextOptional(); } } catch ( const cmtk::CommandLine::Exception& e ) { cmtk::StdErr << e << "\n"; return 1; } cmtk::UniformVolume::IndexType stackDims; cmtk::Types::Coordinate stackDelta[3] = { 1,1,1 }; std::vector volumes; for ( std::list::const_iterator it = inputFilePaths.begin(); it != inputFilePaths.end(); ++it ) { cmtk::UniformVolume::SmartPtr volume( cmtk::VolumeIO::ReadOriented( *it ) ); if ( ! volume || ! volume->GetData() ) { cmtk::StdErr << "ERROR: Could not read image " << *it << "\n"; return 1; } if ( !volumes.empty() ) { // check image dimensions for ( int dim = 0; dim < 3; ++dim ) { if ( dim == axis ) { if ( (volume->m_Dims[dim] != volumes[0]->m_Dims[dim]) && (volume->m_Dims[dim]+1 != volumes[0]->m_Dims[dim]) ) { cmtk::StdErr << "ERROR: interleaving dimension of image " << *it << " must be same as, or one smaller than first image's\n"; throw cmtk::ExitException( 1 ); } stackDims[dim] += volume->m_Dims[dim]; } else { if ( volume->m_Dims[dim] != volumes[0]->m_Dims[dim] ) { cmtk::StdErr << "ERROR: in-plane dimensions of image " << *it << " do not match first image's\n"; throw cmtk::ExitException( 1 ); } } } } else // ! volumes.size() -> first image { // set dims and deltas; will modify later for ( int dim = 0; dim < 3; ++dim ) { stackDims[dim] = volume->m_Dims[dim]; stackDelta[dim] = volume->m_Delta[dim]; } } volumes.push_back( volume ); } if ( forceSpacing ) stackDelta[axis] = forceSpacing; else stackDelta[axis] = volumes[0]->m_Delta[axis] / volumes.size(); cmtk::DebugOutput( 1 ) << "Stacked image will have dimensions " << stackDims[0] << "x" << stackDims[1] << "x" << stackDims[2] << "\n" << "Stacked image will have pixel size " << stackDelta[0] << "x" << stackDelta[1] << "x" << stackDelta[2] << "\n"; cmtk::UniformVolume::SmartPtr stacked( new cmtk::UniformVolume( stackDims, stackDelta[0], stackDelta[1], stackDelta[2] ) ); stacked->CreateDataArray( volumes[0]->GetData()->GetType() ); int toSlice = 0; for ( int fromSlice = 0; fromSlice < volumes[0]->m_Dims[axis]; ++fromSlice ) { for ( size_t fromVolume = 0; fromVolume < volumes.size(); ++fromVolume, ++toSlice ) { cmtk::ScalarImage::SmartPtr slice( volumes[fromVolume]->GetOrthoSlice( axis, fromSlice ) ); stacked->SetOrthoSlice( axis, toSlice, slice ); } } // get origin and orientation from first input image cmtk::AffineXform::MatrixType xformMatrix = volumes[0]->GetImageToPhysicalMatrix(); // and copy to output stacked->m_IndexToPhysicalMatrix *= xformMatrix; for ( std::map::iterator it = stacked->m_AlternativeIndexToPhysicalMatrices.begin(); it != stacked->m_AlternativeIndexToPhysicalMatrices.end(); ++it ) { it->second *= xformMatrix; } stacked->CopyMetaInfo( *(volumes[0]) ); if ( outputFilePath ) { cmtk::VolumeIO::Write( *stacked, outputFilePath ); } return 0; } #include "cmtkSafeMain" cmtk-3.3.1/apps/unwarp_image_phantom.cxx000066400000000000000000000246601276303427400203670ustar00rootroot00000000000000/* // // Copyright 1997-2010 Torsten Rohlfing // // Copyright 2004-2014 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 5153 $ // // $LastChangedDate: 2014-01-11 12:55:54 -0800 (Sat, 11 Jan 2014) $ // // $LastChangedBy: torsten_at_home $ // */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include cmtk::Xform::SmartConstPtr doFitPoly( const cmtk::LandmarkPairList& pairList, const byte polyDegree ) { cmtk::PolynomialXform::SmartConstPtr polyXform; try { // fit polynomial transformation to landmark pairs. polyXform = cmtk::FitPolynomialToLandmarks( pairList, polyDegree ).GetPolynomialXform(); } catch ( cmtk::PolynomialHelper::DegreeUnsupported& ex ) { cmtk::StdErr << "ERROR: library does not support polynomial degree " << polyDegree << "\n"; cmtk::StdErr << ex.what() << "\n"; throw cmtk::ExitException( 1 ); } return polyXform; } cmtk::Xform::SmartConstPtr doFitSpline( const cmtk::LandmarkPairList& pairList, const cmtk::UniformVolume& unwarpImage, const std::string& gridDims, const cmtk::Types::Coordinate gridSpacing, const cmtk::FitSplineWarpToLandmarks::Parameters& fittingParameters, const bool affineFirst ) { // check for inconsistent parameters if ( (gridSpacing > 0) && !gridDims.empty() ) { cmtk::StdErr << "ERROR: must specify either output spline control point spacing or grid dimensions, but not both.\n"; throw cmtk::ExitException( 1 ); } // fit spline warp, potentially preceded by linear transformation, to landmark pairs. cmtk::AffineXform::SmartConstPtr affineXform; if ( affineFirst ) { try { affineXform = cmtk::FitAffineToLandmarks( pairList ).GetAffineXform(); } catch ( const cmtk::AffineXform::MatrixType::SingularMatrixException& ) { cmtk::StdErr << "ERROR: fitted affine transformation has singular matrix\n"; throw cmtk::ExitException( 1 ); } } // fit by final spacing cmtk::SplineWarpXform::SmartConstPtr splineWarp; if ( gridSpacing ) { splineWarp = cmtk::FitSplineWarpToLandmarks( pairList ).Fit( unwarpImage.m_Size, gridSpacing, affineXform.GetPtr(), fittingParameters ); } else { // or fit by final control point grid dimension if ( !gridDims.empty() ) { double dims[3]; if ( 3 != sscanf( gridDims.c_str(), "%20lf,%20lf,%20lf", &(dims[0]), &(dims[1]), &(dims[2]) ) ) { cmtk::StdErr << "ERROR: grid dimensions must be specified as dimsX,dimsY,dimsZ\n"; throw cmtk::ExitException( 1 ); } splineWarp = cmtk::FitSplineWarpToLandmarks( pairList ).Fit( unwarpImage.m_Size, cmtk::FixedVector<3,double>::FromPointer( dims ), affineXform.GetPtr(), fittingParameters ); } else { cmtk::StdErr << "ERROR: must specify either output spline control point spacing or grid dimensions.\n"; throw cmtk::ExitException( 1 ); } } return splineWarp; } int doMain( const int argc, const char* argv[] ) { // input file system paths std::string inputPhantomPath; std::string inputImagePath; // landmark exclusion threshold cmtk::Types::Coordinate residualThreshold = 5.0; // fitting options bool fitInverse = false; bool fitSpline = false; // polynomial fitting options byte polyDegree = 4; // B-spline fitting options std::string gridDims; cmtk::Types::Coordinate gridSpacing = 0; cmtk::FitSplineWarpToLandmarks::Parameters splineFittingParameters; bool affineFirst = true; // output path std::string outputXform; try { cmtk::CommandLine cl; cl.SetProgramInfo( cmtk::CommandLine::PRG_TITLE, "Create transformation to unwarp an image based on a phantom description" ); cl.SetProgramInfo( cmtk::CommandLine::PRG_DESCR, "This tool computes either a polynomial transformation or B-spline free-form deformation to unwarp an image. The transformation is based on expected and detected landmarks in an image of a structural phantom " "acquired on the same scanner. Use the 'detect_adni_phantom' tool to detect landmarks of the ADNI Phantom in an image and generate a phantom description file suitable for use with this tool." ); typedef cmtk::CommandLine::Key Key; cl.BeginGroup( "Fitting", "Fitting Options" ); cl.AddSwitch( Key( "fit-inverse" ), &fitInverse, true, "Fit inverse transformation (mapping actual to expected landmark locations). " "This is useful for computing a Jacobian volume correction map (using 'reformatx') without having to numerically invert the fitted unwarping transformation." ); cl.AddSwitch( Key( "fit-forward" ), &fitInverse, false, "Fit forward transformation (mapping expected to actual landmark locations). This is useful for rectifying the image via reslicing (using 'reformatx')." ); cl.EndGroup(); cl.BeginGroup( "Xform", "Transformation Models" ); cl.AddSwitch( Key( "spline" ), &fitSpline, true, "Fit B-spline free-form deformation." ); cl.AddSwitch( Key( "poly" ), &fitSpline, false, "Fit polynomial transformation." ); cl.EndGroup(); cl.BeginGroup( "PolyFitting", "Polynomial Fitting Options (with --poly)" ); cl.AddOption( Key( "degree" ), &polyDegree, "Degree of the fitted polynomial transformation." ); cl.EndGroup(); cl.BeginGroup( "SplineFitting", "B-Spline Fitting Options (with --spline)" ); cl.AddOption( Key( "final-cp-spacing" ), &gridSpacing, "Final control point grid spacing of the output B-spline transformation." ); cl.AddOption( Key( "final-cp-dims" ), &gridDims, "Final control point grid dimensions (i.e., number of control points) of the output B-spline transformation. To be provided as 'dimX,dimY,dimZ'." ); cl.AddOption( Key( "levels" ), &splineFittingParameters.m_Levels, "Number of levels in the multi-level B-spline approximation procedure." ); cl.AddOption( Key( "iterations-per-level" ), &splineFittingParameters.m_IterationsPerLevel, "Maximum number of spline coefficient update iterations per level in the multi-level B-spline approximation procedure." ); cl.AddOption( Key( "rms-threshold" ), &splineFittingParameters.m_ResidualThreshold, "Threshold for relative improvement of the RMS fitting residual. " "The fitting iteration terminates if (rmsAfterUpdate-rmsBeforeUpdate)/rmsBeforeUpdate < threshold." ); cl.AddSwitch( Key( "no-fit-affine" ), &affineFirst, false, "Disable fitting of affine transformation to initialize spline. Instead, fit spline directly. This usually gives worse results and is discouraged." ); cl.EndGroup(); cl.AddParameter( &inputPhantomPath, "InputPhantom", "Input path of the XML file describing a phantom previously detected in an image." ); cl.AddParameter( &inputImagePath, "InputImage", "Input image path. This is the image that is unwarped. It is important that this image be acquired on the same scanner (not only the same model but the very machine) " "on which the phantom image was also acquired, preferably in close temporal proximity. Also, both this and the phantom image must share and specify the same physical image coordinates, i.e., only images in " "NIFTI or NRRD format can be used." ) ->SetProperties( cmtk::CommandLine::PROPS_IMAGE ); cl.AddParameter( &outputXform, "OutputXform", "Output transformation path. This is the affine phantom-to-image coordinate transformation fitted to the detected landmark spheres." ) ->SetProperties( cmtk::CommandLine::PROPS_XFORM | cmtk::CommandLine::PROPS_OUTPUT ); cl.Parse( argc, argv ); } catch ( const cmtk::CommandLine::Exception& e ) { cmtk::StdErr << e << "\n"; return 1; } // read phantom description cmtk::DetectedPhantomMagphanEMR051::SmartPtr phantom( cmtk::PhantomIO::Read( inputPhantomPath ) ); cmtk::DebugOutput( 5 ) << "INFO: read phantom with " << phantom->LandmarkPairsList().size() << " landmarks.\n"; cmtk::UniformVolume::SmartConstPtr unwarpImage = cmtk::VolumeIO::ReadOriented( inputImagePath ); try { phantom->ApplyXformToLandmarks( cmtk::AffineXform( unwarpImage->GetImageToPhysicalMatrix().GetInverse() ) ); } catch ( const cmtk::AffineXform::MatrixType::SingularMatrixException& ) { cmtk::StdErr << "ERROR: singular image-to-physical space matrix cannot be inverted.\n"; throw cmtk::ExitException( 1 ); } cmtk::LandmarkPairList pairList; for ( std::list::const_iterator it = phantom->LandmarkPairsList().begin(); it != phantom->LandmarkPairsList().end(); ++it ) { if ( it->m_Precise ) // exclude all unprecise landmarks { if ( it->m_Residual < residualThreshold ) // exclude outliers based on residual { if ( fitInverse ) { pairList.push_back( it->GetSwapSourceTarget() ); } else { pairList.push_back( *it ); } } } } cmtk::DebugOutput( 2 ) << "INFO: using " << pairList.size() << " out of " << phantom->LandmarkPairsList().size() << " total phantom landmarks as fiducials.\n"; cmtk::Xform::SmartConstPtr xform; if ( fitSpline ) { xform = doFitSpline( pairList, *unwarpImage, gridDims, gridSpacing, splineFittingParameters, affineFirst ); } else { xform = doFitPoly( pairList, polyDegree ); } // writing resulting transformation cmtk::XformIO::Write( xform, outputXform ); return 0; } #include "cmtkSafeMain" cmtk-3.3.1/apps/vol2csv.cxx000066400000000000000000000235351276303427400155610ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2014 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 5353 $ // // $LastChangedDate: 2014-05-02 14:54:56 -0700 (Fri, 02 May 2014) $ // // $LastChangedBy: torstenrohlfing $ // */ #include #include #include #include #include #include #include #include #include #include #include int doMain ( const int argc, const char *argv[] ) { std::string regionsImagePath; std::string labelsFilePath; double pscaleFactor = 1; std::string pscaleImagePath; std::string outputFilePath; std::string densityLabels; std::vector densityImagePaths; cmtk::Types::DataItem normalizeDensities = 1.0; try { cmtk::CommandLine cl; cl.SetProgramInfo( cmtk::CommandLine::PRG_TITLE, "Compute regional volumes and write to CSV file." ); cl.SetProgramInfo( cmtk::CommandLine::PRG_DESCR, "This tool computes the volumes of regions in a label image. " "It optionally accepts density maps (e.g., for different tissues) and computes and prints the per-region content for each. " "Also, the tool can accept an optional 'pixel volume' map to account for local pixel volume variations, e.g., due to spatial distortion." ); cl.AddParameter( ®ionsImagePath, "RegionsImage", "Image of labeled regions." )->SetProperties( cmtk::CommandLine::PROPS_IMAGE ); cl.AddParameterVector( &densityImagePaths, "DensityImages", "List of density images. For each image given here, the total density per region is computed for each label in the regions image.") ->SetProperties( cmtk::CommandLine::PROPS_IMAGE | cmtk::CommandLine::PROPS_OPTIONAL ); typedef cmtk::CommandLine::Key Key; cl.BeginGroup( "input", "Input Options" ); cl.AddOption( Key( "normalize-densities" ), &normalizeDensities, "Optional normalization factor for density images. Typically, the values in the density images should be in the range 0..1, but often such images are scaled to " "different ranges to accomodate storage as integers. If, for example, densities are stored as values 0..255, set this paramater to 255." ); cl.AddOption( Key( "labels-file" ), &labelsFilePath, "If provided, this text file contains names for all labels in the regions image. These names are then used to label the rows of the CSV output." ); cl.EndGroup(); cl.BeginGroup( "correct", "Correction Options" ); cl.AddOption( Key( "pixel-scale-factor" ), &pscaleFactor, "If provided, this global scale factor is applied to all pixel volumes to compensate for deviations between world and image scale." ); cl.AddOption( Key( "pixel-scale-image" ), &pscaleImagePath, "If provided, this volume contains scale factors for the volume of each pixel. This is typically the Jacobian determinant map of a spatial unwarping deformation." ) ->SetProperties( cmtk::CommandLine::PROPS_IMAGE ); cl.EndGroup(); cl.BeginGroup( "output", "Output Options" ); cl.AddOption( Key( "density-labels" ), &densityLabels, "This option can be used to provide labels for the density maps, which are used as column labels in the output. " "Labels must be separated by commas and must not contain any unescaped spaces." ); cl.AddOption( Key( 'o', "output" ), &outputFilePath, "If provided, program output is written to this file. If not provided, output is written to the STDOUT stream." ); cl.EndGroup(); cl.Parse( argc, argv ); } catch ( const cmtk::CommandLine::Exception& e ) { cmtk::StdErr << e << "\n"; throw cmtk::ExitException( 1 ); } // read ROI image cmtk::UniformVolume::SmartConstPtr regionsImage( cmtk::VolumeIO::ReadOriented( regionsImagePath ) ); if ( ! regionsImage ) { cmtk::StdErr << "ERROR: could not read regions image " << regionsImagePath << "\n"; throw cmtk::ExitException( 1 ); } // read optional label name text file std::map labelToNameMap; if ( !labelsFilePath.empty() ) { std::ifstream labelsFile( labelsFilePath.c_str() ); if ( !labelsFile.good() ) { cmtk::StdErr << "ERROR: could not read label file " << labelsFilePath << "\n"; throw cmtk::ExitException( 1 ); } size_t idx; std::string name; std::string restOfLine; while ( ! labelsFile.eof() ) { labelsFile >> idx >> name; labelToNameMap[idx] = name; std::getline( labelsFile, restOfLine ); } } // read optional pixel volume scale image cmtk::UniformVolume::SmartPtr pscaleImage; if ( !pscaleImagePath.empty() ) { pscaleImage = cmtk::VolumeIO::ReadOriented( pscaleImagePath ); if ( ! pscaleImage ) { cmtk::StdErr << "ERROR: could not read pixel volume image " << pscaleImagePath << "\n"; throw cmtk::ExitException( 1 ); } if ( ! regionsImage->GridMatches( *pscaleImage ) ) { cmtk::StdErr << "ERROR: grid of pixel volume image " << pscaleImagePath << " does not match that of the regions image.\n"; throw cmtk::ExitException( 1 ); } } // read optional density images std::vector densityImages; for ( size_t idx = 0; idx < densityImagePaths.size(); ++idx ) { cmtk::UniformVolume::SmartPtr nextImage( cmtk::VolumeIO::ReadOriented( densityImagePaths[idx] ) ); if ( ! nextImage ) { cmtk::StdErr << "ERROR: could not read density image " << densityImagePaths[idx] << "\n"; throw cmtk::ExitException( 1 ); } if ( ! regionsImage->GridMatches( *nextImage ) ) { cmtk::StdErr << "ERROR: grid of density image " << densityImagePaths[idx] << " does not match that of the regions image.\n"; throw cmtk::ExitException( 1 ); } if ( normalizeDensities != 1.0 ) nextImage->GetData()->Rescale( 1.0 / normalizeDensities ); densityImages.push_back( nextImage ); } // parse optional density labels for output columns std::vector densityLabelsVector; if ( densityLabels.empty() ) { for ( size_t midx = 0; midx < densityImages.size(); ++midx ) { std::ostringstream strm; strm << "density" << midx; densityLabelsVector.push_back( strm.str() ); } } else { densityLabelsVector = cmtk::StrSplit( densityLabels, "," ); } // make sure we have exactly one label per column if ( densityLabelsVector.size() != densityImages.size() ) { cmtk::StdErr << "ERROR: must provide exactly one density label per density image (identified " << densityLabelsVector.size() << " labels for " << densityImages.size() << " images)\n"; throw cmtk::ExitException(); } // compute pixel volume const cmtk::Types::Coordinate pixelVolumeRegionsImage = regionsImage->m_Delta.Product(); // compute number of labels in the ROI image const size_t maxLabel = std::max( 1, static_cast( regionsImage->GetData()->GetRange().m_UpperBound ) ); std::vector regionVolumes( 1+maxLabel, 0.0 ); // prepare vector for volume per label and density map std::vector< std::vector > regionDensities( densityImages.size() ); for ( size_t midx = 0; midx < densityImages.size(); ++midx ) { regionDensities[midx].resize( 1+maxLabel, 0.0 ); } // go over all pixels and count/compound volumes for ( size_t px = 0; px < regionsImage->GetNumberOfPixels(); ++px ) { const size_t label = std::min( maxLabel, std::max( 0, static_cast( regionsImage->GetDataAt( px ) ) ) ); // get size for this pixel and scale, depending on whether we have a per-pixel map or not. cmtk::Types::Coordinate pixelVolume = pixelVolumeRegionsImage * pscaleFactor; if ( pscaleImage ) pixelVolume *= pscaleImage->GetDataAt( px ); regionVolumes[label] += pixelVolume; for ( size_t midx = 0; midx < densityImages.size(); ++midx ) { regionDensities[midx][label] += pixelVolume * densityImages[midx]->GetDataAt( px ); } } // select either output file or standard output std::ofstream outputFile; std::ostream& output = !outputFilePath.empty() ? outputFile.open( outputFilePath.c_str(), std::ios::out), outputFile : std::cout; // write column labels output << "label,volume"; for ( size_t midx = 0; midx < densityImages.size(); ++midx ) { output << "," << densityLabelsVector[midx]; } output << "\n"; // write rows with label volumes for ( size_t label = 0; label <= maxLabel; ++label ) { if ( !labelsFilePath.empty() ) { std::map::const_iterator it = labelToNameMap.find( label ); if ( it == labelToNameMap.end() ) continue; output << "\"" << it->second << "\""; } else { output << label; } output << "," << regionVolumes[label]; for ( size_t midx = 0; midx < densityImages.size(); ++midx ) { output << "," << regionDensities[midx][label]; } output << "\n"; } return 0; } #include "cmtkSafeMain" cmtk-3.3.1/apps/volume_injection.cxx000066400000000000000000000302551276303427400175310ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2014 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 5129 $ // // $LastChangedDate: 2014-01-09 13:48:15 -0800 (Thu, 09 Jan 2014) $ // // $LastChangedBy: torstenrohlfing $ // */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include const char* ReconstructionGridPath = NULL; bool ExcludeFirstImage = false; std::vector XformPaths; std::vector ImagePaths; std::vector Xforms; std::vector Images; const char* OutputImagePath = "volume_injection.nii"; bool WriteImagesAsFloat = false; bool VolumeInjectionIsotropic = false; double VolumeInjectionSigma = 1; int VolumeInjectionRadius = 0; std::map PassWeights; void CallbackSetPassWeight( const char* argv ) { int pass = 0; float weight = 1.0; if ( 2 == sscanf( argv, "%4d:%10f", &pass, &weight ) ) { PassWeights[pass] = weight; } else { cmtk::StdErr << "ERROR: pass weights must be given as 'pass:weight', where 'pass' is an integer and 'weight' is a number between 0 and 1.\n" << " Parameter provided was '" << argv << "'\n"; throw cmtk::ExitException( 1 ); } } bool UseCropRegion = false; cmtk::DataGrid::RegionType CropRegion; void CallbackCrop( const char* arg ) { int cropFrom[3], cropTo[3]; UseCropRegion = (6 == sscanf( arg, "%6d,%6d,%6d,%6d,%6d,%6d", cropFrom, cropFrom+1, cropFrom+2, cropTo,cropTo+1,cropTo+2 ) ); if ( UseCropRegion ) { CropRegion = cmtk::DataGrid::RegionType( cmtk::DataGrid::IndexType::FromPointer( cropFrom ), cmtk::DataGrid::IndexType::FromPointer( cropTo ) ); } else { cmtk::StdErr.printf( "ERROR: string '%s' does not describe a valid crop region\n", arg ); throw cmtk::ExitException( 1 ); } } cmtk::UniformVolume::SmartPtr ReconGrid( NULL ); void CallbackReconGrid( const char* arg ) { int gridDims[3] = { 0, 0, 0 }; float gridDelta[3] = { 0, 0, 0 }; float gridOffset[3] = { 0, 0, 0 }; const size_t numArgs = sscanf( arg, "%6d,%6d,%6d:%15f,%15f,%15f:%15f,%15f,%15f", gridDims, gridDims+1, gridDims+2, gridDelta, gridDelta+1, gridDelta+2, gridOffset, gridOffset+1, gridOffset+2 ); if ( (numArgs != 6) && (numArgs != 9) ) { cmtk::StdErr << "ERROR: reconstruction volume definition must be int,int,int:float,float,float or int,int,int:float,float,float:float,float,float\n"; throw cmtk::ExitException( 1 ); } ReconGrid = cmtk::UniformVolume::SmartPtr( new cmtk::UniformVolume( cmtk::UniformVolume::IndexType::FromPointer( gridDims ), gridDelta[0], gridDelta[1], gridDelta[2] ) ); ReconGrid->SetMetaInfo( cmtk::META_SPACE, cmtk::AnatomicalOrientation::ORIENTATION_STANDARD ); ReconGrid->SetMetaInfo( cmtk::META_SPACE_ORIGINAL, cmtk::AnatomicalOrientation::ORIENTATION_STANDARD ); ReconGrid->SetMetaInfo( cmtk::META_IMAGE_ORIENTATION, cmtk::AnatomicalOrientation::ORIENTATION_STANDARD ); ReconGrid->SetMetaInfo( cmtk::META_IMAGE_ORIENTATION_ORIGINAL, cmtk::AnatomicalOrientation::ORIENTATION_STANDARD ); if ( numArgs == 9 ) { ReconGrid->SetOffset( cmtk::UniformVolume::CoordinateVectorType::FromPointer( gridOffset ) ); } } void WriteOutputImage( cmtk::UniformVolume::SmartPtr& image, const char* path ) { cmtk::UniformVolume::SmartPtr outputImage = image; const cmtk::ScalarDataType type = Images[0]->GetData()->GetType(); if ( !WriteImagesAsFloat && (outputImage->GetData()->GetType() != type) ) { outputImage = cmtk::UniformVolume::SmartPtr( outputImage->CloneGrid() ); outputImage->SetData( cmtk::TypedArray::SmartPtr( image->GetData()->Convert( type ) ) ); } cmtk::VolumeIO::Write( *outputImage, path ); } int doMain( const int argc, const char* argv[] ) { /* // Parse command line */ try { cmtk::CommandLine cl; cl.SetProgramInfo( cmtk::CommandLine::PRG_TITLE, "Volume injection" ); cl.SetProgramInfo( cmtk::CommandLine::PRG_DESCR, "Reconstruction a high-resolution volume from multiple co-registered (low-resolution) images using forward volume injection" ); cl.SetProgramInfo( cmtk::CommandLine::PRG_SYNTX, "volume_injection [options] refImage xform0 inImage0 [xform1 inImage1 ...]" ); typedef cmtk::CommandLine::Key Key; cl.BeginGroup( "Input", "Input Options" ); cl.AddSwitch( Key( 'x', "exclude-first-image" ), &ExcludeFirstImage, true, "Exclude first image from reconstruction as a separate registration target image)" ); cl.AddCallback( Key( 'W', "pass-weight" ), CallbackSetPassWeight, "Set contribution weight for a pass in the form 'pass:weight'" ); cl.EndGroup(); cl.BeginGroup( "Grid", "Reconstruction Grid Options" ); cl.AddCallback( Key( "recon-grid" ), CallbackReconGrid, "Define reconstruction grid as Nx,Ny,Nz:dX,dY,dZ[:Ox,Oy,Oz] (dims:pixel:offset)" ); cl.AddOption( Key( 'R', "recon-grid-path" ), &ReconstructionGridPath, "Give path to grid that defines reconstructed image grid [including offset]" ); cl.AddCallback( Key( "crop" ), CallbackCrop, "Crop reference to pixel region x0,y0,z1:x1,y1,z1" ); cl.EndGroup(); cl.BeginGroup( "Injection", "Volume Injection Options" ); cl.AddSwitch( Key( "isotropic-injection" ), &VolumeInjectionIsotropic, true, "Use isotropic volume injection [default: scaled with pass image pixel size per dimension]" ); cl.AddOption( Key( 'S', "injection-kernel-sigma" ), &VolumeInjectionSigma, "Gauss contribution" ); cl.AddOption( Key( 'r', "injection-kernel-radius" ), &VolumeInjectionRadius, "VolumeInjectionRadius of affected pixel" ); cl.EndGroup(); cl.BeginGroup( "Output", "Output Options" ); cl.AddOption( Key( 'o', "output" ), &OutputImagePath, "Output image path" ); cl.AddSwitch( Key( 'F', "write-images-as-float" ), &WriteImagesAsFloat, true, "Write output images as floating point [default: same as input]" ); cl.EndGroup(); cl.Parse( argc, argv ); ImagePaths.push_back( cl.GetNext() ); XformPaths.push_back( NULL ); const char* nextXform = cl.GetNext(); const char* nextImage = cl.GetNext(); while ( nextXform && nextImage ) { XformPaths.push_back( nextXform ); ImagePaths.push_back( nextImage ); nextXform = cl.GetNextOptional(); nextImage = cl.GetNextOptional(); } } catch ( const cmtk::CommandLine::Exception& e ) { cmtk::StdErr << e << "\n"; return 1; } if ( ExcludeFirstImage ) ReconstructionGridPath = ImagePaths[0]; if ( ReconstructionGridPath ) { ReconGrid = cmtk::UniformVolume::SmartPtr( cmtk::VolumeIO::ReadOriented( ReconstructionGridPath ) ); if ( ! ReconGrid ) { cmtk::StdErr << "ERROR: Could not read reconstruction grid from image " << ReconstructionGridPath << "\n"; throw cmtk::ExitException( 1 ); } } for ( size_t idx = (ExcludeFirstImage?1:0); idx < ImagePaths.size(); ++idx ) { cmtk::UniformVolume::SmartPtr image( cmtk::VolumeIO::ReadOriented( ImagePaths[idx] ) ); if ( ! image || ! image->GetData() ) { cmtk::StdErr << "ERROR: Could not read image " << ImagePaths[idx] << "\n"; throw cmtk::ExitException( 1 ); } cmtk::Xform::SmartPtr xform( new cmtk::AffineXform ); if ( XformPaths[idx] && strcmp( XformPaths[idx], "--" ) ) { xform = cmtk::Xform::SmartPtr( cmtk::XformIO::Read( XformPaths[idx] ) ); if ( ! xform ) { cmtk::StdErr << "ERROR: Could read transformation from file" << XformPaths[idx] << "\n"; } } cmtk::AffineXform::SmartPtr affineXform( cmtk::AffineXform::SmartPtr::DynamicCastFrom( xform ) ); if ( affineXform && (affineXform->GetMetaInfo( cmtk::META_SPACE ) != cmtk::AnatomicalOrientation::ORIENTATION_STANDARD) ) { try { cmtk::TransformChangeFromSpaceAffine toStandardSpace( *affineXform, *ReconGrid, *image ); *affineXform = toStandardSpace.GetTransformation(); } catch ( const cmtk::AffineXform::MatrixType::SingularMatrixException& ) { cmtk::StdErr << "ERROR: singular matrix encountered in cmtk::TransformChangeFromSpaceAffine constructor\n"; throw cmtk::ExitException( 1 ); } affineXform->SetMetaInfo( cmtk::META_SPACE, cmtk::AnatomicalOrientation::ORIENTATION_STANDARD ); } Images.push_back( image ); Xforms.push_back( xform ); } if ( ! ReconGrid ) { // No recon grid from command line: use first input image. ReconGrid = Images[0]; } else { // If we have a pre-defined reconstruction grid, make its physical coordinates match the first input image // First, get the recon grid offset const cmtk::UniformVolume::CoordinateVectorType offset = ReconGrid->m_Offset; // Convert offset to input image index coordinates const cmtk::UniformVolume::CoordinateVectorType indexOffset = cmtk::ComponentDivide( offset, Images[0]->m_Delta ); // New offset is the index grid offset transformed to physical space const cmtk::UniformVolume::CoordinateVectorType newOffset = Images[0]->IndexToPhysical( indexOffset ); // Copy image-to-physical matrix from input to recon image ReconGrid->SetImageToPhysicalMatrix( Images[0]->GetImageToPhysicalMatrix() ); // Finally, copy new offset into recon image-to-physical matrix. for ( int i = 0; i < 3; ++i ) { ReconGrid->m_IndexToPhysicalMatrix[3][i] = newOffset[i]; } } if ( UseCropRegion ) { ReconGrid->CropRegion() = CropRegion; ReconGrid = cmtk::UniformVolume::SmartPtr( ReconGrid->GetCroppedVolume() ); } cmtk::DebugOutput( 1 ).GetStream().printf( "Reconstruction grid: %dx%dx%d pixels, %fx%fx%f pixel size, offset=%f,%f,%f\n", ReconGrid->m_Dims[0], ReconGrid->m_Dims[1], ReconGrid->m_Dims[2], (float)ReconGrid->m_Delta[0], (float)ReconGrid->m_Delta[1], (float)ReconGrid->m_Delta[2], (float)ReconGrid->m_Offset[0], (float)ReconGrid->m_Offset[1], (float)ReconGrid->m_Offset[2] ); cmtk::VolumeInjectionReconstruction injection( ReconGrid, Images ); try { injection.SetTransformationsToPassImages( Xforms ); } catch ( const cmtk::AffineXform::MatrixType::SingularMatrixException& ) { cmtk::StdErr << "ERROR: singular matrix encountered in cmtk::VolumeInjection::SetTransformationsToPassImages()\n"; throw cmtk::ExitException( 1 ); } for ( std::map::const_iterator it = PassWeights.begin(); it != PassWeights.end(); ++it ) { injection.SetPassWeight( it->first, it->second ); } try { if ( VolumeInjectionIsotropic ) injection.VolumeInjectionIsotropic( VolumeInjectionSigma, VolumeInjectionRadius ); else injection.VolumeInjectionAnisotropic( VolumeInjectionSigma, VolumeInjectionRadius ); } catch ( const cmtk::AffineXform::MatrixType::SingularMatrixException& ) { cmtk::StdErr << "ERROR: singular coordinate transformation matrix encountered in volume injection function\n"; throw cmtk::ExitException( 1 ); } if ( OutputImagePath ) { WriteOutputImage( injection.GetCorrectedImage(), OutputImagePath ); } return 0; } #include "cmtkSafeMain" cmtk-3.3.1/apps/volume_reconstruction.cxx000066400000000000000000000462031276303427400206300ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2014 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 5249 $ // // $LastChangedDate: 2014-03-20 14:47:29 -0700 (Thu, 20 Mar 2014) $ // // $LastChangedBy: torstenrohlfing $ // */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include const char* ReconstructionGridPath = NULL; bool ExcludeFirstImage = false; std::vector XformPaths; std::vector ImagePaths; std::vector Xforms; std::vector Images; const char* OutputImagePath = "reconstructed.nii"; const char* LowestMaxErrorImagePath = NULL; bool VolumeInjectionIsotropic = false; double InjectionKernelSigma = 1; double InjectionKernelRadius = 2; bool FourthOrderError = false; double ConstraintWeightLNorm = 0; int InverseInterpolationKernel = cmtk::Interpolators::CUBIC; enum { DEBLURRING_BOX = 1, DEBLURRING_GAUSSIAN = 2 }; int DeblurringKernel = 0; cmtk::Vector3D PointSpreadFunction; bool PointSpreadFunctionSet = false; cmtk::Types::Coordinate PointSpreadFunctionScale = 1.0; void CallbackSetPSF( const char* arg ) { float xyz[3]; if ( 3 != sscanf( arg, "%15f,%15f,%15f", xyz, xyz+1, xyz+2 ) ) { throw "ERROR: point spread function size must be given as three comma-separated real values: x,y,z\n"; } PointSpreadFunction = cmtk::Vector3D::FromPointer( xyz ); PointSpreadFunctionSet = true; } int NumberOfIterations = 20; bool RegionalIntensityTruncation = true; const char* SplattedImagePath = NULL; bool WriteImagesAsFloat = false; std::map PassWeights; void CallbackSetPassWeight( const char* argv ) { int pass = 0; float weight = 1.0; if ( 2 == sscanf( argv, "%4d:%10f", &pass, &weight ) ) { PassWeights[pass] = weight; } else { cmtk::StdErr << "ERROR: pass weights must be given as 'pass:weight', where 'pass' is an integer and 'weight' is a number between 0 and 1.\n" << " Parameter provided was '" << argv << "'\n"; throw cmtk::ExitException( 1 ); } } bool UseCropRegion = false; cmtk::DataGrid::RegionType CropRegion; void CallbackCrop( const char* arg ) { int cropFrom[3], cropTo[3]; UseCropRegion = (6 == sscanf( arg, "%6d,%6d,%6d,%6d,%6d,%6d", cropFrom, cropFrom+1, cropFrom+2, cropTo,cropTo+1,cropTo+2 ) ); if ( UseCropRegion ) { CropRegion = cmtk::DataGrid::RegionType( cmtk::DataGrid::IndexType::FromPointer( cropFrom ), cmtk::DataGrid::IndexType::FromPointer( cropTo ) ); } else { cmtk::StdErr.printf( "ERROR: string '%s' does not describe a valid crop region\n", arg ); throw cmtk::ExitException( 1 ); } } cmtk::UniformVolume::SmartPtr ReconGrid( NULL ); void CallbackReconGrid( const char* arg ) { int gridDims[3] = { 0, 0, 0 }; float gridDelta[3] = { 0, 0, 0 }; float gridOffset[3] = { 0, 0, 0 }; const size_t numArgs = sscanf( arg, "%6d,%6d,%6d:%15f,%15f,%15f:%15f,%15f,%15f", gridDims, gridDims+1, gridDims+2, gridDelta, gridDelta+1, gridDelta+2, gridOffset, gridOffset+1, gridOffset+2 ); if ( (numArgs != 6) && (numArgs != 9) ) { cmtk::StdErr << "ERROR: reconstruction volume definition must be int,int,int:float,float,float or int,int,int:float,float,float:float,float,float\n"; throw cmtk::ExitException( 1 ); } ReconGrid = cmtk::UniformVolume::SmartPtr( new cmtk::UniformVolume( cmtk::UniformVolume::IndexType::FromPointer( gridDims ), gridDelta[0], gridDelta[1], gridDelta[2] ) ); ReconGrid->SetMetaInfo( cmtk::META_SPACE, cmtk::AnatomicalOrientation::ORIENTATION_STANDARD ); ReconGrid->SetMetaInfo( cmtk::META_SPACE_ORIGINAL, cmtk::AnatomicalOrientation::ORIENTATION_STANDARD ); ReconGrid->SetMetaInfo( cmtk::META_IMAGE_ORIENTATION, cmtk::AnatomicalOrientation::ORIENTATION_STANDARD ); ReconGrid->SetMetaInfo( cmtk::META_IMAGE_ORIENTATION_ORIGINAL, cmtk::AnatomicalOrientation::ORIENTATION_STANDARD ); if ( numArgs == 9 ) { ReconGrid->SetOffset( cmtk::Vector3D::FromPointer( gridOffset ) ); } } void WriteOutputImage( cmtk::UniformVolume::SmartPtr& image, const char* path ) { cmtk::UniformVolume::SmartPtr outputImage = image; const cmtk::ScalarDataType type = Images[0]->GetData()->GetType(); if ( !WriteImagesAsFloat && (outputImage->GetData()->GetType() != type) ) { outputImage = cmtk::UniformVolume::SmartPtr( outputImage->CloneGrid() ); outputImage->SetData( cmtk::TypedArray::SmartPtr( image->GetData()->Convert( type ) ) ); } cmtk::VolumeIO::Write( *outputImage, path ); } template cmtk::UniformVolume::SmartPtr ReconstructVolume() { TRecon volRecon( ReconGrid, Images ); volRecon.SetTransformationsToPassImages( Xforms ); for ( std::map::const_iterator it = PassWeights.begin(); it != PassWeights.end(); ++it ) { volRecon.SetPassWeight( it->first, it->second ); } volRecon.SetUseRegionalIntensityTruncation( RegionalIntensityTruncation ); volRecon.SetUseFourthOrderError( FourthOrderError ); volRecon.SetConstraintWeightLNorm( ConstraintWeightLNorm ); cmtk::DebugOutput( 1 ) << "Volume injection...\n"; if ( VolumeInjectionIsotropic ) volRecon.VolumeInjectionIsotropic( InjectionKernelSigma, InjectionKernelRadius ); else volRecon.VolumeInjectionAnisotropic( InjectionKernelSigma, InjectionKernelRadius ); if ( SplattedImagePath ) { WriteOutputImage( volRecon.GetCorrectedImage(), SplattedImagePath ); } const double timeBaseline = cmtk::Timers::GetTimeProcess(); if ( NumberOfIterations ) { volRecon.Optimize( NumberOfIterations ); } cmtk::DebugOutput( 1 ) << "OPT_TIME\t" << cmtk::Timers::GetTimeProcess() - timeBaseline << "\n"; if ( LowestMaxErrorImagePath ) { WriteOutputImage( volRecon.GetLowestMaxErrorImage(), LowestMaxErrorImagePath ); } return volRecon.GetCorrectedImage(); } template cmtk::UniformVolume::SmartPtr ReconstructVolumeDeblurring() { cmtk::Vector3D psf = PointSpreadFunctionScale * PointSpreadFunction; TRecon volRecon( ReconGrid, Images, psf ); volRecon.SetTransformationsToPassImages( Xforms ); for ( std::map::const_iterator it = PassWeights.begin(); it != PassWeights.end(); ++it ) { volRecon.SetPassWeight( it->first, it->second ); } volRecon.SetUseRegionalIntensityTruncation( RegionalIntensityTruncation ); volRecon.SetUseFourthOrderError( FourthOrderError ); volRecon.SetConstraintWeightLNorm( ConstraintWeightLNorm ); cmtk::DebugOutput( 1 ) << "Volume injection...\n"; if ( VolumeInjectionIsotropic ) volRecon.VolumeInjectionIsotropic( InjectionKernelSigma, InjectionKernelRadius ); else volRecon.VolumeInjectionAnisotropic( InjectionKernelSigma, InjectionKernelRadius ); if ( SplattedImagePath ) { WriteOutputImage( volRecon.GetCorrectedImage(), SplattedImagePath ); } const double timeBaseline = cmtk::Timers::GetTimeProcess(); if ( NumberOfIterations ) { volRecon.Optimize( NumberOfIterations ); } cmtk::DebugOutput( 1 ) << "OPT_TIME\t" << cmtk::Timers::GetTimeProcess() - timeBaseline << "\n"; if ( LowestMaxErrorImagePath ) { WriteOutputImage( volRecon.GetLowestMaxErrorImage(), LowestMaxErrorImagePath ); } return volRecon.GetCorrectedImage(); } int doMain( const int argc, const char* argv[] ) { /* // Parse command line */ try { cmtk::CommandLine cl; cl.SetProgramInfo( cmtk::CommandLine::PRG_TITLE, "Volume reconstruction" ); cl.SetProgramInfo( cmtk::CommandLine::PRG_DESCR, "Iterative volume reconstruction from co-registered images using inverse interpolation or joint deblurring" ); cl.SetProgramInfo( cmtk::CommandLine::PRG_SYNTX, "volume_reconstruction [options] refImage xform0 inImage0 [xform1 inImage1 ...]" ); typedef cmtk::CommandLine::Key Key; cl.BeginGroup( "Input", "Input Options" ); cl.AddSwitch( Key( 'x', "exclude-first-image" ), &ExcludeFirstImage, true, "Exclude first image from reconstruction as a separate registration target image)" ); cl.AddCallback( Key( "crop" ), CallbackCrop, "Crop reference to pixel region x0,y0,z1:x1,y1,z1" ); cl.AddCallback( Key( 'W', "pass-weight" ), CallbackSetPassWeight, "Set contribution weight for a pass in the form 'pass:weight'" ); cl.EndGroup(); cl.BeginGroup( "ReconGrid", "Reconstruction Grid" ); cl.AddCallback( Key( "recon-grid" ), CallbackReconGrid, "Define reconstruction grid as Nx,Ny,Nz:dX,dY,dZ[:Ox,Oy,Oz] (dims:pixel:offset)" ); cl.AddOption( Key( 'R', "recon-grid-path" ), &ReconstructionGridPath, "Give path to grid that defines reconstructed image grid [including offset]" ); cl.EndGroup(); cl.BeginGroup( "Injection", "Initial Volume Injection Parameters" ); cl.AddSwitch( Key( "isotropic-injection" ), &VolumeInjectionIsotropic, true, "Use isotropic volume injection [otherwise: scaled with pass image pixel size per dimension]" ); cl.AddOption( Key( 'S', "injection-kernel-sigma" ), &InjectionKernelSigma, "Standard deviation of Gaussian kernel for volume injection in multiples of pixel size in each direction." ); cl.AddOption( Key( 'r', "injection-kernel-radius" ), &InjectionKernelRadius, "Truncation radius factor of injection kernel. The kernel is truncated at sigma*radius, where sigma is the kernel standard deviation." ); cl.EndGroup(); cl.BeginGroup( "Reconstruction", "Volume Reconstruction Options" ); cmtk::CommandLine::EnumGroup::SmartPtr kernelGroup = cl.AddEnum( "inverse-interpolation-kernel", &InverseInterpolationKernel, "Kernel for the inverse interpolation reconstruction" ); kernelGroup->AddSwitch( Key( 'C', "cubic" ), cmtk::Interpolators::CUBIC, "Tricubic interpolation" ); kernelGroup->AddSwitch( Key( 'L', "linear" ), cmtk::Interpolators::LINEAR, "Trilinear interpolation (faster but less accurate)" ); kernelGroup->AddSwitch( Key( 'H', "hamming-sinc" ), cmtk::Interpolators::HAMMING_SINC, "Hamming-windowed sinc interpolation" ); kernelGroup->AddSwitch( Key( 'O', "cosine-sinc" ), cmtk::Interpolators::COSINE_SINC, "Cosine-windowed sinc interpolation (most accurate but slowest)" ); cmtk::CommandLine::EnumGroup::SmartPtr deblurGroup = cl.AddEnum( "deblurring", &DeblurringKernel, "Kernel shape to approximate the point spread function for joint deblurring reconstruction (selecting one of these disables inverse interpolation reconstruction)" ); deblurGroup->AddSwitch( Key( "box" ), (int)DEBLURRING_BOX, "Box-shaped kernel" ); deblurGroup->AddSwitch( Key( "gaussian" ), (int)DEBLURRING_GAUSSIAN, "Gaussian kernel" ); cl.AddCallback( Key( "psf" ), CallbackSetPSF, "Explicitly set point spread function size as x,y,z. Use with 'deblurring' kernel reconstrunction." ); cl.AddOption( Key( "psf-scale" ), &PointSpreadFunctionScale, "Scale point spread function size by this value. Use with 'deblurring' kernel reconstrunction." ); cl.EndGroup(); cl.BeginGroup( "Optimization", "Optimization Parameters" ); cl.AddOption( Key( 'n', "num-iterations" ), &NumberOfIterations, "Maximum number of inverse interpolation iterations" ); cl.AddSwitch( Key( 'f', "fourth-order-error" ), &FourthOrderError, true, "Use fourth-order (rather than second-order) error for optimization." ); cl.EndGroup(); cl.BeginGroup( "Regularization", "Regularization Parameters" ); cl.AddOption( Key( "l-norm-weight" ), &ConstraintWeightLNorm, "Set constraint weight for Tikhonov-type L-Norm regularization (0 disables constraint)" ); cl.AddSwitch( Key( 'T', "no-truncation" ), &RegionalIntensityTruncation, false, "Turn off non-linear regional intensity truncation" ); cl.EndGroup(); cl.BeginGroup( "Output", "Output Options" ); cl.AddOption( Key( 'o', "output" ), &OutputImagePath, "Output path for final reconstructed image" ); cl.AddOption( Key( "write-injected-image" ), &SplattedImagePath, "Write initial volume-injected image to this path" ); cl.AddOption( Key( "write-lowest-max-error-image" ), &LowestMaxErrorImagePath, "Optional path to write reconstructed image with lowest MAXIMUM error." ); cl.AddSwitch( Key( 'F', "write-images-as-float" ), &WriteImagesAsFloat, true, "Write output images as floating point" ); cl.EndGroup(); cl.Parse( argc, argv ); ImagePaths.push_back( cl.GetNext() ); XformPaths.push_back( NULL ); const char* nextXform = cl.GetNext(); const char* nextImage = cl.GetNext(); while ( nextXform && nextImage ) { XformPaths.push_back( nextXform ); ImagePaths.push_back( nextImage ); nextXform = cl.GetNextOptional(); nextImage = cl.GetNextOptional(); } } catch ( const cmtk::CommandLine::Exception& e ) { cmtk::StdErr << e << "\n"; return 1; } if ( ExcludeFirstImage ) ReconstructionGridPath = ImagePaths[0]; if ( ReconstructionGridPath ) { ReconGrid = cmtk::UniformVolume::SmartPtr( cmtk::VolumeIO::ReadOriented( ReconstructionGridPath ) ); if ( ! ReconGrid ) { cmtk::StdErr << "ERROR: Could not read reconstruction grid from image " << ReconstructionGridPath << "\n"; throw cmtk::ExitException( 1 ); } } for ( size_t idx = (ExcludeFirstImage?1:0); idx < ImagePaths.size(); ++idx ) { cmtk::UniformVolume::SmartPtr image( cmtk::VolumeIO::ReadOriented( ImagePaths[idx] ) ); if ( ! image || ! image->GetData() ) { cmtk::StdErr << "ERROR: Could not read image " << ImagePaths[idx] << "\n"; throw cmtk::ExitException( 1 ); } cmtk::AffineXform::SmartPtr affineXform( new cmtk::AffineXform ); if ( XformPaths[idx] && strcmp( XformPaths[idx], "--" ) ) { cmtk::Xform::SmartPtr xform( cmtk::XformIO::Read( XformPaths[idx] ) ); if ( ! xform ) { cmtk::StdErr << "ERROR: Could read affine transformation from file" << XformPaths[idx] << "\n"; } affineXform = cmtk::AffineXform::SmartPtr::DynamicCastFrom( xform ); if ( ! affineXform ) { cmtk::StdErr << "ERROR: transformation " << XformPaths[idx] << " is not affine\n"; } } if ( affineXform->GetMetaInfo( cmtk::META_SPACE ) != cmtk::AnatomicalOrientation::ORIENTATION_STANDARD ) { try { cmtk::TransformChangeFromSpaceAffine toStandardSpace( *affineXform, *ReconGrid, *image ); *affineXform = toStandardSpace.GetTransformation(); } catch ( const cmtk::AffineXform::MatrixType::SingularMatrixException& ) { cmtk::StdErr << "ERROR: singular matrix encountered in cmtk::TransformChangeFromSpaceAffine constructor\n"; throw cmtk::ExitException( 1 ); } affineXform->SetMetaInfo( cmtk::META_SPACE, cmtk::AnatomicalOrientation::ORIENTATION_STANDARD ); } Images.push_back( image ); Xforms.push_back( affineXform ); } if ( ! ReconGrid ) { // No recon grid from command line: use first input image. ReconGrid = Images[0]; } else { // If we have a pre-defined reconstruction grid, make its physical coordinates match the first input image // First, get the recon grid offset const cmtk::UniformVolume::CoordinateVectorType offset = ReconGrid->m_Offset; // Convert offset to input image index coordinates const cmtk::UniformVolume::CoordinateVectorType indexOffset = cmtk::ComponentDivide( offset, Images[0]->m_Delta ); // New offset is the index grid offset transformed to physical space const cmtk::UniformVolume::CoordinateVectorType newOffset = Images[0]->IndexToPhysical( indexOffset ); // Copy image-to-physical matrix from input to recon image ReconGrid->SetImageToPhysicalMatrix( Images[0]->GetImageToPhysicalMatrix() ); // Finally, copy new offset into recon image-to-physical matrix. for ( int i = 0; i < 3; ++i ) { ReconGrid->m_IndexToPhysicalMatrix[3][i] = newOffset[i]; } } if ( UseCropRegion ) { ReconGrid->CropRegion() = CropRegion; ReconGrid = cmtk::UniformVolume::SmartPtr( ReconGrid->GetCroppedVolume() ); } cmtk::DebugOutput( 1 ).GetStream().printf( "Reconstruction grid: %dx%dx%d pixels, %fx%fx%f pixel size, offset=%f,%f,%f\n", ReconGrid->m_Dims[0], ReconGrid->m_Dims[1], ReconGrid->m_Dims[2], (float)ReconGrid->m_Delta[0], (float)ReconGrid->m_Delta[1], (float)ReconGrid->m_Delta[2], (float)ReconGrid->m_Offset[0], (float)ReconGrid->m_Offset[1], (float)ReconGrid->m_Offset[2] ); cmtk::UniformVolume::SmartPtr correctedVolume; if ( !DeblurringKernel ) { switch ( InverseInterpolationKernel ) { case cmtk::Interpolators::LINEAR: default: correctedVolume = ReconstructVolume< cmtk::InverseInterpolationVolumeReconstruction >(); break; case cmtk::Interpolators::CUBIC: correctedVolume = ReconstructVolume< cmtk::InverseInterpolationVolumeReconstruction >(); break; case cmtk::Interpolators::HAMMING_SINC: correctedVolume = ReconstructVolume< cmtk::InverseInterpolationVolumeReconstruction > >(); break; case cmtk::Interpolators::COSINE_SINC: correctedVolume = ReconstructVolume< cmtk::InverseInterpolationVolumeReconstruction > >(); break; } } else { if ( ! PointSpreadFunctionSet ) { cmtk::StdErr << "ERROR: must set point spread function size for deblurring reconstruction\n"; throw cmtk::ExitException( 1 ); } switch ( DeblurringKernel ) { case DEBLURRING_BOX: default: correctedVolume = ReconstructVolumeDeblurring< cmtk::DeblurringVolumeReconstruction >(); break; case DEBLURRING_GAUSSIAN: correctedVolume = ReconstructVolumeDeblurring< cmtk::DeblurringVolumeReconstruction >(); break; } } WriteOutputImage( correctedVolume, OutputImagePath ); return 0; } #include "cmtkSafeMain" cmtk-3.3.1/apps/vtkxform.cxx000066400000000000000000000165211276303427400160400ustar00rootroot00000000000000/* // // Copyright 1997-2010 Torsten Rohlfing // // Copyright 2004-2014 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 1652 $ // // $LastChangedDate: 2010-05-14 14:45:52 -0700 (Fri, 14 May 2010) $ // // $LastChangedBy: torstenrohlfing $ // */ #include #include #include #include #include #include #include #include #include #include #include #include #include int doMain( const int argc, const char* argv[] ) { cmtk::Types::Coordinate inversionTolerance = 0.001; std::vector inputXformPaths; const char* sourceImagePath = NULL; const char* targetImagePath = NULL; try { cmtk::CommandLine cl; cl.SetProgramInfo( cmtk::CommandLine::PRG_TITLE, "Apply coordinate transformation to point coordinates in VTK file." ); cl.SetProgramInfo( cmtk::CommandLine::PRG_DESCR, "An ASCII-format VTK file is read from standard input and a user-provided coordinate transformation (optionally inverted) is applied to the vertex coordinates. A VTK file with transformed points is then written to standard output." ); typedef cmtk::CommandLine::Key Key; cl.AddOption( Key( "inversion-tolerance" ), &inversionTolerance, "Numerical tolerance of B-spline inversion in mm. Smaller values will lead to more accurate inversion, but may increase failure rate." ); cl.AddOption( Key( "source-image" ), &sourceImagePath, "Set source image of the transformation (i.e., the image that the transformation maps points FROM) to correct for differences in orientation and coordinate space." ); cl.AddOption( Key( "target-image" ), &targetImagePath, "Set target image of the transformation (i.e., the image that the transformation maps points TO) to correct for differences in orientation and coordinate space." ); cl.AddParameterVector( &inputXformPaths, "XformList", "List of concatenated transformations. Insert '--inverse' to use the inverse of the transformation listed next. " "(If the first transformation in the sequence is inverted, then '--inverse' must be preceded by '--', i.e., use '-- --inverse xform.path')." ) ->SetProperties( cmtk::CommandLine::PROPS_XFORM | cmtk::CommandLine::PROPS_OPTIONAL ); cl.Parse( argc, argv ); } catch ( cmtk::CommandLine::Exception& ex ) { cmtk::StdErr << ex << "\n"; throw cmtk::ExitException( 1 ); } cmtk::XformList xformList = cmtk::XformListIO::MakeFromStringList( inputXformPaths ); xformList.SetEpsilon( inversionTolerance ); if ( sourceImagePath ) { cmtk::UniformVolume::SmartConstPtr sourceImage( cmtk::VolumeIO::ReadOriented( sourceImagePath ) ); if ( ! sourceImage ) { cmtk::StdErr << "ERROR: could not read source image '" << sourceImagePath << "'\n"; throw cmtk::ExitException( 1 ); } try { xformList.AddToFront( cmtk::AffineXform::SmartPtr( new cmtk::AffineXform( sourceImage->GetImageToPhysicalMatrix() ) )->GetInverse() ); } catch ( const cmtk::AffineXform::MatrixType::SingularMatrixException& ) { cmtk::StdErr << "ERROR: singular source image-to-physical space matrix.\n"; throw cmtk::ExitException( 1 ); } } if ( targetImagePath ) { cmtk::UniformVolume::SmartConstPtr targetImage( cmtk::VolumeIO::ReadOriented( targetImagePath ) ); if ( ! targetImage ) { cmtk::StdErr << "ERROR: could not read target image '" << targetImagePath << "'\n"; throw cmtk::ExitException( 1 ); } try { xformList.Add( cmtk::AffineXform::SmartPtr( new cmtk::AffineXform( targetImage->GetImageToPhysicalMatrix() ) ) ); } catch ( const cmtk::AffineXform::MatrixType::SingularMatrixException& ) { cmtk::StdErr << "ERROR: singular target image-to-physical space matrix.\n"; throw cmtk::ExitException( 1 ); } } // Is VTK file stored in binary format? bool binaryMode = false; // First, read everything up to and including the "POINTS" line and write everything to output unchanged std::string line; while ( !std::cin.eof() ) { std::getline( std::cin, line ); std::cout << line << std::endl; if ( ! line.compare( 0, 6, "BINARY" ) ) binaryMode = true; if ( ! line.compare( 0, 6, "POINTS" ) ) break; } // If we're not at EOF, then "line" must be "POINTS ..." if ( ! std::cin.eof() ) { // Parse number of points out of line std::stringstream sstream( line.substr( 7 ) ); size_t npoints; sstream >> npoints; // Repeat npoints times cmtk::Xform::SpaceVectorType xyz; for ( size_t n = 0; (n( &xyzFloat[0] ), sizeof( xyzFloat ) ); #ifndef WORDS_BIGENDIAN for ( size_t i = 0; i<3; ++i ) cmtk::Memory::ByteSwapInPlace( xyzFloat[i] ); #endif // #ifndef WORDS_BIGENDIAN xyz = cmtk::FixedVector<3,float>::FromPointer( xyzFloat ); } else { std::cin >> xyz[0] >> xyz[1] >> xyz[2]; } // Apply transformation sequence const bool valid = xformList.ApplyInPlace( xyz ); if ( ! valid ) { // well, not sure what to do now... we should delete the current point from the // mesh, but updating the connectivity isn't a local operation. We could also // keep track of the previous and the next point and put the failed one in the // middle, but that would require a memory and all kinds of special case treatment // (multiple consecutive failues, failures at either end, ...) Also assumes // a 1D mesh (polyline). So maybe not. } // Write transformed point to output // Read original point coordinates from file if ( binaryMode ) { float xyzFloat[3] = { xyz[0], xyz[1], xyz[2] }; #ifndef WORDS_BIGENDIAN for ( size_t i = 0; i<3; ++i ) cmtk::Memory::ByteSwapInPlace( xyzFloat[i] ); #endif // #ifndef WORDS_BIGENDIAN std::cout.write( reinterpret_cast( &xyzFloat[0] ), sizeof( xyzFloat ) ); } else { std::cout << xyz[0] << " " << xyz[1] << " " << xyz[2] << std::endl; } } } // Everything else remains unchanged, so copy from input to output. int c = std::cin.get(); while ( std::cin.good() ) { std::cout << static_cast( c ); c = std::cin.get(); } // if we got here, the program probably ran return 0; } #include "cmtkSafeMain" cmtk-3.3.1/apps/warp.cxx000066400000000000000000000032061276303427400151250ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2011 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 2765 $ // // $LastChangedDate: 2011-01-18 16:17:56 -0800 (Tue, 18 Jan 2011) $ // // $LastChangedBy: torstenrohlfing $ // */ #include #include #include #include #include int doMain ( const int argc, const char *argv[] ) { try { cmtk::ElasticRegistrationCommandLine Registration( argc, argv ); // set up console progress reporting cmtk::ProgressConsole progressInstance( "BSplineImageRegistration" ); Registration.Register(); } catch ( const cmtk::VoxelRegistration::ConstructorFailed& ) { return 1; } return 0; } #include "cmtkSafeMain" cmtk-3.3.1/apps/warp2ps.cxx000066400000000000000000000224721276303427400155600ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2013 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4870 $ // // $LastChangedDate: 2013-09-24 11:01:11 -0700 (Tue, 24 Sep 2013) $ // // $LastChangedBy: torstenrohlfing $ // */ #include #include #include #include #include #include #include #include #include const char* RefFileName = NULL; const char* InListName = NULL; bool InvertXform = false; const char* MinusXformPath = NULL; bool InvertMinusXform = false; float ScaleFactor = 1.0; int SamplingFactor = 1; float LineWidth = 0; int SliceIndex = -1; int Axis = 2; bool DrawBox = false; bool Crop = false; int CropRegion[4] = { 0, 0, 0, 0 }; void SetCropRegion( const char* arg ) { if ( 4 == sscanf( arg, "%6d,%6d:%6d,%6d", &CropRegion[0], &CropRegion[1], &CropRegion[2], &CropRegion[3] ) ) Crop = true; else Crop = false; } void DrawLine( const std::vector& outputX, const std::vector& outputY ) { if ( outputX.empty() ) return; std::cout << outputX[0] << " " << outputY[0] << " m\n"; for ( size_t i = 1; i < outputX.size(); ++i ) std::cout << outputX[i] << " " << outputY[i] << " l\n"; std::cout << "s\n"; } int doMain ( const int argc, const char* argv[] ) { try { cmtk::CommandLine cl; cl.SetProgramInfo( cmtk::CommandLine::PRG_TITLE, "Deformation to PostScript" ); cl.SetProgramInfo( cmtk::CommandLine::PRG_DESCR, "Write deformation field as deformed grid in PostScript format for visualization and illustration" ); cl.SetProgramInfo( cmtk::CommandLine::PRG_SYNTX, "warp2ps [options] referenceImage xform" ); typedef cmtk::CommandLine::Key Key; cl.AddSwitch( Key( "invert-xform" ), &InvertXform, true, "Invert the main transformation" ); cl.AddOption( Key( 'm', "minus-xform" ), &MinusXformPath, "Subtract this transformation (e.g., to exclude initial affine component)" ); cl.AddSwitch( Key( "invert-minus-xform" ), &InvertMinusXform, true, "Invert the minus transformation" ); cl.AddSwitch( Key( 'Z', "axis-z" ), &Axis, 2, "Slice orthogonal to z-axis (axial, default)" ); cl.AddSwitch( Key( 'Y', "axis-y" ), &Axis, 1, "Slice orthogonal to y-axis (sagittal)" ); cl.AddSwitch( Key( 'X', "axis-x" ), &Axis, 0, "Slice orthogonal to x-axis (coronal)" ); cl.AddOption( Key( "slice" ), &SliceIndex, "Index of z-slice (default: center )"); cl.AddOption( Key( "line-width" ), &LineWidth, "Line width [default: 0]" ); cl.AddOption( Key( "sampling" ), &SamplingFactor, "Sampling factor (default: 1)"); cl.AddOption( Key( "scale" ), &ScaleFactor, "Coordinage scale factor (default: 1.0)"); cl.AddSwitch( Key( 'b', "box" ), &DrawBox, true, "Draw reference bounding box" ); cl.AddCallback( Key( 'C', "crop" ), SetCropRegion, "Set in-plane crop region as 'x0,y0:x1,y1'" ); if ( ! cl.Parse( argc, argv ) ) return 1; RefFileName = cl.GetNext(); InListName = cl.GetNext(); } catch ( const cmtk::CommandLine::Exception& ex ) { cmtk::StdErr << ex; return 1; } cmtk::Xform::SmartPtr xform( cmtk::XformIO::Read( InListName ) );; if ( ! xform ) { cmtk::StdErr << "ERROR: could not read transformation " << InListName << "\n"; throw cmtk::ExitException( 1 ); } cmtk::UniformVolume::SmartPtr volume( cmtk::VolumeIO::ReadOriented( RefFileName ) ); if ( ! volume ) { cmtk::StdErr << "ERROR: could not read image " << RefFileName << "\n"; throw cmtk::ExitException( 1 ); } cmtk::Xform::SmartPtr minusXform( cmtk::Xform::SmartPtr::Null() ); if ( MinusXformPath ) { minusXform = cmtk::Xform::SmartPtr( cmtk::XformIO::Read( MinusXformPath ) ); if ( ! minusXform ) { cmtk::StdErr << "ERROR: could not read transformation " << MinusXformPath << "\n"; throw cmtk::ExitException( 1 ); } } int axisX = 0, axisY = 1, axisZ = 2; switch ( Axis ) { default: case 0: axisX = 1; axisY = 2; axisZ = 0; break; case 1: axisX = 0; axisY = 2; axisZ = 1; break; case 2: axisX = 0; axisY = 1; axisZ = 2; break; } if ( ! Crop ) { CropRegion[0] = 0; CropRegion[1] = 0; switch ( Axis ) { default: case 0: CropRegion[2] = volume->GetDims()[cmtk::AXIS_Y]; CropRegion[3] = volume->GetDims()[cmtk::AXIS_Z]; break; case 1: CropRegion[2] = volume->GetDims()[cmtk::AXIS_X]; CropRegion[3] = volume->GetDims()[cmtk::AXIS_Z]; break; case 2: CropRegion[2] = volume->GetDims()[cmtk::AXIS_X]; CropRegion[3] = volume->GetDims()[cmtk::AXIS_Y]; break; } } cmtk::DebugOutput( 1 ).GetStream().printf( "Region: [%d,%d] ... [%d,%d]\n", CropRegion[0], CropRegion[1], CropRegion[2], CropRegion[3] ); if ( SliceIndex < 0 ) SliceIndex = volume->GetDims()[Axis] / 2; const cmtk::Types::Coordinate pixelSizeX = volume->m_Size[axisX] / (volume->GetDims()[axisX]-1); const cmtk::Types::Coordinate pixelSizeY = volume->m_Size[axisY] / (volume->GetDims()[axisY]-1); const cmtk::Types::Coordinate xmin = CropRegion[0] * pixelSizeX * ScaleFactor; const cmtk::Types::Coordinate ymin = CropRegion[1] * pixelSizeY * ScaleFactor; const cmtk::Types::Coordinate xmax = CropRegion[2] * pixelSizeX * ScaleFactor; const cmtk::Types::Coordinate ymax = CropRegion[3] * pixelSizeY * ScaleFactor; // PostScript header std::cout << "%!PS-Adobe-1.0\n"; std::cout << "%%BoundingBox: " << xmin << " " << ymin << " " << xmax << " " << ymax << "\n"; std::cout << "/m {moveto} def\n/l {lineto} def\n"; std::cout << "/s {stroke} def\n/n {newpath} def\n"; std::cout << "2 setlinejoin\n"; std::cout << LineWidth << " setlinewidth\n"; cmtk::UniformVolume::CoordinateVectorType v0, v1; std::vector outputX; std::vector outputY; const int SamplingFactorInLine = (SamplingFactor > 1) ? SamplingFactor / 2 : SamplingFactor; int idx[3] = { 0, 0, 0 }; idx[axisZ] = SliceIndex; // vertical lines for ( idx[axisY] = CropRegion[1]; idx[axisY] < CropRegion[3]; idx[axisY] += SamplingFactor ) { std::cout << "n\n"; // std::cout << "[0.7] 1 setdash\n"; std::cout << LineWidth << " setlinewidth\n"; outputX.resize( 0 ); outputY.resize( 0 ); for ( idx[axisX] = CropRegion[0]; idx[axisX] < CropRegion[2]; idx[axisX] += SamplingFactorInLine ) { bool success = true; v0 = v1 = volume->GetGridLocation( idx[0], idx[1], idx[2] ); if ( xform ) { if ( InvertXform ) success = success && xform->ApplyInverse( v1, v1 ); else v1 = xform->Apply( v1 ); } if ( minusXform ) { if ( InvertMinusXform ) success = success && minusXform->ApplyInverse( v0, v0 ); else v0 = minusXform->Apply( v0 ); } if ( success ) { v1 -= v0; v0 = volume->GetGridLocation( idx[0], idx[1], idx[2] ); v1 += v0; outputX.push_back( ScaleFactor*v1[axisX] ); outputY.push_back( ymax - ScaleFactor*v1[axisY] ); } } DrawLine( outputX, outputY ); } // horizontal lines for ( idx[axisX] = CropRegion[0]; idx[axisX] < CropRegion[2]; idx[axisX] += SamplingFactor ) { std::cout << "n\n"; // std::cout << "[0.7] 1 setdash\n"; std::cout << LineWidth << " setlinewidth\n"; outputX.resize( 0 ); outputY.resize( 0 ); for ( idx[axisY] = CropRegion[1]; idx[axisY] < CropRegion[3]; idx[axisY] += SamplingFactorInLine ) { bool success = true; v0 = v1 = volume->GetGridLocation( idx[0], idx[1], idx[2] ); if ( xform ) { if ( InvertXform ) success = success && xform->ApplyInverse( v1, v1 ); else v1 = xform->Apply( v1 ); } if ( minusXform ) { if ( InvertMinusXform ) success = success && minusXform->ApplyInverse( v0, v0 ); else v0 = minusXform->Apply( v0 ); } if ( success ) { v1 -= v0; v0 = volume->GetGridLocation( idx[0], idx[1], idx[2] ); v1 += v0; outputX.push_back( ScaleFactor*v1[axisX] ); outputY.push_back( ymax - ScaleFactor*v1[axisY] ); } } DrawLine( outputX, outputY ); } // reference box if ( DrawBox ) { std::cout << LineWidth << " setlinewidth\n"; std::cout << xmin << " " << ymin << " m " << xmax << " " << ymin << " l " << xmax << " " << ymax << " l " << xmin << " " << ymax << " l " << xmin << " " << ymin << " l\ns\n"; } // finalize std::cout << "showpage\n"; return 0; } #include "cmtkSafeMain" cmtk-3.3.1/apps/warpx.cxx000066400000000000000000000032361276303427400153200ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2011 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 2765 $ // // $LastChangedDate: 2011-01-18 16:17:56 -0800 (Tue, 18 Jan 2011) $ // // $LastChangedBy: torstenrohlfing $ // */ #include #include #include #include #include int doMain ( const int argc, const char *argv[] ) { try { cmtk::ImagePairNonrigidRegistrationCommandLine Registration( argc, argv ); // set up console progress reporting cmtk::ProgressConsole progressInstance( "BSplineImageRegistration" ); Registration.Register(); } catch ( const cmtk::ImagePairRegistration::ConstructorFailed& ) { return 1; } return 0; } #include "cmtkSafeMain" cmtk-3.3.1/apps/xform2dfield.cxx000066400000000000000000000214051276303427400165420ustar00rootroot00000000000000/* // // Copyright 2016 Google, Inc. // // Copyright 1997-2011 Torsten Rohlfing // // Copyright 2004-2014 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 5404 $ // // $LastChangedDate: 2016-01-14 21:02:15 -0800 (Thu, 14 Jan 2016) $ // // $LastChangedBy: torstenrohlfing $ // */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef CMTK_USE_GCD # include #endif bool Mask = false; bool OutputAbsolute = false; std::string RefFileName; std::string OutFileName; std::string TargetImagePath; bool TargetImageFSL = false; std::vector InputXformPaths; std::string Downsample; cmtk::Types::Coordinate InversionToleranceFactor = 0.1; int doMain ( const int argc, const char *argv[] ) { cmtk::Threads::GetNumberOfThreads(); cmtk::UniformVolume::SmartPtr volume; try { cmtk::CommandLine cl; cl.SetProgramInfo( cmtk::CommandLine::PRG_TITLE, "Transformation to Deformation Field" ); cl.SetProgramInfo( cmtk::CommandLine::PRG_DESCR, "Convert parametric rigid or nonrigid transformation to deformation field, sampled at pixel locations of a given reference image" ); typedef cmtk::CommandLine::Key Key; cl.AddSwitch( Key( 'm', "mask" ), &Mask, true, "Use reference image pixels as a binary mask." ); cl.AddOption( Key( "target-image-fsl" ), &TargetImagePath, "Path to optional target image to be used in FSL. This is the image that the transformation (and thus the created deformation field) maps to. " "When creating a deformation field to be used with FSL's \"applywarp\" tool, is may be necessary to determine whether the x coordinate of the resulting deformation vectors has to be flipped.", &TargetImageFSL ); cl.AddOption( Key( "inversion-tolerance-factor" ), &InversionToleranceFactor, "Factor for numerical tolerance of B-spline inversion [multiples of minimum grid pixel size; default=0.1]" ); cl.AddOption( Key( "downsample" ), &Downsample, "Downsample grid by factors 'x,y,z' or by single factor 'xyz'" ); cl.AddSwitch( Key( "output-absolute" ), &OutputAbsolute, true, "Make output deformation field with absolute target location vectors, rather than relative offset vectors." ); cl.AddParameter( &OutFileName, "OutputPath", "Path for the output deformation field." )->SetProperties( cmtk::CommandLine::PROPS_IMAGE | cmtk::CommandLine::PROPS_OUTPUT ); cl.AddParameter( &RefFileName, "ReferenceImage", "Input reference grid path. The dimensions and pixel size of this image determine the geometry of the output." )->SetProperties( cmtk::CommandLine::PROPS_IMAGE ); cl.AddParameterVector( &InputXformPaths, "XformList", "List of concatenated transformations. Insert '--inverse' to use the inverse of the transformation listed next. " "(If the first transformation in the sequence is inverted, then '--inverse' must be preceded by '--', i.e., use '-- --inverse xform.path')." )->SetProperties( cmtk::CommandLine::PROPS_XFORM ); cl.Parse( argc, argv ); } catch ( const cmtk::CommandLine::Exception& e ) { cmtk::StdErr << e; throw cmtk::ExitException( 1 ); } if ( Mask ) volume = cmtk::UniformVolume::SmartPtr( cmtk::VolumeIO::ReadOriented( RefFileName ) ); else volume = cmtk::UniformVolume::SmartPtr( cmtk::VolumeIO::ReadGridOriented( RefFileName ) ); if ( ! volume ) { cmtk::StdErr << "Could not read reference volume " << RefFileName << "\n"; throw cmtk::ExitException(1); } // Do we have a target image given (presumably for FSL use at this point)? cmtk::AffineXform::MatrixType targetImageMatrix = cmtk::AffineXform::MatrixType::Identity(); if ( !TargetImagePath.empty() ) { cmtk::UniformVolume::SmartPtr targetImageGrid( cmtk::VolumeIO::ReadGrid( TargetImagePath ) ); if ( ! targetImageGrid ) { cmtk::StdErr << "Could not read target volume grid from " << TargetImagePath << "\n"; throw cmtk::ExitException(1); } // Get initial matrix from index to physical space targetImageMatrix = targetImageGrid->GetImageToPhysicalMatrix().GetInverse(); if ( TargetImageFSL ) { // Warn if not "absolute" output. if ( ! OutputAbsolute ) { cmtk::StdErr << "WARNING: generating deformation field for FSL, but \"--output--absolute\" is not active.\n"; } // Does the matrix have a positive determinant? Then this is what FSL calls "neurological orientation" and we need to flip x if ( targetImageGrid->GetImageToPhysicalMatrix().GetTopLeft3x3().Determinant() > 0 ) { cmtk::AffineXform::MatrixType targetImageMatrixFlip = cmtk::AffineXform::MatrixType::Identity(); targetImageMatrixFlip[0][0] = -1; targetImageMatrixFlip[3][0] = (targetImageGrid->m_Dims[0]-1) * targetImageGrid->m_Delta[0]; targetImageMatrix = targetImageMatrix * targetImageMatrixFlip; } } targetImageGrid = targetImageGrid->GetReoriented( "RAS" ); targetImageMatrix = targetImageGrid->GetImageToPhysicalMatrix() * targetImageMatrix; } cmtk::XformList xformList = cmtk::XformListIO::MakeFromStringList( InputXformPaths ); xformList.SetEpsilon( InversionToleranceFactor * volume->GetMinDelta() ); const cmtk::XformList& xformListRef = xformList; // need this to work around GCD bug if ( !Downsample.empty() ) { int factors[3] = { 1, 1, 1 }; const size_t nFactors = sscanf( Downsample.c_str(), "%6d,%6d,%6d", factors, factors+1, factors+2 ); if ( nFactors == 1 ) { factors[1] = factors[2] = factors[0]; } else { if ( nFactors != 3 ) { cmtk::StdErr << "ERROR: downsampling factors must either be three integers, x,y,z, or a single integer\n"; throw cmtk::ExitException( 1 ); } } const cmtk::Types::GridIndexType gridIndexFactors[3] = { factors[0], factors[1], factors[2] }; volume = cmtk::UniformVolume::SmartPtr( volume->GetDownsampledAndAveraged( gridIndexFactors ) ); } cmtk::DeformationField::SmartPtr dfield( new cmtk::DeformationField( volume ) ); const cmtk::DataGrid::IndexType& dims = volume->GetDims(); cmtk::Progress::Begin( 0, dims[cmtk::AXIS_Z], 1, "Deformation field generation" ); #ifdef CMTK_USE_GCD dispatch_apply( dims[2], dispatch_get_global_queue(0, 0), ^(size_t z){ #else #pragma omp parallel for for ( int z = 0; z < dims[cmtk::AXIS_Z]; ++z ) #endif { cmtk::Xform::SpaceVectorType v0, v1; cmtk::Progress::SetProgress( z ); size_t offset = 3 * z * dims[cmtk::AXIS_X] * dims[cmtk::AXIS_Y]; for ( int y = 0; y < dims[cmtk::AXIS_Y]; ++y ) { for ( int x = 0; x < dims[cmtk::AXIS_X]; ++x, offset+=3 ) { v1 = v0 = volume->GetGridLocation( x, y, z ); bool invalid = true; if ( (!Mask) || (volume->GetDataAt( x, y, z ) > 0) ) { invalid = !xformListRef.ApplyInPlace( v1 ); } if ( !invalid ) { // do any transformations to account for target image coordinate changes due to reorientation if ( TargetImageFSL ) { v1 *= targetImageMatrix; } if ( !OutputAbsolute ) v1 -= v0; } else { v1 = cmtk::Vector3D( std::numeric_limits::quiet_NaN() ); } // store final vector. if ( TargetImageFSL ) { const size_t flipOffset = offset + 3 * ((dims[cmtk::AXIS_X] - 1) - 2 * x); dfield->m_Parameters[flipOffset+0] = v1[0]; dfield->m_Parameters[flipOffset+1] = v1[1]; dfield->m_Parameters[flipOffset+2] = v1[2]; } else { dfield->m_Parameters[offset+0] = v1[0]; dfield->m_Parameters[offset+1] = v1[1]; dfield->m_Parameters[offset+2] = v1[2]; } } } } #ifdef CMTK_USE_GCD }); #endif cmtk::Progress::Done(); cmtk::XformIO::Write( dfield, OutFileName ); return 0; } #include "cmtkSafeMain" cmtk-3.3.1/apps/xform2itk.cxx000066400000000000000000000137421276303427400161070ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2011, 2013 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 5129 $ // // $LastChangedDate: 2014-01-09 13:48:15 -0800 (Thu, 09 Jan 2014) $ // // $LastChangedBy: torstenrohlfing $ // */ #include #include #include #include #include #include #include #include #include #include #include int doMain( const int argc, const char* argv[] ) { std::string inputPath; std::string outputPath; bool invertInputXform = false; std::string fixedImagePath; std::string movingImagePath; std::string fixedImageSpace = cmtk::AnatomicalOrientationBase::SPACE_ITK; std::string movingImageSpace = cmtk::AnatomicalOrientationBase::SPACE_ITK; try { cmtk::CommandLine cl; cl.SetProgramInfo( cmtk::CommandLine::PRG_TITLE, "Convert affine transformations to ITK format." ); cl.SetProgramInfo( cmtk::CommandLine::PRG_DESCR, "This tool converts coordinate transformations from CMTK format to ITK format and, in the process, also correct for differences in image coordinate conventions" ); typedef cmtk::CommandLine::Key Key; cl.BeginGroup( "Input", "Input parameters" )->SetProperties( cmtk::CommandLine::PROPS_NOXML ); cl.AddOption( Key( "fixed-space" ), &fixedImageSpace, "Change fixed image coordinate space (e.g., 'RAS', 'LPS', ...). This defaults to ITK standard space." ); cl.AddOption( Key( "moving-space" ), &movingImageSpace, "Change moving image coordinate space (e.g., 'RAS', 'LPS', ...). This defaults to ITK standard space." ); cl.AddSwitch( Key( "invert-input" ), &invertInputXform, true, "Invert input transformation before conversion" ); cl.EndGroup(); cl.BeginGroup( "Output", "Output parameters" )->SetProperties( cmtk::CommandLine::PROPS_NOXML ); cl.AddOption( Key( "fixed-image" ), &fixedImagePath, "Override transformation's fixed/reference image (if any) with this one." ); cl.AddOption( Key( "moving-image" ), &movingImagePath, "Override transformation's moving/floating image (if any) with this one." ); cl.EndGroup(); cl.AddParameter( &inputPath, "InputPath", "CMTK input transformation path" )->SetProperties( cmtk::CommandLine::PROPS_XFORM ); cl.AddParameter( &outputPath, "OutputPath", "ITK output transformation path" )->SetProperties( cmtk::CommandLine::PROPS_XFORM | cmtk::CommandLine::PROPS_OUTPUT ); cl.Parse( argc, argv ); } catch ( const cmtk::CommandLine::Exception& e ) { cmtk::StdErr << e << "\n"; throw cmtk::ExitException( 1 ); } cmtk::AffineXform::SmartConstPtr xform = cmtk::AffineXform::SmartConstPtr::DynamicCastFrom( cmtk::XformIO::Read( inputPath ) ); if ( !xform ) { cmtk::StdErr << "ERROR: could not read transformation from '" << inputPath << "'\n"; throw cmtk::ExitException( 1 ); } if ( invertInputXform ) { try { xform = xform->GetInverse(); } catch ( const cmtk::AffineXform::MatrixType::SingularMatrixException& ) { cmtk::StdErr << "ERROR: affine transformation with singular matrix cannot be inverted\n"; throw cmtk::ExitException( 1 ); } } if ( fixedImagePath.empty() ) { if ( !xform->MetaKeyExists( cmtk::META_XFORM_FIXED_IMAGE_PATH ) ) { cmtk::StdErr << "ERROR: could not deduce fixed image path from transformation; must be explicitly provided on the command line instead\n"; throw cmtk::ExitException( 1 ); } fixedImagePath = xform->GetMetaInfo( cmtk::META_XFORM_FIXED_IMAGE_PATH ); } if ( movingImagePath.empty() ) { if ( !xform->MetaKeyExists( cmtk::META_XFORM_MOVING_IMAGE_PATH ) ) { cmtk::StdErr << "ERROR: could not deduce moving image path from transformation; must be explicitly provided on the command line instead\n"; throw cmtk::ExitException( 1 ); } movingImagePath = xform->GetMetaInfo( cmtk::META_XFORM_MOVING_IMAGE_PATH ); } cmtk::UniformVolume::SmartPtr fixedImage = cmtk::VolumeIO::ReadGridOriented( cmtk::MountPoints::Translate( fixedImagePath ) ); if ( ! fixedImage ) { cmtk::StdErr << "ERROR: could not read fixed image '" << fixedImagePath << "'\n"; throw cmtk::ExitException( 1 ); } if ( !fixedImageSpace.empty() ) { fixedImage->SetMetaInfo( cmtk::META_SPACE_ORIGINAL, fixedImageSpace ); } cmtk::UniformVolume::SmartPtr movingImage = cmtk::VolumeIO::ReadGridOriented( cmtk::MountPoints::Translate( movingImagePath ) ); if ( ! movingImage ) { cmtk::StdErr << "ERROR: could not read moving image '" << movingImagePath << "'\n"; throw cmtk::ExitException( 1 ); } if ( !movingImageSpace.empty() ) { movingImage->SetMetaInfo( cmtk::META_SPACE_ORIGINAL, movingImageSpace ); } cmtk::TransformChangeToSpaceAffine toNative( *(xform), *(fixedImage), *(movingImage) ); cmtk::AffineXformITKIO::Write( outputPath, toNative.GetTransformation() ); return 0; } #include "cmtkSafeMain" cmtk-3.3.1/apps/xform2scalar.cxx000066400000000000000000000164071276303427400165660ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2013 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 386 $ // // $LastChangedDate: 2009-08-04 14:31:09 -0700 (Tue, 04 Aug 2009) $ // // $LastChangedBy: torstenrohlfing $ // */ #include #include #include #include #include #include #include #include #include #include #include #ifdef CMTK_USE_GCD # include #endif bool WarpOnly = false; namespace cmtk { /// Mode for scalar value extraction. typedef enum { /// Extract x component. X2S_EXTRACT_X, /// Extract y component. X2S_EXTRACT_Y, /// Extract z component. X2S_EXTRACT_Z, /// Extract Jacobian determinant. X2S_MAGNITUDE } XformToScalarMode; } /// Mode for scalar value extraction. cmtk::XformToScalarMode Mode = cmtk::X2S_MAGNITUDE; /// Data type for scalar data. cmtk::ScalarDataType DataType = cmtk::TYPE_DOUBLE; std::string InputGridPath; std::string OutImagePath; std::vector InputXformPaths; int doMain ( const int argc, const char* argv[] ) { cmtk::Threads::GetNumberOfThreads(); try { cmtk::CommandLine cl( cmtk::CommandLine::PROPS_XML ); cl.SetProgramInfo( cmtk::CommandLine::PRG_TITLE, "Extract scalar measures from transformations and deformation fields" ); cl.SetProgramInfo( cmtk::CommandLine::PRG_DESCR, "This tool extracts scalar measures from transformations and deformation fields, sampled at grid locations, and writes the results to an image. " "Examples of supported scalar measures are: x,y,z component of the transformation, magnitude of the transformation, and Jacobian determinants." ); cl.SetProgramInfo( cmtk::CommandLine::PRG_CATEG, "CMTK.Data Processing" ); typedef cmtk::CommandLine::Key Key; cl.BeginGroup( "operation", "Operating Options" ); cmtk::CommandLine::EnumGroup::SmartPtr modeGroup = cl.AddEnum( "mode", &Mode, "Mode of operation: type of scalar measure to be extracted." ); modeGroup->AddSwitch( Key( "x-component" ), cmtk::X2S_EXTRACT_X, "X component of transformation vector." ); modeGroup->AddSwitch( Key( "y-component" ), cmtk::X2S_EXTRACT_Y, "Y component of transformation vector." ); modeGroup->AddSwitch( Key( "z-component" ), cmtk::X2S_EXTRACT_Z, "Z component of transformation vector." ); modeGroup->AddSwitch( Key( "magnitude" ), cmtk::X2S_MAGNITUDE, "Magnitude of the transformation vector." ); cl.AddSwitch( Key( 'w', "warp-only" ), &WarpOnly, true, "Output warp component only (excluding affine)." ); cl.EndGroup(); cl.BeginGroup( "output", "Output Options" ); cmtk::CommandLine::EnumGroup::SmartPtr typeGroup = cl.AddEnum( "type", &DataType, "Scalar data type of output image." ); typeGroup->AddSwitch( Key( "float" ), cmtk::TYPE_FLOAT, "Single-precision float." ); typeGroup->AddSwitch( Key( "double" ), cmtk::TYPE_DOUBLE, "Double-precision float." ); cl.AddOption( Key( 'o', "output" ), &OutImagePath, "Output path for image with extracted scalar data." )->SetProperties( cmtk::CommandLine::PROPS_IMAGE | cmtk::CommandLine::PROPS_OUTPUT ); cl.AddParameter( &InputGridPath, "InputImage", "Input grid path" )->SetProperties( cmtk::CommandLine::PROPS_IMAGE ); cl.AddParameterVector( &InputXformPaths, "XformList", "List of concatenated transformations. Insert '--inverse' to use the inverse of the transformation listed next. " "(If the first transformation in the sequence is inverted, then '--inverse' must be preceded by '--', i.e., use '-- --inverse xform.path')." )->SetProperties( cmtk::CommandLine::PROPS_XFORM ); cl.Parse( argc, argv ); } catch ( const cmtk::CommandLine::Exception& e ) { cmtk::StdErr << e << "\n"; throw cmtk::ExitException( 1 ); } cmtk::UniformVolume::SmartPtr scalarImage( cmtk::VolumeIO::ReadGridOriented( InputGridPath ) ); if ( ! scalarImage ) { cmtk::StdErr << "Could not read grid from image " << InputGridPath << "\n"; throw cmtk::ExitException(1); } scalarImage->CreateDataArray( DataType ); const cmtk::XformList xformList = cmtk::XformListIO::MakeFromStringList( InputXformPaths ); const cmtk::XformList& xformListRef = xformList; // workaround for GCD segfaults const cmtk::XformList xformListAffine = xformList.MakeAllAffine(); const cmtk::XformList& xformListAffineRef = xformListAffine; // workaround for GCD segfaults cmtk::UniformVolume* image = scalarImage.GetPtr(); const cmtk::DataGrid::IndexType& dims = image->GetDims(); #ifdef CMTK_USE_GCD dispatch_apply( dims[2], dispatch_get_global_queue(0, 0), ^(size_t z){ #else #pragma omp parallel for for ( int z = 0; z < dims[2]; ++z ) { #endif size_t offset = z * dims[0] * dims[1]; for ( int y = 0; y < dims[1]; ++y ) { for ( int x = 0; x < dims[0]; ++x, ++offset ) { // Get current grid location. cmtk::UniformVolume::CoordinateVectorType v = scalarImage->GetGridLocation( x, y, z ); const cmtk::UniformVolume::CoordinateVectorType v0( v ); // Apply transformation and subtract original coordinate if ( xformListRef.ApplyInPlace( v ) ) { v -= v0; // Is this is a B-spline warp and we're only interesting in the nonrigid component? if ( WarpOnly ) { // Transform current location also using affine transformation cmtk::Vector3D vAffine( v0 ); if ( ! xformListAffineRef.ApplyInPlace( vAffine ) ) { cmtk::StdErr << "ERROR: applying affine transformation sequence failed.\n"; throw cmtk::ExitException( 1 ); } vAffine -= v0; // subtract affine-transformed from total transformed v -= vAffine; } switch ( Mode ) { case cmtk::X2S_EXTRACT_X: image->SetDataAt( v[0], offset ); break; case cmtk::X2S_EXTRACT_Y: image->SetDataAt( v[1], offset ); break; case cmtk::X2S_EXTRACT_Z: image->SetDataAt( v[2], offset ); break; case cmtk::X2S_MAGNITUDE: image->SetDataAt( v.RootSumOfSquares(), offset ); break; default: break; } } else { image->GetData()->SetPaddingAt( offset ); } } } } #ifdef CMTK_USE_GCD ); #endif if ( !OutImagePath.empty() ) { cmtk::VolumeIO::Write( *scalarImage, OutImagePath ); } return 0; } #include "cmtkSafeMain" cmtk-3.3.1/cmtkConfigureCPack.cmake000066400000000000000000000123761276303427400172010ustar00rootroot00000000000000## ## Copyright 1997-2010 Torsten Rohlfing ## ## Copyright 2004-2014 SRI International ## ## Copyright 2015 Google, Inc. ## ## This file is part of the Computational Morphometry Toolkit. ## ## http://www.nitrc.org/projects/cmtk/ ## ## The Computational Morphometry Toolkit is free software: you can ## redistribute it and/or modify it under the terms of the GNU General Public ## License as published by the Free Software Foundation, either version 3 of ## the License, or (at your option) any later version. ## ## The Computational Morphometry Toolkit is distributed in the hope that it ## will be useful, but WITHOUT ANY WARRANTY; without even the implied ## warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License along ## with the Computational Morphometry Toolkit. If not, see ## . ## ## $Revision: 5389 $ ## ## $LastChangedDate: 2015-09-05 12:58:18 -0700 (Sat, 05 Sep 2015) $ ## ## $LastChangedBy: torstenrohlfing $ ## IF(${CMAKE_SYSTEM_NAME} MATCHES "Windows") SET(CPACK_GENERATOR "ZIP") ELSE(${CMAKE_SYSTEM_NAME} MATCHES "Windows") IF(${CMAKE_SYSTEM_NAME} MATCHES "Linux") SET(CPACK_GENERATOR "TGZ;RPM") ELSE(${CMAKE_SYSTEM_NAME} MATCHES "Linux") SET(CPACK_GENERATOR "TGZ") ENDIF(${CMAKE_SYSTEM_NAME} MATCHES "Linux") ENDIF(${CMAKE_SYSTEM_NAME} MATCHES "Windows") # Optionally override system name and CPU type IF(NOT CMTK_SYSTEM_NAME) SET(CMTK_SYSTEM_NAME ${CMAKE_SYSTEM_NAME}) ENDIF(NOT CMTK_SYSTEM_NAME) IF(NOT CMTK_SYSTEM_PROCESSOR) SET(CMTK_SYSTEM_PROCESSOR ${CMAKE_SYSTEM_PROCESSOR}) ENDIF(NOT CMTK_SYSTEM_PROCESSOR) SET(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_CURRENT_SOURCE_DIR}/COPYING.txt") SET(CPACK_PACKAGE_DESCRIPTION_SUMMARY "CMTK -- The Computational Morphometry Toolkit") SET(CPACK_PACKAGE_DESCRIPTION_FILE "${CMAKE_CURRENT_SOURCE_DIR}/README.txt") SET(CPACK_PACKAGE_VERSION_MAJOR "${CMTK_VERSION_MAJOR}") SET(CPACK_PACKAGE_VERSION_MINOR "${CMTK_VERSION_MINOR}") SET(CPACK_PACKAGE_VERSION_PATCH "${CMTK_VERSION_PATCH}") SET(CPACK_PACKAGE_FILE_NAME "CMTK-${CMTK_VERSION_MAJOR}.${CMTK_VERSION_MINOR}.${CMTK_VERSION_PATCH}-${CMTK_SYSTEM_NAME}-${CMTK_SYSTEM_PROCESSOR}") SET(CPACK_PACKAGE_INSTALL_DIRECTORY "CMTK-${CMTK_VERSION_MAJOR}.${CMTK_VERSION_MINOR}") SET(CPACK_PACKAGING_INSTALL_PREFIX ${CMAKE_INSTALL_PREFIX}) SET(CPACK_INCLUDE_TOPLEVEL_DIRECTORY OFF) SET(CPACK_RPM_PACKAGE_LICENSE "GPL v3") SET(CPACK_RPM_PACKAGE_DESCRIPTION "${CPACK_PACKAGE_DESCRIPTION_SUMMARY}") SET(CPACK_RPM_COMPONENT_INSTALL ON) SET(CPACK_RPM_PACKAGE_URL "http://nitrc.org/projects/cmtk" ) ##SET(CPACK_RPM_CHANGELOG_FILE "${CMAKE_CURRENT_SOURCE_DIR}/CHANGELOG" ) ## Our Changelog is in wrong format SET(CPACK_TGZ_COMPONENT_INSTALL ON) # mandatory package IF(CMTK_USE_QT) SET(CPACK_RPM_PACKAGE_REQUIRES "qt >= 4.7.0") ENDIF(CMTK_USE_QT) # mandatory package with optional bundled replacement IF(NOT CMTK_BUILD_MXML) SET(CPACK_RPM_PACKAGE_REQUIRES "mxml >= 2.5") ENDIF(NOT CMTK_BUILD_MXML) # optional package with optional bundled replacement IF(CMTK_USE_DCMTK AND NOT CMTK_BUILD_DCMTK) SET(CPACK_RPM_PACKAGE_REQUIRES "dcmtk >= 3.5.4") ENDIF(CMTK_USE_DCMTK AND NOT CMTK_BUILD_DCMTK) # optional package with optional bundled replacement IF(CMTK_USE_SQLITE AND NOT CMTK_BUILD_SQLITE) SET(CPACK_RPM_PACKAGE_REQUIRES "sqlite >= 3.7.4") ENDIF(CMTK_USE_SQLITE AND NOT CMTK_BUILD_SQLITE) # optional package IF(CMTK_USE_BZIP2) SET(CPACK_RPM_PACKAGE_REQUIRES "bzip2-devel") ENDIF(CMTK_USE_BZIP2) # optional package IF(CMTK_USE_LZMA) SET(CPACK_RPM_PACKAGE_REQUIRES "lzma-devel") ENDIF(CMTK_USE_LZMA) # optional package IF(CMTK_USE_FFTW_FOUND) SET(CPACK_RPM_PACKAGE_REQUIRES "fftw-devel >= 3.3") IF(${CMAKE_SYSTEM_NAME} MATCHES "Windows") STRING(REPLACE ".lib" ".dll" SYSTEM_FFTW_LIBRARIES ${CMTK_FFTW_LIBRARIES}) STRING(REPLACE "/" "\\\\" SYSTEM_FFTW_LIBRARIES ${SYSTEM_FFTW_LIBRARIES}) LIST(APPEND CMAKE_INSTALL_SYSTEM_RUNTIME_LIBS ${SYSTEM_FFTW_LIBRARIES}) ENDIF(${CMAKE_SYSTEM_NAME} MATCHES "Windows") ENDIF(CMTK_USE_FFTW_FOUND) set(CPACK_COMPONENTS_ALL tools gui libraries headers documentation) INSTALL(FILES ${CPACK_RESOURCE_FILE_LICENSE} ${CPACK_PACKAGE_DESCRIPTION_FILE} DESTINATION ${CMTK_INSTALL_DATA_DIR}/doc/ COMPONENT documentation) INSTALL(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/Licenses DESTINATION ${CMTK_INSTALL_DATA_DIR}/doc/ COMPONENT documentation) INCLUDE(CPack) CPACK_ADD_COMPONENT(tools DISPLAY_NAME "Command Line Tools" GROUP core) CPACK_ADD_COMPONENT(gui DISPLAY_NAME "Graphical User Interface Applications" GROUP core) CPACK_ADD_COMPONENT(libraries DISPLAY_NAME "Link Libraries" GROUP devel DISABLED) CPACK_ADD_COMPONENT(headers DISPLAY_NAME "C/C++ Header Files" GROUP devel DISABLED) CPACK_ADD_COMPONENT(runtime DISPLAY_NAME "Runtime Components" GROUP core) CPACK_ADD_COMPONENT(documentation DISPLAY_NAME "CMTK Documentation" GROUP core) CPACK_ADD_COMPONENT_GROUP(devel DISPLAY_NAME "Development Components" DEPENDS core) CPACK_ADD_COMPONENT_GROUP(core DISPLAY_NAME "Core Components") IF(BUILD_SHARED_LIBS) SET(CPACK_COMPONENT_TOOLS_DEPENDS libraries) SET(CPACK_COMPONENT_GUI_DEPENDS libraries) ENDIF(BUILD_SHARED_LIBS) INCLUDE(InstallRequiredSystemLibraries) cmtk-3.3.1/cmtkGenerateCMTKConfig.cmake000066400000000000000000000125601276303427400177100ustar00rootroot00000000000000## ## Copyright 1997-2009 Torsten Rohlfing ## ## Copyright 2004-2012 SRI International ## ## This file is part of the Computational Morphometry Toolkit. ## ## http://www.nitrc.org/projects/cmtk/ ## ## The Computational Morphometry Toolkit is free software: you can ## redistribute it and/or modify it under the terms of the GNU General Public ## License as published by the Free Software Foundation, either version 3 of ## the License, or (at your option) any later version. ## ## The Computational Morphometry Toolkit is distributed in the hope that it ## will be useful, but WITHOUT ANY WARRANTY; without even the implied ## warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License along ## with the Computational Morphometry Toolkit. If not, see ## . ## ## $Revision: 4620 $ ## ## $LastChangedDate: 2012-11-16 09:20:23 -0800 (Fri, 16 Nov 2012) $ ## ## $LastChangedBy: torstenrohlfing $ ## ## ## This file borrows heavily from the analogous InsightToolkit file ## # Generate the CMTKConfig.cmake file in the build tree. Also configure # one for installation. The file tells external projects how to use # CMTK. #----------------------------------------------------------------------------- # Settings common to the build and installation tree. # The "use" file. SET(CMTK_USE_FILE UseCMTK.cmake) # The build settings file. SET(CMTK_BUILD_SETTINGS_FILE CMTKBuildSettings.cmake) #----------------------------------------------------------------------------- # Settings specific to the build tree. # Config file prefix is the root of the build tree itself. SET(CMTK_CONFIG_PREFIX_CONFIG ${CMTK_BINARY_DIR}) # The library dependencies file. SET(CMTK_LIBRARY_DEPENDS_FILE CMTKLibraryDepends.cmake) # Library directory. SET(CMTK_LIBRARY_DIRS_CONFIG ${CMTK_LIBRARY_PATH}) # Binary directory. SET(CMTK_BINARY_DIR_CONFIG ${CMTK_EXECUTABLE_PATH}) # Determine the include directories needed. SET(CMTK_INCLUDE_DIRS_CONFIG ${CMTK_INCLUDE_DIRS_BUILD_TREE} ${CMTK_INCLUDE_DIRS_SYSTEM}) # Set data directory SET(CMTK_DATA_ROOT_CONFIG ${CMTK_DATA_ROOT}) # Set DICOM dictionary paths for build AND install tree IF(CMTK_BUILD_DCMTK) SET(CMTK_DCMDICTPATH_CONFIG ${CMTK_LIBRARY_PATH}) SET(CMTK_DCMDICTPATH_INSTALL_CONFIG ${CMAKE_INSTALL_PREFIX}/${CMTK_INSTALL_LIB_DIR}) ELSE() SET(CMTK_DCMDICTPATH_CONFIG ${DCMTK_DCMDICTPATH}) SET(CMTK_DCMDICTPATH_INSTALL_CONFIG ${DCMTK_DCMDICTPATH}) ENDIF() #----------------------------------------------------------------------------- # Configure CMTKConfig.cmake and cmtkconfig.h for the build tree. CONFIGURE_FILE(${CMTK_SOURCE_DIR}/CMTKConfig.cmake.in ${CMTK_BINARY_DIR}/CMTKConfig.cmake @ONLY IMMEDIATE) CONFIGURE_FILE(${CMTK_SOURCE_DIR}/cmtkconfig.h.cmake ${CMTK_BINARY_DIR}/cmtkconfig.h @ONLY IMMEDIATE) #----------------------------------------------------------------------------- # Settings specific to the install tree. # Config prefix is under install tree. SET(CMTK_CONFIG_PREFIX_CONFIG ${CMAKE_INSTALL_PREFIX}/${CMTK_INSTALL_LIB_DIR}) # The library dependencies file. SET(CMTK_LIBRARY_DEPENDS_FILE CMTKLibraryDepends.cmake) # Include directories. SET(CMTK_INCLUDE_DIRS_CONFIG \${CMTK_INSTALL_PREFIX}/${CMTK_INSTALL_INCLUDE_DIR}) # List of CMTK libraries SET(CMTK_LIBRARIES cmtkIO cmtkPipeline cmtkQt cmtkRegistration cmtkSegmentation cmtkRecon cmtkBase cmtkSystem cmtkNumerics) IF(CMTK_BUILD_UNSTABLE) SET(CMTK_LIBRARIES cmtkUnstable ${CMTK_LIBRARIES}) ENDIF(CMTK_BUILD_UNSTABLE) IF(CMTK_USE_CUDA) SET(CMTK_LIBRARIES cmtkGPU ${CMTK_LIBRARIES}) ENDIF(CMTK_USE_CUDA) # Link directories. SET(CMTK_LIBRARY_DIRS_CONFIG "\${CMTK_INSTALL_PREFIX}/${CMTK_INSTALL_LIB_DIR}") # Link directories. # The install tree will use the directory where CMTKConfig.cmake is found, which # happens to be "INSTALLATION/lib". That is, it is already the # same directory where the libraries are installed. Therefore this variable # must be empty here. See CMTKConfig.cmake.in for details on how this variable # is used. SET(CMTK_LIBRARY_DIRS_CONCUR "") # Binary directory. SET(CMTK_BINARY_DIR_CONFIG ${CMAKE_INSTALL_PREFIX}/${CMTK_INSTALL_BIN_DIR}) #----------------------------------------------------------------------------- # Configure CMTKConfig.cmake for the install tree. # Construct the proper number of GET_FILENAME_COMPONENT(... PATH) # calls to compute the installation prefix. STRING(REGEX REPLACE "/" ";" CMTK_INSTALL_LIB_DIR_COUNT "${CMTK_INSTALL_LIB_DIR}") SET(CMTK_CONFIG_CODE " # Compute the installation prefix from this CMTKConfig.cmake file location. GET_FILENAME_COMPONENT(CMTK_INSTALL_PREFIX \"\${CMAKE_CURRENT_LIST_FILE}\" PATH)") FOREACH(p ${CMTK_INSTALL_LIB_DIR_COUNT}) SET(CMTK_CONFIG_CODE "${CMTK_CONFIG_CODE}\nGET_FILENAME_COMPONENT(CMTK_INSTALL_PREFIX \"\${CMTK_INSTALL_PREFIX}\" PATH)" ) ENDFOREACH(p) CONFIGURE_FILE(${CMTK_SOURCE_DIR}/CMTKConfig.cmake.in ${CMTK_BINARY_DIR}/Install/CMTKConfig.cmake @ONLY IMMEDIATE) INSTALL(FILES ${CMAKE_CURRENT_BINARY_DIR}/Install/CMTKConfig.cmake DESTINATION ${CMTK_INSTALL_LIB_DIR} COMPONENT headers) CONFIGURE_FILE(${CMTK_SOURCE_DIR}/cmtkconfig.h.cmake ${CMTK_BINARY_DIR}/Install/cmtkconfig.h @ONLY IMMEDIATE) INSTALL(FILES ${CMAKE_CURRENT_BINARY_DIR}/Install/cmtkconfig.h DESTINATION ${CMTK_INSTALL_INCLUDE_DIR} COMPONENT headers) cmtk-3.3.1/cmtkIncludeDirectories.cmake000066400000000000000000000033251276303427400201300ustar00rootroot00000000000000## ## Copyright 1997-2009 Torsten Rohlfing ## ## Copyright 2004-2010 SRI International ## ## This file is part of the Computational Morphometry Toolkit. ## ## http://www.nitrc.org/projects/cmtk/ ## ## The Computational Morphometry Toolkit is free software: you can ## redistribute it and/or modify it under the terms of the GNU General Public ## License as published by the Free Software Foundation, either version 3 of ## the License, or (at your option) any later version. ## ## The Computational Morphometry Toolkit is distributed in the hope that it ## will be useful, but WITHOUT ANY WARRANTY; without even the implied ## warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License along ## with the Computational Morphometry Toolkit. If not, see ## . ## ## $Revision: 3281 $ ## ## $LastChangedDate: 2011-07-26 14:38:18 -0700 (Tue, 26 Jul 2011) $ ## ## $LastChangedBy: torstenrohlfing $ ## #----------------------------------------------------------------------------- # Include directories for other projects installed on the system. SET(CMTK_INCLUDE_DIRS_SYSTEM ${QT_INCLUDE_DIR} ${QT_INCLUDE_PATH} ) #----------------------------------------------------------------------------- # Include directories from the build tree. SET(CMTK_INCLUDE_DIRS_BUILD_TREE ${CMTK_BINARY_DIR}) # These directories are always needed. SET(CMTK_INCLUDE_DIRS_BUILD_TREE ${CMTK_INCLUDE_DIRS_BUILD_TREE} ${DCMTK_INCLUDE_DIRS} ${NRRD_INCLUDE_DIRS} ${MXML_INCLUDE_DIR} ${SQLITE_INCLUDE_DIR} ${ZCONF_INCLUDE_DIR} ${ZLIB_INCLUDE_DIR} ${CMTK_SOURCE_DIR}/libs ) cmtk-3.3.1/cmtkconfig.h.cmake000066400000000000000000000121601276303427400160400ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2013 SRI International // // Copyright 2015 Google, Inc. // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 5388 $ // // $LastChangedDate: 2015-08-23 16:42:44 -0700 (Sun, 23 Aug 2015) $ // // $LastChangedBy: torstenrohlfing $ // */ /* cmtkconfig.h.cmake */ #ifndef __cmtkconfig_h_included__ #define __cmtkconfig_h_included__ #define CMTK_VERSION_MAJOR @CMTK_VERSION_MAJOR@ #define CMTK_VERSION_MINOR @CMTK_VERSION_MINOR@ #define CMTK_VERSION_PATCH "@CMTK_VERSION_PATCH@" #define CMTK_VERSION_STRING "@CMTK_VERSION_STRING@" #define CMTK_ROOT_PATH_SRI24 "@CMTK_ROOT_PATH_SRI24@" #define CMTK_BINARY_DIR "@CMTK_BINARY_DIR_CONFIG@" #define CMTK_DATADIR "@CMTK_DATA_ROOT_CONFIG@/testing/inputs" #define CMTK_DCMDICTPATH "@CMTK_DCMDICTPATH_CONFIG@" #define CMTK_DCMDICTPATH_INSTALL "@CMTK_DCMDICTPATH_INSTALL_CONFIG@" // Unless in "DEBUG" build, turn off AlgLib assertions #ifndef DEBUG #define NO_AP_ASSERT 1 #endif // // Configuration options // #cmakedefine CMTK_BUILD_UNSTABLE 1 #cmakedefine CMTK_BUILD_STACKTRACE 1 #cmakedefine CMTK_BUILD_RTRACKER 1 #cmakedefine CMTK_BUILD_DEMO 1 #cmakedefine CMTK_BUILD_NRRD 1 #cmakedefine CMTK_USE_SMP 1 #cmakedefine CMTK_USE_PTHREADS 1 #cmakedefine CMTK_USE_CUDA 1 #cmakedefine CMTK_USE_BZIP2 1 #cmakedefine CMTK_USE_LZMA 1 #cmakedefine CMTK_USE_DCMTK 1 #cmakedefine CMTK_USE_SQLITE 1 #cmakedefine CMTK_USE_FFTW_FOUND 1 #cmakedefine CMTK_COORDINATES_DOUBLE 1 #ifndef CMTK_COORDINATES_DOUBLE # define CMTK_COORDINATES_FLOAT 1 #endif #cmakedefine CMTK_DATA_DOUBLE 1 #ifndef CMTK_DATA_DOUBLE # define CMTK_DATA_FLOAT 1 #endif #cmakedefine CMTK_NUMERICS_DOUBLE 1 #ifndef CMTK_NUMERICS_DOUBLE # define CMTK_NUMERICS_FLOAT 1 #endif #cmakedefine CMTK_COMPILER_VAR_AUTO_ARRAYSIZE 1 #cmakedefine HAVE_DIRENT_H 1 #cmakedefine HAVE_EXECINFO_H 1 #cmakedefine HAVE_FCNTL_H 1 #cmakedefine HAVE_IEEEFP_H 1 #cmakedefine HAVE_INTTYPES_H 1 #cmakedefine HAVE_MALLOC_H 1 #cmakedefine HAVE_PTHREAD_H 1 #cmakedefine HAVE_STDINT_H 1 #cmakedefine HAVE_TERMIOS_H 1 #cmakedefine HAVE_UNISTD_H 1 #cmakedefine HAVE_VALUES_H 1 #cmakedefine HAVE_SYS_IOCTL_H 1 #cmakedefine HAVE_SYS_PROCFS_H 1 #cmakedefine HAVE_SYS_STAT_H 1 #cmakedefine HAVE_SYS_TIMES_H 1 #cmakedefine HAVE_SYS_TIME_H 1 #cmakedefine HAVE_SYS_TYPES_H 1 #cmakedefine HAVE_SYS_UTSNAME_H 1 #cmakedefine HAVE_HASH_MAP 1 #cmakedefine HAVE_HASH_MAP_H 1 #cmakedefine HAVE_UNORDERED_MAP 1 #cmakedefine HAVE_UNORDERED_MAP_TR1 1 #cmakedefine WORDS_BIGENDIAN 1 /* Use stat64 on systems where it is available and stat is not 64bit aware. */ #cmakedefine CMTK_USE_STAT64 1 // Flag for Grand Central Dispatch #cmakedefine CMTK_USE_GCD 1 /* The size of a `char', as computed by sizeof. */ #cmakedefine SIZEOF_CHAR @CMAKE_SIZEOF_CHAR@ /* The size of a `double', as computed by sizeof. */ #cmakedefine SIZEOF_DOUBLE @CMAKE_SIZEOF_DOUBLE@ /* The size of a `float', as computed by sizeof. */ #cmakedefine SIZEOF_FLOAT @CMAKE_SIZEOF_FLOAT@ /* The size of a `int', as computed by sizeof. */ #cmakedefine SIZEOF_INT @CMAKE_SIZEOF_INT@ /* The size of a `long', as computed by sizeof. */ #cmakedefine SIZEOF_LONG @CMAKE_SIZEOF_LONG@ /* The size of a `short', as computed by sizeof. */ #cmakedefine SIZEOF_SHORT @CMAKE_SIZEOF_SHORT@ /* The size of a `void *', as computed by sizeof. */ #cmakedefine SIZEOF_VOID_P @CMAKE_SIZEOF_VOID_P@ /// Macro to prevent warnings from unused function arguments. #define UNUSED(a) ((void)a) #ifdef _MSC_VER // disable warnings about insecure functions (we want to be portable) # define _CRT_SECURE_NO_DEPRECATE // disable warnings about unknown (i.e., gcc) pragmas # pragma warning ( disable: 4068 ) // disable warnings about unimplemented "throw()" declaration #pragma warning(disable: 4290) // enable POSIX compliance # define _POSIX_ # define NOMINMAX #include #if _MSC_VER >= 1900 # define STDC99 #else # define snprintf _snprintf # define strdup _strdup #endif # define random rand # define srandom srand #include inline int finite( const double x ) { return _finite(x); } // Fake PATH_MAX if we don't have it #ifndef PATH_MAX #define PATH_MAX 1024 #endif # define CMTK_PATH_SEPARATOR '\\' # define CMTK_PATH_SEPARATOR_STR "\\" #else # define CMTK_PATH_SEPARATOR '/' # define CMTK_PATH_SEPARATOR_STR "/" #endif //#ifdef _MSC_VER #endif // #ifndef __cmtkconfig_h_included__ cmtk-3.3.1/config/000077500000000000000000000000001276303427400137315ustar00rootroot00000000000000cmtk-3.3.1/config/OSX-10.4-i386.cmake000066400000000000000000000046001276303427400164530ustar00rootroot00000000000000## ## Copyright 2010 Greg Jefferis ## ## Copyright 2010-2012, 2014 SRI International ## ## This file is part of the Computational Morphometry Toolkit. ## ## http://www.nitrc.org/projects/cmtk/ ## ## The Computational Morphometry Toolkit is free software: you can ## redistribute it and/or modify it under the terms of the GNU General Public ## License as published by the Free Software Foundation, either version 3 of ## the License, or (at your option) any later version. ## ## The Computational Morphometry Toolkit is distributed in the hope that it ## will be useful, but WITHOUT ANY WARRANTY; without even the implied ## warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License along ## with the Computational Morphometry Toolkit. If not, see ## . ## ## $Revision: 5181 $ ## ## $LastChangedDate: 2014-01-20 14:30:37 -0800 (Mon, 20 Jan 2014) $ ## ## $LastChangedBy: torstenrohlfing $ ## SET(CMAKE_BUILD_TYPE "Release" CACHE STRING "Build type") SET(CMAKE_INSTALL_PREFIX "/opt/local" CACHE PATH "Install prefix") # General settings SET(CMTK_SYSTEM_NAME "MacOSX-10.4" CACHE STRING "System name") SET(CMTK_SYSTEM_PROCESSOR "i686" CACHE STRING "System processor") # 32 bit for OS X >=10.4 SET(CMAKE_OSX_ARCHITECTURES "i386" CACHE STRING "OS-X architectures") SET(CMAKE_OSX_DEPLOYMENT_TARGET "10.4" CACHE STRING "OS-X target") SET(CMAKE_OSX_SYSROOT "/Developer-old/SDKs/MacOSX10.5.sdk" CACHE PATH "OS-X SDK") # Use GCC 4.0 for 10.4 SDK SET(CMAKE_CXX_COMPILER "/usr/bin/g++-4.0" CACHE FILEPATH "C++ Compiler") SET(CMAKE_C_COMPILER "/usr/bin/gcc-4.0" CACHE FILEPATH "C Compiler") # Activate SSE support for floating point SET(CMAKE_CXX_FLAGS "-m32 -march=pentium4 -mmmx -msse -msse2 -mfpmath=sse" CACHE STRING "C++ compiler flags") SET(CMAKE_C_FLAGS ${CMAKE_CXX_FLAGS} CACHE STRING "C compiler flags") # Disable Grand Central Dispatch as it is broken with C++ in 10.4 SDK SET(CMTK_USE_GCD OFF CACHE BOOL "Use Grand Central Dispatch") SET(CMTK_USE_OPENMP OFF CACHE BOOL "Use OpenMP for parallelization" ) # No Qt support SET(CMTK_USE_QT OFF CACHE BOOL "Use Qt toolkit") # Disable FFTW, even if installed on our build system, since it requires MacPorts SET(CMTK_USE_FFTW OFF CACHE BOOL "Use FFTW library (required for ADNI phantom detection)") cmtk-3.3.1/config/OSX-10.5-x86_64.cmake000066400000000000000000000045721276303427400167310ustar00rootroot00000000000000## ## Copyright 2010 Greg Jefferis ## ## Copyright 2010-2012, 2014 SRI International ## ## This file is part of the Computational Morphometry Toolkit. ## ## http://www.nitrc.org/projects/cmtk/ ## ## The Computational Morphometry Toolkit is free software: you can ## redistribute it and/or modify it under the terms of the GNU General Public ## License as published by the Free Software Foundation, either version 3 of ## the License, or (at your option) any later version. ## ## The Computational Morphometry Toolkit is distributed in the hope that it ## will be useful, but WITHOUT ANY WARRANTY; without even the implied ## warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License along ## with the Computational Morphometry Toolkit. If not, see ## . ## ## $Revision: 5179 $ ## ## $LastChangedDate: 2014-01-20 12:59:03 -0800 (Mon, 20 Jan 2014) $ ## ## $LastChangedBy: torstenrohlfing $ ## SET(CMAKE_BUILD_TYPE "Release" CACHE STRING "Build type") SET(CMAKE_INSTALL_PREFIX "/opt/local" CACHE PATH "Install prefix") # General settings SET(CMTK_SYSTEM_NAME "MacOSX-10.5" CACHE STRING "System name") SET(CMTK_SYSTEM_PROCESSOR "x86_64" CACHE STRING "System processor") # 64 bit for OS X >=10.5 SET(CMAKE_OSX_ARCHITECTURES "x86_64" CACHE STRING "OS-X architecture") SET(CMAKE_OSX_DEPLOYMENT_TARGET "10.5" CACHE STRING "OS-X target") SET(CMAKE_OSX_SYSROOT "/Developer/SDKs/MacOSX10.6.sdk" CACHE PATH "OS-X SDK") # Activate SSE support for floating point SET(CMAKE_CXX_FLAGS "-march=core2 -mmmx -msse -msse2 -mfpmath=sse" CACHE STRING "C++ compiler flags") SET(CMAKE_C_FLAGS ${CMAKE_CXX_FLAGS} CACHE STRING "C compiler flags") # CMTK config settings SET(CMTK_USE_SQLITE OFF CACHE BOOL "Use SQLite database") SET(CMTK_USE_QT OFF CACHE BOOL "Use Qt toolkit") SET(BUILD_GUI OFF CACHE BOOL "Build GUI applications (requires Qt)") SET(CMTK_USE_CUDA OFF CACHE BOOL "Use CUDA for GPU acceleration" ) SET(CMTK_USE_LZMA OFF CACHE BOOL "Use LZMA library for decompression") # Disable OpenMP - broken on Mac SET(CMTK_USE_OPENMP OFF CACHE BOOL "Use OpenMP for parallelization" ) # Disable FFTW, even if installed on our build system, since it requires MacPorts SET(CMTK_USE_FFTW OFF CACHE BOOL "Use FFTW library (required for ADNI phantom detection)") cmtk-3.3.1/config/OSX-10.6-x86_64-CUDA.cmake000066400000000000000000000045761276303427400174500ustar00rootroot00000000000000## ## Copyright 2010 Greg Jefferis ## ## Copyright 2010-2012, 2014 SRI International ## ## This file is part of the Computational Morphometry Toolkit. ## ## http://www.nitrc.org/projects/cmtk/ ## ## The Computational Morphometry Toolkit is free software: you can ## redistribute it and/or modify it under the terms of the GNU General Public ## License as published by the Free Software Foundation, either version 3 of ## the License, or (at your option) any later version. ## ## The Computational Morphometry Toolkit is distributed in the hope that it ## will be useful, but WITHOUT ANY WARRANTY; without even the implied ## warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License along ## with the Computational Morphometry Toolkit. If not, see ## . ## ## $Revision: 5179 $ ## ## $LastChangedDate: 2014-01-20 12:59:03 -0800 (Mon, 20 Jan 2014) $ ## ## $LastChangedBy: torstenrohlfing $ ## SET(CMAKE_BUILD_TYPE "Release" CACHE STRING "Build type") SET(CMAKE_INSTALL_PREFIX "/opt/local" CACHE PATH "Install prefix") # General settings SET(CMTK_SYSTEM_NAME "MacOSX-10.6-CUDA" CACHE STRING "System name") SET(CMTK_SYSTEM_PROCESSOR "x86_64" CACHE STRING "System processor") SET(CMTK_USE_CUDA ON CACHE BOOL "Use CUDA for GPU acceleration" ) # 64 bit for OS X >=10.6 SET(CMAKE_OSX_ARCHITECTURES "x86_64" CACHE STRING "OS-X architecture") SET(CMAKE_OSX_DEPLOYMENT_TARGET "10.6" CACHE STRING "OS-X target") SET(CMAKE_OSX_SYSROOT "/Developer/SDKs/MacOSX10.6.sdk" CACHE PATH "OS-X SDK") # Activate SSE support for floating point SET(CMAKE_CXX_FLAGS "-march=core2 -mmmx -msse -msse2 -mfpmath=sse" CACHE STRING "C++ compiler flags") SET(CMAKE_C_FLAGS ${CMAKE_CXX_FLAGS} CACHE STRING "C compiler flags") # CMTK config settings SET(CMTK_USE_SQLITE OFF CACHE BOOL "Use SQLite database") SET(CMTK_USE_QT OFF CACHE BOOL "Use Qt toolkit") SET(BUILD_GUI OFF CACHE BOOL "Build GUI applications (requires Qt)") SET(CMTK_USE_LZMA OFF CACHE BOOL "Use LZMA library for decompression") # Disable OpenMP - broken on Mac SET(CMTK_USE_OPENMP OFF CACHE BOOL "Use OpenMP for parallelization" ) # Disable FFTW, even if installed on our build system, since it requires MacPorts SET(CMTK_USE_FFTW OFF CACHE BOOL "Use FFTW library (required for ADNI phantom detection)") cmtk-3.3.1/config/OSX-10.6-x86_64.cmake000066400000000000000000000045721276303427400167320ustar00rootroot00000000000000## ## Copyright 2010 Greg Jefferis ## ## Copyright 2010-2012, 2014 SRI International ## ## This file is part of the Computational Morphometry Toolkit. ## ## http://www.nitrc.org/projects/cmtk/ ## ## The Computational Morphometry Toolkit is free software: you can ## redistribute it and/or modify it under the terms of the GNU General Public ## License as published by the Free Software Foundation, either version 3 of ## the License, or (at your option) any later version. ## ## The Computational Morphometry Toolkit is distributed in the hope that it ## will be useful, but WITHOUT ANY WARRANTY; without even the implied ## warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License along ## with the Computational Morphometry Toolkit. If not, see ## . ## ## $Revision: 5179 $ ## ## $LastChangedDate: 2014-01-20 12:59:03 -0800 (Mon, 20 Jan 2014) $ ## ## $LastChangedBy: torstenrohlfing $ ## SET(CMAKE_BUILD_TYPE "Release" CACHE STRING "Build type") SET(CMAKE_INSTALL_PREFIX "/opt/local" CACHE PATH "Install prefix") # General settings SET(CMTK_SYSTEM_NAME "MacOSX-10.6" CACHE STRING "System name") SET(CMTK_SYSTEM_PROCESSOR "x86_64" CACHE STRING "System processor") # 64 bit for OS X >=10.6 SET(CMAKE_OSX_ARCHITECTURES "x86_64" CACHE STRING "OS-X architecture") SET(CMAKE_OSX_DEPLOYMENT_TARGET "10.6" CACHE STRING "OS-X target") SET(CMAKE_OSX_SYSROOT "/Developer/SDKs/MacOSX10.6.sdk" CACHE PATH "OS-X SDK") # Activate SSE support for floating point SET(CMAKE_CXX_FLAGS "-march=core2 -mmmx -msse -msse2 -mfpmath=sse" CACHE STRING "C++ compiler flags") SET(CMAKE_C_FLAGS ${CMAKE_CXX_FLAGS} CACHE STRING "C compiler flags") # CMTK config settings SET(CMTK_USE_SQLITE OFF CACHE BOOL "Use SQLite database") SET(CMTK_USE_QT OFF CACHE BOOL "Use Qt toolkit") SET(BUILD_GUI OFF CACHE BOOL "Build GUI applications (requires Qt)") SET(CMTK_USE_CUDA OFF CACHE BOOL "Use CUDA for GPU acceleration" ) SET(CMTK_USE_LZMA OFF CACHE BOOL "Use LZMA library for decompression") # Disable OpenMP - broken on Mac SET(CMTK_USE_OPENMP OFF CACHE BOOL "Use OpenMP for parallelization" ) # Disable FFTW, even if installed on our build system, since it requires MacPorts SET(CMTK_USE_FFTW OFF CACHE BOOL "Use FFTW library (required for ADNI phantom detection)") cmtk-3.3.1/config/gcc-p4-sse.cmake000066400000000000000000000023531276303427400166030ustar00rootroot00000000000000## ## Copyright 1997-2009 Torsten Rohlfing ## ## Copyright 2004-2010 SRI International ## ## This file is part of the Computational Morphometry Toolkit. ## ## http://www.nitrc.org/projects/cmtk/ ## ## The Computational Morphometry Toolkit is free software: you can ## redistribute it and/or modify it under the terms of the GNU General Public ## License as published by the Free Software Foundation, either version 3 of ## the License, or (at your option) any later version. ## ## The Computational Morphometry Toolkit is distributed in the hope that it ## will be useful, but WITHOUT ANY WARRANTY; without even the implied ## warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License along ## with the Computational Morphometry Toolkit. If not, see ## . ## ## $Revision: 2558 $ ## ## $LastChangedDate: 2010-11-18 10:46:11 -0800 (Thu, 18 Nov 2010) $ ## ## $LastChangedBy: torstenrohlfing $ ## SET(CMAKE_CXX_FLAGS "-m32 -march=pentium4 -mmmx -msse -msse2 -mfpmath=sse" CACHE STRING "C compiler flags") SET(CMAKE_C_FLAGS ${CMAKE_CXX_FLAGS} CACHE STRING "C++ compiler flags") cmtk-3.3.1/config/gcc-x86_64-sse.cmake000066400000000000000000000022631276303427400172160ustar00rootroot00000000000000## ## Copyright 2010 SRI International ## ## This file is part of the Computational Morphometry Toolkit. ## ## http://www.nitrc.org/projects/cmtk/ ## ## The Computational Morphometry Toolkit is free software: you can ## redistribute it and/or modify it under the terms of the GNU General Public ## License as published by the Free Software Foundation, either version 3 of ## the License, or (at your option) any later version. ## ## The Computational Morphometry Toolkit is distributed in the hope that it ## will be useful, but WITHOUT ANY WARRANTY; without even the implied ## warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License along ## with the Computational Morphometry Toolkit. If not, see ## . ## ## $Revision: 2558 $ ## ## $LastChangedDate: 2010-11-18 10:46:11 -0800 (Thu, 18 Nov 2010) $ ## ## $LastChangedBy: torstenrohlfing $ ## SET(CMAKE_CXX_FLAGS "-march=nocona -mmmx -msse -msse2 -mfpmath=sse" CACHE STRING "C compiler flags") SET(CMAKE_C_FLAGS ${CMAKE_CXX_FLAGS} CACHE STRING "C++ compiler flags") cmtk-3.3.1/config/gcc4.7-macports-OSX-10.6.cmake000066400000000000000000000050011276303427400205730ustar00rootroot00000000000000## ## Copyright 2010 Greg Jefferis ## ## Copyright 2010-2012, 2014 SRI International ## ## This file is part of the Computational Morphometry Toolkit. ## ## http://www.nitrc.org/projects/cmtk/ ## ## The Computational Morphometry Toolkit is free software: you can ## redistribute it and/or modify it under the terms of the GNU General Public ## License as published by the Free Software Foundation, either version 3 of ## the License, or (at your option) any later version. ## ## The Computational Morphometry Toolkit is distributed in the hope that it ## will be useful, but WITHOUT ANY WARRANTY; without even the implied ## warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License along ## with the Computational Morphometry Toolkit. If not, see ## . ## ## $Revision: 5351 $ ## ## $LastChangedDate: 2014-05-01 16:05:28 -0700 (Thu, 01 May 2014) $ ## ## $LastChangedBy: torstenrohlfing $ ## SET(CMAKE_BUILD_TYPE "Release" CACHE STRING "Build type") SET(CMAKE_INSTALL_PREFIX "/opt/local" CACHE PATH "Install prefix") # General settings SET(CMTK_SYSTEM_NAME "MacOSX-10.6-MacPorts" CACHE STRING "System name") SET(CMTK_SYSTEM_PROCESSOR "x86_64" CACHE STRING "System processor") # MacOS stuff SET(CMAKE_OSX_SYSROOT "/" CACHE STRING "OS-X architecture") # Select MacPorts compiler SET(CMAKE_CXX_COMPILER "/opt/local/bin/g++-mp-4.7" CACHE FILEPATH "C++ compiler path") SET(CMAKE_C_COMPILER "/opt/local/bin/gcc-mp-4.7" CACHE FILEPATH "C compiler path") # Activate SSE support for floating point SET(CMAKE_CXX_FLAGS "-march=core2 -mmmx -msse -msse2 -mfpmath=sse" CACHE STRING "C++ compiler flags") SET(CMAKE_C_FLAGS ${CMAKE_CXX_FLAGS} CACHE STRING "C compiler flags") # CMTK config settings SET(CMTK_USE_MACPORTS ON CACHE BOOL "Use libraries from MacPorts" ) SET(CMTK_USE_OPENMP ON CACHE BOOL "Use OpenMP for parallelization" ) SET(CMTK_USE_GCD OFF CACHE BOOL "Use Grand Central Dispatch for SMP parallelism" ) SET(CMTK_USE_SQLITE OFF CACHE BOOL "Use SQLite database") SET(CMTK_USE_QT ON CACHE BOOL "Use Qt toolkit") SET(BUILD_GUI ON CACHE BOOL "Build GUI applications (requires Qt)") SET(CMTK_USE_CUDA OFF CACHE BOOL "Use CUDA for GPU acceleration" ) SET(CMTK_USE_LZMA OFF CACHE BOOL "Use LZMA library for decompression") # Enable FFTW, since we require MacPorts for this build anyway SET(CMTK_USE_FFTW ON CACHE BOOL "Use FFTW library (required for ADNI phantom detection)") cmtk-3.3.1/config/gcc4.8-macports-OSX-10.6.cmake000066400000000000000000000047731276303427400206130ustar00rootroot00000000000000## ## Copyright 2010 Greg Jefferis ## ## Copyright 2010-2014 SRI International ## ## This file is part of the Computational Morphometry Toolkit. ## ## http://www.nitrc.org/projects/cmtk/ ## ## The Computational Morphometry Toolkit is free software: you can ## redistribute it and/or modify it under the terms of the GNU General Public ## License as published by the Free Software Foundation, either version 3 of ## the License, or (at your option) any later version. ## ## The Computational Morphometry Toolkit is distributed in the hope that it ## will be useful, but WITHOUT ANY WARRANTY; without even the implied ## warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License along ## with the Computational Morphometry Toolkit. If not, see ## . ## ## $Revision: 5351 $ ## ## $LastChangedDate: 2014-05-01 16:05:28 -0700 (Thu, 01 May 2014) $ ## ## $LastChangedBy: torstenrohlfing $ ## SET(CMAKE_BUILD_TYPE "Release" CACHE STRING "Build type") SET(CMAKE_INSTALL_PREFIX "/opt/local" CACHE PATH "Install prefix") # General settings SET(CMTK_SYSTEM_NAME "MacOSX-10.6-MacPorts" CACHE STRING "System name") SET(CMTK_SYSTEM_PROCESSOR "x86_64" CACHE STRING "System processor") # MacOS stuff SET(CMAKE_OSX_SYSROOT "/" CACHE STRING "OS-X architecture") # Select MacPorts compiler SET(CMAKE_CXX_COMPILER "/opt/local/bin/g++-mp-4.8" CACHE FILEPATH "C++ compiler path") SET(CMAKE_C_COMPILER "/opt/local/bin/gcc-mp-4.8" CACHE FILEPATH "C compiler path") # Activate SSE support for floating point SET(CMAKE_CXX_FLAGS "-march=core2 -mmmx -msse -msse2 -mfpmath=sse" CACHE STRING "C++ compiler flags") SET(CMAKE_C_FLAGS ${CMAKE_CXX_FLAGS} CACHE STRING "C compiler flags") # CMTK config settings SET(CMTK_USE_MACPORTS ON CACHE BOOL "Use libraries from MacPorts" ) SET(CMTK_USE_OPENMP ON CACHE BOOL "Use OpenMP for parallelization" ) SET(CMTK_USE_GCD OFF CACHE BOOL "Use Grand Central Dispatch for SMP parallelism" ) SET(CMTK_USE_SQLITE OFF CACHE BOOL "Use SQLite database") SET(CMTK_USE_QT ON CACHE BOOL "Use Qt toolkit") SET(BUILD_GUI ON CACHE BOOL "Build GUI applications (requires Qt)") SET(CMTK_USE_CUDA OFF CACHE BOOL "Use CUDA for GPU acceleration" ) SET(CMTK_USE_LZMA OFF CACHE BOOL "Use LZMA library for decompression") # Enable FFTW, since we require MacPorts for this build anyway SET(CMTK_USE_FFTW ON CACHE BOOL "Use FFTW library (required for ADNI phantom detection)") cmtk-3.3.1/config/package.cmake000066400000000000000000000060671276303427400163370ustar00rootroot00000000000000## ## Copyright 2009 Torsten Rohlfing ## ## Copyright 2010-2012 SRI International ## ## This file is part of the Computational Morphometry Toolkit. ## ## http://www.nitrc.org/projects/cmtk/ ## ## The Computational Morphometry Toolkit is free software: you can ## redistribute it and/or modify it under the terms of the GNU General Public ## License as published by the Free Software Foundation, either version 3 of ## the License, or (at your option) any later version. ## ## The Computational Morphometry Toolkit is distributed in the hope that it ## will be useful, but WITHOUT ANY WARRANTY; without even the implied ## warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License along ## with the Computational Morphometry Toolkit. If not, see ## . ## ## $Revision: 4607 $ ## ## $LastChangedDate: 2012-11-13 11:41:56 -0800 (Tue, 13 Nov 2012) $ ## ## $LastChangedBy: torstenrohlfing $ ## SET(CMAKE_BUILD_TYPE "Release" CACHE STRING "CMake build type") SET(BUILD_APPS "ON" CACHE BOOL "Build command line applications") SET(BUILD_DOCUMENTATION "ON" CACHE BOOL "Build documentation") SET(BUILD_GUI "ON" CACHE BOOL "Build GUI components") SET(BUILD_SHARED_LIBS "OFF" CACHE BOOL "Build shared libraries") SET(BUILD_TESTING "ON" CACHE BOOL "Build testing components") SET(BUILD_VALIDATION "ON" CACHE BOOL "Build validation components") SET(CMTK_BUILD_NRRD "ON" CACHE BOOL "Build with NRRD file format support") SET(CMTK_USE_SMP "ON" CACHE BOOL "Build with SMP parallelism support") SET(CMTK_BUILD_SQLITE "ON" CACHE BOOL "Build with database support") SET(CMTK_BUILD_STACKTRACE "ON" CACHE BOOL "Build with stack backtrace printing for crashes") SET(CMTK_BUILD_UNSTABLE "OFF" CACHE BOOL "Build unstable, experimental code") SET(CMTK_COORDINATES_DOUBLE "ON" CACHE BOOL "Use double-precision image coordinates") SET(CMTK_DATA_DOUBLE "ON" CACHE BOOL "Use double-precision data exchange") SET(CMTK_NUMERICS_DOUBLE "ON" CACHE BOOL "Use double-precision numerics code") SET(CMTK_SINGLE_COMMAND_BINARY "OFF" CACHE BOOL "Build a single command line binary tool") SET(CMTK_TESTING_MEMORYCHECK "OFF" CACHE BOOL "Build with support for memory checking") SET(CMTK_USE_DCMTK "ON" CACHE BOOL "Build with DICOM support") SET(CMTK_USE_MPI "OFF" CACHE BOOL "Support for Message passing Interface distributed parallelism") SET(CMTK_USE_OPENMP "ON" CACHE BOOL "Use OpenMP parallelism") SET(CMTK_USE_QT "ON" CACHE BOOL "Use Gt toolkit for GUI tools") SET(CMTK_USE_LZMA "OFF" CACHE BOOL "Support for LZMA on-the-fly decompression") # Enable building N-CANDA pipelines and Greg's "munger" script SET(BUILD_CONTRIB ON CACHE BOOL "Build, install, and package third-party contributions") SET(BUILD_CONTRIB_NCANDA ON CACHE BOOL "Build, install, and package pipelines for the National Consortium on Alcohol and Neurodevelopment in Adolescence (N-CANDA)") SET(BUILD_CONTRIB_MUNGER ON CACHE BOOL "Build, install, and package Greg Jefferis' munger script (requires perl)") cmtk-3.3.1/contrib/000077500000000000000000000000001276303427400141245ustar00rootroot00000000000000cmtk-3.3.1/contrib/CMakeLists.txt000077500000000000000000000025001276303427400166640ustar00rootroot00000000000000## ## Copyright 2012, 2013 SRI International ## ## This file is part of the Computational Morphometry Toolkit. ## ## http://www.nitrc.org/projects/cmtk/ ## ## The Computational Morphometry Toolkit is free software: you can ## redistribute it and/or modify it under the terms of the GNU General Public ## License as published by the Free Software Foundation, either version 3 of ## the License, or (at your option) any later version. ## ## The Computational Morphometry Toolkit is distributed in the hope that it ## will be useful, but WITHOUT ANY WARRANTY; without even the implied ## warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License along ## with the Computational Morphometry Toolkit. If not, see ## . ## ## $Revision: 4773 $ ## ## $LastChangedDate: 2013-05-29 13:38:14 -0700 (Wed, 29 May 2013) $ ## ## $LastChangedBy: torstenrohlfing $ ## #----------------------------------------------------------------------------- # Configure third-party contributions. OPTION(BUILD_CONTRIB_MUNGER "Build, install, and package Greg Jefferis' munger script (requires perl)" OFF) IF(BUILD_CONTRIB_MUNGER) ADD_SUBDIRECTORY(munger) ENDIF(BUILD_CONTRIB_MUNGER) cmtk-3.3.1/contrib/munger/000077500000000000000000000000001276303427400154215ustar00rootroot00000000000000cmtk-3.3.1/contrib/munger/CMakeLists.txt000077500000000000000000000035241276303427400201700ustar00rootroot00000000000000## ## Copyright 2012 SRI International ## ## Copyright 2014 Ashley Manton / Greg Jefferis ## ## This file is part of the Computational Morphometry Toolkit. ## ## http://www.nitrc.org/projects/cmtk/ ## ## The Computational Morphometry Toolkit is free software: you can ## redistribute it and/or modify it under the terms of the GNU General Public ## License as published by the Free Software Foundation, either version 3 of ## the License, or (at your option) any later version. ## ## The Computational Morphometry Toolkit is distributed in the hope that it ## will be useful, but WITHOUT ANY WARRANTY; without even the implied ## warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License along ## with the Computational Morphometry Toolkit. If not, see ## . ## ## $Revision: 5364 $ ## ## $LastChangedDate: 2014-07-20 13:45:49 -0700 (Sun, 20 Jul 2014) $ ## ## $LastChangedBy: torsten_at_home $ ## FIND_PACKAGE(Perl) IF(PERL_FOUND) ## Configure for the build tree SET(CMTK_BINARY_DIR_CONFIG ${CMAKE_BINARY_DIR}/bin) CONFIGURE_FILE(${CMAKE_CURRENT_SOURCE_DIR}/munger.in ${CMAKE_BINARY_DIR}/bin/munger @ONLY) ## Configure for the install tree IF(CMTK_BUILD_WRAPPER) SET(CMTK_BINARY_DIR_CONFIG ${CMAKE_INSTALL_PREFIX}/${CMTK_INSTALL_LIB_DIR}/bin) ELSE(CMTK_BUILD_WRAPPER) SET(CMTK_BINARY_DIR_CONFIG ${CMAKE_INSTALL_PREFIX}/${CMTK_INSTALL_BIN_DIR}) ENDIF(CMTK_BUILD_WRAPPER) CONFIGURE_FILE(${CMAKE_CURRENT_SOURCE_DIR}/munger.in ${CMAKE_BINARY_DIR}/Install/munger @ONLY) INSTALL(PROGRAMS ${CMAKE_BINARY_DIR}/Install/munger DESTINATION ${CMTK_INSTALL_BIN_DIR} COMPONENT tools) ELSE(PERL_FOUND) MESSAGE(ERROR "Perl is needed for 'munger' script") ENDIF(PERL_FOUND) cmtk-3.3.1/contrib/munger/munger.in000077500000000000000000001147021276303427400172560ustar00rootroot00000000000000#!/usr/bin/env perl # munger.pl # script to process all of the images in the specified directory tree # 1) Run affine transform # 2) Run Warp transform # 3) Reformat # v1.01 041126 to allow inverse consistent switch and to change # default ref brain for HPCF and change bin directory # v1.02 modified 050111 to allow # 1) conserver images dir hierarchy in ouput dirs # 2) fix problem with cwd preventing from # v1.03 050124 added version string # v1.04 050124 fixed paths for vesalius # v1.05 050124 added support for deep dirs to reformat # v1.06 050127 fixed luog5 paths and allowed affine reformat # v1.07 051927 fixed bug in "rootDir" and now copes with .study dirs for # both ref or target # v1.08 060130 added option to specify Registration subdirectory # v1.09 060207 fixed ability to run on gjz5 remotely (when it is called notched4) # v1.10 2006-10-13 - added directory locking for warp since on biox cluster # multiple processes are started simultaneously and a directory mod time # check was not enough # Also a great deal of tidying up including reducing host dependence # v1.11 2006-10-14 - fixed a locking bug for reformat and significantly # improved status function (faster, prints dirs with -v option) # v1.12 2009-07-14 - Substantial code tidying, usage when no arguments given # v1.13 2009-07-14 - Switch to using reformatx (reformat was dropped from cmtk) # v1.14 2009-07-20 - Prevent munger from processing images beginning with . # v1.15 2009-08-08 - Allow a max number of registrations to be set allowing # jobs to be somewhat time-limited # v1.16 2009-08-08 - Add option to delete/truncate input images after reformat # v1.17 2009-10-27 - Add ability to specify multiple input images / dirs # v1.18 2010-10-12 - fix duplicated stems in reformatted images # v1.19 2012-05-31 - Option to handle input from Amira Global Hough Transform # v1.20 2013-11-26 - Replaced command line tools by perl functions for better compatibility # v1.21 2015-10-08 - Option to add extra params/change of target to reformatx (prev in private v1.20 2012-09-17) require 5.004; use strict; my $version= "1.21"; my $isWin = $^O eq 'MSWin32'; # check for MSWin32 os use vars qw/ %opt /; # for command line options - see init() use File::Find; use File::Basename; use File::Spec; use IO::Compress::Gzip qw(gzip $GzipError) ; use Cwd; # Autoflush stdout - so that it always keeps up with STDERR $|=1; # A global variable that will be set when the QUIT signal is received my $quitNextImage=0; $SIG{'QUIT'} = 'interrupt'; # hvtawuc:r:s:b:f:E:X:M:C:G:R: init(); # process command line options my $energyweight=$opt{E}?$opt{E}:"1e-1"; my $exploration=$opt{X}?$opt{X}:"16"; my $metric=$opt{M}?$opt{M}:"nmi"; # 0=NMI, 1= MI my $coarsest=$opt{C}?$opt{C}:"4"; my $gridspacing=$opt{G}?$opt{G}:"40"; my $refine=$opt{R}?$opt{R}:"3"; my $jacobian=$opt{J}?$opt{J}:"0"; my $outputType=$opt{o}?$opt{o}:"nrrd"; # find the index of the metric among (currently) 5 options sub arrayidx { 1 while $_[0] ne pop; @_-1; } my @metricOptions = ("nmi", "mi", "cr", "msd", "ncc"); my $metricIndex = arrayidx($metric,@metricOptions); die "Unrecognised metric $metric" unless ($metricIndex > -1); # find the warp suffix that will be used to name the warp output files my $warpSuffix="warp_m".$metricIndex."g".$gridspacing."c".$coarsest."e".$energyweight."x".$exploration."r".$refine; my $icweight=$opt{I}?$opt{I}:"0"; # STORE CURRENT HOSTNAME # YOU CAN USE THIS TO SET MACHINE-SPECIFIC OPTIONS # EDIT TO RECOGNISE YOUR MACHINE IF IT CAN HAVE DIFFERENT HOSTNAMES my $hostName=`hostname`; chomp($hostName); print STDERR "hostname = $hostName"; print STDERR "; short hostname = $hostName\n"; if( $isWin ){ print `echo User path is %PATH%` if $opt{v}; # User path is defined by %path% under Win32 systems. Martin } else { print `echo User path is \$PATH` if $opt{v}; } my $threads=$opt{T}?$opt{T}:"auto"; my $referenceImage=$opt{s}; print "Reference brain is $referenceImage\n" if $opt{v}; die "Unable to read reference brain $referenceImage" unless -r($referenceImage); # Extract standard stem from reference image name my $referenceStem=$referenceImage; # remove any terminal slashes # (which would cause basename to return empty) $referenceStem=~s/^(.*)\/$/$1/; $referenceStem=basename($referenceStem); # if this is a reformatted brain then change the underscore to a hyphen # between brain id and warp/9d0f $referenceStem=~s/[_](warp|9dof)/-$1/; # remove up to first dot or underscore $referenceStem =~ s/^([^._]*)[._].*$/$1/; print "Reference brain stem is $referenceStem\n" if $opt{v}; # Set up location of warping tools # Specify non standard locations explicitly or add to $PATH my $binDir=$opt{b}; if($opt{b}){ $binDir=$opt{b}; }else{ # use configured path $binDir="@CMTK_BINARY_DIR_CONFIG@" } die "Can't access binary directory $binDir" unless -r $binDir; my $warpCommand=File::Spec->catdir($binDir,"warp"); my $affCommand=File::Spec->catdir($binDir,"registration"); my $initialAffCommand=File::Spec->catdir($binDir,"make_initial_affine"); my $landmarksAffCommand=File::Spec->catdir($binDir,"align_landmarks"); my $reformatCommand=File::Spec->catdir($binDir,"reformatx"); my $regChannels=$opt{c}?$opt{c}:"01"; my $reformatChannels=$opt{r}?$opt{r}:"01"; my $reformatLevels=($opt{l} ne "")?$opt{l}:"f"; my $referenceImageChannel=$opt{f}?$opt{f}:"01"; my $regRoot="Registration"; my $imageRoot="images"; my $reformatRoot="reformatted"; my $rootDir=""; if($opt{d}){ my $dir=$opt{d}; if($dir =~ m/^[.]\w+/){ # if the output directory option supplied begins with a . # then append that to BOTH reg and reformat root $regRoot.=$opt{d}; $reformatRoot.=$opt{d}; } else { $regRoot=$opt{d}; } } # Set default lock message #my $lockmessage=$opt{k}?$opt{k}:""; my $lockmessage=$opt{k}?$opt{k}:""; if ( ! $isWin ) { my $lockmessage=$opt{k}?$opt{k}:$hostName.":".getpgrp(0); } print "JOB ID = $lockmessage\n"; my $deleteInputImage = $opt{x}?$opt{x}:"never"; my $affineTotal=0; my $initialAffineTotal=0; my $affineTotalFailed=0; my $initialAffineTotalFailed=0; my $warpTotal=0; my $reformatTotal=0; my $maxtime=$opt{m}?$opt{m}:8760; # one year (in hours) $maxtime=$maxtime*3600; # convert to seconds my $starttime=time(); # record our starting time (in seconds) print STDERR "Start time is: $starttime seconds\n" if $opt{v}; my %found; # hash to store filenames for status function if ($opt{p}){ print "Generating script file $opt{p}" if $opt{v}; die "Unable to open script file $opt{p} for writing" unless open SCRIPT, "> $opt{p}"; } $rootDir = $isWin ? getdcwd() : getcwd(); print "Root directory is $rootDir\n"; my $nargs=$#ARGV+1; die usage() if($nargs<1); print "There are $nargs arguments\n" if $nargs>1; # process multiple arguments foreach my $inputFileSpec (@ARGV){ # remove any terminal slashes $inputFileSpec=~s/^(.*)\/$/$1/; # so that we can do munger.pl . instead of munger.pl `pwd` $inputFileSpec=$rootDir if $inputFileSpec eq "."; if($opt{F}) { # we've specified that files are actually lists of images, one per line if(-f $inputFileSpec){ open(MYINPUTFILE, "<$inputFileSpec"); } else { die "Unable to to open file $inputFileSpec"; } while(){ my($nextfile) = $_; chomp($nextfile); if(-f $nextfile) { &munge($nextfile) } else { warn "Unable to read $nextfile"; } } close(MYINPUTFILE); } elsif(-f $inputFileSpec || $inputFileSpec=~m/\.study/ ) { # find the root dir even if we specifed images dir or subdir # $rootDir=findRootDir($inputFileSpec); # 2005-10-18 Actually would rather just use current as root dir # that isn't too much of a hardship and if the image # is off in some other location ... # Hmm not sure that this will work # print "inputFileSpec = $inputFileSpec\n"; &munge($inputFileSpec) ; } elsif(-d $inputFileSpec){ # find the root dir even if we specifed images dir or subdir $rootDir=findRootDir($inputFileSpec); print "Changing to root directory: $rootDir\n"; chdir($rootDir); if($opt{u}){ status(); exit 0; } # GJ 130105 - I think I prefer $imageRoot to $rootDir # ie only look for images in specifed images dir. # which will be the images subdir of $rootDir # nb follow=1 implies that we will follow symlinks # GJ 2006-10-22 - I want to be able to specify a subdir of image # dir and restrict action to that if ($inputFileSpec=~/$imageRoot/){ $imageRoot=$inputFileSpec; print "Setting image root to: ",$imageRoot,"\n"; } else { print "image root is: ",$imageRoot,"\n"; } find({ wanted => \&handleFind, follow => 1 },$imageRoot); print "-"x25,"\nRescanning images directory a second time\n","-"x25,"\n" if($opt{v}); find({ wanted => \&handleFind, follow => 1 },$imageRoot); print "\nRan $initialAffineTotal initial affine registrations of which $initialAffineTotalFailed failed\n"; print "Ran $affineTotal affine registrations of which $affineTotalFailed failed\n"; print "Ran $warpTotal warp registrations\n"; print "Reformatted $reformatTotal images\n"; } else { print STDERR "inputFileSpec: $inputFileSpec does not match any file/directory\n"; } } sub findRootDir { # returns the root directory for a working tree # by looking for the dir which has an images subdirectory # or returning the original path if no luck my $fullpath=shift; # nb it is necesary to convert the directory specification # to an absolute path to ensure that the open in &readheader # works properly during multi directory traversal # since we can't always rely on an up to date File::Spec module # have to make my own if(!File::Spec->file_name_is_absolute($fullpath)){ my $curDir=cwd(); $rootDir=File::Spec->catdir($curDir,File::Spec->canonpath($fullpath)); } else { $rootDir=File::Spec->canonpath($fullpath); } my $partialpath=$rootDir; my $sysroot=File::Spec->rootdir(); while ($partialpath ne $sysroot){ # Chop off the last directory in the path we are checking $partialpath=dirname($partialpath); # check if we have a dir called images last if (-d File::Spec->catdir($partialpath,"images")); } # if we have a valid partial path, return that, else return what we were given return ($partialpath eq File::Spec->rootdir()?$fullpath:$partialpath); } sub findRelPathToImgDir { # returns the relative path of an image filepath to the images directory # this just involves removing the first and last elements # if we assume that input paths are relative to root dir my $filepath=shift; # the quick way, but not all systems have File::Spec->abs2rel # my($volume,$directories,$file)= File::Spec->splitpath(File::Spec->abs2rel($filepath,$imageRoot)); my ($volume,$directories,$file) = File::Spec->splitpath( $filepath ); my @dirs = File::Spec->splitdir($directories); # check this works if @dirs has one element # not clever, just removes the first and last element @dirs=@dirs[1..($#dirs-1)]; my $dirs=File::Spec->catdir(@dirs); # print STDERR "$dirs\n"; return ($dirs); } sub handleFind { # check if file ends in pic(.gz) nrrd or nhdr case insensitive # also insist that it does not begin with a period (hidden file) munge($File::Find::name) if /^[^.].*\.(pic(\.gz){0,1}|n(rrd|hdr))$/i; } sub munge { my $filepath=shift; die "Quitting after receiving QUIT signal" if $quitNextImage; my $filename=basename($filepath); # get the brain name my $brain=$filename; $brain=~s/(_raw)?(0\d)?\.(pic(\.gz){0,1}|n(rrd|hdr))//i; # the channel of the image my $channel=($filename=~/(0\d)\.(pic(\.gz){0,1}|n(rrd|hdr))/i)?$1:""; print "Found brain name $brain $channel ($filepath)\n" if $opt{v}; # change the working dir to the root dir # (rather than wherever we are in the image dir hierarchy) use Cwd; my $old_directory = cwd; chdir($rootDir) or die "Can't change directory: $!"; print "New working directory is: $rootDir, Old one was $old_directory \n" if $opt{v}; # nb by using local we only affect downstream stuff if (!$opt{i} && $filename eq basename($referenceImage)){ print STDERR "Bailing out because target: ",$filename," and ref: ",$referenceImage," are the same\n" if $opt{v}; return 0; } # run registrations if this file is of the correct channel if($channel eq "" || $regChannels=~/$channel/){ if ($opt{L}){ runLandmarksAffine( $filepath,$brain,$channel); } else { runInitialAffine( $filepath,$brain,$channel) if $opt{P}; } if((time()-$starttime)<$maxtime){ # only run registrations if we haven't run too many already runAffine( $filepath,$brain,$channel) if $opt{a}; # run the warp transformation runWarp($filepath,$brain,$channel) if $opt{w}; } else { print STDERR "Skipping registrations because maxtime exceeded\n" if $opt{v}; } } if ($channel eq "" || $reformatChannels=~/$channel/) { foreach (split(//,$reformatLevels)){ runReformat( $filepath,$brain,$channel,$_) if $opt{r}; } } # unset the dir change chdir($old_directory) or die "Can't change directory: $!"; return; } sub runWarp { my ($filepath,$brain,$channel) = @_; my $inlist=File::Spec->catdir($regRoot,"affine",&findRelPathToImgDir($filepath),$referenceStem."_".$brain.$channel."_9dof.list"); print "inlist = $inlist\n" if $opt{v}; # new version has relative filenames in output dir depending on input hierarchy my $outlist=File::Spec->catdir($regRoot,"warp",&findRelPathToImgDir($filepath),$referenceStem."_".$brain.$channel."_".$warpSuffix.".list"); print "W: outlist = $outlist\n" if $opt{v}; my $args="-v --registration-metric $metric --jacobian-weight $jacobian"; if ($threads ne "auto"){ $ENV{'CMTK_NUM_THREADS'}=$threads; } $args.=" --fast -e $exploration --grid-spacing $gridspacing "; $args.=" --energy-weight $energyweight --refine $refine --coarsest $coarsest"; $args.=" --ic-weight $icweight"; $args.=" --output-intermediate" unless $opt{0}; # add any extra arguments $args.=" ".$opt{W}; # bail out if infile doesn't exist (or is empty) return 0 unless (-s "$inlist/registration.gz") || (-s "$inlist/registration") ; my $outfile; my $finalRegLevel=$opt{R}+1; if($opt{0}){ # test for the final registration output file ... $outfile = File::Spec->catfile($outlist,"registration"); } else { # ... BUT where possible prefer to test for the final level file which is identical # to the one that is then saved in the root directory (if all went well) # this will avoid an incomplete terminated registration which does # generate a registration file in the root dir from blocking reregistration $outfile = File::Spec->catfile($outlist,"level-0${finalRegLevel}.list","registration"); } # Continue if outdir doesn't exist OR # if age of indir > age of outdir & there is a registration if( (! -d "$outlist") ){ if( $isWin ){ myexec("md \"$outlist\"") unless $opt{t}; # md creates folder tree by default. Martin } else { myexec("mkdir -p \"$outlist\"") unless $opt{t}; } } else { print "outdir exists\n" if $opt{v}; # there is an output dir ... is there a registration? $outfile="${outfile}.gz" if(-f "${outfile}.gz"); # check for a zipped one if ( (-s $outfile) && # there is a non-zero registration file already (-M $outfile < -M "$inlist/registration") ) { # and it's newer than the input affine reg return 0; # then bail out } } # try to make a lockfile (and bail if we can't because someone else already has) return 0 unless makelock("$outlist/registration.lock"); my @cmd=( $warpCommand, split(/\s+/,$args), "-o", $outlist, $inlist ); my $cmd_string=join( ' ', @cmd ); if($opt{v}){ print "W: Running warp reg with command: $cmd_string\n"; } else { print "Warp $brain,"; } if (!$opt{t}){ my $cmdnote="# Command run by process with lock ID".$opt{k}; dumpcommand( $cmdnote, join("\0",@cmd), "$outlist/cmd.sh" ) unless $opt{t}; myexec(@cmd); $warpTotal++; # the glob here didn't work in perl (only sh) #`gzip -f -9 \"${outlist}/registration\" \"${outlist}/level*/registration\"`; # so try this: # nb opt z indicates that we don't want to gzip; nb -f to over-write # myexec( "find", $outlist, "-name", "registration", "-exec", "gzip", "-f", "-9", "{}", ";" ) unless $opt{z}; # generic solution that replaces the commandline calls. Martin find(sub { gzip $_ => "$_.gz", -Level => 9 or die "gzip failed: $GzipError\n" if $_ eq "registration" && ! -d }, "$outlist"); removelock("$outlist/registration.lock"); return $outlist; } else { return 0; } } sub runReformat { my ($inputimgfilepath,$brain,$channel,$level) = @_; # nb note that the input registration may not be in the same channel as the image to be reformatted # therefore use $referenceImageChannel to specify the channel of the input registration # (not $channel of current image) my ($baseinlist,$inlist); if($level eq "p"){ # reformat from principal axis $baseinlist=File::Spec->catdir($regRoot,"affine",&findRelPathToImgDir($inputimgfilepath),$referenceStem."_".$brain.$referenceImageChannel."_"."pa.list"); $inlist=$baseinlist; } elsif($level eq "a"){ # reformat from affine $baseinlist=File::Spec->catdir($regRoot,"affine",&findRelPathToImgDir($inputimgfilepath),$referenceStem."_".$brain.$referenceImageChannel."_"."9dof.list"); $inlist=$baseinlist; } elsif ($level=~m/[f0-9]/) { $baseinlist=File::Spec->catdir($regRoot,"warp",&findRelPathToImgDir($inputimgfilepath),$referenceStem."_".$brain.$referenceImageChannel."_".$warpSuffix.".list"); $inlist=$baseinlist; if($level ne "f"){ # registration will be in a subdir in this case $inlist.="/level-0".$level.".list"; } } else { print STDERR "Unrecognised reformat level specifier: $level\n"; return 0; } print "Reformat:inlist would be: $inlist\n" if $opt{v}; # bail out if input registration itself doesn't exist return 0 if (! -s "$inlist/registration" && ! -s "$inlist/registration.gz"); print "inlist exists\n" if $opt{v}; # Construct the outlist - basename gets the name of the file from full path my $outlist=basename($baseinlist); #eg averagegoodbrain_brainame_warp40-5_e1e-1_c4.list # Remove everything up to warp or 9dof $outlist=~s/^.*_((warp|9dof|pa).*)\.list$/$1/i; # nb registration channel may be different from channel of current image # which is what we want here if( $level=~m/[0-9]/ ){ # specific warp level ... registration will be in a subdir in this case $outlist=$referenceStem."_".$brain.$channel."_".$outlist."_lev".$level; } elsif ($level =~m /[fap]/) { # final warp level $outlist=$referenceStem."_".$brain.$channel."_".$outlist; } $outlist=File::Spec->catdir($reformatRoot,&findRelPathToImgDir($inputimgfilepath),$outlist); # Set things up for different image ouput types my ($outfile,$makedir,$outputSpec)=('',1,''); if($outputType eq "nrrd" || $outputType eq "nhdr"){ $outfile=$outlist.".".$outputType; $makedir=0; } else { $outfile = File::Spec->catfile($outlist,"image.bin"); $outputSpec="RAW3D:"; } my $testoutfile=$outfile; print "outlist is: $outlist\n" if $opt{v}; # nb -M gives time since last modification of file # it's in days but it's a float with lots of digits # Bail out if we have already reformatted if ( ! -d $outlist && $makedir){ # no output dir, so make one and continue if( $isWin ){ myexec("md \"$outlist\"") unless $opt{t}; # md creates folder tree by default. Martin } else { myexec("mkdir -p \"$outlist\"") unless $opt{t}; } } else { print "outdir exists\n" if $opt{v} && $makedir; # there is an output dir ... is there an image file? # check for a zipped one $testoutfile="${outfile}.gz" if(-f "${outfile}.gz"); if ( (-f $testoutfile) && # there is an image file already (-M $testoutfile < -M $inlist) && # and it's newer than the input reg (-M $testoutfile < -M $inputimgfilepath) ) { # and newer than input img truncatefile($inputimgfilepath) if($deleteInputImage =~ /^any/); return 0; # then bail out } } # If we are running with -x any:delete or any:truncate then we # don't want to do any reformatting - we are just a separate # job that has been started to clear some space, so bail now! return 0 if($deleteInputImage =~ /^any/); # try to make a lockfile (and bail if we can't because someone else already has) return 0 unless makelock("${outlist}.lock"); # make command my @args=("-v","--pad-out","0"); # makes null pixels black instead of white # add any extra arguments @args=(@args,$opt{2}) if $opt{2}; # change the reference image (specifically for reformatting) if requested $referenceImage=$opt{1} if ($opt{1}); my @cmd=( $reformatCommand, @args, "-o", $outputSpec.${outfile}, "--floating", $inputimgfilepath, $referenceImage, $inlist ); my $cmd_string=join(' ',@cmd); if($opt{v}){ print "Running reformat with command: $cmd_string\n"; } else { # print full name of the file being reformatted print "Reformat ".basename($inputimgfilepath).","; } if(!$opt{t}){ print myexec(@cmd); $reformatTotal++; # generic approach for gzip comandline call. Martin gzip "${outlist}/image.bin" => "${outlist}/image.bin.gz", -Level => 9 or die "gzip failed: $GzipError\n" unless $opt{z} or ($outputType ne "bin"); removelock("${outlist}.lock"); truncatefile($inputimgfilepath) if($deleteInputImage =~ /^only/); return $outlist; } else { truncatefile($inputimgfilepath) if($deleteInputImage =~ /^only/); return 0; } } sub runAffine { my ($imagepath,$brain,$channel) = @_; my $args="-i -v --dofs 6 --dofs 9"; if ($threads ne "auto"){ $ENV{'CMTK_NUM_THREADS'}=$threads; } # add any extra arguments $args.=" ".$opt{A}; # new version has relative filenames in output dir depending on input hierarchy my $listroot=File::Spec->catdir($regRoot,"affine",&findRelPathToImgDir($imagepath),$referenceStem."_".$brain.$channel); my $outlist=$listroot."_9dof.list"; my $inputfile = $imagepath; my $initialreg = ""; $initialreg = File::Spec->catdir($listroot."_pa.list","registration") if $opt{P} || $opt{L}; if($opt{H}){ # will use output of Generalised Hough Transform (calculated by Amira) to initialise # affine transformation. Will use this where available, otherwise just fall back to # regular affine my $ghtfilepath=File::Spec->catdir($listroot."_ght.list","registration"); # use GHT file as input if it exists if(-e $ghtfilepath) { $initialreg=$ghtfilepath ; } else { # try again, looking for registration.gz $ghtfilepath=$ghtfilepath.".gz"; $initialreg=$ghtfilepath if(-e $ghtfilepath); } } # Continue if an output file doesn't exist or # -s means file exists and has non zero size if( ! -s File::Spec->catfile($outlist,"registration") ){ # no output file, so just continue } elsif ($initialreg) { # we have an initial registration print STDERR "Inital registration $initialreg missing\n" if(! -e $initialreg); # input registration AND input image are both older than output so can return return 0 if ( -M "$imagepath" > -M File::Spec->catfile($outlist,"registration") && -M "$initialreg" > -M File::Spec->catfile($outlist,"registration") ); } elsif ( -M "$imagepath" > -M File::Spec->catfile($outlist,"registration") ) { # ok age of input image > age of outdir so no need to rerun return 0; } # bail out if somebody else is working on this return 0 unless makelock("$outlist/registration.lock"); my @cmd=( $affCommand, split(/\s+/,$args), "-o", $outlist, $referenceImage, $imagepath ); # pass initial transformation if required @cmd=( $affCommand, split(/\s+/,$args), "-o", $outlist, "--initial", $initialreg, $referenceImage, $imagepath ) if ($initialreg); my $cmd_string = join( ' ', @cmd ); if( $opt{v}){ print "A: Running affine reg with command: $cmd_string\n"; } else { print "Aff:$brain$channel "; } # if not in test mode if(!$opt{t}){ # keep a copy of the commandline dumpcommand( "Command was:", join("\0",@cmd), "$outlist/cmd.sh" ) unless $opt{t}; # run the command #print "Actually running cmd\n"; #print `$cmd`; my $rval=myexec (@cmd); $affineTotalFailed++ unless ($rval==0); #print "Actually finished cmd\n"; removelock("$outlist/registration.lock"); $affineTotal++; return $outlist; } else { return 0; } } sub getLandmarksFile { my ($filepath) = @_; my $landmarks; # if a directory, then there should be a file inside called landmarks if(-d $filepath){ $landmarks=File::Spec->catfile($filepath,"landmarks"); return (-f $landmarks)?$landmarks:""; } # trim off gz ending $filepath=~s|\.gz$||; # print $filepath."\n"; # check for x.y.landmarks $landmarks=$filepath.".landmarks"; # print $landmarks."\n"; return $landmarks if(-f $landmarks); # check for x.landmarks $landmarks=$filepath; $landmarks=~s|\.([^.]+)$|.landmarks|; # print $landmarks."\n"; return $landmarks if(-f $landmarks); return ""; } sub runLandmarksAffine { my ($filepath,$brain,$channel) = @_; my @args=("--affine","--reference-image",$referenceImage,"--floating-image",$filepath); my $sampleLandmarks = getLandmarksFile($filepath); my $refLandmarks = getLandmarksFile($referenceImage); if ($refLandmarks eq ""){ print STDERR "AlignLandmarks: cannot find reference brain landmarks at $refLandmarks\n"; return 0; } if ($refLandmarks eq ""){ print STDERR "AlignLandmarks: cannot find sample brain landmarks at $sampleLandmarks\n"; return 0; } print "AlignLandmarks: sample = $sampleLandmarks; ref = $refLandmarks\n" if $opt{v}; my $outlist=File::Spec->catdir($regRoot,"affine",&findRelPathToImgDir($filepath),$referenceStem."_".$brain.$channel."_pa.list"); my $outfile=File::Spec->catfile($outlist,"registration"); if( ! -s $outfile ){ # no output file, so just continue } elsif ( -M $sampleLandmarks > -M $outfile && -M $refLandmarks > -M $outfile ) { # ok age of input & ref landmarks > age of registration file so no need to rerun return 0; } # bail out if somebody else is working on this # try to make a lockfile (and bail if we can't because someone else already has) return 0 unless makelock("$outlist/registration.lock"); if($opt{v}){ print "$referenceImage exists ", (-e $referenceImage)," writeable ", (-w $referenceImage),"\n"; print "$filepath exists ", (-e $filepath)," writeable ", (-w $filepath),"\n"; print "$sampleLandmarks exists ", (-e $sampleLandmarks)," writeable ", (-w $sampleLandmarks),"\n"; print "$refLandmarks exists ", (-e $refLandmarks)," writeable ", (-w $refLandmarks),"\n"; print "$outlist exists ", (-e $outlist)," writeable ", (-w $outlist),"\n"; } my @cmd=( $landmarksAffCommand, @args, $refLandmarks, $sampleLandmarks, $outlist ); my $cmd_string = join( ' ', @cmd ); if( $opt{v}){ print "A: Running align_landmarks with command: $cmd_string\n"; } else { print "AlignLandmarks:$brain$channel "; } # if not in test mode if(!$opt{t}){ # keep a copy of the commandline dumpcommand( "Command was:", join("\0",@cmd), "$outlist/cmd.sh" ) unless $opt{t}; # run the command #print "Actually running cmd\n"; #print `$cmd`; my $rval=myexec (@cmd); $initialAffineTotalFailed++ unless ($rval==0); #print "Actually finished cmd\n"; removelock("$outlist/registration.lock"); $initialAffineTotal++; return $outlist; } else { return 0; } } sub runInitialAffine { my ($filepath,$brain,$channel) = @_; my $args="-v --principal-axes"; # add any extra arguments # new version has relative filenames in output dir depending on input hierarchy my $outlist=File::Spec->catdir($regRoot,"affine",&findRelPathToImgDir($filepath),$referenceStem."_".$brain.$channel."_pa.list"); my $outfile=File::Spec->catfile($outlist,"registration"); # Continue if an output file doesn't exist or # -s means file exists and has non zero size if( ! -s $outfile ){ # no output file, so just continue } elsif ( -M "$filepath" > -M $outfile ) { # ok age of input image > age of registration file so no need to rerun return 0; } # try to make a lockfile (and bail if we can't because someone else already has) return 0 unless makelock("$outlist/registration.lock"); my @cmd=( $initialAffCommand, split(/\s+/,$args), $referenceImage, $filepath, $outfile ); my $cmd_string = join( ' ', @cmd ); if( $opt{v}){ print "A: Running make_initial_affine with command: $cmd_string\n"; } else { print "InitialAff:$brain$channel "; } # if not in test mode if(!$opt{t}){ # keep a copy of the commandline dumpcommand( "Command was:", join("\0",@cmd), "$outlist/cmd.sh" ) unless $opt{t}; # run the command #print "Actually running cmd\n"; #print `$cmd`; my $rval=myexec (@cmd); $affineTotalFailed++ unless ($rval==0); #print "Actually finished cmd\n"; removelock("$outlist/registration.lock"); $affineTotal++; return $outlist; } else { return 0; } } sub status { # Displays number of images # affine registrations, warp registations, and reformatted # images (separated into the two channels) # nb follow=1 implies that we will follow symlinks print "Searching directory tree ..." if $opt{v}; find({ wanted => \&findAllFiles, follow => 1 },$rootDir); print " Finished!\n" if $opt{v}; my @paths=keys %found; my @filenames=values %found; my @images=grep /\.(pic(\.gz){0,1}|n(rrd|hdr))$/i, @paths; my @channel1images=grep /01\.(pic(\.gz){0,1}|n(rrd|hdr))/i, @images; my @channel2images=grep /02\.(pic(\.gz){0,1}|n(rrd|hdr))/i, @images; print "Total Images: ".scalar(@images)."\n"; print "Channel 1 images: ".scalar(@channel1images)."\n"; print "Channel 2 images: ".scalar(@channel2images)."\n"; my @affineRegistrations=grep /affine.*9dof\.list$/i, @paths; my @lockedAffineRegistrations=grep /affine.*registration.lock$/i, @paths; my @lockedAffineIDs=map {&getidfromlockfile($_)} @lockedAffineRegistrations; my @finishedAffineRegistrations=grep /affine.*9dof\.list\/registration$/i, @paths; # make a hash containing the directory name of all finished affines my %finished = map { dirname($_) => $_ } @finishedAffineRegistrations; # Now go through the array of all registration dirs tossing those that # are in the finished hash my @unfinishedAffineRegistrations = grep { !exists $finished{$_} } @affineRegistrations; my @warpRegistrations=grep /\/warp\/.*warp[^\/]*\.list$/, @paths; my @finishedWarpRegistrations=grep /\/warp\/.*warp[^\/]*\.list\/registration(.gz)$/, @paths; my @lockedWarpRegistrations=grep /\/warp\/.*warp[^\/]*\.list\/registration.lock$/i, @paths; # make a hash containing the directory name of all finished warps %finished = map { dirname($_) => $_ } @finishedWarpRegistrations; # Now go through all registration dirs tossing those that # are in the finished hash my @unfinishedWarpRegistrations = grep { !exists $finished{$_} } @warpRegistrations; my @reformattedImages=`find $rootDir/$reformatRoot/ -type d -name \'*.study\'`; @channel1images=grep /^[^_]+01_/i, @reformattedImages; @channel2images=grep /^[^_]+02_/i, @reformattedImages; print "\nAffine registration directories: ".scalar(@affineRegistrations)."\n"; print "Locked affine registration directories: ".scalar(@lockedAffineRegistrations)."\n"; if($opt{v}){ for (my $i=0;$i<$#lockedAffineRegistrations;$i++) { print "\t",$lockedAffineRegistrations[$i],"\t",$lockedAffineIDs[$i],"\n" } } #print "\t",join("\n\t",sort @lockedAffineRegistrations),"\n" if $opt{v} && @lockedAffineRegistrations; print "Unfinished affine registration directories: ".scalar(@unfinishedAffineRegistrations)."\n"; print "\t",join("\n\t",sort @unfinishedAffineRegistrations),"\n" if $opt{v} && @unfinishedAffineRegistrations; print "\nWarp registration directories: ".scalar(@warpRegistrations)."\n"; print "Unfinished warp registration directories: ".scalar(@unfinishedWarpRegistrations)."\n"; print "\t",join("\n\t",sort @unfinishedWarpRegistrations),"\n" if $opt{v} && @unfinishedWarpRegistrations; print "Locked warp registration directories: ".scalar(@lockedWarpRegistrations)."\n"; # print "\t",join("\n\t",sort @lockedWarpRegistrations),"\n" if $opt{v} && @lockedWarpRegistrations; if($opt{v}){ foreach (@lockedWarpRegistrations) { print "\t",$_,"\t",getidfromlockfile($_),"\n" } } print "Reformatted image directories: ".scalar(@reformattedImages)."\n"; return; } sub findAllFiles { $found{$File::Find::name}=$_; } sub myexec { my (@cmd) = @_; my ($cmd_string) = join(' ',@cmd); if ($opt{p}){ print SCRIPT $cmd_string,"\n"; } else { # should get to see output with system my $rval = system @cmd; if ($? == -1) { print "MYEXEC: CMD = $cmd_string failed to execute: $!\n"; } elsif ($? & 127) { printf "MYEXEC: CMD = $cmd_string died with signal %d, %s coredump\n", ($? & 127), ($? & 128) ? 'with' : 'without'; } else { printf "MYEXEC: CMD = $cmd_string exited with value %d\n", $? >> 8; } print STDERR "MYEXEC-DEBUG: RVAL = $rval: CMD = $cmd_string\n" if($opt{g}); return $rval; } return; } sub dumpcommand { my ($note, $command, $filename) = @_; my $FH; die "Unable to write command file $filename" unless open ($FH, '>' . $filename); print $FH "$note\n"; print $FH "$command\n"; close ($FH); return; } sub makelock { my ($lockfile)=@_; # just return if we are in test mode return 1 if $opt{t}; # check dir exists and make it if it doesn't my $lockdir=dirname($lockfile); if( $isWin ){ myexec("md","$lockdir") unless -d $lockdir; # md creates folder tree by default. Martin } else { myexec("mkdir","-p","$lockdir") unless -d $lockdir; } # Bail if someone else has already made a lock file return 0 if (-f "$lockfile"); # write our lock message my $FH; open($FH,">> $lockfile") or die ("Can't make lockfile at $lockfile: $!\n"); print $FH "$lockmessage\n"; close $FH; # now read back in ... my $firstLine=getidfromlockfile($lockfile); # and check we wrote the first line (assuming msg is unique to this process) return 0 unless ($firstLine eq $lockmessage); $SIG{'INT'}=sub { unlink $lockfile and die "Received Interrupt signal\n" }; $SIG{'USR2'}=sub { unlink $lockfile and die "Received USR2 signal\n" }; return 1; } sub removelock { my ($lockfile)=@_; print STDERR "Unable to remove lock $lockfile\n" unless (unlink $lockfile); $SIG{'INT'}='DEFAULT'; $SIG{'USR2'}='DEFAULT'; return; } sub getidfromlockfile { my ($file)=@_; my $FH; open ($FH,"$file") or return "NULL"; my $line=<$FH>; chomp $line; close($FH); return($line); } sub truncatefile { my ($filename)=@_; return 0 unless ( -w $filename && -f $filename ); # writeable, plain file my ($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size, $atime,$mtime,$ctime,$blksize,$blocks) = stat($filename); return 0 unless $deleteInputImage =~ /(truncate|delete)/; my $action = $1; return 0 if ($action eq "truncate") && ($size==0); # already truncated? print STDERR "About to $action file $filename\n" if $opt{v}; return -1 if $opt{t}; # bail if testing return unlink($filename) if($action eq "delete"); truncate $filename,0; # change modification times back to that of original file return utime $atime,$mtime,$filename; } sub interrupt { my($signal)=@_; print STDERR "Caught Interrupt\: $signal \n"; $quitNextImage=1 if($signal eq 'QUIT'); return; } sub usage { print STDOUT << "EOF"; Usage: $0 [OPTIONS] [ ...] Version: $version A wrapper script for CMTK registration software. For more on CMTK see: http://www.nitrc.org/projects/cmtk/ -h print this help -v verbose (provide extra feed back at runtime) -t test run (no commands are actually run) -g debug: prints every command run by myexec and the return value -p make a scriPt from commands that would be run (nb cannot produce commands that depend on earlier commands) -u statUs - display number of images, registrations etc -z turn gzip off (on by default) -k lock message ie contents of lock file (defaults to hostname:process id) -m maximum time to keep starting registrations (in hours, default 8760=1y) nb this will not stop any running registrations -x [never|only|any]:[truncate|delete] Clear input images when done. default is never, eg only:truncate, any:delete only => only the job that runs reformat can clear input any => any job can delete; in fact a job started with any will not reformat at all. This is useful because you can run a cleanup job if it becomes clear that you are running short of space. truncate => leave a 0 length file with same mtime as original image -a run affine transform -w run warp transform -c [01|02|..] channels for registration (default 01 or "") -r [01|02|..] run reformat on these channels -l [p|a|0..9|f] run reformat on these levels (default f=final warp, p=principal axis, a=affine, 0..9=warp intermediates) -f [01|02|..] channel of the images used for registration - default is 01 [nb use -f to specify the channel of the images that were previously used to generate a registration if you now want to reformat a different channel using that registration information] -i register brain to itself if possible (default is to skip) -0 Don't output intermediate warp registration levels -s [file|fileStem] Reference brain (average e-2 by default) -b [path] bin directory -d [stem] registration subdirectory (default ./Registration) [nb if this begins in a dot then the value will be appended to both reformatted and Registration directories] -F Read path to input files, one per line, from ... NB path should be absolute or relative to registration directory e.g. images/imagea-01.nrrd images/imageb-01.nrrd -e File ending of input images (pic, nrrd, nhdr) -o File ending of output images (bin, nrrd, nhdr) - defaults to nrrd -H use Amira's Generalised Hough Transform to initialise affine registration when available (nb the registration folder should be called Registration/affine/XXX_ght.list) -P find initial affine transform using image principal axes -L find initial affine transform using landmarks -I inverse consistent warp weight (--ic-weight) default 0, try 1e-5 -E [energy] energy of warp transform (default e-1) -X [exploration] (default 16) -M [metric] (Supported values: nmi, mi, cr, msd, ncc, default is nmi) See warp --help for details -C [coarsest] (default 4) -G [grid-spacing] (default 40) -R [refine] (default 3) -J [0 to 1] jacobian-weight volume constraining param (default 0) -T [threads] (default auto) -A [option] additional options for affine transformation -W [option] additional options for warp transformation -1 use this brain for reformatx target -2 [option] additional options for reformatx Munge a BioRad PIC or nrrd file or (recursively) parse a directory of PIC/nrrd files by running CMTK affine and warp registrations and reformatting images as required. Final argument must be the images directory or a single image. EOF exit(); } sub init { # copied from: http://www.cs.mcgill.ca/~abatko/computers/programming/perl/howto/getopts use Getopt::Std; # to handle command line options my $opt_string = 'hvtawuic:r:l:s:b:f:FE:X:M:C:G:R:T:J:I:zp:d:k:g0A:W:e:o:HPLm:x:1:'; getopts( "$opt_string", \%opt ) or usage(); usage() if $opt{h} or $#ARGV==-1; return; } cmtk-3.3.1/gui/000077500000000000000000000000001276303427400132505ustar00rootroot00000000000000cmtk-3.3.1/gui/CMakeLists.txt000066400000000000000000000023251276303427400160120ustar00rootroot00000000000000## ## Copyright 1997-2009 Torsten Rohlfing ## ## Copyright 2004-2011, 2013 SRI International ## ## This file is part of the Computational Morphometry Toolkit. ## ## http://www.nitrc.org/projects/cmtk/ ## ## The Computational Morphometry Toolkit is free software: you can ## redistribute it and/or modify it under the terms of the GNU General Public ## License as published by the Free Software Foundation, either version 3 of ## the License, or (at your option) any later version. ## ## The Computational Morphometry Toolkit is distributed in the hope that it ## will be useful, but WITHOUT ANY WARRANTY; without even the implied ## warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License along ## with the Computational Morphometry Toolkit. If not, see ## . ## ## $Revision: 4814 $ ## ## $LastChangedDate: 2013-09-09 16:28:14 -0700 (Mon, 09 Sep 2013) $ ## ## $LastChangedBy: torstenrohlfing $ ## SET(CMTK_LIBRARIES "cmtkQt;cmtkPipeline;cmtkSegmentation;cmtkRegistration;cmtkIO;cmtkBase;cmtkNumerics;cmtkSystem") SUBDIRS(triplanar) SUBDIRS(fview) cmtk-3.3.1/gui/fview/000077500000000000000000000000001276303427400143705ustar00rootroot00000000000000cmtk-3.3.1/gui/fview/CMakeLists.txt000066400000000000000000000033521276303427400171330ustar00rootroot00000000000000## ## Copyright 1997-2009 Torsten Rohlfing ## ## Copyright 2004-2011 SRI International ## ## This file is part of the Computational Morphometry Toolkit. ## ## http://www.nitrc.org/projects/cmtk/ ## ## The Computational Morphometry Toolkit is free software: you can ## redistribute it and/or modify it under the terms of the GNU General Public ## License as published by the Free Software Foundation, either version 3 of ## the License, or (at your option) any later version. ## ## The Computational Morphometry Toolkit is distributed in the hope that it ## will be useful, but WITHOUT ANY WARRANTY; without even the implied ## warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License along ## with the Computational Morphometry Toolkit. If not, see ## . ## ## $Revision: 3544 $ ## ## $LastChangedDate: 2011-11-03 14:14:15 -0700 (Thu, 03 Nov 2011) $ ## ## $LastChangedBy: torstenrohlfing $ ## QT4_WRAP_UI(FVIEW_UIS_H fviewMainWindow.ui) SET(fview_SRCS cmtkFusionViewApplication.cxx ) SET(fview_MOC_SRCS cmtkFusionViewApplication.h ) INCLUDE_DIRECTORIES( ${CMAKE_CURRENT_BINARY_DIR} ) IF(QT_WRAP_CPP) QT_WRAP_CPP(fview fview_SRCS ${fview_MOC_SRCS} ) ENDIF(QT_WRAP_CPP) ADD_EXECUTABLE(fview main.cxx ${fview_SRCS} ${FVIEW_UIS_H}) TARGET_LINK_LIBRARIES(fview ${CMTK_LIBRARIES} ${QT_LIBRARIES} ${CMTK_SYSTEM_LIBRARIES}) IF(CMTK_BUILD_WRAPPER) INSTALL(TARGETS fview RUNTIME DESTINATION ${CMTK_INSTALL_LIB_DIR}/bin COMPONENT gui) ELSE(CMTK_BUILD_WRAPPER) INSTALL(TARGETS fview RUNTIME DESTINATION ${CMTK_INSTALL_BIN_DIR} COMPONENT gui) ENDIF(CMTK_BUILD_WRAPPER) cmtk-3.3.1/gui/fview/cmtkFusionViewApplication.cxx000066400000000000000000000572601276303427400222670ustar00rootroot00000000000000/* // // Copyright 2010-2011, 2013 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 5190 $ // // $LastChangedDate: 2014-01-29 14:54:47 -0800 (Wed, 29 Jan 2014) $ // // $LastChangedBy: torstenrohlfing $ // */ #include "cmtkFusionViewApplication.h" #include #include #include #include #include #include #include #include #include #include #include #include cmtk::FusionViewApplication ::FusionViewApplication( int& argc, char* argv[] ) : QApplication( argc, argv ), m_MainWindow( new QMainWindow ), m_XformModel( 2 ), // default to full nonrigid m_SliceAxis( -1 ), m_SliceIndex( -1 ), m_Interpolator( Interpolators::LINEAR ), m_ZoomFactor( 1.0 ), m_Transparency( 1.0 ), m_CursorDisplayed( true ) { CommandLine cl; cl.SetProgramInfo( CommandLine::PRG_TITLE, "Fusion viewer." ); std::string imagePathFix = ""; cl.AddOption( CommandLine::Key( "fixed" ), &imagePathFix, "Fixed image path. If this is not provided, the program attempts to automatically deduce the fixed image path of the first transformation in the given sequence." ) ->SetProperties( cmtk::CommandLine::PROPS_IMAGE ); std::string imagePathMov = ""; cl.AddOption( CommandLine::Key( "moving" ), &imagePathMov, "Moving image path. If this is not provided, the program attempts to automatically deduce the fixed image path of the first transformation in the given sequence." ) ->SetProperties( cmtk::CommandLine::PROPS_IMAGE ); std::vector xformList; cl.AddParameterVector( &xformList, "XformList", "List of concatenated transformations. Insert '--inverse' to use the inverse of the transformation listed next." ) ->SetProperties( cmtk::CommandLine::PROPS_XFORM | cmtk::CommandLine::PROPS_OPTIONAL ); try { cl.Parse( argc, const_cast( argv ) ); } catch ( const CommandLine::Exception& ex ) { throw(ex); } this->m_XformList = XformListIO::MakeFromStringList( xformList ); try { this->m_XformListAllAffine = this->m_XformList.MakeAllAffine(); } catch ( const AffineXform::MatrixType::SingularMatrixException& ) { StdErr << "WARNING: encountered singular matrix when creating affine-only transformation. Will substitute (incorrect) identify transformation instead.\n"; this->m_XformListAllAffine = XformList(); } if ( imagePathFix.empty() ) { imagePathFix = this->m_XformList.GetFixedImagePath(); if ( !imagePathFix.empty() ) { DebugOutput( 2 ) << "INFO: deduced fixed image path as " << imagePathFix << "\n"; } else { StdErr << "ERROR: could not deduce fixed image path from transformation(s).\n"; throw( ExitException( 1 ) ); } } this->m_Fixed.m_Volume = VolumeIO::ReadOriented( imagePathFix ); if ( ! this->m_Fixed.m_Volume ) { StdErr << "Fixed image '" << imagePathFix << "' could not be read.\n"; throw( ExitException( 1 ) ); } this->m_Fixed.m_DataRange = this->m_Fixed.m_Volume->GetData()->GetRange(); // per-dimension scale factors make sure non-square pixels are displayed square this->m_ScalePixels = this->m_Fixed.m_Volume->Deltas(); this->m_ScalePixels *= 1.0 / this->m_ScalePixels.MinValue(); (this->m_CursorPosition = this->m_Fixed.m_Volume->GetDims() ) *= 0.5; if ( imagePathMov.empty() ) { imagePathMov = this->m_XformList.GetMovingImagePath(); if ( !imagePathMov.empty() ) { DebugOutput( 2 ) << "INFO: deduced moving image path as " << imagePathMov << "\n"; } else { StdErr << "ERROR: could not deduce moving image path from transformation(s).\n"; throw( ExitException( 1 ) ); } } this->m_Moving.m_Volume = VolumeIO::ReadOriented( imagePathMov ); if ( ! this->m_Moving.m_Volume ) { StdErr << "Moving image '" << imagePathMov << "' could not be read.\n"; throw( ExitException( 1 ) ); } this->m_Moving.m_DataRange = this->m_Moving.m_Volume->GetData()->GetRange(); this->m_MainWindowUI.setupUi( this->m_MainWindow ); this->m_MainWindow->setWindowIcon( QtIcons::WindowIcon() ); this->InitViewData( this->m_Fixed, this->m_MainWindowUI.fixedView ); this->InitViewData( this->m_Moving, this->m_MainWindowUI.movingView ); QObject::connect( this->m_MainWindowUI.blackSliderFix, SIGNAL( valueChanged( int ) ), this, SLOT( fixedBlackWhiteChanged() ) ); QObject::connect( this->m_MainWindowUI.whiteSliderFix, SIGNAL( valueChanged( int ) ), this, SLOT( fixedBlackWhiteChanged() ) ); QObject::connect( this->m_MainWindowUI.blackSliderMov, SIGNAL( valueChanged( int ) ), this, SLOT( movingBlackWhiteChanged() ) ); QObject::connect( this->m_MainWindowUI.whiteSliderMov, SIGNAL( valueChanged( int ) ), this, SLOT( movingBlackWhiteChanged() ) ); QActionGroup* zoomGroup = new QActionGroup( this->m_MainWindow ); zoomGroup->setExclusive( true ); this->m_MainWindowUI.actionZoom25->setData( QVariant( 0.25 ) ); zoomGroup->addAction( this->m_MainWindowUI.actionZoom25 ); this->m_MainWindowUI.actionZoom50->setData( QVariant( 0.50 ) ); zoomGroup->addAction( this->m_MainWindowUI.actionZoom50 ); this->m_MainWindowUI.actionZoom100->setData( QVariant( 1.00 ) ); zoomGroup->addAction( this->m_MainWindowUI.actionZoom100 ); this->m_MainWindowUI.actionZoom200->setData( QVariant( 2.00 ) ); zoomGroup->addAction( this->m_MainWindowUI.actionZoom200 ); this->m_MainWindowUI.actionZoom300->setData( QVariant( 3.00 ) ); zoomGroup->addAction( this->m_MainWindowUI.actionZoom300 ); this->m_MainWindowUI.actionZoom400->setData( QVariant( 4.00 ) ); zoomGroup->addAction( this->m_MainWindowUI.actionZoom400 ); QObject::connect( zoomGroup, SIGNAL( triggered( QAction* ) ), this, SLOT( changeZoom( QAction* ) ) ); QActionGroup* fixedColorGroup = new QActionGroup( this->m_MainWindow ); fixedColorGroup->setExclusive( true ); this->m_MainWindowUI.actionFixedGrey->setData( QVariant( 0 ) ); fixedColorGroup->addAction( this->m_MainWindowUI.actionFixedGrey ); this->m_MainWindowUI.actionFixedRed->setData( QVariant( 1 ) ); fixedColorGroup->addAction( this->m_MainWindowUI.actionFixedRed ); this->m_MainWindowUI.actionFixedGreen->setData( QVariant( 2 ) ); fixedColorGroup->addAction( this->m_MainWindowUI.actionFixedGreen ); this->m_MainWindowUI.actionFixedBlue->setData( QVariant( 3 ) ); fixedColorGroup->addAction( this->m_MainWindowUI.actionFixedBlue ); this->m_MainWindowUI.actionFixedCyan->setData( QVariant( 4 ) ); fixedColorGroup->addAction( this->m_MainWindowUI.actionFixedCyan ); this->m_MainWindowUI.actionFixedYellow->setData( QVariant( 5 ) ); fixedColorGroup->addAction( this->m_MainWindowUI.actionFixedYellow ); this->m_MainWindowUI.actionFixedMagenta->setData( QVariant( 6 ) ); fixedColorGroup->addAction( this->m_MainWindowUI.actionFixedMagenta ); this->m_MainWindowUI.actionFixedBlueRed->setData( QVariant( 7 ) ); fixedColorGroup->addAction( this->m_MainWindowUI.actionFixedBlueRed ); this->m_MainWindowUI.actionFixedRedBlue->setData( QVariant( 8 ) ); fixedColorGroup->addAction( this->m_MainWindowUI.actionFixedRedBlue ); this->m_MainWindowUI.actionFixedLabels->setData( QVariant( 9 ) ); fixedColorGroup->addAction( this->m_MainWindowUI.actionFixedLabels ); QObject::connect( fixedColorGroup, SIGNAL( triggered( QAction* ) ), this, SLOT( changeFixedColor( QAction* ) ) ); QActionGroup* movingColorGroup = new QActionGroup( this->m_MainWindow ); movingColorGroup->setExclusive( true ); this->m_MainWindowUI.actionMovingGrey->setData( QVariant( 0 ) ); movingColorGroup->addAction( this->m_MainWindowUI.actionMovingGrey ); this->m_MainWindowUI.actionMovingRed->setData( QVariant( 1 ) ); movingColorGroup->addAction( this->m_MainWindowUI.actionMovingRed ); this->m_MainWindowUI.actionMovingGreen->setData( QVariant( 2 ) ); movingColorGroup->addAction( this->m_MainWindowUI.actionMovingGreen ); this->m_MainWindowUI.actionMovingBlue->setData( QVariant( 3 ) ); movingColorGroup->addAction( this->m_MainWindowUI.actionMovingBlue ); this->m_MainWindowUI.actionMovingCyan->setData( QVariant( 4 ) ); movingColorGroup->addAction( this->m_MainWindowUI.actionMovingCyan ); this->m_MainWindowUI.actionMovingYellow->setData( QVariant( 5 ) ); movingColorGroup->addAction( this->m_MainWindowUI.actionMovingYellow ); this->m_MainWindowUI.actionMovingMagenta->setData( QVariant( 6 ) ); movingColorGroup->addAction( this->m_MainWindowUI.actionMovingMagenta ); this->m_MainWindowUI.actionMovingBlueRed->setData( QVariant( 7 ) ); movingColorGroup->addAction( this->m_MainWindowUI.actionMovingBlueRed ); this->m_MainWindowUI.actionMovingRedBlue->setData( QVariant( 8 ) ); movingColorGroup->addAction( this->m_MainWindowUI.actionMovingRedBlue ); this->m_MainWindowUI.actionMovingLabels->setData( QVariant( 9 ) ); movingColorGroup->addAction( this->m_MainWindowUI.actionMovingLabels ); QObject::connect( movingColorGroup, SIGNAL( triggered( QAction* ) ), this, SLOT( changeMovingColor( QAction* ) ) ); QActionGroup* sliceGroup = new QActionGroup( this->m_MainWindow ); sliceGroup->setExclusive( true ); sliceGroup->addAction( this->m_MainWindowUI.actionSliceAxial_XY ); this->m_MainWindowUI.actionSliceAxial_XY->setData( QVariant( AXIS_Z ) ); sliceGroup->addAction( this->m_MainWindowUI.actionSliceCoronal_XZ ); this->m_MainWindowUI.actionSliceCoronal_XZ->setData( QVariant( AXIS_Y ) ); sliceGroup->addAction( this->m_MainWindowUI.actionSliceSagittal_YZ ); this->m_MainWindowUI.actionSliceSagittal_YZ->setData( QVariant( AXIS_X ) ); QObject::connect( sliceGroup, SIGNAL( triggered( QAction* ) ), this, SLOT( changeSliceDirection( QAction* ) ) ); QActionGroup* interpGroup = new QActionGroup( this->m_MainWindow ); interpGroup->setExclusive( true ); this->m_MainWindowUI.actionInterpLinear->setData( QVariant( Interpolators::LINEAR ) ); interpGroup->addAction( this->m_MainWindowUI.actionInterpLinear ); this->m_MainWindowUI.actionInterpCubic->setData( QVariant( Interpolators::CUBIC ) ); interpGroup->addAction( this->m_MainWindowUI.actionInterpCubic ); this->m_MainWindowUI.actionInterpSinc->setData( QVariant( Interpolators::COSINE_SINC ) ); interpGroup->addAction( this->m_MainWindowUI.actionInterpSinc ); this->m_MainWindowUI.actionInterpNearestNeighbour->setData( QVariant( Interpolators::NEAREST_NEIGHBOR ) ); interpGroup->addAction( this->m_MainWindowUI.actionInterpNearestNeighbour ); this->m_MainWindowUI.actionInterpPartialVolume->setData( QVariant( Interpolators::PARTIALVOLUME ) ); interpGroup->addAction( this->m_MainWindowUI.actionInterpPartialVolume ); QObject::connect( interpGroup, SIGNAL( triggered( QAction* ) ), this, SLOT( changeInterpolator( QAction* ) ) ); QActionGroup* xformGroup = new QActionGroup( this->m_MainWindow ); xformGroup->setExclusive( true ); this->m_MainWindowUI.actionXformIdentity->setData( QVariant( 0 ) ); xformGroup->addAction( this->m_MainWindowUI.actionXformIdentity ); this->m_MainWindowUI.actionXformAffine->setData( QVariant( 1 ) ); xformGroup->addAction( this->m_MainWindowUI.actionXformAffine ); this->m_MainWindowUI.actionXformWarp->setData( QVariant( 2 ) ); xformGroup->addAction( this->m_MainWindowUI.actionXformWarp ); QObject::connect( xformGroup, SIGNAL( triggered( QAction* ) ), this, SLOT( changeXform( QAction* ) ) ); this->m_MainWindowUI.alphaSlider->setRange( 0, 1000 ); this->m_MainWindowUI.alphaSlider->setValue( this->m_Transparency * 1000 ); QObject::connect( this->m_MainWindowUI.alphaSlider, SIGNAL( valueChanged( int ) ), this, SLOT( setTransparency( int ) ) ); this->changeSliceDirection( AXIS_Z ); QObject::connect( this->m_MainWindowUI.sliceSlider, SIGNAL( valueChanged( int ) ), this, SLOT( setFixedSlice( int ) ) ); QObject::connect( this->m_MainWindowUI.actionLinkedCursor, SIGNAL( toggled( bool ) ), this, SLOT( setLinkedCursorFlag( bool ) ) ); // synchronize sliders of the two graphics views QObject::connect( this->m_MainWindowUI.fixedView->horizontalScrollBar(), SIGNAL( valueChanged( int ) ), this->m_MainWindowUI.movingView->horizontalScrollBar(), SLOT( setValue( int ) ) ); QObject::connect( this->m_MainWindowUI.fixedView->verticalScrollBar(), SIGNAL( valueChanged( int ) ), this->m_MainWindowUI.movingView->verticalScrollBar(), SLOT( setValue( int) ) ); QObject::connect( this->m_MainWindowUI.movingView->horizontalScrollBar(), SIGNAL( valueChanged( int ) ), this->m_MainWindowUI.fixedView->horizontalScrollBar(), SLOT( setValue( int ) ) ); QObject::connect( this->m_MainWindowUI.movingView->verticalScrollBar(), SIGNAL( valueChanged( int ) ), this->m_MainWindowUI.fixedView->verticalScrollBar(), SLOT( setValue( int ) ) ); this->m_MainWindow->show(); } void cmtk::FusionViewApplication ::InitViewData( Self::Data& data, QGraphicsView* view ) { data.m_ColorMapIndex = 0; const QPen redPen( QColor( 255, 0, 0 ) ); data.m_Scene = new QGraphicsScene; (data.m_CursorLines[0] = data.m_Scene->addLine( 0, 0, 0, 0, redPen ))->setZValue( 100 ); (data.m_CursorLines[1] = data.m_Scene->addLine( 0, 0, 0, 0, redPen ))->setZValue( 100 ); (data.m_PixmapItem = new QGraphicsPixmapItemEvents)->setZValue( 0 ); data.m_Scene->addItem( data.m_PixmapItem ); QObject::connect( data.m_PixmapItem, SIGNAL( mousePressed( QGraphicsSceneMouseEvent* ) ), this, SLOT( mousePressed( QGraphicsSceneMouseEvent* ) ) ); data.m_View = view; data.m_View->setScene( data.m_Scene ); } void cmtk::FusionViewApplication ::setTransparency( int slice ) { this->m_Transparency = static_cast( slice ) / 1000; this->UpdateMovingImage(); } void cmtk::FusionViewApplication ::changeFixedColor( QAction* action ) { this->m_Fixed.m_ColorMapIndex = action->data().toInt(); this->UpdateFixedImage(); } void cmtk::FusionViewApplication ::changeMovingColor( QAction* action ) { this->m_Moving.m_ColorMapIndex = action->data().toInt(); this->UpdateMovingImage(); } void cmtk::FusionViewApplication ::setLinkedCursorFlag( bool flag ) { this->m_CursorDisplayed = flag; for ( int i = 0; i < 2; ++i ) { this->m_Fixed.m_CursorLines[i]->setVisible( this->m_CursorDisplayed ); this->m_Moving.m_CursorLines[i]->setVisible( this->m_CursorDisplayed ); } } void cmtk::FusionViewApplication ::setFixedSlice( int slice ) { if ( this->m_SliceIndex != slice ) { this->m_SliceIndex = slice; this->m_CursorPosition[this->m_SliceAxis] = this->m_SliceIndex; this->m_Fixed.m_Slice = this->m_Fixed.m_Volume->ExtractSlice( this->m_SliceAxis, this->m_SliceIndex ); this->UpdateFixedImage(); this->UpdateMovingSlice(); this->m_MainWindowUI.sliceLabel->setText( QString("Slice: %1").arg( this->m_SliceIndex ) ); } } void cmtk::FusionViewApplication ::changeZoom( QAction* action ) { this->m_ZoomFactor = static_cast( action->data().toDouble() ); // older Qt doesn't have QVariant::toFloat() this->UpdateFixedImage(); this->UpdateMovingImage(); } void cmtk::FusionViewApplication ::changeInterpolator( QAction* action ) { this->m_Interpolator = static_cast( action->data().toInt() ); this->UpdateMovingSlice(); } void cmtk::FusionViewApplication ::changeXform( QAction* action ) { this->m_XformModel = action->data().toInt(); this->UpdateMovingSlice(); } void cmtk::FusionViewApplication ::fixedBlackWhiteChanged() { this->UpdateFixedImage(); } void cmtk::FusionViewApplication ::movingBlackWhiteChanged() { this->UpdateMovingImage(); } void cmtk::FusionViewApplication ::changeSliceDirection( QAction* action ) { this->changeSliceDirection( action->data().toInt() ); } void cmtk::FusionViewApplication ::mousePressed( QGraphicsSceneMouseEvent* event ) { const int idxX = this->GetAxis2DX(); const int idxY = this->GetAxis2DY(); this->m_CursorPosition[idxX] = event->pos().x() / this->m_ScalePixels[idxX]; this->m_CursorPosition[idxY] = event->pos().y() / this->m_ScalePixels[idxY]; if ( this->m_CursorDisplayed ) { this->UpdateView( this->m_Fixed, this->m_Fixed.m_Image ); this->UpdateView( this->m_Moving, this->m_FusedImage ); } } void cmtk::FusionViewApplication ::changeSliceDirection( const int sliceAxis ) { if ( sliceAxis != this->m_SliceAxis ) { this->m_SliceAxis = sliceAxis; this->m_SliceIndex = -1; // unset previously set slice index to ensure update of moving slice const int newSliceIndex = static_cast( this->m_CursorPosition[this->m_SliceAxis] ); // store to safeguard against unwanted updates this->m_MainWindowUI.sliceSlider->setRange( 0, this->m_Fixed.m_Volume->GetDims()[this->m_SliceAxis]-1 ); this->setFixedSlice( newSliceIndex ); this->m_MainWindowUI.sliceSlider->setValue( this->m_SliceIndex ); this->UpdateMovingSlice(); const char* labelFrom[3] = { "Left", "Posterior", "Inferior" }; const char* labelTo[3] = { "Right", "Anterior", "Superior" }; this->m_MainWindowUI.sliceLabelFrom->setText( labelFrom[this->m_SliceAxis] ); this->m_MainWindowUI.sliceLabelTo->setText( labelTo[this->m_SliceAxis] ); } } void cmtk::FusionViewApplication ::UpdateMovingSlice() { ReformatVolume::Plain plain( TYPE_FLOAT ); UniformVolumeInterpolatorBase::SmartPtr interpolator ( ReformatVolume::CreateInterpolator( this->m_Interpolator, this->m_Moving.m_Volume ) ); const XformList noXforms; TypedArray::SmartPtr reformatData; switch ( this->m_XformModel ) { case 0: reformatData = ReformatVolume::ReformatUnmasked( this->m_Fixed.m_Slice, noXforms, noXforms, plain, this->m_Moving.m_Volume, interpolator ); break; case 1: reformatData = ReformatVolume::ReformatUnmasked( this->m_Fixed.m_Slice, this->m_XformListAllAffine, noXforms, plain, this->m_Moving.m_Volume, interpolator ); break; case 2: default: reformatData = ReformatVolume::ReformatUnmasked( this->m_Fixed.m_Slice, this->m_XformList, noXforms, plain, this->m_Moving.m_Volume, interpolator ); break; } UniformVolume::SmartPtr movingSlice = this->m_Fixed.m_Slice->CloneGrid(); movingSlice->SetData( reformatData ); this->m_Moving.m_Slice = movingSlice; this->UpdateMovingImage(); } void cmtk::FusionViewApplication ::MakeColorTable( Self::Data& data ) { data.m_ColorTable.resize( 256 ); switch ( data.m_ColorMapIndex ) { default: case 0: // black/white for ( int i = 0; i < 256; ++i ) { data.m_ColorTable[i] = QColor( i, i, i ).rgb(); } break; case 1: // red for ( int i = 0; i < 256; ++i ) { data.m_ColorTable[i] = QColor( i, 0, 0 ).rgb(); } break; case 2: // green for ( int i = 0; i < 256; ++i ) { data.m_ColorTable[i] = QColor( 0, i, 0 ).rgb(); } break; case 3: // blue for ( int i = 0; i < 256; ++i ) { data.m_ColorTable[i] = QColor( 0, 0, i ).rgb(); } break; case 4: // cyan for ( int i = 0; i < 256; ++i ) { data.m_ColorTable[i] = QColor( 0, i, i ).rgb(); } break; case 5: // yellow for ( int i = 0; i < 256; ++i ) { data.m_ColorTable[i] = QColor( i, i, 0 ).rgb(); } break; case 6: // magenta for ( int i = 0; i < 256; ++i ) { data.m_ColorTable[i] = QColor( i, 0, i ).rgb(); } break; case 7: // blue to red for ( int i = 0; i < 256; ++i ) { QColor color; color.setHsv( 255-i, 255, 255 ); data.m_ColorTable[i] = color.rgb(); } break; case 8: // red to blue for ( int i = 0; i < 256; ++i ) { QColor color; color.setHsv( i, 255, 255 ); data.m_ColorTable[i] = color.rgb(); } break; } } void cmtk::FusionViewApplication ::UpdateFixedImage() { this->MakeColorTable( this->m_Fixed ); const float black = this->m_Fixed.m_DataRange.m_LowerBound + this->m_Fixed.m_DataRange.Width() * this->m_MainWindowUI.blackSliderFix->value() / 500; const float white = this->m_Fixed.m_DataRange.m_LowerBound + this->m_Fixed.m_DataRange.Width() * this->m_MainWindowUI.whiteSliderFix->value() / 500; this->MakeImage( this->m_Fixed.m_Image, *(this->m_Fixed.m_Slice), this->m_Fixed.m_ColorTable, black, white ); this->UpdateView( this->m_Fixed, this->m_Fixed.m_Image ); } void cmtk::FusionViewApplication ::UpdateMovingImage() { this->MakeColorTable( this->m_Moving ); const float black = this->m_Moving.m_DataRange.m_LowerBound + this->m_Moving.m_DataRange.Width() * this->m_MainWindowUI.blackSliderMov->value() / 500; const float white = this->m_Moving.m_DataRange.m_LowerBound + this->m_Moving.m_DataRange.Width() * this->m_MainWindowUI.whiteSliderMov->value() / 500; this->MakeImage( this->m_Moving.m_Image, *(this->m_Moving.m_Slice), this->m_Moving.m_ColorTable, black, white ); this->m_FusedImage = QImage( this->m_Moving.m_Image.width(), this->m_Moving.m_Image.height(), QImage::Format_RGB32 ); for ( int y = 0; y < this->m_Moving.m_Image.height(); ++y ) { for ( int x = 0; x < this->m_Moving.m_Image.width(); ++x ) { QColor rgbMov( this->m_Moving.m_Image.pixel( x, y ) ); const QColor rgbFix( this->m_Fixed.m_Image.pixel( x, y ) ); rgbMov = QColor( this->m_Transparency * rgbMov.red() + (1.0-this->m_Transparency) * rgbFix.red(), this->m_Transparency * rgbMov.green() + (1.0-this->m_Transparency) * rgbFix.green(), this->m_Transparency * rgbMov.blue() + (1.0-this->m_Transparency) * rgbFix.blue() ); this->m_FusedImage.setPixel( x, y, rgbMov.rgb() ); } } this->UpdateView( this->m_Moving, this->m_FusedImage ); } void cmtk::FusionViewApplication ::MakeImage( QImage& image, const UniformVolume& slice, const QVector& colorTable, const float blackLevel, const float whiteLevel ) { const int idxX = this->GetAxis2DX(); const int idxY = this->GetAxis2DY(); int dimX = slice.GetDims()[idxX]; int dimY = slice.GetDims()[idxY]; image = QImage( dimX, dimY, QImage::Format_Indexed8 ); image.setColorTable( colorTable ); const float scaleLevel = 1.0 / (whiteLevel-blackLevel); size_t idx = 0; for ( int y = 0; y < dimY; ++y ) { for ( int x = 0; x < dimX; ++x, ++idx ) { image.setPixel( x, y, static_cast( 255 * std::min( 1, std::max( 0, (slice.GetDataAt( idx ) - blackLevel) * scaleLevel ) ) ) ); } } } void cmtk::FusionViewApplication ::UpdateView( Self::Data& data, QImage& image ) { data.m_PixmapItem->setPixmap( QPixmap::fromImage( image ) ); const QRectF bb = data.m_PixmapItem->boundingRect(); data.m_Scene->setSceneRect( bb ); const int idxX = this->GetAxis2DX(); const int idxY = this->GetAxis2DY(); if ( this->m_CursorDisplayed ) { data.m_CursorLines[0]->setLine( this->m_CursorPosition[idxX], bb.top(), this->m_CursorPosition[idxX], bb.bottom() ); data.m_CursorLines[1]->setLine( bb.left(), this->m_CursorPosition[idxY], bb.right(), this->m_CursorPosition[idxY] ); } QTransform zoomTransform = QTransform::fromScale( -this->m_ZoomFactor * this->m_ScalePixels[idxX], -this->m_ZoomFactor * this->m_ScalePixels[idxY] ); data.m_View->setTransform( zoomTransform ); } cmtk-3.3.1/gui/fview/cmtkFusionViewApplication.h000066400000000000000000000154151276303427400217100ustar00rootroot00000000000000/* // // Copyright 2010-2011, 2013 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4815 $ // // $LastChangedDate: 2013-09-10 09:34:06 -0700 (Tue, 10 Sep 2013) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkFusionViewApplication_h_included_ #define __cmtkFusionViewApplication_h_included_ #include #include #include #include #include #include #include #include #include #include #include #include namespace cmtk { /// Application class for fusion viewer. class FusionViewApplication : public QApplication { Q_OBJECT public: /// This class. typedef FusionViewApplication Self; /// Constructor. FusionViewApplication( int& argc, char* argv[] ); private slots: /// Update displayed fixed image slice. void setFixedSlice( int slice ); /// Update moving image transparency. void setTransparency( int slice ); /// Update flag for displaying linked cursors. void setLinkedCursorFlag( bool flag ); /// Update zoom factor from UI. void changeZoom( QAction* action /*!< Action to set new zoom factor. */ ); /// Change fixed image color map. void changeFixedColor( QAction* ); /// Change moving image color map. void changeMovingColor( QAction* ); /// Update interpolator from UI. void changeInterpolator( QAction* action /*!< Action to set new interpolator. */ ); /// Update transformation model selection from UI. void changeXform( QAction* action /*!< Action to set new transformation model. */ ); /// Update slice direction from UI. void changeSliceDirection( QAction* action /*!< Action to set new slice direction. */ ); /// Fixed image black/white has changed. void fixedBlackWhiteChanged(); /// Moving image black/white has changed. void movingBlackWhiteChanged(); /// Update slice direction from integer. void changeSliceDirection( const int sliceAxis ); /// Mouse button pressed in one of the graphics views. void mousePressed( QGraphicsSceneMouseEvent* event ); private: /// Application main window. QMainWindow* m_MainWindow; /// Designed-generated User Interface for the main window. Ui::fviewMainWindow m_MainWindowUI; /// Class to bundle fixed and moving image objects. class Data { public: /// Default constructor. Data() : m_DataRange( 0, 0 ), m_ColorMapIndex( 0 ), m_View( NULL ), m_Scene( NULL ), m_PixmapItem( NULL ) { m_CursorLines[0] = m_CursorLines[1] = NULL; } /// The volume. UniformVolume::SmartConstPtr m_Volume; /// Data range. Types::DataItemRange m_DataRange; /// Data for the current image slice. UniformVolume::SmartConstPtr m_Slice; /// Color map index: this selects one of the predefined color maps. int m_ColorMapIndex; /// Color table. QVector m_ColorTable; /// QImage for the current slice. QImage m_Image; /// The graphics view (this is a link to the view created from the main window uic). QGraphicsView* m_View; /// The graphics scene for this volume. QGraphicsScene* m_Scene; /// The pixmap graphics item with mouse events. QGraphicsPixmapItemEvents* m_PixmapItem; /// The line items for the cross cursor. QGraphicsLineItem* m_CursorLines[2]; }; /// The fixed volume data. Self::Data m_Fixed; /// The fixed volume data. Self::Data m_Moving; /// Initialize the view data for the given volume (fixed or moving). void InitViewData( Self::Data& data, /*!< Bundled data for given volume.*/ QGraphicsView* view /*!< The view we want to attach this volume to.*/ ); /// The list of concatenated transformations. XformList m_XformList; /** The list of all-affine concatenated transformations. * these are the same transformations as in m_XformList, but every nonrigid * transformation therein is replaced with its affine initializer. */ XformList m_XformListAllAffine; /** Selection of transformation model. * 0: apply no transformation, 1: apply only affine transformation components, * 2: apply complete nonrigid transformation. */ int m_XformModel; /// The slice axis (0=x, sagittal; 1=y, coronal; 2=z, axial). int m_SliceAxis; /// Slice index in the fixed image along the slice axis. int m_SliceIndex; /// Interpolator for the moving image. Interpolators::InterpolationEnum m_Interpolator; /// QImage for the current fused slice. QImage m_FusedImage; /// Zoom scale factor. float m_ZoomFactor; /// Scale factors for non-square pixels. FixedVector<3,float> m_ScalePixels; /// Moving image transparency. float m_Transparency; /// Flag for linked cursor display. float m_CursorDisplayed; /// Linked cursor position in 3D. FixedVector<3,float> m_CursorPosition; /// Update displayed fixed image slice. void UpdateFixedImage(); /// Update interpolated moving image slice. void UpdateMovingSlice(); /// Update displayed moving image slice. void UpdateMovingImage(); /// Make a color table based on the color map index. void MakeColorTable( Self::Data& data ); /// Make a QImage from slice data and color table. void MakeImage( QImage& image, const UniformVolume& slice, const QVector& colorTable, const float blackLevel, const float whiteLevel ); /// Update graphics view using a given image. void UpdateView( Self::Data& data, QImage& image ); /// Get 3D coordinate axis corresponding to 2D x axis. int GetAxis2DX() const { static const int idxXtable[3] = { 1, 0, 0 }; return idxXtable[this->m_SliceAxis]; } /// Get 3D coordinate axis corresponding to 2D y axis. int GetAxis2DY() const { static const int idxYtable[3] = { 2, 2, 1 }; return idxYtable[this->m_SliceAxis]; } }; } // namespace cmtk #endif // #ifndef __cmtkFusionViewApplication_h_included_ cmtk-3.3.1/gui/fview/fviewMainWindow.ui000066400000000000000000000477441276303427400200640ustar00rootroot00000000000000 fviewMainWindow 0 0 808 611 CMTK Fusion Viewer QLayout::SetMaximumSize 499 Qt::Horizontal 499 Qt::Horizontal Fixed Image Black / White Moving Image Black / White Fixed Image CrossCursor Moving/Fused Image CrossCursor 499 499 Qt::Horizontal 0 0 Transparency Qt::AlignCenter Fixed Image Qt::Horizontal Moving Image Qt::Horizontal I 0 0 Slice Qt::AlignCenter S 499 499 Qt::Horizontal true 0 0 808 20 &File &View &Zoom &Annotations &Fixed Image Color Map &Moving Image Color Map &Transform Slice Slice &Orientation Interpolation Qt::LeftToRight &Help false &Quit &Zoom true true &Axial (XY) Alt+Z true &Coronal (XZ) Alt+Y true &Sagittal (YZ) Alt+X true true &100% Alt+1 true &200% Alt+2 true &400% Alt+4 true &50% Alt+5 true &300% Alt+3 true 25% true true &Linear Alt+L true &Cubic Alt+C true &Sinc Alt+S true &Nearest Neighbour Alt+N true &Partial Volume Alt+P true true Linked &Cursor Ctrl++ true &Affine Only Alt+A About... &Moving Image Color Map true true Black/&White true &Red true &Green true &Blue true &Cyan true &Yellow true &Magenta true Blue to Red true Red to Blue true &Labels true true Black / &White true &Red true &Green true &Blue true &Cyan true &Yellow true &Magenta true Blue to Red true Red to Blue true &Labels true &Identity Alt+I true &Warp Alt+W action_Quit activated() fviewMainWindow close() -1 -1 403 305 cmtk-3.3.1/gui/fview/main.cxx000066400000000000000000000032111276303427400160350ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2011 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 3408 $ // // $LastChangedDate: 2011-08-23 13:50:09 -0700 (Tue, 23 Aug 2011) $ // // $LastChangedBy: torstenrohlfing $ // */ #include #include namespace cmtk { static StackBacktrace StackBacktraceInstance; } #include #include "cmtkFusionViewApplication.h" int main( int argc, char* argv[] ) { int returnCode = 0; try { cmtk::FusionViewApplication app( argc, argv ); returnCode = app.exec(); } catch ( const cmtk::CommandLine::Exception& ex ) { cmtk::StdErr << ex << "\n"; returnCode = 1; } catch ( const cmtk::ExitException& ex ) { returnCode = ex.ExitCode(); } return returnCode; } cmtk-3.3.1/gui/triplanar/000077500000000000000000000000001276303427400152445ustar00rootroot00000000000000cmtk-3.3.1/gui/triplanar/CMakeLists.txt000066400000000000000000000026721276303427400200130ustar00rootroot00000000000000## ## Copyright 1997-2009 Torsten Rohlfing ## ## Copyright 2004-2011 SRI International ## ## This file is part of the Computational Morphometry Toolkit. ## ## http://www.nitrc.org/projects/cmtk/ ## ## The Computational Morphometry Toolkit is free software: you can ## redistribute it and/or modify it under the terms of the GNU General Public ## License as published by the Free Software Foundation, either version 3 of ## the License, or (at your option) any later version. ## ## The Computational Morphometry Toolkit is distributed in the hope that it ## will be useful, but WITHOUT ANY WARRANTY; without even the implied ## warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License along ## with the Computational Morphometry Toolkit. If not, see ## . ## ## $Revision: 3544 $ ## ## $LastChangedDate: 2011-11-03 14:14:15 -0700 (Thu, 03 Nov 2011) $ ## ## $LastChangedBy: torstenrohlfing $ ## ADD_EXECUTABLE(triplanar main.cxx) TARGET_LINK_LIBRARIES(triplanar ${CMTK_LIBRARIES} ${QT_LIBRARIES} ${CMTK_SYSTEM_LIBRARIES}) IF(CMTK_BUILD_WRAPPER) INSTALL(TARGETS triplanar RUNTIME DESTINATION ${CMTK_INSTALL_LIB_DIR}/bin COMPONENT gui) ELSE(CMTK_BUILD_WRAPPER) INSTALL(TARGETS triplanar RUNTIME DESTINATION ${CMTK_INSTALL_BIN_DIR} COMPONENT gui) ENDIF(CMTK_BUILD_WRAPPER) cmtk-3.3.1/gui/triplanar/main.cxx000066400000000000000000000052531276303427400167210ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2011, 2013 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4879 $ // // $LastChangedDate: 2013-09-26 15:08:17 -0700 (Thu, 26 Sep 2013) $ // // $LastChangedBy: torstenrohlfing $ // */ #include #include namespace cmtk { static StackBacktrace StackBacktraceInstance; } #include #include #include #include #include #include #include #include #include int doMain( int argc, char* argv[] ) { QApplication app( argc, argv ); app.setStyle( new QPlastiqueStyle ); cmtk::QtTriplanarViewer* viewer = new cmtk::QtTriplanarViewer; if ( viewer ) { cmtk::QtProgress progressInstance( viewer ); progressInstance.SetProgressWidgetMode( cmtk::QtProgress::PROGRESS_DIALOG ); cmtk::Progress::SetProgressInstance( &progressInstance ); if ( (argc > 1) ) { if ( !strcmp( argv[1], "--exec" ) ) { viewer->hide(); return viewer->ExecuteBatchMode( argc-2, argv+2 ); } else if ( !strcmp( argv[1], "--xml" ) ) { exit(1); } } for ( int i = 1; i < argc; ++i ) { viewer->slotAddStudy( argv[i] ); } viewer->show(); return app.exec(); } return 0; } int main( const int argc, char* argv[] ) { #ifdef _MSC_VER _set_output_format( _TWO_DIGIT_EXPONENT ); #endif cmtk::Threads::CheckEnvironment(); // need this to check for "CMTK_NUM_THREADS" and constrain OpenMP accordingly #ifdef CMTK_BUILD_STACKTRACE cmtk::StackBacktrace::Static(); #endif int exitCode = 0; try { exitCode = doMain( argc, argv ); } catch ( const cmtk::ExitException& ex ) { exitCode = ex.ExitCode(); } return exitCode; } cmtk-3.3.1/libs/000077500000000000000000000000001276303427400134155ustar00rootroot00000000000000cmtk-3.3.1/libs/Base/000077500000000000000000000000001276303427400142675ustar00rootroot00000000000000cmtk-3.3.1/libs/Base/CMakeLists.txt000066400000000000000000000114561276303427400170360ustar00rootroot00000000000000## ## Copyright 1997-2010 Torsten Rohlfing ## ## Copyright 2004-2014 SRI International ## ## This file is part of the Computational Morphometry Toolkit. ## ## http://www.nitrc.org/projects/cmtk/ ## ## The Computational Morphometry Toolkit is free software: you can ## redistribute it and/or modify it under the terms of the GNU General Public ## License as published by the Free Software Foundation, either version 3 of ## the License, or (at your option) any later version. ## ## The Computational Morphometry Toolkit is distributed in the hope that it ## will be useful, but WITHOUT ANY WARRANTY; without even the implied ## warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License along ## with the Computational Morphometry Toolkit. If not, see ## . ## ## $Revision: 5185 $ ## ## $LastChangedDate: 2014-01-28 13:46:10 -0800 (Tue, 28 Jan 2014) $ ## ## $LastChangedBy: torstenrohlfing $ ## # Sources of non-templated classes. SET(cmtkBase_SRCS cmtkActiveDeformationModel.cxx cmtkActiveShapeModel.cxx cmtkAffineXform.cxx cmtkAffineXformUniformVolume.cxx cmtkAnatomicalOrientation.cxx cmtkAnatomicalOrientationBase.cxx cmtkAnatomicalOrientationPermutationMatrix.cxx cmtkBitVector.cxx cmtkCompatibilityMatrix4x4.cxx cmtkDataGrid.cxx cmtkDataGrid_Crop.cxx cmtkDataGridFilter.cxx cmtkDataGridConnectedComponents.cxx cmtkDataGridLocalCorrelation.cxx cmtkDataGridMorphologicalOperators.cxx cmtkDeformationField.cxx cmtkDeformationField_Jacobian.cxx cmtkDirectionSet.cxx cmtkFilterVolume.cxx cmtkFitAffineToLandmarks.cxx cmtkFitAffineToWarpXform.cxx cmtkFitAffineToXformList.cxx cmtkFitPolynomialToLandmarks.cxx cmtkFitRigidToLandmarks.cxx cmtkFitSplineWarpToDeformationField.cxx cmtkFitSplineWarpToLandmarks.cxx cmtkFitSplineWarpToXformList.cxx cmtkFitToXformListBase.cxx cmtkFunctional.cxx cmtkGeneralLinearModel.cxx cmtkHistogram.cxx cmtkHypothesisTests.cxx cmtkImageOperation.cxx cmtkImageOperationCropRegion.cxx cmtkImageOperationCropThreshold.cxx cmtkImageOperationDistanceMap.cxx cmtkImageOperationDownsample.cxx cmtkImageOperationHistogramEqualization.cxx cmtkImageOperationMapValues.cxx cmtkImageOperationRegionFilter.cxx cmtkImageOperationResampleIsotropic.cxx cmtkImageOperationScaleToRange.cxx cmtkImageOperationThreshold.cxx cmtkImageOperationOtsuThreshold.cxx cmtkImageOperationPruneHistogram.cxx cmtkImageOperationRevert.cxx cmtkJointHistogram.cxx cmtkJointHistogramBase.cxx cmtkLandmark.cxx cmtkLandmarkPair.cxx cmtkLandmarkList.cxx cmtkLandmarkPairList.cxx cmtkMagphanEMR051.cxx cmtkMagphanEMR051_GetPhantomImage.cxx cmtkMathFunctionWrappers.cxx cmtkMathUtil_LinAlg.cxx cmtkMathUtil_Random.cxx cmtkMathUtil_Statistics.cxx cmtkMatrix3x3.cxx cmtkMatrix4x4.cxx cmtkMetaInformationObject.cxx cmtkParametricPlane.cxx cmtkPolynomialXform.cxx cmtkScalarImage.cxx cmtkScalarImageGradientField.cxx cmtkSegmentationLabel.cxx cmtkSplineWarpXform.cxx cmtkSplineWarpXform_Inverse.cxx cmtkSplineWarpXform_Jacobian.cxx cmtkSplineWarpXform_Rigidity.cxx cmtkSplineWarpXformUniformVolume.cxx cmtkTransformChangeToSpaceAffine.cxx cmtkTransformChangeFromSpaceAffine.cxx cmtkTransformedVolumeAxes.cxx cmtkTypedArray.cxx cmtkTypedArray_Statistics.cxx cmtkTypedArrayFunctionHistogramMatching.cxx cmtkTypedArrayFunctionHistogramEqualization.cxx cmtkTypedArrayNoiseEstimatorNaiveGaussian.cxx cmtkTypes.cxx cmtkUniformDistanceMap.cxx cmtkUniformVolume.cxx cmtkUniformVolume_Crop.cxx cmtkUniformVolume_Differential.cxx cmtkUniformVolume_Resample.cxx cmtkUniformVolume_Space.cxx cmtkUniformVolumeGaussianFilter.cxx cmtkUniformVolumeLaplaceFilter.cxx cmtkUniformVolumeInterpolatorBase.cxx cmtkUniformVolumeInterpolatorPartialVolume.cxx cmtkUniformVolumeMorphologicalOperators.cxx cmtkUniformVolumePainter.cxx cmtkVolumeClipping.cxx cmtkVolume.cxx cmtkVolumeGridToGridLookup.cxx cmtkWarpXform.cxx cmtkXform.cxx cmtkXform_Inverse.cxx cmtkXformList.cxx cmtkXformListEntry.cxx ) ADD_LIBRARY(cmtkBase ${cmtkBase_SRCS}) TARGET_LINK_LIBRARIES(cmtkBase cmtkSystem cmtkNumerics ) IF(CMTK_LIBRARY_PROPERTIES) SET_TARGET_PROPERTIES(cmtkBase PROPERTIES ${CMTK_LIBRARY_PROPERTIES}) ENDIF(CMTK_LIBRARY_PROPERTIES) INSTALL(TARGETS cmtkBase RUNTIME DESTINATION ${CMTK_INSTALL_BIN_DIR} COMPONENT runtime LIBRARY DESTINATION ${CMTK_INSTALL_LIB_DIR} COMPONENT runtime ARCHIVE DESTINATION ${CMTK_INSTALL_LIB_DIR} COMPONENT libraries) FILE(GLOB files_h "${CMAKE_CURRENT_SOURCE_DIR}/*.h") FILE(GLOB files_txx "${CMAKE_CURRENT_SOURCE_DIR}/*.txx") INSTALL(FILES ${files_h} ${files_txx} DESTINATION ${CMTK_INSTALL_INCLUDE_DIR}/Base/ COMPONENT headers) cmtk-3.3.1/libs/Base/cmtkActiveDeformationModel.cxx000066400000000000000000000134751276303427400222700ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2013 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4871 $ // // $LastChangedDate: 2013-09-24 11:47:59 -0700 (Tue, 24 Sep 2013) $ // // $LastChangedBy: torstenrohlfing $ // */ #include "cmtkActiveDeformationModel.h" #include #include #include namespace cmtk { /** \addtogroup Base */ //@{ template ActiveDeformationModel::ActiveDeformationModel ( const std::list< SmartPointer >& deformationList, const unsigned int numberOfModes, const bool includeScaleInModel, const bool includeReferenceInModel ) { IncludeScaleInModel = includeScaleInModel; IncludeReferenceInModel = includeReferenceInModel; unsigned int numberOfSamples = deformationList.size(); if ( IncludeReferenceInModel ) ++numberOfSamples; Types::Coordinate** samplePoints = Memory::ArrayC::Allocate( numberOfSamples ); unsigned int numberOfPoints = 0; typename std::list< SmartPointer >::const_iterator it = deformationList.begin(); // prepare this object to act as an actual deformation. this->InitGrid( (*it)->m_Domain, (*it)->m_Dims ); // copy Origin field of first warp. this->m_Offset = (*it)->m_Offset; unsigned int sample = 0; Types::Coordinate globalScaling = 0; if ( IncludeReferenceInModel ) { samplePoints[sample++] = this->MakeSamplePointsReference( *it ); } while ( it != deformationList.end() ) { if ( it == deformationList.begin() ) { numberOfPoints = (*it)->m_NumberOfParameters; } else { if ( numberOfPoints != (*it)->m_NumberOfParameters ) { StdErr << "WARNING: differing numbers of parameters encountered in " << "ActiveDeformationModel constructor. Skipping this " << "sample."; --numberOfSamples; ++it; continue; } } samplePoints[sample++] = (*it)->GetPureDeformation( this->IncludeScaleInModel ); globalScaling += static_cast( log( (*it)->GetGlobalScaling() ) ); ++it; } // Set Initial Affine Transform to Identity AffineXform::SmartPtr identity( new AffineXform() ); this->SetInitialAffineXform( identity ); // Set global scaling to average of individual scale factors, unless it // was preserved as part of the actual model. if ( sample && !IncludeScaleInModel ) { this->m_GlobalScaling = exp( globalScaling / sample ); } else { this->m_GlobalScaling = 1.0; } this->Construct( samplePoints, numberOfSamples, numberOfPoints, numberOfModes ); for ( unsigned int n = 0; n < numberOfSamples; ++n ) Memory::ArrayC::Delete( samplePoints[ n ] ); Memory::ArrayC::Delete( samplePoints ); } template Types::Coordinate* ActiveDeformationModel::MakeSamplePointsReference( const W* deformation ) { const unsigned int numberOfParameters = deformation->m_NumberOfParameters; Types::Coordinate* points = Memory::ArrayC::Allocate( numberOfParameters ); Types::Coordinate* ptr = points; for ( unsigned int pointIdx = 0; pointIdx < numberOfParameters / 3; ++pointIdx, ptr += 3 ) { // get original (undeformed) control point position const Vector3D v = deformation->GetOriginalControlPointPositionByOffset( pointIdx ); // copy the result into ouput array for ( unsigned int dim = 0; dim < 3; ++dim ) ptr[dim] = v[dim]; } return points; } template Types::Coordinate* ActiveDeformationModel::MakeSamplePoints( const W* deformation ) { const unsigned int numberOfParameters = deformation->m_NumberOfParameters; Types::Coordinate* points = Memory::ArrayC::Allocate( numberOfParameters ); memcpy( points, deformation->m_Parameters, sizeof( *points ) * numberOfParameters ); AffineXform::SmartPtr xform( deformation->GetInitialAffineXform()->MakeInverse() ); if ( IncludeScaleInModel ) { xform->SetScales( 1.0, 1.0, 1.0 ); } Types::Coordinate* ptr = points; Vector3D u; for ( unsigned int pointIdx = 0; pointIdx < numberOfParameters / 3; ++pointIdx, ptr += 3 ) { // undo affine transformation component const FixedVector<3,Types::Coordinate> v = xform->Apply( FixedVector<3,Types::Coordinate>::FromPointer( ptr ) ); // copy the result into ouput array for ( unsigned int dim = 0; dim < 3; ++dim ) ptr[dim] = v[dim]; } return points; } template W* ActiveDeformationModel::Compose ( const Types::Coordinate* weights ) { this->m_Parameters = this->Generate( this->m_Parameters, weights ); return this; } template float ActiveDeformationModel::Decompose ( const W* input, Types::Coordinate *const weights ) const { CoordinateVector inputVector( this->GetNumberOfPoints(), input->GetPureDeformation( this->IncludeScaleInModel ) ); return this->ActiveShapeModel::Decompose( &inputVector, weights ); } /// Instance the spline warp ADM. template class ActiveDeformationModel; } // namespace cmtk cmtk-3.3.1/libs/Base/cmtkActiveDeformationModel.h000066400000000000000000000076221276303427400217120ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // Copyright 2004-2009 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4868 $ // // $LastChangedDate: 2013-09-24 10:08:31 -0700 (Tue, 24 Sep 2013) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkActiveDeformationModel_h_included_ #define __cmtkActiveDeformationModel_h_included_ #include #include #include #include #include namespace cmtk { /** \addtogroup Base */ //@{ /// Active deformation model. template class ActiveDeformationModel : /// This is an active shape model of sorts. public ActiveShapeModel, /// This class is also a deformation (as defined by template parameter). public W { public: /// Smart pointer to spline ADM. typedef SmartPointer< ActiveDeformationModel > SmartPtr; /** Build deformation model from list of deformations. * All deformations in the list must be of the same type, have the same * arrangement of control points, and must be defined on the same * domain (reference image). */ ActiveDeformationModel( const std::list< SmartPointer >& deformationList, const unsigned int numberOfModes, const bool includeScaleInModel = true, const bool includeReferenceInModel = true ); /// Compose deformation from mean deformation and modes of variation. W* Compose( const Types::Coordinate* weights = NULL ); /** Decompose a deformation into mean and modes of this model. *\param input Input deformation. *\param weights Weights of the modes that make up the given input vector. * This parameter is optional. If not given, no weights will be returned. *\return The value of the multivariate Gaussian PDF represented by this * model atr the location of the input vector. */ float Decompose( const W* input, Types::Coordinate *const weights = NULL ) const; private: /** Make ADM sample points from the undeformed c.p.g. of a given deformation. * This is for inclusion of the reference individual into the model by * putting the identity transformation into the sample set. */ Types::Coordinate* MakeSamplePointsReference( const W* deformation ); /** Make ADM sample points from a given deformation. * This function applies the inverse affine transformation of the given * deformation to all of its control points. This brings all deformations * into the same reference system, which is a necessary condition for model * building. Note that otherwise all control point positions are in the * coordinate system of the respective floating image, which varies from * deformation to deformation. */ Types::Coordinate* MakeSamplePoints( const W* deformation ); /// Flag whether to include scale factors in model. bool IncludeScaleInModel; /// Flag whether to include reference image in model. bool IncludeReferenceInModel; }; /// Spline warp active deformation model. typedef ActiveDeformationModel SplineActiveDeformationModel; //@} } // namespace cmtk #endif // #ifndef __cmtkActiveDeformationModel_h_included_ cmtk-3.3.1/libs/Base/cmtkActiveShapeModel.cxx000066400000000000000000000173671276303427400210650ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2013 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4840 $ // // $LastChangedDate: 2013-09-13 11:02:03 -0700 (Fri, 13 Sep 2013) $ // // $LastChangedBy: torstenrohlfing $ // */ #include #include #include #include #include #include namespace cmtk { /** \addtogroup Base */ //@{ ActiveShapeModel::ActiveShapeModel ( CoordinateVector::SmartPtr& mean, DirectionSet::SmartPtr& modes, CoordinateVector::SmartPtr& modeVariances ) : Mean( mean ), Modes( modes ), ModeVariances( modeVariances ) { NumberOfPoints = Mean->Dim; NumberOfModes = Modes->GetNumberOfDirections(); } float ActiveShapeModel::Construct ( const Types::Coordinate *const* trainingSet, const unsigned int numberOfSamples, const unsigned int numberOfPoints, const unsigned int numberOfModes ) { if ( numberOfSamples < numberOfModes ) { StdErr << "WARNING: number of modes of an ASM can be no higher than number of training samples.\n"; this->Allocate( numberOfPoints, numberOfSamples ); } else { this->Allocate( numberOfPoints, numberOfModes ); } // first, compute mean shape Types::Coordinate *meanPtr = Mean->Elements; for ( unsigned int point = 0; point < NumberOfPoints; ++point, ++meanPtr ) { Types::Coordinate mean = trainingSet[0][point]; for ( unsigned int sample = 1; sample < numberOfSamples; ++sample ) { mean += trainingSet[sample][point]; } (*meanPtr) = (mean / numberOfSamples ); } // now generate covariance matrix; actually, we're using a slightly // modified approach following Cootes' 1995 CVIU paper. This is much // more efficient when the number of samples is smaller than the // number of data dimensions. SymmetricMatrix cc( numberOfSamples ); for ( unsigned int sampleY = 0; sampleY < numberOfSamples; ++sampleY ) { for ( unsigned int sampleX = 0; sampleX <= sampleY; ++sampleX ) { Types::Coordinate ccXY = 0; const Types::Coordinate* meanPtr2 = Mean->Elements; for ( unsigned int point = 0; point < NumberOfPoints; ++point, ++meanPtr2 ) { ccXY += ( trainingSet[sampleX][point] - (*meanPtr2) ) * ( trainingSet[sampleY][point] - (*meanPtr2) ); } cc(sampleX,sampleY) = ccXY / numberOfSamples; } } // here comes the hard part: compute Eigenvectors of cc... // we do this in a separate routine, for clarity. const EigenSystemSymmetricMatrix eigensystem( cc ); // determine permutation that orders eigenvectors by descending eigenvalues const std::vector eigenvalues = eigensystem.GetEigenvalues(); std::vector permutation( numberOfSamples ); // initialize permutation array for ( unsigned int i = 0; i < numberOfSamples; ++i ) permutation[i] = i; // now do a simple bubble sort bool sorted = false; while ( ! sorted ) { sorted = true; for ( unsigned int i = 0; i < numberOfSamples-1; ++i ) if ( eigenvalues[permutation[i]] < eigenvalues[permutation[i+1]] ) { std::swap( permutation[i], permutation[i+1] ); sorted = false; } } // now, we need to convert the eigenvectors of the simplified matrix // back to those of the actual covariance matrix. Again, this follows // Cootes et al., CVIU 1995 for ( unsigned int mode = 0; mode < NumberOfModes; ++mode ) { ModeVariances->Elements[mode] = eigenvalues[permutation[mode]]; Types::Coordinate* modePtr = (*Modes)[mode]->Elements; for ( unsigned int point = 0; point < NumberOfPoints; ++point, ++modePtr ) { unsigned int fromMode = permutation[mode]; Types::Coordinate meanValue = Mean->Elements[point]; *modePtr = 0; for ( unsigned int sample = 0; sample < numberOfSamples; ++sample ) *modePtr += (eigensystem.EigenvectorElement(sample,fromMode) * (trainingSet[sample][point] - meanValue) ); } // finally, normalize mode vectors... if Geremy is right ;) (*(*Modes)[mode]) *= (sqrt( eigenvalues[permutation[mode]] ) / (*Modes)[mode]->EuclidNorm()); } return 0; } Types::Coordinate* ActiveShapeModel::Generate ( Types::Coordinate *const instance, const Types::Coordinate* modeWeights ) const { Types::Coordinate* target = instance; if ( !target ) target = Memory::ArrayC::Allocate( NumberOfPoints ); memcpy( target, Mean->Elements, sizeof( *target ) * NumberOfPoints ); if ( modeWeights ) { for ( unsigned int mode = 0; mode < NumberOfModes; ++mode ) { Types::Coordinate modeWeight = modeWeights[mode]; Types::Coordinate* targetPtr = target; const Types::Coordinate* modePtr = (*Modes)[mode]->Elements; for ( unsigned int point = 0; point < NumberOfPoints; ++point, ++targetPtr, ++modePtr ) (*targetPtr) += ( modeWeight * (*modePtr) ); } } return target; } float ActiveShapeModel::Decompose ( const CoordinateVector* input, Types::Coordinate *const weights ) const { std::vector w( this->NumberOfModes ); CoordinateVector deviation( *input ); deviation -= *(this->Mean); #define RETURN_PDF #ifdef RETURN_PDF float pdf = 1.0; for ( size_t mode = 0; mode < this->NumberOfModes; ++mode ) { const CoordinateVector* thisMode = (*this->Modes)[mode]; // since Modes are orthogonal basis, we can decompose using scalar product w[mode] = (deviation * *thisMode) / thisMode->EuclidNorm(); const Types::Coordinate variance = (*(this->ModeVariances))[mode]; pdf *= static_cast( exp( -(w[mode]*w[mode]) / (2.0 * variance) ) / sqrt( 2.0 * M_PI * variance) ); } #else float distance = 0.0; for ( size_t mode = 0; mode < this->NumberOfModes; ++mode ) { const CoordinateVector* thisMode = (*this->Modes)[mode]; // since Modes are orthogonal basis, we can decompose using scalar product w[mode] = (deviation * *thisMode) / thisMode->EuclidNorm(); const Types::Coordinate variance = (*(this->ModeVariances))[mode]; distance += w[mode] * w[mode] / variance; } distance = sqrt( distance ); #endif if ( weights ) memcpy( weights, &w[0], this->NumberOfModes * sizeof( *weights ) ); #ifdef RETURN_PDF return pdf; #else return distance; #endif } void ActiveShapeModel::Allocate ( const unsigned int numberOfPoints, const unsigned int numberOfModes ) { NumberOfModes = numberOfModes; NumberOfPoints = numberOfPoints; Modes = DirectionSet::SmartPtr( new DirectionSet( NumberOfPoints ) ); for ( unsigned int mode = 0; mode < NumberOfModes; ++mode ) Modes->push_back( CoordinateVector::SmartPtr( new CoordinateVector( NumberOfPoints ) ) ); ModeVariances = CoordinateVector::SmartPtr( new CoordinateVector( NumberOfModes ) ); Mean = CoordinateVector::SmartPtr( new CoordinateVector( NumberOfPoints ) ); } } // namespace cmtk cmtk-3.3.1/libs/Base/cmtkActiveShapeModel.h000066400000000000000000000122231276303427400204740ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2010, 2013 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4877 $ // // $LastChangedDate: 2013-09-24 13:25:18 -0700 (Tue, 24 Sep 2013) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkActiveShapeModel_h_included_ #define __cmtkActiveShapeModel_h_included_ #include #include #include #include namespace cmtk { /** \addtogroup Base */ //@{ /// Class for a three-dimensional active shape model. class ActiveShapeModel { public: /// Smart pointer to active shape model. typedef SmartPointer SmartPtr; /// Number of points in this model. unsigned int NumberOfPoints; /// Get number of points. unsigned int GetNumberOfPoints() const { return NumberOfPoints; } /// Point positions of the mean shape. CoordinateVector::SmartPtr Mean; /// Number of modes of variation in this model. unsigned int NumberOfModes; /// Get number of modes. unsigned int GetNumberOfModes() const { return NumberOfModes; } /// Delta vectors for the modes of variation. DirectionSet::SmartPtr Modes; /// Eigenvalue (= variance) associated with each mode CoordinateVector::SmartPtr ModeVariances; /// Default constructor. ActiveShapeModel() : NumberOfPoints( 0 ), Mean( NULL ), NumberOfModes( 0 ), Modes( NULL ) {} /// Construct using given mean and modes. ActiveShapeModel( CoordinateVector::SmartPtr& mean, DirectionSet::SmartPtr& modes, CoordinateVector::SmartPtr& modeVariances ); /** Generative constructor. * For a description of the parameters, see the Construct() member function. *\see ActiveShapeModel::Construct */ ActiveShapeModel( const Types::Coordinate *const* trainingSet, const unsigned int numberOfSamples, const unsigned int numberOfPoints, const unsigned int numberOfModes ) : NumberOfPoints( 0 ), Mean( NULL ), NumberOfModes( 0 ), Modes( NULL ) { this->Construct( trainingSet, numberOfSamples, numberOfPoints, numberOfModes ); } /** Construct model. *\param trainingSet The training set. This is a an array with size * [numberOfSamples]. Each entry is a pointer to an array of size * [numberOfPoints] Types::Coordinate values. Each of these latter arrays is a * sample vector from the training set, for example a sequential * concatenation of 2-D or 3-D point coordinates. The order of these values * within the vectors is irrelevant, as long as the order is the same in all * vectors. This order also defines the order of parameters in the generated * model. *\param numberOfSamples The number of samples in the training set. This is * the size of the pointer array given as parameter "trainingSet". *\param numberOfPoints This is the number of values in each array pointed * to by a pointer in the "trainingSet" array. *\param numberOfModes Number of modes in the generated model. This can be * at most as many as "numberOfSamples", the number of samples in the * training set. If the value of this parameter is to large, it will * automatically be reduced to equal numberOfSamples. *\return Percentage of the total variance of the training set that is * explained by the generated model. */ float Construct( const Types::Coordinate *const* trainingSet, const unsigned int numberOfSamples, const unsigned int numberOfPoints, const unsigned int numberOfModes ); /** Generate a model instance. */ Types::Coordinate* Generate( Types::Coordinate *const instance, const Types::Coordinate* modeWeights ) const; /** Decompose a vector into mean and modes of this model. *\param input Input vector. *\param weights Weights of the modes that make up the given input vector. * This parameter is optional. If not given, no weights will be returned. *\return The value of the multivariate Gaussian PDF represented by this * model atr the location of the input vector. */ float Decompose( const CoordinateVector* input, Types::Coordinate *const weights = NULL ) const; protected: /** Allocate data structures. * If this instance already has data structures allocated, these are * deallocated first. */ void Allocate( const unsigned int numberOfPoints, const unsigned int numberOfModes ); }; //@} } // namespace cmtk #endif // #ifndef __cmtkActiveShapeModel_h_included_ cmtk-3.3.1/libs/Base/cmtkAffineXform.cxx000066400000000000000000000317451276303427400201100ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2014 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 5312 $ // // $LastChangedDate: 2014-04-11 10:16:45 -0700 (Fri, 11 Apr 2014) $ // // $LastChangedBy: torstenrohlfing $ // */ #include #include #include #include #include #include namespace cmtk { /** \addtogroup Base */ //@{ void AffineXform::MakeIdentityXform () { this->m_ParameterVector->Clear(); Types::Coordinate* scales = this->RetScales(); if ( ! m_LogScaleFactors ) scales[0] = scales[1] = scales[2] = 1; try { this->ComposeMatrix(); } catch ( const Self::MatrixType::SingularMatrixException& ) { StdErr << "ERROR: identify matrix construction created singular matrix - this should not happen! Aborting...\n"; throw ExitException( 1 ); } } AffineXform::AffineXform ( const AffineXform& other ) : Xform( other ), m_LogScaleFactors( false ) { this->AllocateParameterVector( TotalNumberOfParameters ); (*this->m_ParameterVector) = (*other.m_ParameterVector); this->m_LogScaleFactors = other.m_LogScaleFactors; this->NumberDOFs = other.NumberDOFs; try { this->ComposeMatrix(); } catch ( const Self::MatrixType::SingularMatrixException& ) { StdErr << "ERROR: construction from existing AffineXform object created singular matrix - this should not happen! Aborting...\n"; throw ExitException( 1 ); } } AffineXform::AffineXform ( const Types::Coordinate matrix[4][4], const Types::Coordinate* center ) : Matrix( &matrix[0][0] ), m_LogScaleFactors( false ), InverseXform( NULL ) { this->AllocateParameterVector( TotalNumberOfParameters ); this->NumberDOFs = this->DefaultNumberOfDOFs(); if ( center ) memcpy( this->RetCenter(), center, 3 * sizeof( Types::Coordinate ) ); else memset( this->RetCenter(), 0, 3 * sizeof( Types::Coordinate ) ); this->DecomposeMatrix(); } AffineXform::AffineXform ( const MatrixType& matrix, const Types::Coordinate* center ) : Matrix( matrix ), m_LogScaleFactors( false ), InverseXform( NULL ) { this->AllocateParameterVector( TotalNumberOfParameters ); this->NumberDOFs = this->DefaultNumberOfDOFs(); if ( center ) memcpy( this->RetCenter(), center, 3 * sizeof( Types::Coordinate ) ); else memset( this->RetCenter(), 0, 3 * sizeof( Types::Coordinate ) ); this->DecomposeMatrix(); } AffineXform& AffineXform::operator=( const AffineXform& other ) { (*this->m_ParameterVector) = (*other.m_ParameterVector); this->NumberDOFs = other.NumberDOFs; this->m_LogScaleFactors = other.m_LogScaleFactors; try { this->ComposeMatrix(); } catch ( const Self::MatrixType::SingularMatrixException& ) { StdErr << "ERROR: assignment from existing AffineXform object created singular matrix - this should not happen! Aborting...\n"; throw ExitException( 1 ); } return *this; } void AffineXform::SetNumberDOFs ( const int numberDOFs ) { this->NumberDOFs = numberDOFs; if ( this->NumberDOFs == 7 ) { this->m_Parameters[8] = (this->m_Parameters[7] = this->m_Parameters[6]); this->ComposeMatrix(); } } void AffineXform::SetUseLogScaleFactors( const bool logScaleFactors ) { if ( logScaleFactors != this->m_LogScaleFactors ) { if ( logScaleFactors ) { for ( int i = 6; i < 9; ++i ) this->m_Parameters[i] = log( this->m_Parameters[i] ); } else { for ( int i = 6; i < 9; ++i ) this->m_Parameters[i] = exp( this->m_Parameters[i] ); } this->m_LogScaleFactors = logScaleFactors; } } void AffineXform::ComposeMatrix () { // For 7 parameter form (rigid plus global scaling) be sure to use equal // scalings for all coordinates. if ( this->NumberDOFs == 7 ) this->m_Parameters[8] = (this->m_Parameters[7] = this->m_Parameters[6]); // Now build matrix. this->Matrix.Compose( this->m_Parameters, this->m_LogScaleFactors ); try { this->UpdateInverse(); } catch ( const Self::MatrixType::SingularMatrixException& ) { StdErr << "ERROR: AffineXform::ComposeMatrix created singular matrix\n"; throw ExitException( 1 ); } } bool AffineXform::DecomposeMatrix () { return this->Matrix.Decompose( this->m_Parameters, this->RetCenter(), this->m_LogScaleFactors ); } AffineXform::SpaceVectorType AffineXform::RotateScaleShear ( const Self::SpaceVectorType& v ) const { Self::SpaceVectorType Mv; for ( size_t i = 0; i<3; ++i ) { Mv[i] = v[0] * Matrix[0][i] + v[1] * Matrix[1][i] + v[2] * Matrix[2][i]; } return Mv; } AffineXform* AffineXform::MakeInverse () const { Self* inverseXform = new AffineXform(); inverseXform->m_LogScaleFactors = this->m_LogScaleFactors; inverseXform->SetNumberDOFs( this->NumberDOFs ); inverseXform->Matrix = this->Matrix.GetInverse(); inverseXform->DecomposeMatrix(); const Self::SpaceVectorType newCenter = Self::SpaceVectorType::FromPointer( this->RetCenter() ) * this->Matrix; inverseXform->ChangeCenter( newCenter ); if ( this->NumberDOFs == 7 ) { inverseXform->m_Parameters[8] = (inverseXform->m_Parameters[7] = inverseXform->m_Parameters[6]); inverseXform->Matrix.Compose( inverseXform->m_Parameters, this->m_LogScaleFactors ); } inverseXform->CopyMetaInfo( *this, META_SPACE ); inverseXform->CopyMetaInfo( *this, META_XFORM_FIXED_IMAGE_PATH ); inverseXform->CopyMetaInfo( *this, META_XFORM_MOVING_IMAGE_PATH ); return inverseXform; } void AffineXform::ChangeCenter ( const Self::SpaceVectorType& newCenter ) { Types::Coordinate *const xlate = this->RetXlate(); Types::Coordinate *const center = this->RetCenter(); Self::SpaceVectorType deltaCenter = newCenter - Self::SpaceVectorType::FromPointer( center ); for ( size_t i = 0; i<3; ++i ) xlate[i] -= deltaCenter[i]; deltaCenter = this->RotateScaleShear( deltaCenter ); for ( size_t i = 0; i<3; ++i ) { xlate[i] += deltaCenter[i]; center[i] = newCenter[i]; } } void AffineXform::SetMatrix( const MatrixType& matrix ) { this->Matrix = matrix; this->DecomposeMatrix(); this->UpdateInverse(); } void AffineXform::SetParamVector ( CoordinateVector& v ) { Superclass::SetParamVector( v ); this->CanonicalRotationRange(); this->ComposeMatrix(); v = (*this->m_ParameterVector); } void AffineXform::SetParamVector ( const CoordinateVector& v ) { Superclass::SetParamVector( v ); this->CanonicalRotationRange(); this->ComposeMatrix(); } void AffineXform::SetParameter ( const size_t idx, const Types::Coordinate p ) { Superclass::SetParameter( idx, p ); this->CanonicalRotationRange(); this->ComposeMatrix(); } std::set AffineXform::GetSupportedDOFs() { const short supportedDOFs[] = { 0, 3, 6, 7, 9, 12, 3003, 3033, 3303, -1 }; return std::set( &supportedDOFs[0], &supportedDOFs[9] ); } Types::Coordinate AffineXform::GetParamStep ( const size_t idx, const Self::SpaceVectorType& volSize, const Types::Coordinate mmStep ) const { if ( (int)idx >= this->NumberDOFs ) return 0.0; switch ( idx ) { case 0: case 1: case 2: return mmStep; case 3: if ( this->NumberDOFs == 3003 || this->NumberDOFs == 3033 ) { // special case: 3 DOFs translation plus 3 DOFs shear, but no rotation return 0; } else { return mmStep * 180 / (M_PI * sqrt( MathUtil::Square( volSize[1] ) + MathUtil::Square( volSize[2] ) ) ); } case 4: if ( this->NumberDOFs == 3003 || this->NumberDOFs == 3033 ) { // special case: 3 DOFs translation plus 3 DOFs shear, but no rotation return 0; } else { return mmStep * 180 / (M_PI * sqrt( MathUtil::Square( volSize[0] ) + MathUtil::Square( volSize[2] ) ) ); } case 5: if ( this->NumberDOFs == 3003 || this->NumberDOFs == 3033 ) { // special case: 3 DOFs translation plus 3 DOFs shear, but no rotation return 0; } else { return mmStep * 180 / (M_PI * sqrt( MathUtil::Square( volSize[0] ) + MathUtil::Square( volSize[1] ) ) ); } case 6: case 7: case 8: if ( this->NumberDOFs == 3303 || this->NumberDOFs == 3003 ) { // special case: 6 DOFs rigid plus 3 DOFs shear, but no scale return 0; } else { if ( this->m_LogScaleFactors ) return log( 1 + 0.5 * mmStep / volSize.MaxValue() ); else return 0.5 * mmStep / volSize.MaxValue(); } case 9: case 10: case 11: return 0.5 * mmStep / volSize.MaxValue(); } return mmStep; } void AffineXform::Concat( const Self& other ) { this->Matrix *= other.Matrix; try { this->DecomposeMatrix(); } catch ( const Self::MatrixType::SingularMatrixException& ) { StdErr << "ERROR: singular matrix in cmtk::AffineXform::Concat; this should not happen.\n"; throw ExitException( 1 ); } } void AffineXform::Insert( const Self& other ) { Self::MatrixType composed( other.Matrix ); composed *= this->Matrix; this->Matrix = composed; try { this->DecomposeMatrix(); } catch ( const Self::MatrixType::SingularMatrixException& ) { StdErr << "ERROR: singular matrix in cmtk::AffineXform::Insert; this should not happen.\n"; throw ExitException( 1 ); } } void AffineXform::RotateWXYZ ( const Units::Radians angle, const Self::SpaceVectorType& direction, const Types::Coordinate* center, Self::MatrixType *const accumulate ) { Self::SpaceVectorType unit( direction ); Self::SpaceVectorType center3D; if ( center ) center3D = Self::SpaceVectorType::FromPointer( center ); else center3D = Self::SpaceVectorType::FromPointer( this->RetCenter() ); if ( accumulate ) { unit += center3D; unit *= *accumulate; center3D *= *accumulate; unit -= center3D; } // translation into rotation center Self::MatrixType xlate; for ( int dim = 0; dim < 3; ++dim ) xlate[3][dim] = -center3D[dim]; if ( accumulate ) { *accumulate *= xlate; } this->Matrix *= xlate; double x = unit[0]; double y = unit[1]; double z = unit[2]; // make a normalized quaternion const double w = MathUtil::Cos(0.5*angle); const double f = MathUtil::Sin(0.5*angle)/sqrt(x*x+y*y+z*z); x *= f; y *= f; z *= f; // convert the quaternion to a matrix Self::MatrixType matrix; const double ww = w*w; const double wx = w*x; const double wy = w*y; const double wz = w*z; const double xx = x*x; const double yy = y*y; const double zz = z*z; const double xy = x*y; const double xz = x*z; const double yz = y*z; const double s = ww - xx - yy - zz; matrix[0][0] = xx*2 + s; matrix[1][0] = (xy + wz)*2; matrix[2][0] = (xz - wy)*2; matrix[0][1] = (xy - wz)*2; matrix[1][1] = yy*2 + s; matrix[2][1] = (yz + wx)*2; matrix[0][2] = (xz + wy)*2; matrix[1][2] = (yz - wx)*2; matrix[2][2] = zz*2 + s; this->Matrix *= matrix; xlate = xlate.GetInverse(); this->Matrix *= xlate; try { this->DecomposeMatrix(); } catch ( const Self::MatrixType::SingularMatrixException& ) { StdErr << "ERROR: singular matrix in cmtk::AffineXform::WXYZ; this should not happen.\n"; throw ExitException( 1 ); } if ( accumulate ) { *accumulate *= matrix; *accumulate *= xlate; } } AffineXform::SmartPtr& AffineXform::GetInverse() { if ( !InverseXform ) { InverseXform = AffineXform::SmartPtr( this->MakeInverse() ); } else { this->UpdateInverse(); } return this->InverseXform; } const AffineXform::SmartPtr& AffineXform::GetInverse() const { if ( !InverseXform ) { InverseXform = AffineXform::SmartPtr( this->MakeInverse() ); } else { this->UpdateInverse(); } return this->InverseXform; } void AffineXform::UpdateInverse() const { if ( InverseXform ) { InverseXform->NumberDOFs = this->NumberDOFs; InverseXform->m_LogScaleFactors = this->m_LogScaleFactors; InverseXform->Matrix = this->Matrix.GetInverse(); InverseXform->DecomposeMatrix(); } } void AffineXform::CanonicalRotationRange() { for ( int rotIdx = 0; rotIdx<3; ++rotIdx ) { while ( this->m_Parameters[3+rotIdx] > 180 ) this->m_Parameters[3+rotIdx] -= 360; while ( this->m_Parameters[3+rotIdx] < -180 ) this->m_Parameters[3+rotIdx] += 360; } } } // namespace cmtk-3.3.1/libs/Base/cmtkAffineXform.h000066400000000000000000000437631276303427400175400ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2012, 2014 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 5312 $ // // $LastChangedDate: 2014-04-11 10:16:45 -0700 (Fri, 11 Apr 2014) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkAffineXform_h_included_ #define __cmtkAffineXform_h_included_ #include #include #include #include #include #include #include #include namespace cmtk { /** \addtogroup Base */ //@{ /** 3D affine transformation. * This transformation class allows translations, rotations, componentwise * scalings, and shears. Transformation is done by vector-matrix-multiplication * with a homogeneous 4x4 matrix. The transformation can be defined by either * the matrix itself or a parameter vector. Both representations are * permanently accessible and held mutually up-to-date whenever one of them * changes. *\author $Author: torstenrohlfing $ */ class AffineXform : /// Inherit virtual interface from generic transformation. public Xform { public: /// This class type. typedef AffineXform Self; /// Superclass type. typedef Xform Superclass; /// Smart pointer to AffineXform. typedef SmartPointer SmartPtr; /// Smart pointer to const AffineXform. typedef SmartConstPointer SmartConstPtr; /// Transformation matrix.type. typedef Matrix4x4 MatrixType; /// Create identity transformation. void MakeIdentityXform (); /** Homogeneous transformation matrix. * Vectors are transformed by right-multiplication with this matrix, i.e. * v being a vector, the transformed vector is (v Matrix). */ Self::MatrixType Matrix; /** Total number of parameters. * The values stored in the parameter vector are: * Three translations (x, y, z), three rotation * angles (around x-, y-, and z-axis) in degrees, three scaling factors * (x-, y-, and z-direction), and three shears. The last three values * define the center of rotation, scaling, and shearing. */ static const size_t TotalNumberOfParameters = 15; /** Apply rotation, scaling, and shearing but no translation to vector. * This function only used the upper left 3x3 sub-matrix of Matrix to * transform a given vector. This is needed for rotation-center changes. *\note It is safe to use the same address for source and destination of this * operation. *\return The rotated, scaled, and sheared vector. *\param v The vector to be rotated, scaled, and sheared. */ Self::SpaceVectorType RotateScaleShear ( const Self::SpaceVectorType& v ) const; /** Create identity transformation. */ AffineXform () : m_LogScaleFactors( false ) { this->AllocateParameterVector( TotalNumberOfParameters ); this->NumberDOFs = this->DefaultNumberOfDOFs(); this->MakeIdentityXform(); } /** Create transformation from parameter vector. *\see Parameters */ AffineXform ( const CoordinateVector& v /*!< The parameter vector defining the desired transformation. Refer to 'Parameters' for a detailed description. */, const bool logScaleFactors = false /*!< Flag for using log scale factors instead of plain scale factors.*/ ) : m_LogScaleFactors( logScaleFactors ) { this->AllocateParameterVector( TotalNumberOfParameters ); this->NumberDOFs = this->DefaultNumberOfDOFs(); this->SetParamVector( v ); } /** Create transformation from raw parameter array. *\see Parameters */ AffineXform ( const Types::Coordinate v[15] /*!< The parameter vector defining the desired transformation. Refer to 'Parameters' for a detailed description. */, const bool logScaleFactors = false /*!< Flag for using log scale factors instead of plain scale factors.*/ ) : m_LogScaleFactors( logScaleFactors ) { this->AllocateParameterVector( TotalNumberOfParameters ); this->NumberDOFs = this->DefaultNumberOfDOFs(); memcpy( this->m_Parameters, v, 15 * sizeof(Types::Coordinate) ); this->ComposeMatrix(); this->CanonicalRotationRange(); } /** Create transformation from transformation matrix. *\param matrix The homogeneous 4x4 affine transformation matrix. *\param center If non-NULL, this parameter points to a three-coordinate * vector defining the rotation center of the constructed transformation. * This does only effect the computation of the translation vector when * decomposing the given matrix into its parameter representation. */ AffineXform ( const Types::Coordinate matrix[4][4], const Types::Coordinate* center = NULL ); /** Create transformation from transformation matrix. *\param matrix The homogeneous 4x4 affine transformation matrix. *\param center If non-NULL, this parameter points to a three-coordinate * vector defining the rotation center of the constructed transformation. * This does only effect the computation of the translation vector when * decomposing the given matrix into its parameter representation. */ AffineXform ( const MatrixType& matrix, const Types::Coordinate* center = NULL ); /** Copy transform by reference. *\todo This is calling this->ComposeMatrix(), which can throw an exception and is probably not necessary. */ AffineXform ( const AffineXform& other ); /** Virtual destructor. * Frees the linked inverse transformation if one exists. */ virtual ~AffineXform() { InverseXform = Self::SmartPtr( NULL ); } /// Clone and return smart pointer. Self::SmartPtr Clone () const { return Self::SmartPtr( this->CloneVirtual() ); } /// Clone inverse of this transformation. virtual Self* MakeInverse () const; /// Get linked inverse of this transformation. Self::SmartPtr& GetInverse(); /// Get linked inverse of this transformation. const Self::SmartPtr& GetInverse() const; /// Get global scaling factor. virtual Types::Coordinate GetGlobalScaling() const { if ( this->m_LogScaleFactors ) { return exp( this->m_Parameters[6] + this->m_Parameters[7] + this->m_Parameters[8] ); } else { return this->m_Parameters[6] * this->m_Parameters[7] * this->m_Parameters[8]; } } /** Get local Jacobian matrix. * For affine transformations, the Jacobian is the top-left 3x3 submatrix of the * homogenous 4x4 transformation matrix. */ virtual const CoordinateMatrix3x3 GetJacobian( const Self::SpaceVectorType& ) const { return this->Matrix.GetTopLeft3x3(); } /** Compute Jacobian determinant at a certain location. * For an affine transformation, the Jacobian determinant is the product * of the anisotropic scale factors at any location. */ virtual Types::Coordinate GetJacobianDeterminant ( const Self::SpaceVectorType& ) const { return this->GetGlobalScaling(); } /// Concatenate this transformation with another. void Concat( const AffineXform& other ); /// Insert another transformation before this one. void Insert( const AffineXform& other ); /** Rotate around axis. *\param angle Rotation angle. *\param direction Direction vector of the rotation axis. *\param origin If this parameter is given, it defines the 3D coordinates of * a point on the rotation axis. If this parameter is not given, the * rotation axis goes through the rotation center of this transformation. *\param accumulate This parameter, if given, points to a 4x4 transformation * matrix that can accumulate successive rotations. If given, two things * happen: a) the current rotation axis is rotated according to the * accumulated transformation, and b) the new rotation is concatenated onto * the accumulated matrix by multiplication. */ void RotateWXYZ( const Units::Radians angle, const Self::SpaceVectorType& direction, const Types::Coordinate* origin = NULL, Self::MatrixType *const accumulate = NULL ); /// Change transformation coordinate system. void ChangeCoordinateSystem ( const Self::SpaceVectorType& newX, const Self::SpaceVectorType& newY ) { this->Matrix.ChangeCoordinateSystem( newX, newY ); this->DecomposeMatrix(); } /// Apply transformation to vector. virtual Self::SpaceVectorType Apply ( const Self::SpaceVectorType& vec ) const { return vec * this->Matrix; } /** Apply inverse of this transformation to vector. */ virtual bool ApplyInverse ( const Self::SpaceVectorType& v, Self::SpaceVectorType& u, const Types::Coordinate = 0.01 ) const { u = this->GetInverse()->Apply( v ); return true; } /**\name Read-only parameter retrieval. */ //@{ /// Return pointer to translation parameters. const Types::Coordinate* RetXlate () const { return this->m_Parameters; } /// Return pointer to rotation angles. const Types::Coordinate* RetAngles () const { return this->m_Parameters+3; } /// Return pointer to scaling factors. const Types::Coordinate* RetScales () const { return this->m_Parameters+6; } /// Return pointer to shear coefficients. const Types::Coordinate* RetShears () const { return this->m_Parameters+9; } /// Return pointer to center of rotation, scaling, and shearing. const Types::Coordinate* RetCenter () const { return this->m_Parameters+12; } //@} /**\name Modifyable parameter retrieval. */ //@{ /// Return pointer to translation parameters. Types::Coordinate* RetXlate () { return this->m_Parameters; } /// Return pointer to rotation angles. Types::Coordinate* RetAngles () { return this->m_Parameters+3; } /// Return pointer to scaling factors. Types::Coordinate* RetScales () { return this->m_Parameters+6; } /// Return pointer to shear coefficients. Types::Coordinate* RetShears () { return this->m_Parameters+9; } /// Return pointer to center of rotation, scaling, and shearing. Types::Coordinate* RetCenter () { return this->m_Parameters+12; } //@} /**\name Direct parameter modifications. */ //@{ /// Set transformation's translation vector. void SetTranslation( const Self::SpaceVectorType& delta ) { for ( int dim = 0; dim < 3; ++dim ) this->m_Parameters[dim] = delta[dim]; this->ComposeMatrix(); } /// Set transformation's translation vector. void SetXlate ( const Types::Coordinate* xlate ) { if ( this->RetXlate() != xlate ) memcpy( this->RetXlate(), xlate, 3 * sizeof(Types::Coordinate) ); this->ComposeMatrix(); } /// Set transformation's translation vector. void SetXlate ( const Types::Coordinate dx, const Types::Coordinate dy, const Types::Coordinate dz ) { this->m_Parameters[0] = dx; this->m_Parameters[1] = dy; this->m_Parameters[2] = dz; this->ComposeMatrix(); } /// Add to transformation's translation vector. void Translate( const Types::Coordinate dx, const Types::Coordinate dy, const Types::Coordinate dz ) { this->m_Parameters[0] += dx; this->m_Parameters[1] += dy; this->m_Parameters[2] += dz; this->ComposeMatrix(); } /// Add to transformation's translation vector. void Translate( const Self::SpaceVectorType& delta ) { for ( int dim = 0; dim < 3; ++dim ) this->m_Parameters[dim] += delta[dim]; this->ComposeMatrix(); } /// Set transformation's rotation angles. void SetAngles ( const Types::Coordinate* angles ) { if ( angles != this->RetAngles() ) memcpy( this->RetAngles(), angles, 3 * sizeof(Types::Coordinate) ); this->ComposeMatrix(); } /// Get scale factors with implicit conversion of log scales. FixedVector<3,Types::Coordinate> GetScales() const { FixedVector<3,Types::Coordinate> scales; if ( this->m_LogScaleFactors ) { for ( size_t i = 0; i < 3; ++i ) { scales[i] = exp( this->m_Parameters[6+i] ); } } else { for ( size_t i = 0; i < 3; ++i ) { scales[i] = this->m_Parameters[6+i]; } } return scales; } /// Set transformation's scaling factors. void SetScales ( const Types::Coordinate* scales ) { if ( this->RetScales() != scales ) memcpy( this->RetScales(), scales, 3 * sizeof(Types::Coordinate) ); this->ComposeMatrix(); } /// Set transformation's scaling factors. void SetScales ( const Types::Coordinate sx, const Types::Coordinate sy, const Types::Coordinate sz ) { this->m_Parameters[6] = sx; this->m_Parameters[7] = sy; this->m_Parameters[8] = sz; } /// Set transformation's shears. void SetShears ( const Types::Coordinate* shears ) { if ( this->RetShears() != shears ) memcpy( this->RetShears(), shears, 3 * sizeof(Types::Coordinate) ); this->ComposeMatrix(); } /// Set transformation's rotation, scaling, and shearing center. void SetCenter( const Self::SpaceVectorType& center ) { for ( size_t dim = 0; dim < 3; ++dim ) this->m_Parameters[12+dim] = center[dim]; this->ComposeMatrix(); } /// Set transformation's rotation, scaling, and shearing center. void SetCenter ( const Types::Coordinate* center ) { if ( this->RetCenter() != center ) memcpy( RetCenter(), center, 3 * sizeof(Types::Coordinate) ); this->ComposeMatrix(); } //@} /**\name Matrix access. */ //@{ /// Return transformation matrix. const Types::Coordinate* RetMatrix () const { return &Matrix[0][0]; } /// Return transformation matrix. Types::Coordinate* RetMatrix () { return &Matrix[0][0]; } /// Set transformation matrix. void SetMatrix( const MatrixType& matrix ); //@} /** Create equivalent transformation with different rotational center. * In fact, this function only computes a new translation vector reflecting * the changed rotation center. The transformation matrix itself is not * changed. */ void ChangeCenter ( const Self::SpaceVectorType& center ); /// Get dimension of parameter vector. virtual size_t ParamVectorDim () const { return 15; } /** Get dimension of variable parameter vector. * The rotation center is not considered variable, therefore 6 is returned. */ virtual size_t VariableParamVectorDim () const { return std::min( 12, NumberDOFs ); } /// Set the number of degrees of freedom for this object. virtual void SetNumberDOFs ( const int numberDOFs = 12 ); /// Get the number of degrees of freedom for this object. virtual unsigned int GetNumberDOFs () const { return NumberDOFs; } /// Get a set of supported DOF values. static std::set GetSupportedDOFs(); /// Return flag for log scale factors. bool GetUseLogScaleFactors() const { return this->m_LogScaleFactors; } /// Switch between log and ordinary scale factors. void SetUseLogScaleFactors( const bool logScaleFactors ); /// Set Xform by parameter vector. virtual void SetParamVector ( CoordinateVector& v ); /** Set Xform by constant parameter vector. * Other than setting the transformation from a non-constant vector object, * this function does not update the given parameter vector to match the * internal representation of the igsAfffineXform object. It is therefore * not guaranteed that subsequent calls to GetParamVector() will yield the * same parameters. */ virtual void SetParamVector ( const CoordinateVector& v ); /// Set a single parameter value. virtual void SetParameter ( const size_t idx, const Types::Coordinate p ); /// Get parameter stepping. virtual Types::Coordinate GetParamStep( const size_t idx, const Self::SpaceVectorType& volSize, const Types::Coordinate step_mm = 1 ) const; /** Assignment operator. *\todo This is calling this->ComposeMatrix(), which can throw an exception and is probably not necessary. */ AffineXform& operator=( const AffineXform& other ); protected: /// Clone this object. virtual Self* CloneVirtual() const { return new AffineXform( *this ); } /** Compose this object's transformation matrix from parameter vector. */ void ComposeMatrix(); /** Decompose this object's transformation matrix into parameter vector. */ bool DecomposeMatrix(); /** Actual number of degrees of freedom. * This value should be one out of four choices: 6 (rigid transformation), * 7 (rigid with global scaling), 9 (rigid with componentwise scaling), or * 12 (full affine). The number of elements of the parameter vector does NOT * change, even in the 7 DOF case. Then, all scaling factors are simply kept * equal. */ int NumberDOFs; private: /// Flag for logarithmic vs. ordinary scale factors. bool m_LogScaleFactors; /// Return the default number of degrees of freedom. static int DefaultNumberOfDOFs() { return 12; } /// Link to an auto-updated inverse transformation. mutable Self::SmartPtr InverseXform; /// Update linked inverse transformation. void UpdateInverse() const; /** Set transformation matrix. * Other than the plain SetMatrix() function, this implementation DOES NOT * also update the associated inverse transformation. Therefore, it may * safely be called by all other SetXXX() functions in order to update * the inverse transformation without producing an infinite loop. */ void SetMatrixDirect ( const Types::Coordinate* matrix ) { Matrix.Set( matrix ); this->DecomposeMatrix(); } /// Correct rotation parameters to canonical range -180 to 180. void CanonicalRotationRange(); }; //@} } // namespace cmtk #endif // #define __cmtkAffineXform_h_included_ cmtk-3.3.1/libs/Base/cmtkAffineXformUniformVolume.cxx000066400000000000000000000046361276303427400226370ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2012 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4538 $ // // $LastChangedDate: 2012-10-02 15:16:13 -0700 (Tue, 02 Oct 2012) $ // // $LastChangedBy: torstenrohlfing $ // */ #include "cmtkAffineXformUniformVolume.h" cmtk::AffineXformUniformVolume::AffineXformUniformVolume( const UniformVolume& volume, const AffineXform& xform ) : m_VolumeAxesX( volume.m_Dims[0] ), m_VolumeAxesY( volume.m_Dims[1] ), m_VolumeAxesZ( volume.m_Dims[2] ) { // define volume corners const UniformVolume::CoordinateVectorType V = xform.Apply( FixedVectorStaticInitializer<3,Types::Coordinate>::Init(0,0,0) ); const UniformVolume::CoordinateVectorType dX = xform.Apply( FixedVectorStaticInitializer<3,Types::Coordinate>::Init(1,0,0) ) - V; const UniformVolume::CoordinateVectorType dY = xform.Apply( FixedVectorStaticInitializer<3,Types::Coordinate>::Init(0,1,0) ) - V; const UniformVolume::CoordinateVectorType dZ = xform.Apply( FixedVectorStaticInitializer<3,Types::Coordinate>::Init(0,0,1) ) - V; const Types::Coordinate deltaX = volume.m_Delta[0]; const Types::Coordinate deltaY = volume.m_Delta[1]; const Types::Coordinate deltaZ = volume.m_Delta[2]; for ( size_t idx = 0; idx < static_cast( volume.m_Dims[0] ); ++idx ) this->m_VolumeAxesX[idx] = deltaX*idx*dX; for ( size_t idx = 0; idx < static_cast( volume.m_Dims[1] ); ++idx ) this->m_VolumeAxesY[idx] = deltaY*idx*dY; for ( size_t idx = 0; idx < static_cast( volume.m_Dims[2] ); ++idx ) (this->m_VolumeAxesZ[idx] = deltaZ*idx*dZ) += V; } cmtk-3.3.1/libs/Base/cmtkAffineXformUniformVolume.h000066400000000000000000000057021276303427400222570ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2010 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 2453 $ // // $LastChangedDate: 2010-10-18 10:33:06 -0700 (Mon, 18 Oct 2010) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkAffineXformUniformVolume_h_included_ #define __cmtkAffineXformUniformVolume_h_included_ #include #include #include namespace cmtk { /** \addtogroup Base */ //@{ /** Pre-compute transformation for grid locations in a uniform volume. */ class AffineXformUniformVolume : /// Inherit from class to prevent copying. public XformUniformVolume { public: /// This class. typedef AffineXformUniformVolume Self; /// Parent class. typedef XformUniformVolume Superclass; /// Smart pointer to this class. typedef SmartPointer SmartPtr; /// Smart pointer-to-const to this class. typedef SmartConstPointer SmartConstPtr; /// Constructor. AffineXformUniformVolume( const UniformVolume& volume, const AffineXform& xform ); /// Virtual destructor. virtual ~AffineXformUniformVolume() {} /** Get transformed location of linked grid pixel. */ virtual void GetTransformedGrid( Vector3D& v, const int idxX, const int idxY, const int idxZ ) const { ( (v = this->m_VolumeAxesX[idxX]) += this->m_VolumeAxesY[idxY]) += this->m_VolumeAxesZ[idxZ]; } /** Get transformed locations of a series (scanline) of linked grid pixels. */ virtual void GetTransformedGridRow( Vector3D *const v, const size_t numPoints, const int idxX, const int idxY, const int idxZ ) const { Vector3D v0 = this->m_VolumeAxesY[idxY]; v0 += this->m_VolumeAxesZ[idxZ]; int idx = idxX; for ( size_t n=0; n < numPoints; ++n, ++idx ) { (v[n] = v0) += this->m_VolumeAxesX[idx]; } } private: /// Axes hash for the points of a registered Volume. std::vector m_VolumeAxesX; std::vector m_VolumeAxesY; std::vector m_VolumeAxesZ; }; //@} } // namespace cmtk #endif // #ifdef __cmtkAffineXformUniformVolume_h_included_ cmtk-3.3.1/libs/Base/cmtkAnatomicalOrientation.cxx000066400000000000000000000065651276303427400221720ustar00rootroot00000000000000/* // // Copyright 2016 Google, Inc. // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2010, 2013 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 5396 $ // // $LastChangedDate: 2016-01-13 21:24:32 -0800 (Wed, 13 Jan 2016) $ // // $LastChangedBy: torstenrohlfing $ // */ #include "cmtkAnatomicalOrientation.h" #include #include namespace cmtk { /** \addtogroup Base */ //@{ void AnatomicalOrientation ::GetOrientationFromDirections( char* orientation, const AffineXform::MatrixType& directions, const char* spaceAxes ) { const Types::Coordinate spacing[3] = { sqrt( directions[0][0]*directions[0][0] + directions[0][1]*directions[0][1] + directions[0][2]*directions[0][2] ), sqrt( directions[1][0]*directions[1][0] + directions[1][1]*directions[1][1] + directions[1][2]*directions[1][2] ), sqrt( directions[2][0]*directions[2][0] + directions[2][1]*directions[2][1] + directions[2][2]*directions[2][2] ) }; // keep track of which axes are already used in the direction code - need 4 entries to allow for access when axes are non-ortogonal bool axisUsed[4] = { false, false, false, true }; for ( int axis = 0; axis < 3; ++axis ) { // skip axes already used int maxDim = 0; while ( axisUsed[maxDim] ) ++maxDim; // get closest aligned of remaining axes Types::Coordinate max = fabs( directions[axis][0] / spacing[axis] ); for ( int dim = 1; dim < 3; ++dim ) { const Types::Coordinate positive = fabs( directions[axis][dim] / spacing[axis] ); if ( (positive > max) && !axisUsed[dim] ) { max = positive; maxDim = dim; } else { if ( positive == max ) { maxDim = 3; } } } if ( maxDim == 3 ) { StdErr << "WARNING: image seems to have an oblique orientation. This is not going to end well...\n"; } orientation[axis] = (directions[axis][maxDim] > 0) ? spaceAxes[maxDim] : Self::OppositeDirection( spaceAxes[maxDim] ); axisUsed[maxDim] = true; } orientation[3] = 0; } void AnatomicalOrientation ::GetImageToSpaceAxesPermutation( Types::GridIndexType (&imageToSpaceAxesPermutation)[3][3], const char* orientation, const char* spaceAxes ) { for ( int j = 0; j < 3; ++j ) { for ( int i = 0; i < 3; ++i ) { if ( orientation[j] == spaceAxes[i] ) imageToSpaceAxesPermutation[j][i] = 1; else if ( Self::OnSameAxis( orientation[j], spaceAxes[i] ) ) imageToSpaceAxesPermutation[j][i] = -1; else imageToSpaceAxesPermutation[j][i] = 0; } } } } // namespace cmtk cmtk-3.3.1/libs/Base/cmtkAnatomicalOrientation.h000066400000000000000000000145541276303427400216140ustar00rootroot00000000000000/* // // Copyright 2016 Google, Inc. // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2012 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 5396 $ // // $LastChangedDate: 2016-01-13 21:24:32 -0800 (Wed, 13 Jan 2016) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkAnatomicalOrientation_h_included_ #define __cmtkAnatomicalOrientation_h_included_ #include #include #include #include #include #include #include namespace cmtk { /** \addtogroup Base */ //@{ /// Class with helper functions for handling anatomical image orientation. class AnatomicalOrientation : public AnatomicalOrientationBase { public: /// This class. typedef AnatomicalOrientation Self; /// Parent class. typedef AnatomicalOrientationBase Superclass; /** Get closest anatomical orientation based on space dimensions and image direction vectors. *\param orientation The resulting orientation string (three characters plus terminating zero) will * be put into this string. *\param directions The image direction vectors. "directions[0]" is the 3-D vector of the in-plane x image * direction, "directions[1]" is in-plane y, and "directions[2]" is the vector from one plane origin to the * origin of the next plane. *\param spaceAxes Six-character string that defines the orientation of the underlying space. The first two characters * relate to the x coordinate, the second two to the y coordinate, and the final two to the z coordinate. */ static void GetOrientationFromDirections( char* orientation, const AffineXform::MatrixType& directions, const char* spaceAxes ); /// Get permutation table of coordinate axes from space axes and image orientation. static void GetImageToSpaceAxesPermutation( Types::GridIndexType (&imageToSpaceAxesPermutation)[3][3], const char* orientation, const char* spaceAxes ); /// Class for permutation matrix that can be applied to pixel indexes, as well as dimension, and pixel size arrays. class PermutationMatrix { public: /// This class. typedef PermutationMatrix Self; /// Smart pointer to this class. typedef SmartPointer SmartPtr; /// Constructor: determine axes permutation, flipping, and store local copy of reoriented dimensions array. PermutationMatrix( const FixedVector<3,Types::GridIndexType>& sourceDims, const std::string& curOrientation, const char newOrientation[3] ); /** Takes the dimensions of a volume (e.g. a grid, a voxel) * and returns the dimensions of that volume after the * reorientation described by this permutation matrix *\param source Original array *\return target A three-element array to contain the permuted results. */ template const FixedVector<3,T> GetPermutedArray( const FixedVector<3,T>& source ) const { FixedVector<3,T> target; for ( int i = 0; i < 3; i++ ) { target[i] = source[this->m_Axes[i]]; } return target; } /** Get proper permutation matrix */ AffineXform::MatrixType GetMatrix() const; /** Permute index-to-physical matrix */ AffineXform::MatrixType GetPermutedMatrix( const AffineXform::MatrixType& inMatrix ) const; /** Get new point index from old point index. *\param origPoint The input pixel index in the original image grid. *\param newPoint The output pixel index in the reoriented image grid. */ void GetReorientedIndex( const Types::GridIndexType* origPoint, Types::GridIndexType* newPoint ) const { for ( int i = 0; i < 3; ++i ) newPoint[i] = this->m_Multipliers[i] * origPoint[this->m_Axes[i]] + this->m_Offsets[i]; } /** Applies the permutation described by this matrix to a pixel index. *\param origPoint The input pixel index. *\return The offset of the corresponding pixel in the reoriented volume. */ size_t NewOffsetFromPoint( const Types::GridIndexType* origPoint ) const { return ( this->m_Multipliers[0] * origPoint[this->m_Axes[0]] + this->m_Offsets[0] ) + this->m_NewDims[0] * ( ( this->m_Multipliers[1] * origPoint[this->m_Axes[1]] + this->m_Offsets[1] ) + this->m_NewDims[1] * ( this->m_Multipliers[2] * origPoint[this->m_Axes[2]] + this->m_Offsets[2] ) ); } private: /** Input-to-output axis index assignments. * m_Axes[i] contains the index (0, 1, or 2) of the * axis of the input orientation which gets moved to * as the i'th axis of the output orientation. * (where i is 0, 1, or 2, corresponding to X, Y, * and Z, respectively) */ FixedVector<3,int> m_Axes; /** Multiplies (flip direction) table. * m_Multipliers[i] contains -1 if the axis at * m_Axes[i] is to be reversed from its direction * prior to re-orientation, and 1 otherwise */ FixedVector<3,Types::GridIndexType> m_Multipliers; /** Dimension of the reoriented * image in the direction of m_Axes[i] */ FixedVector<3,Types::GridIndexType> m_NewDims; /** m_Offsets[i] contains 0 if m_Multipliers[i] == 1, * and (m_NewDims[i] - 1) if m_Multipliers[i] == -1. * (Since in the latter case, we will be writing from * the highest-valued pixel in direction i and iterating * backwards.) */ FixedVector<3,int> m_Offsets; }; }; //@} } // namespace cmtk #endif // #ifndef __cmtkAnatomicalOrientation_h_included_ cmtk-3.3.1/libs/Base/cmtkAnatomicalOrientationBase.cxx000066400000000000000000000051371276303427400227570ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2010 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 2479 $ // // $LastChangedDate: 2010-10-20 15:35:37 -0700 (Wed, 20 Oct 2010) $ // // $LastChangedBy: torstenrohlfing $ // */ #include "cmtkAnatomicalOrientationBase.h" #include #include namespace cmtk { /** \addtogroup Base */ //@{ const char *const AnatomicalOrientationBase::ORIENTATION_STANDARD = "RAS"; const char *const AnatomicalOrientationBase::SPACE_CMTK = "RAS"; const char *const AnatomicalOrientationBase::SPACE_ITK = "LPS"; const char* AnatomicalOrientationBase ::GetClosestOrientation( const char* desiredOrientation, const char *const availableOrientations[] ) { const char* result = NULL; int minPenalty = 100; const char *const *next = availableOrientations; while ( *next ) { int penalty = 0; for ( int axis = 0; axis < 3; ++axis ) { if ( desiredOrientation[axis] != (*next)[axis] ) { if ( Self::OnSameAxis( desiredOrientation[axis], (*next)[axis] ) ) penalty += 1; else penalty += 4; } } if ( penalty < minPenalty ) { result = *next; minPenalty = penalty; } ++next; } return result; } bool AnatomicalOrientationBase::OnSameAxis( const char from, const char to ) { // Set up lists such that the direction corresponding to the // character at index "from-'A'" is the character that represents // the same axis in reverse direction. Lowercase characters are // for padding and orientation. assert( (from=='A') || (from=='P') || (from=='L') || (from=='R') || (from=='I') || (from=='S') ); assert( (to=='A') || (to=='P') || (to=='L') || (to=='R') || (to=='I') || (to=='S') ); return (Self::OppositeDirection( from ) == to); } } // namespace cmtk cmtk-3.3.1/libs/Base/cmtkAnatomicalOrientationBase.h000066400000000000000000000051331276303427400224000ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2010 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 2479 $ // // $LastChangedDate: 2010-10-20 15:35:37 -0700 (Wed, 20 Oct 2010) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkAnatomicalOrientationBase_h_included_ #define __cmtkAnatomicalOrientationBase_h_included_ #include namespace cmtk { /** \addtogroup Base */ //@{ /// Base class for handling anatomical image orientation. class AnatomicalOrientationBase { public: /// This class. typedef AnatomicalOrientationBase Self; /// Orientation of standardized reoriented image axes (LR/AP/IS). static const char *const ORIENTATION_STANDARD; /// Standard CMTK coordinate space (LR/PA/IS). static const char *const SPACE_CMTK; /// Standard ITK coordinate space (RL/AP/IS). static const char *const SPACE_ITK; /** Get closest orientation from a list. * This function is used to determine which orientation to bring an image into so it can be written to a file * format with limited orientation support (e.g., Analyze). */ static const char* GetClosestOrientation( const char* desiredOrientation, const char *const availableOrientations[] ); /** Return true if the direction corresponding to the * character 'from' is on the same axis as that corresponding * to 'to'. *\param from Either L, R, A, P, I, or S *\param to Either L, R, A, P, I, or S */ static bool OnSameAxis( const char from, const char to ); protected: /// Get inverse of axis orientation. static char OppositeDirection( const char direction ) { const char table[27] = "PbcdefghSjkRmnoAqLItuvwxyz"; return table[direction-'A']; } }; //@} } // namespace cmtk #endif // #ifndef __cmtkAnatomicalOrientationBase_h_included_ cmtk-3.3.1/libs/Base/cmtkAnatomicalOrientationPermutationMatrix.cxx000066400000000000000000000056521276303427400256030ustar00rootroot00000000000000/* // // Copyright 2016 Google, Inc. // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2010, 2013 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 5396 $ // // $LastChangedDate: 2016-01-13 21:24:32 -0800 (Wed, 13 Jan 2016) $ // // $LastChangedBy: torstenrohlfing $ // */ #include "cmtkAnatomicalOrientation.h" #include #include #include namespace cmtk { /** \addtogroup Base */ //@{ AnatomicalOrientation::PermutationMatrix::PermutationMatrix ( const FixedVector<3,Types::GridIndexType>& sourceDims, const std::string& curOrientation, const char newOrientation[3] ) { // Build a permutation matrix and store it in compressed form for ( int i = 0; i < 3; i++ ) { for ( int j = 0; j < 3; j++ ) { if ( newOrientation[i] == curOrientation[j] ) { this->m_Axes[i] = j; this->m_Multipliers[i] = 1; this->m_Offsets[i] = 0; break; } else if ( AnatomicalOrientation::OnSameAxis( newOrientation[i], curOrientation[j] ) ) { this->m_Axes[i] = j; this->m_Multipliers[i] = -1; this->m_Offsets[i] = sourceDims[j] - 1; break; } } } this->m_NewDims = this->GetPermutedArray( sourceDims ); } AffineXform::MatrixType AnatomicalOrientation::PermutationMatrix::GetMatrix() const { AffineXform::MatrixType permutation = AffineXform::MatrixType::Identity(); for ( int j = 0; j < 3; ++j ) { for ( int i = 0; i < 3; ++i ) { if ( i == this->m_Axes[j] ) permutation[i][j] = this->m_Multipliers[j]; else permutation[i][j] = 0; } permutation[3][j] = this->m_Offsets[j]; } try { return permutation.GetInverse(); } catch ( const AffineXform::MatrixType::SingularMatrixException& ) { StdErr << "ERROR: orientation permutation matrix connot be inverted.\n"; throw ExitException( 1 ); } } AffineXform::MatrixType AnatomicalOrientation::PermutationMatrix::GetPermutedMatrix( const AffineXform::MatrixType& inMatrix ) const { return this->GetMatrix() * inMatrix; } } // namespace cmtk cmtk-3.3.1/libs/Base/cmtkBitVector.cxx000066400000000000000000000056131276303427400176000ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2011 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 3517 $ // // $LastChangedDate: 2011-10-27 12:42:15 -0700 (Thu, 27 Oct 2011) $ // // $LastChangedBy: torstenrohlfing $ // */ #include "cmtkBitVector.h" #include #include namespace cmtk { /** \addtogroup Base */ //@{ BitVector::BitVector( const size_t size, const bool initial ) { this->m_Size = (size+7) / 8; // +7 to allocate an extra byte for 8n+1...8n+7 bits this->m_BitVector = Memory::ArrayC::Allocate( this->m_Size ); if ( initial ) this->Set(); else this->Reset(); } BitVector::BitVector( const size_t size, byte *const bitSet ) { this->m_Size = (size+7) / 8; // +7 to allocate an extra byte for 8n+1...8n+7 bits this->m_BitVector = bitSet; } BitVector::~BitVector() { Memory::ArrayC::Delete( this->m_BitVector ); } BitVector* BitVector::Clone() const { byte *newBitVector = Memory::ArrayC::Allocate( this->m_Size ); memcpy( newBitVector, this->m_BitVector, this->m_Size ); return new BitVector( 8*this->m_Size, newBitVector ); } void BitVector::Set() { memset( this->m_BitVector, 255, sizeof( *this->m_BitVector ) * this->m_Size ); } void BitVector::Set( const size_t pos, const bool val ) { if ( val ) { this->m_BitVector[pos/8] |= (1<<(pos%8)); } else { this->m_BitVector[pos/8] &= ~(1<<(pos%8)); } } void BitVector::Reset( const bool value ) { if ( value ) memset( this->m_BitVector, 255, sizeof( *this->m_BitVector ) * this->m_Size ); else memset( this->m_BitVector, 0, sizeof( *this->m_BitVector ) * this->m_Size ); } void BitVector::Reset( const size_t pos ) { this->m_BitVector[pos/8] &= ~(1<<(pos%8)); } void BitVector::Flip() { for ( size_t i=0; im_Size; ++i ) this->m_BitVector[i] = ~this->m_BitVector[i]; } void BitVector::Flip( const size_t pos ) { this->m_BitVector[pos/8] ^= (1<<(pos%8)); } bool BitVector::operator[]( const size_t pos ) const { return ( this->m_BitVector[pos/8] >> (pos%8) ) & 1; } } // namespace cmtk cmtk-3.3.1/libs/Base/cmtkBitVector.h000066400000000000000000000056421276303427400172270ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2011 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 2752 $ // // $LastChangedDate: 2011-01-17 11:33:31 -0800 (Mon, 17 Jan 2011) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkBitVector_h_included_ #define __cmtkBitVector_h_included_ #include #include #include namespace cmtk { /** \addtogroup Base */ //@{ /** Set of binary values. * This class provides functions similar to the STL's bitset class. However, * our class does not require the set size to be known at compile time. It * therefore allows creation of different size bitsets as they are needed by * the program. */ class BitVector { public: /// Smart pointer to BitVector. typedef SmartPointer SmartPtr; /** Constructor. *\param size Number of bits handled by this object. *\param initial Initial value for the bits in the array. */ BitVector( const size_t size, const bool initial = false ); /** Constructor. *\param size Number of bits handled by this object. *\param bitset Byte array that is used to initialize the array. */ BitVector( const size_t size, byte *const bitset ); /** Destructor. */ ~BitVector(); /** Create copy of this object. */ BitVector* Clone() const; /// Set all bits to 1. void Set(); /// Set one bit to a given value. void Set( const size_t pos, const bool val = true ); /// Set all bits to given flag (default: clear all). void Reset( const bool value = false ); /// Set one bit to 0. void Reset( const size_t pos ); /// Flip (invert) the whole bitset. void Flip(); /// Flip (invert) one bit. void Flip( const size_t pos ); /// Return a given bit. bool operator[]( const size_t pos ) const; /// Get pointer to bitset data. const byte* GetBitVector() const { return this->m_BitVector; } private: /// The bitset. byte *m_BitVector; /// The size of the allocated bitset in BYTES (!!). size_t m_Size; }; //@} } // namespace cmtk #endif // #ifndef __cmtkBitVector_h_included_ cmtk-3.3.1/libs/Base/cmtkCompatibilityMatrix4x4.cxx000066400000000000000000000072031276303427400222320ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2013 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 5044 $ // // $LastChangedDate: 2013-11-27 13:19:12 -0800 (Wed, 27 Nov 2013) $ // // $LastChangedBy: torsten_at_home $ // */ #include "cmtkCompatibilityMatrix4x4.h" template cmtk::CompatibilityMatrix4x4::CompatibilityMatrix4x4( const CoordinateVector& dofs, const bool logScaleFactors ) { const Units::Radians alpha = Units::Degrees( dofs[3] ); const Units::Radians theta = Units::Degrees( dofs[4] ); const Units::Radians phi = Units::Degrees( dofs[5] ); const double cos0 = MathUtil::Cos(alpha), sin0 = MathUtil::Sin(alpha); const double cos1 = MathUtil::Cos(theta), sin1 = MathUtil::Sin(theta); const double cos2 = MathUtil::Cos( phi), sin2 = MathUtil::Sin( phi); const double sin0xsin1 = sin0 * sin1; const double cos0xsin1 = cos0 * sin1; const double scaleX = (logScaleFactors) ? exp( dofs[6] ) : dofs[6]; const double scaleY = (logScaleFactors) ? exp( dofs[7] ) : dofs[7]; const double scaleZ = (logScaleFactors) ? exp( dofs[8] ) : dofs[8]; this->m_Matrix[0][0] = static_cast( cos1*cos2 * scaleX ); this->m_Matrix[0][1] = static_cast( -cos1*sin2 * scaleX ); this->m_Matrix[0][2] = static_cast( -sin1 * scaleX ); this->m_Matrix[0][3] = static_cast( 0 ); this->m_Matrix[1][0] = static_cast( (sin0xsin1*cos2 + cos0*sin2) * scaleY ); this->m_Matrix[1][1] = static_cast( (-sin0xsin1*sin2 + cos0*cos2) * scaleY ); this->m_Matrix[1][2] = static_cast( sin0*cos1 * scaleY ); this->m_Matrix[1][3] = static_cast( 0 ); this->m_Matrix[2][0] = static_cast( (cos0xsin1*cos2 - sin0*sin2) * scaleZ ); this->m_Matrix[2][1] = static_cast( (-cos0xsin1*sin2 - sin0*cos2) * scaleZ ); this->m_Matrix[2][2] = static_cast( cos0*cos1 * scaleZ ); this->m_Matrix[2][3] = static_cast( 0 ); this->m_Matrix[3][0] = this->m_Matrix[3][1] = this->m_Matrix[3][2] = static_cast( 0 ); this->m_Matrix[3][3] = static_cast( 1.0 ); // generate shears for ( int i = 2; i >= 0; --i ) { Superclass shear = Superclass::Identity(); shear[i/2][(i/2)+(i%2)+1] = dofs[9+i]; *this *= shear; } // transform rotation center const Types::Coordinate cM[3] = { dofs[12]*this->m_Matrix[0][0] + dofs[13]*this->m_Matrix[1][0] + dofs[14]*this->m_Matrix[2][0], dofs[12]*this->m_Matrix[0][1] + dofs[13]*this->m_Matrix[1][1] + dofs[14]*this->m_Matrix[2][1], dofs[12]*this->m_Matrix[0][2] + dofs[13]*this->m_Matrix[1][2] + dofs[14]*this->m_Matrix[2][2] }; // set translations this->m_Matrix[3][0] = dofs[0] - cM[0] + dofs[12]; this->m_Matrix[3][1] = dofs[1] - cM[1] + dofs[13]; this->m_Matrix[3][2] = dofs[2] - cM[2] + dofs[14]; } template class cmtk::CompatibilityMatrix4x4; cmtk-3.3.1/libs/Base/cmtkCompatibilityMatrix4x4.h000066400000000000000000000044351276303427400216630ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2013 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 5044 $ // // $LastChangedDate: 2013-11-27 13:19:12 -0800 (Wed, 27 Nov 2013) $ // // $LastChangedBy: torsten_at_home $ // */ #ifndef __cmtkCompatibilityMatrix4x4_h_included_ #define __cmtkCompatibilityMatrix4x4_h_included_ #include #include #include namespace cmtk { /** \addtogroup Base */ //@{ /** Compatibility class for homogeneous 4x4 transformation matrix. * The sole purpose of this class is to take a parameter vector describing the degrees of freedom of a * cmtk::Matrix4x4 object constructed prior to CMTK release 2.4. Older releases of CMTK generated matrices * in which scale and shear cooefficients were not fully independent, making it impossible to recover the * exact parameters from the matrix. *\see https://www.nitrc.org/tracker/index.php?func=detail&aid=7179&group_id=212&atid=877 */ template class CompatibilityMatrix4x4 : public Matrix4x4 { public: /// This class. typedef CompatibilityMatrix4x4 Self; /// Parent class.. typedef Matrix4x4 Superclass; /// Constructor: create matrix from parameter vector as it would have been done prior to CMTK 2.4. CompatibilityMatrix4x4( const CoordinateVector& dofs, const bool logScaleFactors = false ); }; } // namespace cmtk #endif // #ifndef __cmtkCompatibilityMatrix4x4_h_included_ cmtk-3.3.1/libs/Base/cmtkCubicInterpolator.h000066400000000000000000000043531276303427400207540ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2014 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 5240 $ // // $LastChangedDate: 2014-03-18 14:30:29 -0700 (Tue, 18 Mar 2014) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkCubicInterpolator_h_included_ #define __cmtkCubicInterpolator_h_included_ #include #include namespace cmtk { /** \addtogroup Base */ //@{ namespace Interpolators { /// Cubic interpolator. class Cubic { public: /// Size of the interpolation region in grid points to the left and right. static const int RegionSizeLeftRight = 2; /// Flag whether this interpolator is suitable for labels. static const bool SuitableForLabels = false; /// Get specific interpolation weight for relative coordinate. static Types::Coordinate GetWeight( const int weight, const Types::Coordinate x ) { const Types::Coordinate xsquare = x * x; const Types::Coordinate xcube = xsquare * x; switch (weight) { case -1: return -0.5 * xcube + xsquare - 0.5 * x; case 0: return 1.5 * xcube - 2.5 * xsquare + 1; case 1: return -1.5 * xcube + 2 * xsquare + 0.5 * x; case 2: return 0.5 * xcube - 0.5 * xsquare; default: break; } StdErr << "weight=" << weight << " shouldn't happen!\n"; throw ExitException( 1 ); } }; } // namespace Interpolators //@} } // namespace cmtk #endif cmtk-3.3.1/libs/Base/cmtkCubicSpline.h000066400000000000000000000214071276303427400175230ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2010 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 2398 $ // // $LastChangedDate: 2010-10-05 14:54:37 -0700 (Tue, 05 Oct 2010) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkCubicSpline_h_included_ #define __cmtkCubicSpline_h_included_ #include #include #include namespace cmtk { /** \addtogroup Base */ //@{ /** Class computing cubic splines. * This class is used for example by tri-cubic intensity interpolation and * B-spline deformations. It supports both approximating splines (for * deformations) and interpolating splines (for intensity interpolation). */ class CubicSpline { public: /// Compute a value of the 0th approximating spline function. static Types::Coordinate ApproxSpline0 ( const Types::Coordinate t ) { return ( MathUtil::Square(1-t) * (1-t) ) / 6; } /// Compute a value of the 1st approximating spline function. static Types::Coordinate ApproxSpline1 ( const Types::Coordinate t ) { return ( 4 + MathUtil::Square(t) * ( 3 * t - 6 ) ) / 6; } /// Compute a value of the 2nd approximating spline function. static Types::Coordinate ApproxSpline2 ( const Types::Coordinate t ) { return ( 1 + t * (3 + t * (3 - 3*t))) / 6; } /// Compute a value of the 3rd approximating spline function. static Types::Coordinate ApproxSpline3 ( const Types::Coordinate t ) { return t*t*t/6; } /// Compute the value of a given approximating spline function. static Types::Coordinate ApproxSpline ( const int k, const Types::Coordinate t ) { switch (k) { case 0: return ApproxSpline0( t ); case 1: return ApproxSpline1( t ); case 2: return ApproxSpline2( t ); case 3: return ApproxSpline3( t ); default: return 0; } #ifdef MSDOS return 0; #endif } /// Compute the derivative of the 0th approximating spline function. static Types::Coordinate DerivApproxSpline0 ( const Types::Coordinate t ) { return -MathUtil::Square(1-t) / 2; } /// Compute derivative of the 1st approximating spline function. static Types::Coordinate DerivApproxSpline1 ( const Types::Coordinate t ) { return 3*t*t/2-2*t; } /// Compute derivative of the 2nd approximating spline function. static Types::Coordinate DerivApproxSpline2 ( const Types::Coordinate t ) { return ( 1 + 2*t - 3*t*t ) / 2; } /// Compute derivative of the 3rd approximating spline function. static Types::Coordinate DerivApproxSpline3 ( const Types::Coordinate t ) { return t*t/2; } /// Compute the derivative of a given approximating spline function. static Types::Coordinate DerivApproxSpline ( const int k, const Types::Coordinate t ) { switch (k) { case 0: return DerivApproxSpline0( t ); case 1: return DerivApproxSpline1( t ); case 2: return DerivApproxSpline2( t ); case 3: return DerivApproxSpline3( t ); default: return 0; } #ifdef MSDOS return 0; #endif } /// Compute the second derivative of the 0th approximating spline function. static Types::Coordinate SecondDerivApproxSpline0 ( const Types::Coordinate t ) { return 1 - t; } /// Compute second derivative of the 1st approximating spline function. static Types::Coordinate SecondDerivApproxSpline1 ( const Types::Coordinate t ) { return 3 * t - 2; } /// Compute second derivative of the 2nd approximating spline function. static Types::Coordinate SecondDerivApproxSpline2 ( const Types::Coordinate t ) { return 1 - 3 * t; } /// Compute second derivative of the 3rd approximating spline function. static Types::Coordinate SecondDerivApproxSpline3 ( const Types::Coordinate t ) { return t; } /// Compute the second derivative of a given approximating spline function. static Types::Coordinate SecondDerivApproxSpline ( const int k, const Types::Coordinate t ) { switch (k) { case 0: return SecondDerivApproxSpline0( t ); case 1: return SecondDerivApproxSpline1( t ); case 2: return SecondDerivApproxSpline2( t ); case 3: return SecondDerivApproxSpline3( t ); default: return 0; } #ifdef MSDOS return 0; #endif } /// Compute a value of the 0th interpolating spline function. static Types::Coordinate InterpSpline0 ( const Types::Coordinate t ) { return (Types::Coordinate)( t * ( 0.5 * ( -1 + t * ( 2 - t ) ) ) ); } /// Compute a value of the 1st interpolating spline function. static Types::Coordinate InterpSpline1 ( const Types::Coordinate t ) { return (Types::Coordinate)( 0.5 * ( 3 * t - 5 ) * t*t + 1 ); } /// Compute a value of the 2nd interpolating spline function. static Types::Coordinate InterpSpline2 ( const Types::Coordinate t ) { return (Types::Coordinate)( 0.5 * t * ( 1 + t * ( 4 - 3 * t ) ) ); } /// Compute a value of the 3rd interpolating spline function. static Types::Coordinate InterpSpline3 ( const Types::Coordinate t ) { return (Types::Coordinate)( 0.5 * ( t*t * ( t-1 ) ) ); } /// Compute the value of a given interpolating spline function. static Types::Coordinate InterpSpline ( const int k, const Types::Coordinate t ) { switch (k) { case 0: return InterpSpline0( t ); case 1: return InterpSpline1( t ); case 2: return InterpSpline2( t ); case 3: return InterpSpline3( t ); default: return 0; } #ifdef MSDOS return 0; #endif } /// Compute derivative of the 0th interpolating spline function. static Types::Coordinate DerivInterpSpline0 ( const Types::Coordinate t ) { return ((-3 * t + 4) * t - 1) / 2; } /// Compute derivative of the 1st interpolating spline function. static Types::Coordinate DerivInterpSpline1 ( const Types::Coordinate t ) { return ( ( 9 * t - 10 ) * t ) / 2; } /// Compute derivative of the 2nd interpolating spline function. static Types::Coordinate DerivInterpSpline2 ( const Types::Coordinate t ) { return ( ( -9 * t + 8 ) * t + 1 ) / 2; } /// Compute derivative of the 3rd interpolating spline function. static Types::Coordinate DerivInterpSpline3 ( const Types::Coordinate t ) { return (( 3 * t - 2 ) * t) / 2; } /// Compute derivative of a given interpolating spline function. static Types::Coordinate DerivInterpSpline ( const int k, const Types::Coordinate t ) { switch (k) { case 0: return DerivInterpSpline0( t ); case 1: return DerivInterpSpline1( t ); case 2: return DerivInterpSpline2( t ); case 3: return DerivInterpSpline3( t ); default: return 0; } #ifdef MSDOS return 0; #endif } /// Compute second derivative of the 0th interpolating spline function. static Types::Coordinate SecondDerivInterpSpline0 ( const Types::Coordinate t ) { return -3 * t + 2; } /// Compute second derivative of the 1st interpolating spline function. static Types::Coordinate SecondDerivInterpSpline1 ( const Types::Coordinate t ) { return 9 * t - 5; } /// Compute second derivative of the 2nd interpolating spline function. static Types::Coordinate SecondDerivInterpSpline2 ( const Types::Coordinate t ) { return -9 * t + 4; } /// Compute second derivative of the 3rd interpolating spline function. static Types::Coordinate SecondDerivInterpSpline3 ( const Types::Coordinate t ) { return 3 * t - 1; } /// Compute second derivative of a given interpolating spline function. static Types::Coordinate SecondDerivInterpSpline ( const int k, const Types::Coordinate t ) { switch (k) { case 0: return SecondDerivInterpSpline0( t ); case 1: return SecondDerivInterpSpline1( t ); case 2: return SecondDerivInterpSpline2( t ); case 3: return SecondDerivInterpSpline3( t ); default: return 0; } #ifdef MSDOS return 0; #endif } }; } // namespace #endif // #ifndef __cmtkCubicSpline_h_included_ cmtk-3.3.1/libs/Base/cmtkDataGrid.cxx000066400000000000000000000452651276303427400173650ustar00rootroot00000000000000/* // // Copyright 2016 Google, Inc. // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2014 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 5396 $ // // $LastChangedDate: 2016-01-13 21:24:32 -0800 (Wed, 13 Jan 2016) $ // // $LastChangedBy: torstenrohlfing $ // */ #include "cmtkDataGrid.h" #include #include #include namespace cmtk { DataGrid::DataGrid( const Self& other ) : MetaInformationObject( other ), m_Dims( other.m_Dims ), m_GridIncrements( other.m_GridIncrements ), m_CropRegion( other.m_CropRegion ) { if (other.m_Data ) this->m_Data = other.m_Data->Clone(); this->ComputeGridIncrements(); } DataGrid* DataGrid::CloneVirtual( const bool copyData ) { if ( copyData ) { return this->CloneVirtual(); } else { DataGrid *result = new Self( this->m_Dims, this->GetData() ); result->m_CropRegion = this->m_CropRegion; return result; } } DataGrid* DataGrid::CloneVirtual() const { DataGrid *result = new Self( this->m_Dims ); result->m_CropRegion = this->m_CropRegion; if ( this->GetData() ) { TypedArray::SmartPtr clonedData( this->GetData()->Clone() ); result->SetData( clonedData ); } return result; } const DataGrid::RegionType DataGrid::GetWholeImageRegion() const { return Self::RegionType( Self::IndexType( 0 ), Self::IndexType( this->m_Dims ) ); } const DataGrid::RegionType DataGrid::GetSliceRegion( const int axis, const Types::GridIndexType slice ) const { Self::IndexType regionFrom( 0 ), regionTo( this->m_Dims ); regionFrom[axis] = slice; regionTo[axis] = slice+1; return Self::RegionType( regionFrom, regionTo ); } TypedArray::SmartPtr DataGrid::CreateDataArray( const ScalarDataType dataType, const bool setToZero ) { TypedArray::SmartPtr data( TypedArray::Create( dataType, this->GetNumberOfPixels() ) ); if ( setToZero ) data->ClearArray(); this->SetData( data ); return data; } DataGrid* DataGrid::GetDownsampledAndAveraged( const Types::GridIndexType (&downsample)[3] ) const { const Types::GridIndexType newDims[3] = { (this->m_Dims[0]-1) / downsample[0] + 1, (this->m_Dims[1]-1) / downsample[1] + 1, (this->m_Dims[2]-1) / downsample[2] + 1 }; DataGrid* newDataGrid = new DataGrid( Self::IndexType::FromPointer( newDims ) ); const TypedArray* thisData = this->GetData(); if ( thisData ) { TypedArray::SmartPtr newData = TypedArray::Create( thisData->GetType(), newDataGrid->GetNumberOfPixels() ); #pragma omp parallel for for ( Types::GridIndexType z = 0; z < newDims[2]; ++z ) { Types::GridIndexType toOffset = z * newDims[0] * newDims[1]; Types::GridIndexType oldZ = z * downsample[2]; Types::GridIndexType oldY = 0; for ( Types::GridIndexType y = 0; y < newDims[1]; ++y, oldY += downsample[1] ) { Types::GridIndexType oldX = 0; for ( Types::GridIndexType x = 0; x < newDims[0]; ++x, oldX += downsample[0], ++toOffset ) { Types::DataItem sum = 0; Types::GridIndexType count = 0; for ( Types::GridIndexType zz = 0; (zz < downsample[2]) && (oldZ+zzm_Dims[2]); ++zz ) { const Types::GridIndexType ofsZ = (oldZ+zz)*this->nextK; for ( Types::GridIndexType yy = 0; (yy < downsample[1]) && (oldY+yym_Dims[1]); ++yy ) { const Types::GridIndexType ofsYZ = ofsZ + (oldY+yy)*this->nextJ; for ( Types::GridIndexType xx = 0; (xx < downsample[0]) && (oldX+xxm_Dims[0]); ++xx ) { Types::DataItem value; if ( thisData->Get( value, (oldX+xx) + ofsYZ ) ) { sum += value; ++count; } } } } if ( count ) { newData->Set( sum / count, toOffset ); } else { newData->SetPaddingAt( toOffset ); } } } } // end for (z) newDataGrid->SetData( TypedArray::SmartPtr( newData ) ); } newDataGrid->CopyMetaInfo( *this, META_IMAGE_ORIENTATION ); newDataGrid->CopyMetaInfo( *this, META_IMAGE_ORIENTATION_ORIGINAL ); return newDataGrid; } DataGrid* DataGrid::GetDownsampled( const Types::GridIndexType (&downsample)[3] ) const { const Types::GridIndexType newDims[3] = { (this->m_Dims[0]-1) / downsample[0] + 1, (this->m_Dims[1]-1) / downsample[1] + 1, (this->m_Dims[2]-1) / downsample[2] + 1 }; DataGrid* newDataGrid = new DataGrid( Self::IndexType::FromPointer( newDims ) ); const TypedArray* thisData = this->GetData(); if ( thisData ) { TypedArray::SmartPtr newData = TypedArray::Create( thisData->GetType(), newDataGrid->GetNumberOfPixels() ); #pragma omp parallel for for ( Types::GridIndexType z = 0; z < newDims[2]; ++z ) { Types::GridIndexType toOffset = z * newDims[0] * newDims[1]; Types::GridIndexType oldZ = z * downsample[2]; Types::GridIndexType oldY = 0; for ( Types::GridIndexType y = 0; y < newDims[1]; ++y, oldY += downsample[1] ) { Types::GridIndexType oldX = 0; for ( Types::GridIndexType x = 0; x < newDims[0]; ++x, oldX += downsample[0], ++toOffset ) { Types::DataItem value = 0; if ( thisData->Get( value, this->GetOffsetFromIndex( oldX, oldY, oldZ ) ) ) newData->Set( value, toOffset ); else newData->SetPaddingAt( toOffset ); } } } // end for (z) newDataGrid->SetData( TypedArray::SmartPtr( newData ) ); } newDataGrid->CopyMetaInfo( *this, META_IMAGE_ORIENTATION ); newDataGrid->CopyMetaInfo( *this, META_IMAGE_ORIENTATION_ORIGINAL ); return newDataGrid; } const DataGrid::SmartPtr DataGrid::GetReoriented( const char* newOrientation ) const { std::string curOrientation = this->GetMetaInfo( META_IMAGE_ORIENTATION ); if ( curOrientation.length() != 3 ) { curOrientation = std::string( AnatomicalOrientation::ORIENTATION_STANDARD ); } if ( !newOrientation ) { newOrientation = AnatomicalOrientation::ORIENTATION_STANDARD; } // 1. get a permutation matrix AnatomicalOrientation::PermutationMatrix pmatrix( this->m_Dims, curOrientation, newOrientation ); Self::IndexType newDims = pmatrix.GetPermutedArray( this->m_Dims ); DataGrid* newDataGrid = new DataGrid( newDims ); const TypedArray* oldData = this->GetData(); if ( oldData ) { newDataGrid->CreateDataArray( oldData->GetType() ); TypedArray* newData = newDataGrid->GetData().GetPtr(); if ( oldData->GetPaddingFlag() ) { newData->SetPaddingValue( oldData->GetPaddingValue() ); } newData->SetDataClass( oldData->GetDataClass() ); const char* fromPtr = static_cast( oldData->GetDataPtr() ); char* toPtr = static_cast( newData->GetDataPtr() ); // 2. loop through the data, applying transformation const Types::GridIndexType bytesPerPixel = oldData->GetItemSize(); Types::GridIndexType fromPoint[3]; for ( fromPoint[2] = 0; fromPoint[2] < this->m_Dims[2]; fromPoint[2]++ ) { for ( fromPoint[1] = 0; fromPoint[1] < this->m_Dims[1]; fromPoint[1]++ ) { for ( fromPoint[0] = 0; fromPoint[0] < this->m_Dims[0]; fromPoint[0]++, fromPtr += bytesPerPixel ) { memcpy( toPtr + bytesPerPixel * pmatrix.NewOffsetFromPoint( fromPoint ), fromPtr, bytesPerPixel ); } } } } newDataGrid->CopyMetaInfo( *this ); newDataGrid->SetMetaInfo( META_IMAGE_ORIENTATION, newOrientation ); return Self::SmartPtr( newDataGrid ); } void DataGrid::ComputeGridIncrements() { this->m_GridIncrements[0] = 1; for ( int i = 1; i < 3; ++i ) this->m_GridIncrements[i] = this->m_GridIncrements[i-1] * this->m_Dims[i-1]; nextI = 1; nextJ = nextI * this->m_Dims[0]; nextK = nextJ * this->m_Dims[1]; nextIJ = nextI + nextJ; nextIK = nextI + nextK; nextJK = nextJ + nextK; nextIJK = nextI + nextJ + nextK; } bool DataGrid::TrilinearInterpolation ( Types::DataItem& value, const Types::GridIndexType X, const Types::GridIndexType Y, const Types::GridIndexType Z, const Self::SpaceVectorType& Location, const Types::Coordinate* from, const Types::Coordinate* to ) const { Types::DataItem corners[8]; const Types::GridIndexType offset = this->GetOffsetFromIndex( X, Y, Z ); const TypedArray* data = this->GetData(); bool data_present = data->Get( corners[0], offset ); if ( Xm_Dims[0]-1 ) { data_present &= data->Get( corners[1] , offset+nextI ); if ( Ym_Dims[1]-1 ) { data_present &= data->Get( corners[3], offset+nextIJ ); if ( Zm_Dims[2]-1 ) data_present &= data->Get( corners[7], offset+nextIJK ); else return false; } else { return false; } data_present &= data->Get( corners[5], offset+nextIK ); } else { return false; } data_present &= data->Get( corners[2], offset+nextJ ); data_present &= data->Get( corners[6], offset+nextJK ); data_present &= data->Get( corners[4], offset+nextK ); if (data_present) { const Types::Coordinate deltaX=1/(to[0]-from[0]), deltaY=1/(to[1]-from[1]), deltaZ=1/(to[2]-from[2]); const Types::Coordinate revX = deltaX*(Location[0]-from[0]); const Types::Coordinate revY = deltaY*(Location[1]-from[1]); const Types::Coordinate revZ = deltaZ*(Location[2]-from[2]); const Types::Coordinate offsX = 1-revX; const Types::Coordinate offsY = 1-revY; const Types::Coordinate offsZ = 1-revZ; value = static_cast( offsZ*(offsY*(offsX*corners[0]+revX*corners[1])+ revY*(offsX*corners[2]+revX*corners[3]))+ revZ*(offsY*(offsX*corners[4]+revX*corners[5])+ revY*(offsX*corners[6]+revX*corners[7]))); return true; } return false; } TypedArray::SmartPtr DataGrid::GetDataMirrorPlane( const int axis ) const { TypedArray::SmartPtr result( this->GetData()->Clone() ); this->MirrorPlaneInPlace( *result, this->m_Dims, axis ); return result; } void DataGrid::ApplyMirrorPlane( const int axis ) { this->MirrorPlaneInPlace( *(this->GetData()), this->m_Dims, axis ); } void DataGrid::MirrorPlaneInPlace ( TypedArray& data, const Self::IndexType& dims, const int axis ) { switch ( axis ) { case AXIS_X: { Types::GridIndexType offset = 0; for ( Types::GridIndexType z = 0; z < dims[2]; ++z ) for ( Types::GridIndexType y = 0; y < dims[1]; ++y, offset += dims[0] ) data.BlockReverse( offset, dims[0] ); } break; case AXIS_Y: { Types::GridIndexType zOffset = 0; for ( Types::GridIndexType z = 0; z < dims[2]; ++z, zOffset += dims[0] * dims[1] ) { for ( Types::GridIndexType y = 0; y < (dims[1] / 2); ++y ) data.BlockSwap( zOffset + y * dims[0], zOffset + (dims[1] - 1 - y) * dims[0], dims[0] ); } } break; case AXIS_Z: { Types::GridIndexType blockSize = dims[0] * dims[1]; for ( Types::GridIndexType z = 0; z < (dims[2] / 2); ++z ) { data.BlockSwap( z * blockSize, (dims[2] - 1 - z) * blockSize, blockSize ); } } break; } } ScalarImage::SmartPtr DataGrid::GetOrthoSlice ( const int axis, const Types::GridIndexType plane ) const { Types::GridIndexType dims[2], depth, incX, incY, incZ; switch ( axis ) { case AXIS_X: dims[0] = this->m_Dims[1]; dims[1] = this->m_Dims[2]; depth = this->m_Dims[0]; incX = this->m_Dims[0]; incY = this->m_Dims[0] * this->m_Dims[1]; incZ = 1; break; case AXIS_Y: dims[0] = this->m_Dims[0]; dims[1] = this->m_Dims[2]; depth = this->m_Dims[1]; incX = 1; incY = this->m_Dims[0] * this->m_Dims[1]; incZ = this->m_Dims[0]; break; case AXIS_Z: default: dims[0] = this->m_Dims[0]; dims[1] = this->m_Dims[1]; depth = this->m_Dims[2]; incX = 1; incY = this->m_Dims[0]; incZ = this->m_Dims[0] * this->m_Dims[1]; break; } const TypedArray* data = this->GetData(); TypedArray::SmartPtr sliceData = TypedArray::Create( data->GetType(), dims[0] * dims[1] ); if ( data->GetPaddingFlag() ) { sliceData->SetPaddingValue( data->GetPaddingValue() ); } if ( (plane < 0) || (plane >= depth) ) { sliceData->ClearArray( true /* PaddingData */ ); } else { const Types::GridIndexType itemSize = data->GetItemSize(); Types::GridIndexType sliceOffset = 0; Types::GridIndexType offset = plane * incZ; for ( Types::GridIndexType y = 0; y < dims[1]; ++y ) { Types::GridIndexType offsetY = offset + incY; for ( Types::GridIndexType x = 0; x < dims[0]; ++x, ++sliceOffset ) { Types::GridIndexType offsetX = offset + incX; memcpy( sliceData->GetDataPtr( sliceOffset ), data->GetDataPtr( offset ), itemSize ); offset = offsetX; } offset = offsetY; } } ScalarImage::SmartPtr sliceImage( new ScalarImage( dims[0], dims[1] ) ); sliceImage->SetPixelData( sliceData ); return sliceImage; } DataGrid::SmartPtr DataGrid::ExtractSlice ( const int axis, const Types::GridIndexType plane ) const { Types::GridIndexType dims[2], incX, incY, incZ; switch ( axis ) { case AXIS_X: dims[0] = this->m_Dims[1]; dims[1] = this->m_Dims[2]; incX = this->m_Dims[0]; incY = this->m_Dims[0] * this->m_Dims[1]; incZ = 1; break; case AXIS_Y: dims[0] = this->m_Dims[0]; dims[1] = this->m_Dims[2]; incX = 1; incY = this->m_Dims[0] * this->m_Dims[1]; incZ = this->m_Dims[0]; break; case AXIS_Z: default: dims[0] = this->m_Dims[0]; dims[1] = this->m_Dims[1]; incX = 1; incY = this->m_Dims[0]; incZ = this->m_Dims[0] * this->m_Dims[1]; break; } const TypedArray& data = *(this->GetData()); TypedArray::SmartPtr sliceData = TypedArray::Create( data.GetType(), dims[0] * dims[1] ); if ( data.GetPaddingFlag() ) { sliceData->SetPaddingValue( data.GetPaddingValue() ); } if ( plane >= this->m_Dims[axis] ) { sliceData->ClearArray( true /* PaddingData */ ); } else { const Types::GridIndexType itemSize = data.GetItemSize(); Types::GridIndexType sliceOffset = 0; Types::GridIndexType offset = plane * incZ; for ( Types::GridIndexType y = 0; y < dims[1]; ++y ) { Types::GridIndexType offsetY = offset + incY; for ( Types::GridIndexType x = 0; x < dims[0]; ++x, ++sliceOffset ) { Types::GridIndexType offsetX = offset + incX; memcpy( sliceData->GetDataPtr( sliceOffset ), data.GetDataPtr( offset ), itemSize ); offset = offsetX; } offset = offsetY; } } IndexType newDims = this->m_Dims; newDims[axis] = 1; return Self::SmartPtr( new Self( newDims, sliceData ) ); } void DataGrid::SetOrthoSlice ( const int axis, const Types::GridIndexType idx, const ScalarImage* slice ) { const TypedArray* sliceData = slice->GetPixelData(); if ( ! sliceData ) return; TypedArray::SmartPtr data = this->GetData(); if ( ! data ) { data = this->CreateDataArray( sliceData->GetType() ); } Types::GridIndexType dims[2], depth, incX, incY, incZ; switch ( axis ) { case AXIS_X: dims[0] = this->m_Dims[1]; dims[1] = this->m_Dims[2]; depth = this->m_Dims[0]; incX = this->m_Dims[0]; incY = this->m_Dims[0] * this->m_Dims[1]; incZ = 1; break; case AXIS_Y: dims[0] = this->m_Dims[0]; dims[1] = this->m_Dims[2]; depth = this->m_Dims[1]; incX = 1; incY = this->m_Dims[0] * this->m_Dims[1]; incZ = this->m_Dims[0]; break; case AXIS_Z: default: dims[0] = this->m_Dims[0]; dims[1] = this->m_Dims[1]; depth = this->m_Dims[2]; incX = 1; incY = this->m_Dims[0]; incZ = this->m_Dims[0] * this->m_Dims[1]; break; } if ( (idx >= 0) && (idx < depth) ) { Types::GridIndexType sliceOffset = 0; Types::GridIndexType offset = idx * incZ; for ( Types::GridIndexType y = 0; y < dims[1]; ++y ) { Types::GridIndexType offsetY = offset + incY; for ( Types::GridIndexType x = 0; x < dims[0]; ++x, ++sliceOffset ) { Types::GridIndexType offsetX = offset + incX; sliceData->BlockCopy( *data, offset, sliceOffset, 1 ); offset = offsetX; } offset = offsetY; } } } FixedVector<3,Types::Coordinate> DataGrid ::GetCenterOfMassGrid() const { FixedVector<3,Types::Coordinate> com( 0.0 ); double sumOfSamples = 0; Types::GridIndexType ofs = 0; for ( Types::GridIndexType z = 0; z < this->m_Dims[2]; ++z ) for ( Types::GridIndexType y = 0; y < this->m_Dims[1]; ++y ) for ( Types::GridIndexType x = 0; x < this->m_Dims[0]; ++x, ++ofs ) { Types::DataItem value; if ( this->GetDataAt( value, x, y, z ) && MathUtil::IsFinite( value ) ) { const Types::Coordinate pixelCOM[3] = { value * x, value * y, value * z }; com += Self::SpaceVectorType::FromPointer( pixelCOM ); sumOfSamples += value; } } com *= (1.0 / sumOfSamples); return com; } FixedVector<3,Types::Coordinate> DataGrid ::GetCenterOfMassGrid( FixedVector<3,Types::Coordinate>& firstOrderMoment ) const { FixedVector<3,Types::Coordinate> com = this->Self::GetCenterOfMassGrid(); // do not use overloaded function firstOrderMoment = FixedVector<3,Types::Coordinate>( 0.0 ); double sumOfSamples = 0; Types::GridIndexType ofs = 0; for ( Types::GridIndexType z = 0; z < this->m_Dims[2]; ++z ) for ( Types::GridIndexType y = 0; y < this->m_Dims[1]; ++y ) for ( Types::GridIndexType x = 0; x < this->m_Dims[0]; ++x, ++ofs ) { Types::DataItem value; if ( this->GetDataAt( value, x, y, z ) && MathUtil::IsFinite( value ) ) { const Types::Coordinate pixelMoment[3] = { value * fabs(x - com[0]), value * fabs(y - com[1]), value * fabs(z - com[2]) }; firstOrderMoment += Self::SpaceVectorType::FromPointer( pixelMoment ); sumOfSamples += value; } } firstOrderMoment *= (1.0 / sumOfSamples); return com; } void DataGrid::Print() const { StdErr.printf( "DataGrid at %p\n", this ); StdErr.printf( "\tthis->m_Dims: [%d,%d,%d])\n", this->m_Dims[0], this->m_Dims[1], this->m_Dims[2] ); StdErr.printf( "\tData array at %p\n", this ); } } // namespace cmtk cmtk-3.3.1/libs/Base/cmtkDataGrid.h000066400000000000000000000404771276303427400170120ustar00rootroot00000000000000/* // // Copyright 2016 Google, Inc. // // Copyright 2004-2013 SRI International // // Copyright 1997-2010 Torsten Rohlfing // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 5396 $ // // $LastChangedDate: 2016-01-13 21:24:32 -0800 (Wed, 13 Jan 2016) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkDataGrid_h_included_ #define __cmtkDataGrid_h_included_ #include #include #include #include #include #include #include #include #include #include #include #include namespace cmtk { /** \addtogroup Base */ //@{ /** Grid topology of data arranged in a 3D lattice. * This class extends the plain data handling functions of TypedArray * with a 3D topology. Real world coordinates, however, are not considered and * need to be handled by derived classes. Thus, this class provides the coordinate * independent services such as median filtering and, to a certain extent, * interpolation. */ class DataGrid : /// Inherit class that handles meta information. public MetaInformationObject { public: /// This class. typedef DataGrid Self; /// Smart pointer to DataGrid. typedef SmartPointer SmartPtr; /// Smart pointer-to-const to DataGrid. typedef SmartConstPointer SmartConstPtr; /// Region type. typedef Region<3,Types::GridIndexType> RegionType; /// Index type. typedef RegionType::IndexType IndexType; /// Space vector type. typedef FixedVector<3,Types::Coordinate> SpaceVectorType; /// Number of grid samples in the three spatial dimensions Self::IndexType m_Dims; /// Offset increments when moving to the next pixel in each of the three grid dimensions. Self::IndexType m_GridIncrements; /// Data array (element type is variable) cmtkGetSetMacro(TypedArray::SmartPtr,Data); public: /// Copy constructor. DataGrid( const Self& other ); /// Constructor. DataGrid( const Self::IndexType& dims, TypedArray::SmartPtr& data = TypedArray::SmartPtr::Null() ) : m_Dims( dims ), m_Data( data ) { this->ComputeGridIncrements(); this->m_CropRegion = this->GetWholeImageRegion(); } /// Virtual destructor. virtual ~DataGrid() {} /** Create a physical copy of this object. *\param copyData If true, the associated data array is also copied. */ Self::SmartPtr Clone( const bool copyData ) { return Self::SmartPtr( this->CloneVirtual( copyData ) ); } /** Create a physical copy of this object. */ Self::SmartPtr Clone() const { return Self::SmartPtr( this->CloneVirtual() ); } /// Test whether this grid matches another one, i.e., has the same dimensions. bool GridMatches( const Self& other ) const { return (this->m_Dims == other.m_Dims); } /// Downsampling and pixel-averaging constructor function. virtual DataGrid* GetDownsampledAndAveraged( const Types::GridIndexType (&downsample)[3] ) const; /// Downsampling without averaging constructor function. virtual DataGrid* GetDownsampled( const Types::GridIndexType (&downsample)[3] ) const; /** Reorientation constructor function. *\param newOrientation Three letter orientation code that specifies the anatomically-based * orientation of the reoriented volume. Each letter can be one of the following: R, L, A, * P, I, S. These stand for Right, Left, Anterior, Posterior, Inferior, Superior. * * The three letters in the orientation string define the directions of the positive x, y, * and z axes, in this order. For example, "RAS", the standard orientation for this software, * means that the pixels along the x axis are arranged from the subject's Left to the Right * side, along the y axis from the subject's Posterior (back) to Anterior (front), and along * the z axis from Inferior (feet) to Superior (head). * * The current orientation of this volume is to be taken from its meta information, * as this->m_MetaInformation[CMTK_META_IMAGE_ORIENTATION]. This is also a three-letter string of the * same form as the one given to this function as a parameter. * * If the current orientation is not set, a warning message should be printed to StdErr, and * a NULL pointer returned. * *\return Reoriented data grid with permuted pixels in this->Data and permuted grid dimensions * in this->Dims. The returned pointers points to a newly allocated object, which can be * wrapped in an SmartPointer. */ const DataGrid::SmartPtr GetReoriented( const char* newOrientation = AnatomicalOrientation::ORIENTATION_STANDARD ) const; /// Get dimensions array. const Self::IndexType GetDims() const { return this->m_Dims; } /** Create data array. *\param dataType ID of scalar data type for the array. This is the image pixel type. *\param setToZero If this flag is set, all values in the newly created array will be initialized to zero. */ virtual TypedArray::SmartPtr CreateDataArray( const ScalarDataType dataType, const bool setToZero = false ); /// Get number of data items in the volume. size_t GetNumberOfPixels () const { return this->m_Dims[0]*this->m_Dims[1]*this->m_Dims[2]; } /// Check whether given pixel index is inside grid. bool IndexIsInRange( const Types::GridIndexType x, const Types::GridIndexType y, const Types::GridIndexType z ) const { return (x>=0) && (xm_Dims[0]) && (y>=0) && (ym_Dims[1]) && (z>=0) && (zm_Dims[2]); } /// Get offset of a pixel. Types::GridIndexType GetOffsetFromIndex( const Types::GridIndexType x, const Types::GridIndexType y, const Types::GridIndexType z ) const { return x + nextJ * y + nextK * z; } /// Get offset of a pixel. Types::GridIndexType GetOffsetFromIndex( const Self::IndexType& index ) const { return index[0] + this->nextJ * index[1] + this->nextK * index[2]; } /// Get index of a pixel identified by its offset. void GetIndexFromOffset( const size_t offset, Types::GridIndexType& x, Types::GridIndexType& y, Types::GridIndexType& z ) const { z = offset / nextK; y = (offset % nextK) / nextJ; x = (offset % nextK) % nextJ; } /// Get index of a pixel identified by its offset. Self::IndexType GetIndexFromOffset( const Types::GridIndexType offset ) const { Self::IndexType index; index[2] = offset / nextK; index[1] = (offset % nextK) / nextJ; index[0] = (offset % nextK) % nextJ; return index; } /// Return data at specified offset bool GetDataAt ( Types::DataItem& data, const size_t offset ) const { return this->m_Data->Get( data, offset ); } /// Return data at specified grid point. bool GetDataAt ( Types::DataItem& data, const Types::GridIndexType x, const Types::GridIndexType y, const Types::GridIndexType z ) const { return this->m_Data->Get( data, this->GetOffsetFromIndex( x, y, z ) ); } /// Set data at specified offset void SetDataAt ( const Types::DataItem data, const size_t offset ) { this->m_Data->Set( data, offset ); } /// Set data at specified grid point. void SetDataAt ( const Types::DataItem data, const Types::GridIndexType x, const Types::GridIndexType y, const Types::GridIndexType z ) { this->SetDataAt( data, this->GetOffsetFromIndex( x, y, z ) ); } /// Return data at specified grid point, or a given default value if no data exists there. Types::DataItem GetDataAt ( const Types::GridIndexType x, const Types::GridIndexType y, const Types::GridIndexType z, const Types::DataItem defaultValue = 0.0 ) const { Types::DataItem value; if ( this->GetDataAt( value, this->GetOffsetFromIndex( x, y, z ) ) ) return value; else return defaultValue; } /// Return data at specified grid offset, or a given default value if no data exists there. Types::DataItem GetDataAt ( const size_t offset, const Types::DataItem defaultValue = 0.0 ) const { return this->m_Data->ValueAt( offset, defaultValue ); } /** Return data after mirroring. *\param axis Coordinate axis normal to mirror plane. Default is AXIS_X * (mirror about mid-sagittal plane). */ TypedArray::SmartPtr GetDataMirrorPlane( const int axis = AXIS_X ) const; /// Replace data with mirrored version. void ApplyMirrorPlane( const int axis = AXIS_X ); public: /** Get cropped region reference. */ Self::RegionType& CropRegion() { return this->m_CropRegion; } /** Set cropped region. * This function can deal with negative crop region values, which refer to the upper grid * boundary and are automatically converted. */ virtual void SetCropRegion( const Self::RegionType& region ); /** Get cropped region reference. */ const Self::RegionType& CropRegion() const { return this->m_CropRegion; } /// Get whole image region. const Self::RegionType GetWholeImageRegion() const; /// Get region covering one slice of the image. const Self::RegionType GetSliceRegion( const int axis /*!< Coordinate axis perpendicular to the slice, i.e., 0 for x, 1 for y, 2 for z.*/, const Types::GridIndexType slice /*!< Index of selected slice. */ ) const; /// Get index increments for crop region. const Self::IndexType GetCropRegionIncrements() const; /** Automatically crop to minimal region above given threshold. *\param threshold The cropping threshold. *\param recrop If this flag is true, then the cropping will be performed * inside an already existing cropping region. If this flag is false * (default), then any pre-set crop region is ignored. *\param margin Width of additional margin added around the threshold-cropped region. *\return The crop region that was applied. */ Self::RegionType AutoCrop( const Types::DataItem threshold, const bool recrop = false, const Types::GridIndexType margin = 0 ); /// Fill volume outside current crop region with constant value. void FillCropBackground( const Types::DataItem value ); /// Return data for a region of the volume. TypedArray::SmartPtr GetRegionData( const Self::RegionType& region ) const; /// Accessor functions for protected member variables Types::GridIndexType GetNextI() const { return nextI; } Types::GridIndexType GetNextJ() const { return nextJ; } Types::GridIndexType GetNextK() const { return nextK; } Types::GridIndexType GetNextIJ() const { return nextIJ; } Types::GridIndexType GetNextIK() const { return nextIK; } Types::GridIndexType GetNextJK() const { return nextJK; } Types::GridIndexType GetNextIJK() const { return nextIJK; } /// Get center of mass of pixel data. virtual FixedVector<3,Types::Coordinate> GetCenterOfMassGrid() const; /// Get center of mass and first-order moments of pixel data. virtual FixedVector<3,Types::Coordinate> GetCenterOfMassGrid( FixedVector<3,Types::Coordinate>& firstOrderMoment ) const; /** Return orthogonal slice as a 2D image. */ virtual ScalarImage::SmartPtr GetOrthoSlice( const int axis, const Types::GridIndexType plane ) const; /** Extract orthogonal slice as a data grid object. */ Self::SmartPtr ExtractSlice( const int axis /*!< Coordinate axis perpendicular to extracted plane*/, const Types::GridIndexType plane /*!< Index of extracted plane */ ) const; /** Set orthogonal slice from a 2D image. */ virtual void SetOrthoSlice( const int axis, const Types::GridIndexType idx, const ScalarImage* slice ); /// Print object. void Print() const; protected: /** Create a physical copy of this object. *\param copyData If true, the associated data array is also copied. */ virtual Self* CloneVirtual( const bool copyData ); virtual Self* CloneVirtual() const; /** Utility function for trilinear interpolation. *\param data This reference is set to the interpolated data value. It is * valid if and only if this function returns 1. *\param location 3D coordinate to interpolate data at. *\param x Grid index x. *\param y Grid index y. *\param z Grid index z. *\param location Location within grid cell. *\param cellFrom 3D coordinate of the lower-left-front voxel of the cell * enclosing the given location. *\param cellTo 3D coordinate of the upper-right-rear voxel of the cell * enclosing the given location. *\return True if there is valid data for all eight voxels enclosing the * given location, so that the interpolation could be completed successfully, * False otherwise. */ bool TrilinearInterpolation( Types::DataItem& data, const Types::GridIndexType x, const Types::GridIndexType y, const Types::GridIndexType z, const Self::SpaceVectorType& location, const Types::Coordinate* cellFrom, const Types::Coordinate* cellTo ) const; /** Utility function for trilinear interpolation from a primitive data array. * This function is provided for computational efficiency when a large number * of interpolations from a given data volume of known pixel type are required. * *\param dataPtr Pointer to the primitive data array. *\param x Grid index x. *\param y Grid index y. *\param z Grid index z. *\param gridPosition (x,y,z) indices of the voxel containing the given * location. *\param cellFrom 3D coordinate of the lower-left-front voxel of the cell * enclosing the given location. *\param cellTo 3D coordinate of the upper-right-rear voxel of the cell * enclosing the given location. *\return The interpolated data value.. */ template inline TData TrilinearInterpolation ( const TData* dataPtr, const Types::GridIndexType x, const Types::GridIndexType y, const Types::GridIndexType z, const Self::SpaceVectorType& gridPosition, const Types::Coordinate* cellFrom, const Types::Coordinate* cellTo ) const; /** Utility function for trilinear interpolation from multiple primitive data arrays of identical grid structure. * This function is provided for computational efficiency when a large number * of interpolations from a given data volume of known pixel type are required. */ template inline void TrilinearInterpolation ( TOutputIterator result /*!< Output iterator to store interpolated values.*/, const std::vector& dataPtr /*!< Vector of data arrays to interpolate from */, const Types::GridIndexType x /*!< Grid position x */, const Types::GridIndexType y /*!< Grid position y */, const Types::GridIndexType z /*!< Grid position z */, const Types::Coordinate fracX /*!< Fractional coordinate X within pixel */, const Types::Coordinate fracY /*!< Fractional coordinate Y within pixel */, const Types::Coordinate fracZ /*!< Fractional coordinate Z within pixel */ ) const; /// Offset to next voxel column. Types::GridIndexType nextI; /// Offset to next voxel row. Types::GridIndexType nextJ; /// Offset to next voxel plane. Types::GridIndexType nextK; /// Offset to next column and row. Types::GridIndexType nextIJ; /// Offset to next column and plane. Types::GridIndexType nextIK; /// Offset to next row and plane. Types::GridIndexType nextJK; /// Offset to next column, row, and plane. Types::GridIndexType nextIJK; private: /** Crop region. */ Self::RegionType m_CropRegion; /** Compute grid increments for fast n-dimensional offset computations. */ virtual void ComputeGridIncrements(); /// Mirror about plane without allocating additional memory. static void MirrorPlaneInPlace( TypedArray& data, const Self::IndexType& dims, const int axis = AXIS_X ); }; //@} } // namespace cmtk #include "cmtkDataGrid.txx" #endif // #ifndef __cmtkDataGrid_h_included_ cmtk-3.3.1/libs/Base/cmtkDataGrid.txx000066400000000000000000000071661276303427400174040ustar00rootroot00000000000000/* // // Copyright 2016 Google, Inc. // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2012 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 5396 $ // // $LastChangedDate: 2016-01-13 21:24:32 -0800 (Wed, 13 Jan 2016) $ // // $LastChangedBy: torstenrohlfing $ // */ namespace cmtk { /** \addtogroup Base */ //@{ template TData DataGrid::TrilinearInterpolation ( const TData* dataPtr, const Types::GridIndexType X, const Types::GridIndexType Y, const Types::GridIndexType Z, const Self::SpaceVectorType& Location, const Types::Coordinate* from, const Types::Coordinate* to ) const { const size_t offset = X+this->m_Dims[0]*(Y+this->m_Dims[1]*Z); const TData* data = dataPtr + offset; const Types::Coordinate deltaX=1.0/(to[0]-from[0]), deltaY=1.0/(to[1]-from[1]), deltaZ=1.0/(to[2]-from[2]); const Types::Coordinate revX = deltaX*(Location[0]-from[0]); const Types::Coordinate revY = deltaY*(Location[1]-from[1]); const Types::Coordinate revZ = deltaZ*(Location[2]-from[2]); const Types::Coordinate offsX = 1-revX; const Types::Coordinate offsY = 1-revY; const Types::Coordinate offsZ = 1-revZ; return static_cast( offsZ*(offsY*(offsX*data[0] + revX*data[nextI])+ revY*(offsX*data[nextJ]+ revX*data[nextIJ]))+ revZ*(offsY*(offsX*data[nextK]+ revX*data[nextIK])+ revY*(offsX*data[nextJK]+ revX*data[nextIJK]))); } template inline void DataGrid ::TrilinearInterpolation ( TOutputIterator result, const std::vector& dataPtr, const Types::GridIndexType x, const Types::GridIndexType y, const Types::GridIndexType z, const Types::Coordinate fracX, const Types::Coordinate fracY, const Types::Coordinate fracZ ) const { const size_t offset = x + this->m_Dims[0] * ( y + this->m_Dims[1] * z); const Types::Coordinate offsX = 1.0-fracX; const Types::Coordinate offsY = 1.0-fracY; const Types::Coordinate offsZ = 1.0-fracZ; const Types::Coordinate tmp0 = offsZ * offsY; const Types::Coordinate w0 = tmp0 * offsX; const Types::Coordinate w1 = tmp0 * fracX; const Types::Coordinate tmp1 = offsZ * fracY; const Types::Coordinate w2 = tmp1 * offsX; const Types::Coordinate w3 = tmp1 * fracX; const Types::Coordinate tmp2 = fracZ * offsY; const Types::Coordinate w4 = tmp2 * offsX; const Types::Coordinate w5 = tmp2 * fracX; const Types::Coordinate tmp3 = fracZ * fracY; const Types::Coordinate w6 = tmp3 * offsX; const Types::Coordinate w7 = tmp3 * fracX; for ( size_t i = 0; i < dataPtr.size(); ++i, ++result ) { const TData* data = dataPtr[i] + offset; *result = static_cast( w0 * data[0] + w1 * data[nextI] + w2 * data[nextJ] + w3 * data[nextIJ] + w4 * data[nextK]+ w5 * data[nextIK] + w6 * data[nextJK] + w7 * data[nextIJK] ); } } } // namespace cmtk cmtk-3.3.1/libs/Base/cmtkDataGridConnectedComponents.cxx000066400000000000000000000126061276303427400232470ustar00rootroot00000000000000/* // // Copyright 1997-2010 Torsten Rohlfing // // Copyright 2011, 2014 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 5268 $ // // $LastChangedDate: 2014-03-28 11:41:16 -0700 (Fri, 28 Mar 2014) $ // // $LastChangedBy: torstenrohlfing $ // */ #include "cmtkDataGridMorphologicalOperators.h" #include #include #include #include cmtk::TypedArray::SmartPtr cmtk::DataGridMorphologicalOperators::GetBinaryConnectedComponents() const { const size_t numberOfPixels = this->m_DataGrid->GetNumberOfPixels(); // this vector will hold the component index per pixel; since we move strictly forward and only look back in the algorithm below, // there is no need to initialize the vector elements. std::vector result( numberOfPixels); const DataGrid::IndexType& dims = this->m_DataGrid->GetDims(); DataGrid::IndexType relative; relative[0] = this->m_DataGrid->GetNextI(); relative[1] = this->m_DataGrid->GetNextJ(); relative[2] = this->m_DataGrid->GetNextK(); UnionFind connected; int nextComponent = 1; DataGrid::IndexType index; size_t offset = 0; for ( index[2] = 0; index[2] < dims[2]; ++index[2] ) { for ( index[1] = 0; index[1] < dims[1]; ++index[1] ) { for ( index[0] = 0; index[0] < dims[0]; ++index[0], ++offset ) { // initialize as "background" int component = 0; // foreground pixel? if ( this->m_DataGrid->GetDataAt( offset ) != 0 ) { // loop over x,y,z neighbor for ( int dim = 2; dim >= 0; --dim ) { // is there a preceding neighbor in this direction? if ( index[dim] ) { // get component ID for that neighbor const int existing = result[offset - relative[dim]]; // is there something there? if ( existing ) { // did we already get a different component ID from another neighbor? if ( component && (component != existing) ) // note: this implies "component != 0" because "existing" is at least 1 in this branch. { // link old and new component via this pixel connected.Union( connected.Find( component ), connected.Find( existing ) ); } // mark current pixel as belonging to the same component component = existing; } } } // none of the neighbors is foreground, so use next available component ID. if ( !component ) { component = nextComponent++; connected.Insert( component ); } } // set component result for this pixel result[offset] = component; } } } // now collapse all component indexes into their unique representative std::map linkMap; for ( int component = 1; component < nextComponent; ++component ) { linkMap[component] = connected.FindKey( component ); } // re-number components TypedArray::SmartPtr resultArray( TypedArray::Create( TYPE_INT, numberOfPixels ) ); #pragma omp parallel for for ( int px = 0; px < static_cast( numberOfPixels ); ++px ) { resultArray->Set( linkMap[result[px]], px ); } resultArray->SetDataClass( DATACLASS_LABEL ); return resultArray; } cmtk::TypedArray::SmartPtr cmtk::DataGridMorphologicalOperators::GetRegionsRenumberedBySize() const { const size_t numberOfPixels = this->m_DataGrid->GetNumberOfPixels(); // first, count pixels in each region std::map regionSizeMap; for ( size_t px = 0; px < numberOfPixels; ++px ) { const int value = static_cast( this->m_DataGrid->GetDataAt( px ) ); if ( value ) ++regionSizeMap[value]; } // now create list sorted by region size std::list< std::pair > sortedList; for ( std::map::const_iterator it = regionSizeMap.begin(); it != regionSizeMap.end(); ++it ) { std::list< std::pair >::iterator ins = sortedList.begin(); while ( (ins != sortedList.end()) && (ins->second >= it->second) ) ++ins; sortedList.insert( ins, *it ); // need to explicitly create new pair because some STL implementation have it->first as "const". } // create renumbering lookup map std::map renumberMap; int component = 1; for ( std::list< std::pair >::iterator it = sortedList.begin(); it != sortedList.end(); ++it ) { renumberMap[it->first] = component++; } // re-number components TypedArray::SmartPtr resultArray( TypedArray::Create( TYPE_INT, numberOfPixels ) ); for ( size_t px = 0; px < numberOfPixels; ++px ) { resultArray->Set( renumberMap[static_cast( this->m_DataGrid->GetDataAt( px ) )], px ); } resultArray->SetDataClass( DATACLASS_LABEL ); return resultArray; } cmtk-3.3.1/libs/Base/cmtkDataGridFilter.cxx000066400000000000000000000453351276303427400205310ustar00rootroot00000000000000/* // // Copyright 2016 Google, Inc. // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2013 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 5396 $ // // $LastChangedDate: 2016-01-13 21:24:32 -0800 (Wed, 13 Jan 2016) $ // // $LastChangedBy: torstenrohlfing $ // */ #include "cmtkDataGridFilter.h" #include #include #include #include #include namespace cmtk { DataGridFilter ::DataGridFilter( DataGrid::SmartConstPtr dataGrid ) : m_DataGrid( dataGrid ) {} cmtk::Types::DataItem cmtk::DataGridFilter::MedianOperator::Reduce( std::vector& regionData ) { std::sort( regionData.begin(), regionData.end() ); if ( regionData.size() % 2 ) return regionData[regionData.size()/2]; else return (Types::DataItem) ( 0.5 * (regionData[regionData.size()/2] + regionData[regionData.size()/2-1]) ); } TypedArray::SmartPtr DataGridFilter::GetDataMedianFiltered( const Types::GridIndexType radiusX, const Types::GridIndexType radiusY, const Types::GridIndexType radiusZ ) const { return this->ApplyRegionFilter( radiusX, radiusY, radiusZ ); } cmtk::Types::DataItem cmtk::DataGridFilter::MeanOperator::Reduce( std::vector& regionData ) { Types::DataItem sum = 0; for ( Types::GridIndexType i = 0; i < regionData.size(); ++i ) sum += regionData[i]; return sum / regionData.size(); } TypedArray::SmartPtr DataGridFilter::RegionMeanFilter( const Types::GridIndexType radiusX, const Types::GridIndexType radiusY, const Types::GridIndexType radiusZ ) const { return this->ApplyRegionFilter( radiusX, radiusY, radiusZ ); } TypedArray::SmartPtr DataGridFilter::FastRegionMeanFilter( const Types::GridIndexType radiusX, const Types::GridIndexType radiusY, const Types::GridIndexType radiusZ ) const { const DataGrid& dataGrid = *(this->m_DataGrid); if ( !dataGrid.GetData() ) return TypedArray::SmartPtr( NULL ); const TypedArray& dataArray = *(dataGrid.GetData()); DataGrid::IndexType radius; radius[0] = radiusX; radius[1] = radiusY; radius[2] = radiusZ; const Types::GridIndexType nPixels = dataGrid.GetNumberOfPixels(); const DataGrid::RegionType wholeImageRegion = dataGrid.GetWholeImageRegion(); std::vector sums( nPixels ); std::fill( sums.begin(), sums.end(), 0 ); std::vector cnts( nPixels ); std::fill( cnts.begin(), cnts.end(), 0 ); // // Mean filter is separable - process x,y,z separately // for ( int dim = 0; dim < 3; ++dim ) { const DataGrid::RegionType face = wholeImageRegion.GetFaceRegion( dim ); const Types::GridIndexType columnFrom = wholeImageRegion.From()[dim]; const Types::GridIndexType columnTo = wholeImageRegion.To()[dim]; const Types::GridIndexType nPixelsColumn = columnTo - columnFrom; std::vector sumsColumn( nPixelsColumn ); std::vector cntsColumn( nPixelsColumn ); for ( RegionIndexIterator fIt = RegionIndexIterator( face ); fIt != fIt.end(); ++fIt ) { double sum = 0; unsigned short count = 0; // // PASS 1 - accumulate all values and counts upwards // Types::GridIndexType idx0 = 0; DataGrid::IndexType idx = fIt.Index(); for ( idx[dim] = columnFrom; idx[dim] < columnTo; ++idx[dim], ++idx0 ) { const Types::GridIndexType offset = dataGrid.GetOffsetFromIndex( idx ); if ( ! dim ) // get first-pass values from data array { Types::DataItem value; if ( dataArray.Get( value, offset ) ) { ++count; } else { value = 0; } cntsColumn[idx0] = count; sumsColumn[idx0] = (sum+=value); } else { cntsColumn[idx0] = (count+=cnts[offset]); sumsColumn[idx0] = (sum+=sums[offset]); } } // // PASS 2 - compute differences between upper and lower end of kernel window // idx0 = 0; for ( idx[dim] = columnFrom; idx[dim] < columnTo; ++idx[dim], ++idx0 ) { const Types::GridIndexType offset = dataGrid.GetOffsetFromIndex( idx ); const Types::GridIndexType upper = static_cast( std::min( idx0+radius[dim], nPixelsColumn-1 ) ); sums[offset] = sumsColumn[upper]; cnts[offset] = cntsColumn[upper]; // if lower end of window is outside range, implicitly subtract zero from sums and counts const Types::GridIndexType lower = idx0-radius[dim]-1; if ( lower >= 0 ) { sums[offset] -= sumsColumn[lower]; cnts[offset] -= cntsColumn[lower]; } } } } TypedArray::SmartPtr result( TypedArray::Create( dataArray.GetType(), nPixels ) ); for ( Types::GridIndexType ofs = 0; ofs < nPixels; ++ofs ) { if ( cnts[ofs] ) { result->Set( sums[ofs] / cnts[ofs], ofs ); } else { result->SetPaddingAt( ofs ); } } return result; } TypedArray::SmartPtr DataGridFilter::FastRegionVarianceFilter( const Types::GridIndexType radiusX, const Types::GridIndexType radiusY, const Types::GridIndexType radiusZ ) const { if ( !this->m_DataGrid->GetData() ) return TypedArray::SmartPtr( NULL ); // // see http://en.wikipedia.org/wiki/Algorithms_for_calculating_variance // TypedArray::SmartPtr mean = this->FastRegionMeanFilter( radiusX, radiusY, radiusZ ); DataGrid::SmartPtr square( this->m_DataGrid->Clone() ); square->GetData()->ApplyFunctionDouble( Wrappers::Square ); square->SetData( Self( square ).FastRegionMeanFilter( radiusX, radiusY, radiusZ ) ); TypedArray& squareData = *(square->GetData()); const Types::GridIndexType nPixels = square->GetNumberOfPixels(); for ( Types::GridIndexType i = 0; i < nPixels; ++i ) { Types::DataItem vMean, vSquare; if ( mean->Get( vMean, i ) && squareData.Get( vSquare, i ) ) { squareData.Set( vSquare - vMean * vMean, i ); } else { squareData.SetPaddingAt( i ); } } return square->GetData(); } cmtk::Types::DataItem cmtk::DataGridFilter::VarianceOperator::Reduce( std::vector& regionData ) { const Types::DataItem mean = MeanOperator::Reduce( regionData ); Types::DataItem sum = 0; for ( Types::GridIndexType i = 0; i < regionData.size(); ++i ) { const Types::DataItem diff = mean - regionData[i]; sum += diff*diff; } return sum / regionData.size(); } TypedArray::SmartPtr DataGridFilter::RegionVarianceFilter( const Types::GridIndexType radiusX, const Types::GridIndexType radiusY, const Types::GridIndexType radiusZ ) const { return this->ApplyRegionFilter( radiusX, radiusY, radiusZ ); } cmtk::Types::DataItem cmtk::DataGridFilter::StandardDeviationOperator::Reduce( std::vector& regionData ) { return sqrt( VarianceOperator::Reduce( regionData ) ); } TypedArray::SmartPtr DataGridFilter::RegionStandardDeviationFilter( const Types::GridIndexType radiusX, const Types::GridIndexType radiusY, const Types::GridIndexType radiusZ ) const { return this->ApplyRegionFilter( radiusX, radiusY, radiusZ ); } cmtk::Types::DataItem cmtk::DataGridFilter::SmoothnessOperator::Reduce( std::vector& regionData ) { return (1.0 - 1.0/(1.0 + VarianceOperator::Reduce( regionData ) )); } TypedArray::SmartPtr DataGridFilter::RegionSmoothnessFilter( const Types::GridIndexType radiusX, const Types::GridIndexType radiusY, const Types::GridIndexType radiusZ ) const { return this->ApplyRegionFilter( radiusX, radiusY, radiusZ ); } cmtk::Types::DataItem cmtk::DataGridFilter::ThirdMomentOperator::Reduce( std::vector& regionData ) { const Types::DataItem mean = MeanOperator::Reduce( regionData ); Types::DataItem sum = 0.0; for ( Types::GridIndexType i = 0; i < regionData.size(); ++i ) { const Types::DataItem diff = mean - regionData[i]; sum += diff*diff*diff; } return sum / MathUtil::Square( regionData.size() ); } TypedArray::SmartPtr DataGridFilter::RegionThirdMomentFilter( const Types::GridIndexType radiusX, const Types::GridIndexType radiusY, const Types::GridIndexType radiusZ ) const { return this->ApplyRegionFilter( radiusX, radiusY, radiusZ ); } TypedArray::SmartPtr DataGridFilter::GetDataSobelFiltered() const { const TypedArray* data = this->m_DataGrid->GetData(); if ( !data ) return TypedArray::SmartPtr( NULL ); TypedArray::SmartPtr result = TypedArray::Create( data->GetType(), data->GetDataSize() ); Types::DataItem value = 0; Types::DataItem fov[3][3][3]; Progress::Begin( 0, this->m_DataGrid->m_Dims[2], 1 ); Types::GridIndexType offset = 0; for ( Types::GridIndexType z = 0; z < this->m_DataGrid->m_Dims[2]; ++z ) { Progress::SetProgress( z ); for ( Types::GridIndexType y = 0; y < this->m_DataGrid->m_Dims[1]; ++y ) for ( Types::GridIndexType x = 0; x < this->m_DataGrid->m_Dims[0]; ++x, ++offset ) { if ( x && y && z && ( xm_DataGrid->m_Dims[0]-1 ) && ( ym_DataGrid->m_Dims[1]-1 ) && ( zm_DataGrid->m_Dims[2]-1 ) ) { for ( Types::GridIndexType dz=-1; dz<2; ++dz ) for ( Types::GridIndexType dy=-1; dy<2; ++dy ) for ( Types::GridIndexType dx=-1; dx<2; ++dx ) if ( ! data->Get( fov[1+dx][1+dy][1+dz], offset+this->m_DataGrid->GetOffsetFromIndex( dx, dy, dz ) ) ) fov[1+dx][1+dy][1+dz] = 0; value = (Types::DataItem) ( fabs( fov[0][0][1] - fov[2][0][1] + 2 * ( fov[0][1][1] - fov[2][1][1] ) + fov[0][2][1] - fov[2][2][1] ) + fabs( fov[0][0][1] - fov[0][2][1] + 2 * ( fov[1][0][1] - fov[1][2][1] ) + fov[2][0][1] - fov[2][2][1] )+ fabs( fov[0][1][0] - fov[2][1][0] + 2 * ( fov[0][1][1] - fov[2][1][1] ) + fov[0][1][2] - fov[2][1][2] ) + fabs( fov[0][1][0] - fov[0][1][2] + 2 * ( fov[1][1][0] - fov[1][1][2] ) + fov[2][1][0] - fov[2][1][2] ) + fabs( fov[1][0][0] - fov[1][2][0] + 2 * ( fov[1][0][1] - fov[1][2][1] ) + fov[1][0][2] - fov[1][2][2] ) + fabs( fov[1][0][0] - fov[1][0][2] + 2 * ( fov[1][1][0] - fov[1][1][2] ) + fov[1][2][0] - fov[1][2][2] ) ) / 6; result->Set( value, offset ); } else { result->Set( 0, offset ); } } } Progress::Done(); return result; } TypedArray::SmartPtr DataGridFilter::GetDataKernelFiltered ( const std::vector& filterX, const std::vector& filterY, const std::vector& filterZ, const bool normalize ) const { if ( !this->m_DataGrid->GetData() ) return TypedArray::SmartPtr( NULL ); // create and initialize result (data are copied at this point from the source array) TypedArray::SmartPtr result( this->m_DataGrid->GetData()->Clone() ); ThreadPool& threadPool = ThreadPool::GetGlobalThreadPool(); const Types::GridIndexType numberOfTasks = 4 * threadPool.GetNumberOfThreads() - 3; // initialize common thread data std::vector params( numberOfTasks ); for ( Types::GridIndexType task = 0; task < numberOfTasks; ++task ) { params[task].thisObject = this; params[task].m_Result = result; params[task].m_Normalize = normalize; } // run x filter if ( filterX.size() > 1 ) { for ( Types::GridIndexType task = 0; task < numberOfTasks; ++task ) { params[task].m_Filter = &filterX; } threadPool.Run( GetFilteredDataThreadX, params ); } // run y filter if ( filterY.size() > 1 ) { for ( Types::GridIndexType task = 0; task < numberOfTasks; ++task ) { params[task].m_Filter = &filterY; } threadPool.Run( GetFilteredDataThreadY, params ); } // run z filter if ( filterZ.size() > 1 ) { for ( Types::GridIndexType task = 0; task < numberOfTasks; ++task ) { params[task].m_Filter = &filterZ; } threadPool.Run( GetFilteredDataThreadZ, params ); } return result; } void DataGridFilter ::GetFilteredDataThreadX( void* args, const size_t taskIdx, const size_t taskCnt, const size_t, const size_t ) { FilterThreadParameters* params = static_cast( args ); const Self* ThisConst = params->thisObject; const DataGrid::IndexType& dims = ThisConst->m_DataGrid->m_Dims; Types::GridIndexType maxDim = std::max( dims[0], std::max( dims[1], dims[2] ) ); const bool normalize = params->m_Normalize; const std::vector& filter = *(params->m_Filter); const Types::GridIndexType filterSize = filter.size(); std::vector pixelBufferFrom( maxDim ); std::vector pixelBufferTo( maxDim ); TypedArray::SmartPtr& result = params->m_Result; for ( Types::GridIndexType z=taskIdx; z < dims[2]; z += taskCnt ) { for ( Types::GridIndexType y=0; y < dims[1]; ++y ) { // copy row data to buffer Types::GridIndexType ofs = ThisConst->m_DataGrid->GetOffsetFromIndex( 0, y, z ); for ( Types::GridIndexType x=0; x < dims[0]; ++x, ++ofs ) if ( !result->Get( pixelBufferFrom[x], ofs ) ) pixelBufferFrom[x] = 0; // convolve row with filter for ( Types::GridIndexType x=0; x < dims[0]; ++x ) { // this keeps track of outside FOV data Types::DataItem correctOverlap = filter[0]; // central element first to initialize target value pixelBufferTo[x] = pixelBufferFrom[x] * filter[0]; // now convolve side elements for ( Types::GridIndexType t=1; t < (Types::GridIndexType)filterSize; ++t ) { // is x-t still inside the image? if ( x >= t ) { // yes: convolve pixelBufferTo[x] += pixelBufferFrom[x-t] * filter[t]; correctOverlap += filter[t]; } // same for x+t: if ( x+t < dims[0] ) { pixelBufferTo[x] += pixelBufferFrom[x+t] * filter[t]; correctOverlap += filter[t]; } } // correct value scaling for all missing (outside) elements if ( normalize && (correctOverlap != 0) ) pixelBufferTo[x] /= correctOverlap; } ofs = ThisConst->m_DataGrid->GetOffsetFromIndex( 0, y, z ); for ( Types::GridIndexType x=0; x < dims[0]; ++x, ++ofs ) result->Set( pixelBufferTo[x], ofs ); } } } void DataGridFilter ::GetFilteredDataThreadY( void* args, const size_t taskIdx, const size_t taskCnt, const size_t, const size_t ) { FilterThreadParameters* params = static_cast( args ); const Self* ThisConst = params->thisObject; const DataGrid::IndexType& dims = ThisConst->m_DataGrid->m_Dims; Types::GridIndexType maxDim = std::max( dims[0], std::max( dims[1], dims[2] ) ); const bool normalize = params->m_Normalize; const std::vector& filter = *(params->m_Filter); const Types::GridIndexType filterSize = filter.size(); std::vector pixelBufferFrom( maxDim ); std::vector pixelBufferTo( maxDim ); TypedArray::SmartPtr& result = params->m_Result; for ( Types::GridIndexType z=taskIdx; z < dims[2]; z+=taskCnt ) { for ( Types::GridIndexType x=0; x < dims[0]; ++x ) { for ( Types::GridIndexType y=0; y < dims[1]; ++y ) if ( !result->Get( pixelBufferFrom[y], ThisConst->m_DataGrid->GetOffsetFromIndex( x, y, z ) ) ) pixelBufferFrom[y] = 0; for ( Types::GridIndexType y=0; y < dims[1]; ++y ) { Types::DataItem correctOverlap = filter[0]; pixelBufferTo[y] = pixelBufferFrom[y] * filter[0]; for ( Types::GridIndexType t=1; t < (Types::GridIndexType)filterSize; ++t ) { if ( y >= t ) { pixelBufferTo[y] += pixelBufferFrom[y-t] * filter[t]; correctOverlap += filter[t]; } if ( y+t < dims[1] ) { pixelBufferTo[y] += pixelBufferFrom[y+t] * filter[t]; correctOverlap += filter[t]; } } // correct value scaling for all missing (outside) elements if ( normalize && (correctOverlap != 0) ) pixelBufferTo[y] /= correctOverlap; } // write back convolved data for ( Types::GridIndexType y=0; y < dims[1]; ++y ) result->Set( pixelBufferTo[y], ThisConst->m_DataGrid->GetOffsetFromIndex( x, y, z ) ); } } } void DataGridFilter ::GetFilteredDataThreadZ( void* args, const size_t taskIdx, const size_t taskCnt, const size_t, const size_t ) { FilterThreadParameters* params = static_cast( args ); const Self* ThisConst = params->thisObject; const DataGrid::IndexType& dims = ThisConst->m_DataGrid->m_Dims; Types::GridIndexType maxDim = std::max( dims[0], std::max( dims[1], dims[2] ) ); const bool normalize = params->m_Normalize; const std::vector& filter = *(params->m_Filter); const Types::GridIndexType filterSize = filter.size(); std::vector pixelBufferFrom( maxDim ); std::vector pixelBufferTo( maxDim ); TypedArray::SmartPtr& result = params->m_Result; for ( Types::GridIndexType y=taskIdx; y < dims[1]; y+=taskCnt ) { for ( Types::GridIndexType x=0; x < dims[0]; ++x ) { for ( Types::GridIndexType z=0; z < dims[2]; ++z ) if ( !result->Get( pixelBufferFrom[z], ThisConst->m_DataGrid->GetOffsetFromIndex( x, y, z ) ) ) pixelBufferFrom[z] = 0; for ( Types::GridIndexType z=0; z < dims[2]; ++z ) { Types::DataItem correctOverlap = filter[0]; pixelBufferTo[z] = pixelBufferFrom[z] * filter[0]; for ( Types::GridIndexType t=1; t < (Types::GridIndexType)filterSize; ++t ) { if ( z >= t ) { pixelBufferTo[z] += pixelBufferFrom[z-t] * filter[t]; correctOverlap += filter[t]; } if ( z+t < dims[2] ) { pixelBufferTo[z] += pixelBufferFrom[z+t] * filter[t]; correctOverlap += filter[t]; } } // correct value scaling for all missing (outside) elements if ( normalize && (correctOverlap != 0) ) pixelBufferTo[z] /= correctOverlap; } // write back convolved data for ( Types::GridIndexType z=0; z < dims[2]; ++z ) result->Set( pixelBufferTo[z], ThisConst->m_DataGrid->GetOffsetFromIndex( x, y, z ) ); } } } } // namespace cmtk cmtk-3.3.1/libs/Base/cmtkDataGridFilter.h000066400000000000000000000226331276303427400201520ustar00rootroot00000000000000/* // // Copyright 2016 Google, Inc. // // Copyright 2004-2013 SRI International // // Copyright 1997-2009 Torsten Rohlfing // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 5396 $ // // $LastChangedDate: 2016-01-13 21:24:32 -0800 (Wed, 13 Jan 2016) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkDataGridFilter_h_included_ #define __cmtkDataGridFilter_h_included_ #include #include #include #include namespace cmtk { /** \addtogroup Base */ //@{ /** Filter operations for data on 3D grids. * The filters in this class operate independent of grid spacings. */ class DataGridFilter : /// Prevent copying by inheritance. private CannotBeCopied { public: /// This class. typedef DataGridFilter Self; /// Constructor: link to DataGrid object. explicit DataGridFilter( DataGrid::SmartConstPtr dataGrid ); /// Return data after median-filtering with global filter radius (convenience function). TypedArray::SmartPtr GetDataMedianFiltered( const Types::GridIndexType radius ) const { return this->GetDataMedianFiltered( radius, radius, radius ); } /** Return data after median-filtering with per-dimension filter radius. *\param radiusX Region radius in x direction. *\param radiusY Region radius in y direction. *\param radiusZ Region radius in z direction. *\return Newly allocated data array with filtered data. */ TypedArray::SmartPtr GetDataMedianFiltered( const Types::GridIndexType radiusX, const Types::GridIndexType radiusY, const Types::GridIndexType radiusZ ) const; /** Apply neighborhood-mean filter. *\param radiusX Region radius in x direction. *\param radiusY Region radius in y direction. *\param radiusZ Region radius in z direction. *\return Newly allocated data array with filtered data. */ TypedArray::SmartPtr RegionMeanFilter( const Types::GridIndexType radiusX, const Types::GridIndexType radiusY, const Types::GridIndexType radiusZ ) const; /** Apply fast, recursive neighborhood-mean filter. *\param radiusX Region radius in x direction. *\param radiusY Region radius in y direction. *\param radiusZ Region radius in z direction. *\return Newly allocated data array with filtered data. */ TypedArray::SmartPtr FastRegionMeanFilter( const Types::GridIndexType radiusX, const Types::GridIndexType radiusY, const Types::GridIndexType radiusZ ) const; /** Apply neighborhood-variance filter. *\param radiusX Region radius in x direction. *\param radiusY Region radius in y direction. *\param radiusZ Region radius in z direction. *\return Newly allocated data array with filtered data. */ TypedArray::SmartPtr RegionVarianceFilter( const Types::GridIndexType radiusX, const Types::GridIndexType radiusY, const Types::GridIndexType radiusZ ) const; /** Apply fast neighborhood-variance filter based on linear-time region mean filter. *\param radiusX Region radius in x direction. *\param radiusY Region radius in y direction. *\param radiusZ Region radius in z direction. *\return Newly allocated data array with filtered data. */ TypedArray::SmartPtr FastRegionVarianceFilter( const Types::GridIndexType radiusX, const Types::GridIndexType radiusY, const Types::GridIndexType radiusZ ) const; /** Apply neighborhood-third-moment filter. *\param radiusX Region radius in x direction. *\param radiusY Region radius in y direction. *\param radiusZ Region radius in z direction. *\return Newly allocated data array with filtered data. */ TypedArray::SmartPtr RegionThirdMomentFilter( const Types::GridIndexType radiusX, const Types::GridIndexType radiusY, const Types::GridIndexType radiusZ ) const; /** Apply neighborhood-standard-deviation filter. *\param radiusX Region radius in x direction. *\param radiusY Region radius in y direction. *\param radiusZ Region radius in z direction. *\return Newly allocated data array with filtered data. */ TypedArray::SmartPtr RegionStandardDeviationFilter( const Types::GridIndexType radiusX, const Types::GridIndexType radiusY, const Types::GridIndexType radiusZ ) const; /** Apply neighborhood-smoothness filter. *\param radiusX Region radius in x direction. *\param radiusY Region radius in y direction. *\param radiusZ Region radius in z direction. *\return Newly allocated data array with filtered data. */ TypedArray::SmartPtr RegionSmoothnessFilter( const Types::GridIndexType radiusX, const Types::GridIndexType radiusY, const Types::GridIndexType radiusZ ) const; /** Apply neighborhood-entropy filter. *\param radiusX Region radius in x direction. *\param radiusY Region radius in y direction. *\param radiusZ Region radius in z direction. *\return Newly allocated data array with filtered data. */ TypedArray::SmartPtr RegionEntropyFilter( const Types::GridIndexType radiusX, const Types::GridIndexType radiusY, const Types::GridIndexType radiusZ ) const; /// Return data after Sobel filtering. TypedArray::SmartPtr GetDataSobelFiltered() const; /// Return after filtering with a separable kernel TypedArray::SmartPtr GetDataKernelFiltered( const std::vector& filterX, const std::vector& filterY, const std::vector& filterZ, const bool normalize = true /*!< Flag for normalization: if set, convolution result is divided by sum of actually used filter elements. */ ) const; private: /// The DataGrid object we're working on. DataGrid::SmartConstPtr m_DataGrid; /// Thread parameter for entropy evaluation. class FilterThreadParameters : /// Inherit from generic thread parameter class. public ThreadParameters { public: /// Filter kernel. const std::vector* m_Filter; /// Flag for normalization: if set, convolution result is divided by sum of actually used filter elements. bool m_Normalize; /// Pointer to result pixel data TypedArray::SmartPtr m_Result; }; /// Thread function for separable filtering in x-direction. static void GetFilteredDataThreadX( void *args, const size_t taskIdx, const size_t taskCnt, const size_t, const size_t ); /// Thread function for separable filtering in y-direction. static void GetFilteredDataThreadY( void *args, const size_t taskIdx, const size_t taskCnt, const size_t, const size_t ); /// Thread function for separable filtering in z-direction. static void GetFilteredDataThreadZ( void *args, const size_t taskIdx, const size_t taskCnt, const size_t, const size_t ); /** Median operator. * Reduce a vector of values to their median. */ class MedianOperator { public: /// Reduction operator: sort vector values in place, then return median element. static Types::DataItem Reduce( std::vector& regionData ); }; /** Mean operator. * Reduce a vector of values to their mean (average). */ class MeanOperator { public: /// Reduction operator: compute and return average of vector elements. static Types::DataItem Reduce( std::vector& regionData ); }; /** Variance operator. * Reduce a vector of values to their variance. */ class VarianceOperator { public: /// Reduction operator: compute and return variance of vector elements. static Types::DataItem Reduce( std::vector& regionData ); }; /** Standard deviation operator. * Reduce a vector of values to their standard deviation. */ class StandardDeviationOperator { public: /// Reduction operator: compute and return standard deviation of vector elements. static Types::DataItem Reduce( std::vector& regionData ); }; /** Smoothness operator. * Reduce a vector of values to their "smoothness". */ class SmoothnessOperator { public: /// Reduction operator: compute and return "smoothness" of vector elements. static Types::DataItem Reduce( std::vector& regionData ); }; /** Third moment operator. * Reduce a vector of values to their third moment. */ class ThirdMomentOperator { public: /// Reduction operator: compute and return third moment of vector elements. static Types::DataItem Reduce( std::vector& regionData ); }; /// Apply a regional filter operator. The actual operator is given as a class template parameter. template TypedArray::SmartPtr ApplyRegionFilter( const Types::GridIndexType radiusX, const Types::GridIndexType radiusY, const Types::GridIndexType radiusZ ) const; }; } // namespace cmtk #include "cmtkDataGridFilter.txx" #endif // #ifndef __cmtkDataGridFilter_h_included_ cmtk-3.3.1/libs/Base/cmtkDataGridFilter.txx000066400000000000000000000066041276303427400205460ustar00rootroot00000000000000/* // // Copyright 2016 Google, Inc. // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2011, 2013-2014 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 5396 $ // // $LastChangedDate: 2016-01-13 21:24:32 -0800 (Wed, 13 Jan 2016) $ // // $LastChangedBy: torstenrohlfing $ // */ #include template cmtk::TypedArray::SmartPtr cmtk::DataGridFilter::ApplyRegionFilter( const Types::GridIndexType radiusX, const Types::GridIndexType radiusY, const Types::GridIndexType radiusZ ) const { const TypedArray* data = this->m_DataGrid->GetData(); if ( !data ) return TypedArray::SmartPtr( NULL ); TypedArray::SmartPtr result = TypedArray::Create( data->GetType(), data->GetDataSize() ); const Types::GridIndexType widthX = 1 + 2*radiusX; const Types::GridIndexType widthY = 1 + 2*radiusY; const Types::GridIndexType widthZ = 1 + 2*radiusZ; const Types::GridIndexType pixelsPerPlane = this->m_DataGrid->m_Dims[0] * this->m_DataGrid->m_Dims[1]; #pragma omp parallel for for ( Types::GridIndexType z = 0; z < this->m_DataGrid->m_Dims[2]; ++z ) { Types::GridIndexType offset = z * pixelsPerPlane; std::vector regionData( widthX*widthY*widthZ ); Types::GridIndexType zFrom = ( z > radiusZ ) ? ( z - radiusZ ) : 0; Types::GridIndexType zTo = std::min( z+radiusZ+1, this->m_DataGrid->m_Dims[2] ); for ( Types::GridIndexType y = 0; y < this->m_DataGrid->m_Dims[1]; ++y ) { Types::GridIndexType yFrom = ( y > radiusY ) ? ( y - radiusY ) : 0; Types::GridIndexType yTo = std::min( y+radiusY+1, this->m_DataGrid->m_Dims[1] ); for ( Types::GridIndexType x = 0; x < this->m_DataGrid->m_Dims[0]; ++x, ++offset ) { Types::GridIndexType xFrom = ( x > radiusX ) ? ( x - radiusX ) : 0; Types::GridIndexType xTo = std::min( x+radiusX+1, this->m_DataGrid->m_Dims[0] ); regionData.clear(); Types::GridIndexType ofsZ = yFrom + this->m_DataGrid->m_Dims[1] * zFrom; for ( Types::GridIndexType zz = zFrom; zz < zTo; ++zz, ofsZ += this->m_DataGrid->m_Dims[1] ) { Types::GridIndexType ofsYZ = this->m_DataGrid->m_Dims[0] * ofsZ ; for ( Types::GridIndexType yy = yFrom; yy < yTo; ++yy, ofsYZ += this->m_DataGrid->m_Dims[0] ) { Types::GridIndexType toYZ = ofsYZ + xTo; for ( Types::GridIndexType xx = xFrom + ofsYZ; xx < toYZ; ++xx ) { Types::DataItem value = 0; if ( data->Get( value, xx ) ) { regionData.push_back( value ); } } } } result->Set( TFilter::Reduce( regionData ), offset ); } } } return result; } cmtk-3.3.1/libs/Base/cmtkDataGridLocalCorrelation.cxx000066400000000000000000000021631276303427400225300ustar00rootroot00000000000000/* // // Copyright 2011 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 2943 $ // // $LastChangedDate: 2011-03-04 11:40:52 -0800 (Fri, 04 Mar 2011) $ // // $LastChangedBy: torstenrohlfing $ // */ #include "cmtkDataGridLocalCorrelation.h" void cmtk::DataGridLocalCorrelation::ComputeResult() { } cmtk-3.3.1/libs/Base/cmtkDataGridLocalCorrelation.h000066400000000000000000000042451276303427400221600ustar00rootroot00000000000000/* // // Copyright 2011 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 2943 $ // // $LastChangedDate: 2011-03-04 11:40:52 -0800 (Fri, 04 Mar 2011) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkDataGridLocalCorrelation_h_included_ #define __cmtkDataGridLocalCorrelation_h_included_ #include #include #include namespace cmtk { /** \addtogroup Base */ //@{ /** Compute local correlation between two data grid objects. */ class DataGridLocalCorrelation { public: /// This class. typedef DataGridLocalCorrelation Self; /// Constructor. DataGridLocalCorrelation( const DataGrid& dg1 /*!< First input data grid. */, const DataGrid& dg2 /*!< Second input data grid */ ) : m_Grid1( dg1 ), m_Grid2( dg2 ), m_Result( NULL ) {} /// Get result. DataGrid::SmartPtr& GetResult() { if ( ! this->m_Result ) this->ComputeResult(); return this->m_Result; } private: /// First input data grid. const DataGrid& m_Grid1; /// Second input data grid. const DataGrid& m_Grid2; /// Window radius. DataGrid::IndexType m_Radius; /// Result data grid. DataGrid::SmartPtr m_Result; /// Compute the result using the current inputs and parameters. void ComputeResult(); }; //@} } // namespace cmtk #endif // #ifndef __cmtkDataGridLocalCorrelation_h_included_ cmtk-3.3.1/libs/Base/cmtkDataGridMorphologicalOperators.cxx000066400000000000000000000160631276303427400237760ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2014 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 5265 $ // // $LastChangedDate: 2014-03-28 11:10:05 -0700 (Fri, 28 Mar 2014) $ // // $LastChangedBy: torstenrohlfing $ // */ #include "cmtkDataGridMorphologicalOperators.h" #include #include #include namespace cmtk { DataGridMorphologicalOperators ::DataGridMorphologicalOperators( const DataGrid::SmartConstPtr& dataGrid ) : m_DataGrid( dataGrid ) {} TypedArray::SmartPtr DataGridMorphologicalOperators::GetEroded( const int iterations ) const { TypedArray::SmartPtr dataArray = this->m_DataGrid->GetData(); if ( ! dataArray ) return TypedArray::SmartPtr( NULL ); // if not byte array, convert first (copy will be de-allocated automatically upon exiting this function). if ( dataArray->GetType() != TYPE_BYTE ) { dataArray = dataArray->Convert( TYPE_BYTE ); } const byte* data = static_cast( dataArray->GetDataPtr() ); std::vector tmp( dataArray->GetDataSize() ); ByteArray::SmartPtr erodedArray = ByteArray::Create( dataArray->GetDataSize() ); byte* eroded = erodedArray->GetDataPtrConcrete(); memcpy( eroded, data, erodedArray->GetDataSizeBytes() ); for ( int i = 0; i < iterations; ++i ) { size_t offset = 0; for ( int z = 0; z < this->m_DataGrid->m_Dims[2]; ++z ) { const int dzFrom = z ? -1 : 0, dzTo = (zm_DataGrid->m_Dims[2]-1) ? 1 : 0; for ( int y = 0; y < this->m_DataGrid->m_Dims[1]; ++y ) { const int dyFrom = y ? -1 : 0, dyTo = (ym_DataGrid->m_Dims[1]-1) ? 1 : 0; for ( int x = 0; x < this->m_DataGrid->m_Dims[0]; ++x, ++offset ) { const int dxFrom = x ? -1 : 0, dxTo = (xm_DataGrid->m_Dims[0]-1) ? 1 : 0; if ( eroded[offset] ) { bool erodePixel = false; for ( int dz = dzFrom; (dz <= dzTo) && !erodePixel; ++dz ) for ( int dy = dyFrom; (dy <= dyTo) && !erodePixel; ++dy ) for ( int dx = dxFrom; (dx <= dxTo) && !erodePixel; ++dx ) if ( dx || dy || dz ) if ( ! eroded[offset+this->m_DataGrid->GetOffsetFromIndex( dx, dy, dz )] ) erodePixel = true; if ( erodePixel ) tmp[offset] = 0; else tmp[offset] = eroded[offset]; } else { // if data tmp[offset] = 0; } // if data } // for x } // for y } // for z memcpy( eroded, &(tmp[0]), erodedArray->GetDataSizeBytes() ); } // for i erodedArray->SetDataClass( DATACLASS_LABEL ); return erodedArray; } TypedArray::SmartPtr DataGridMorphologicalOperators::GetDilated( const int iterations ) const { TypedArray::SmartPtr dataArray = this->m_DataGrid->GetData(); if ( ! dataArray ) return TypedArray::SmartPtr( NULL ); // if not byte array, convert first (copy will be de-allocated automatically upon exiting this function). if ( dataArray->GetType() != TYPE_BYTE ) { dataArray = dataArray->Convert( TYPE_BYTE ); } const byte* data = static_cast( dataArray->GetDataPtr() ); std::vector tmp( dataArray->GetDataSize() ); ByteArray::SmartPtr dilatedArray = ByteArray::Create( dataArray->GetDataSize() ); byte* dilated = dilatedArray->GetDataPtrConcrete(); memcpy( dilated, data, dilatedArray->GetDataSizeBytes() ); for ( int i = 0; i < iterations; ++i ) { size_t offset = 0; for ( int z = 0; z < this->m_DataGrid->m_Dims[2]; ++z ) { const int dzFrom = z ? -1 : 0, dzTo = (zm_DataGrid->m_Dims[2]-1) ? 1 : 0; for ( int y = 0; y < this->m_DataGrid->m_Dims[1]; ++y ) { const int dyFrom = y ? -1 : 0, dyTo = (ym_DataGrid->m_Dims[1]-1) ? 1 : 0; for ( int x = 0; x < this->m_DataGrid->m_Dims[0]; ++x, ++offset ) { const int dxFrom = x ? -1 : 0, dxTo = (xm_DataGrid->m_Dims[0]-1) ? 1 : 0; if ( ! dilated[offset] ) { byte dilatePixel = 0; for ( int dz = dzFrom; (dz <= dzTo) && !dilatePixel; ++dz ) for ( int dy = dyFrom; (dy <= dyTo) && !dilatePixel; ++dy ) for ( int dx = dxFrom; (dx <= dxTo) && !dilatePixel; ++dx ) if ( dx || dy || dz ) dilatePixel = dilated[offset+this->m_DataGrid->GetOffsetFromIndex(dx,dy,dz)]; if ( dilatePixel ) tmp[offset] = dilatePixel; else tmp[offset] = 0; } else { // !data tmp[offset] = dilated[offset]; } // else !data } // for x } // for y } // for z memcpy( dilated, &(tmp[0]), dilatedArray->GetDataSizeBytes() ); } // for i dilatedArray->SetDataClass( DATACLASS_LABEL ); return dilatedArray; } TypedArray::SmartPtr DataGridMorphologicalOperators::GetBoundaryMap( const bool multiValued ) const { TypedArray::SmartPtr dataArray = this->m_DataGrid->GetData(); if ( ! dataArray ) return TypedArray::SmartPtr( NULL ); ShortArray::SmartPtr boundaryArray = ShortArray::Create( dataArray->GetDataSize() ); short* boundary = boundaryArray->GetDataPtrConcrete(); #pragma omp parallel for for ( int z = 0; z < this->m_DataGrid->m_Dims[2]; ++z ) { size_t offset = z * this->m_DataGrid->m_Dims[0] * this->m_DataGrid->m_Dims[1]; Types::DataItem value, neighbor; const int dzFrom = z ? -1 : 0, dzTo = (zm_DataGrid->m_Dims[2]-1) ? 1 : 0; for ( int y = 0; y < this->m_DataGrid->m_Dims[1]; ++y ) { const int dyFrom = y ? -1 : 0, dyTo = (ym_DataGrid->m_Dims[1]-1) ? 1 : 0; for ( int x = 0; x < this->m_DataGrid->m_Dims[0]; ++x, ++offset ) { const int dxFrom = x ? -1 : 0, dxTo = (xm_DataGrid->m_Dims[0]-1) ? 1 : 0; bool bp = false; if ( dataArray->Get( value, offset ) ) { for ( int dz = dzFrom; (dz <= dzTo) && !bp; ++dz ) for ( int dy = dyFrom; (dy <= dyTo) && !bp; ++dy ) for ( int dx = dxFrom; (dx <= dxTo) && !bp; ++dx ) if ( dx || dy || dz ) if ( dataArray->Get( neighbor, offset+this->m_DataGrid->GetOffsetFromIndex(dx,dy,dz) ) ) bp = (value != neighbor); } else { value = 0; } if ( bp ) { if ( multiValued ) boundary[offset] = static_cast( value ); else boundary[offset] = 1; } else boundary[offset] = 0; } // for x } // for y } // for z boundaryArray->SetDataClass( DATACLASS_LABEL ); return boundaryArray; } } // namespace cmtk cmtk-3.3.1/libs/Base/cmtkDataGridMorphologicalOperators.h000066400000000000000000000072301276303427400234170ustar00rootroot00000000000000/* // // Copyright 2004-2011, 2013 SRI International // // Copyright 1997-2010 Torsten Rohlfing // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 5000 $ // // $LastChangedDate: 2013-10-21 12:11:07 -0700 (Mon, 21 Oct 2013) $ // // $LastChangedBy: torsten_at_home $ // */ #ifndef __cmtkDataGridMorphologicalOperators_h_included_ #define __cmtkDataGridMorphologicalOperators_h_included_ #include #include #include namespace cmtk { /** \addtogroup Base */ //@{ /** Morphological operators applied to a 3D grid of data. * This class provides a selection of morphological operators that act on data arranged on * a regular 3D grid, but do not require the real-world spacing of the grid points for * their operation. */ class DataGridMorphologicalOperators : /// Prevent copying by inheritance. private CannotBeCopied { public: /// This class. typedef DataGridMorphologicalOperators Self; /// Constructor: link to DataGrid object. DataGridMorphologicalOperators( const DataGrid::SmartConstPtr& dataGrid ); /** Return map of region boundaries. * This function returns a byte data array where each pixel is one if it is * a boundary pixel, i.e., if one of its neighbours in this object has a * different value than it has itself. All other pixels are set to zero. *\param multiValued If this is set (default: false), then the resulting * boundary map is multi valued, i.e., instead of setting boundary pixels * to "1", they are set to the value present in the image at that location. *\note The boundary contours are at least 2 pixels wide since "boundaryness" * is a symmetric relationship. */ TypedArray::SmartPtr GetBoundaryMap( const bool multiValued = false ) const; /// Get data after erosion operator. TypedArray::SmartPtr GetEroded( const int iterations = 1 /*!< Number of erosion iterations. */ ) const; /// Get data after dilation operator. TypedArray::SmartPtr GetDilated( const int iterations = 1 /*!< Number of dilation iterations. */ ) const; /** Get connected components of a binary image. * All pixels with non-zero values are considered "foreground," and the result * of this function is a partitioning of the foreground into connected components. * Connectivity is determined based on 8 neighbours in the 3D grid. */ TypedArray::SmartPtr GetBinaryConnectedComponents() const; /** Get data with region labels renumbered by decreasing region size. * This is typically run after GetBinaryConnectedComponents() to order the * components and be able to easily select, say, the largest k components. */ TypedArray::SmartPtr GetRegionsRenumberedBySize() const; private: /// The DataGrid object we're working on. DataGrid::SmartConstPtr m_DataGrid; }; } // namespace cmtk #endif // #ifndef __cmtkDataGridMorphologicalOperators_h_included_ cmtk-3.3.1/libs/Base/cmtkDataGrid_Crop.cxx000066400000000000000000000144711276303427400203430ustar00rootroot00000000000000/* // // Copyright 2016 Google, Inc. // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2013 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 5396 $ // // $LastChangedDate: 2016-01-13 21:24:32 -0800 (Wed, 13 Jan 2016) $ // // $LastChangedBy: torstenrohlfing $ // */ #include "cmtkDataGrid.h" namespace cmtk { void DataGrid::SetCropRegion( const Self::RegionType& region ) { this->m_CropRegion = region; for ( int dim = 0; dim < 3; ++dim ) { // for negative crop region index values, go from upper, rather than lower, grid boundary if ( this->m_CropRegion.From()[dim] < 0 ) this->m_CropRegion.From()[dim] = this->m_Dims[dim] + this->m_CropRegion.From()[dim]; if ( this->m_CropRegion.To()[dim] < 0 ) this->m_CropRegion.To()[dim] = this->m_Dims[dim] + this->m_CropRegion.To()[dim]; // check whether all cropping index values are within the valid range and truncate if necessary this->m_CropRegion.To()[dim] = std::min( this->m_Dims[dim], std::max( 0, this->m_CropRegion.To()[dim] ) ); this->m_CropRegion.From()[dim] = std::min( this->m_Dims[dim], std::max( 0, this->m_CropRegion.From()[dim] ) ); } } const DataGrid::IndexType DataGrid::GetCropRegionIncrements () const { DataGrid::IndexType increments; increments[0] = this->m_CropRegion.From()[0] + this->m_Dims[0] * ( this->m_CropRegion.From()[1] + this->m_Dims[1] * this->m_CropRegion.From()[2] ); increments[1] = this->m_CropRegion.From()[0] + (this->m_Dims[0] - this->m_CropRegion.To()[0]); increments[2] = this->m_Dims[0] * ( this->m_CropRegion.From()[1] + ( this->m_Dims[1] - this->m_CropRegion.To()[1]) ); return increments; } DataGrid::RegionType DataGrid::AutoCrop ( const Types::DataItem threshold, const bool recrop, const Types::GridIndexType margin ) { const TypedArray* data = this->GetData(); Self::IndexType cropFrom = this->CropRegion().From(), cropTo = this->CropRegion().To(); if ( ! recrop ) { for ( int dim = 0; dim < 3; ++dim ) { cropFrom[dim] = 0; cropTo[dim] = this->m_Dims[dim]; } } const Types::GridIndexType nextRow = this->m_Dims[0] - cropTo[0] + this->m_CropRegion.From()[0]; const Types::GridIndexType nextPlane = this->m_Dims[0] * (this->m_Dims[1] - cropTo[1] + this->m_CropRegion.From()[1]); Types::GridIndexType offset = cropFrom[0] + this->m_Dims[0] * ( cropFrom[1] + this->m_Dims[1] * cropFrom[2] ); Self::IndexType newCropFrom = cropTo, newCropTo = cropFrom; Self::IndexType xyz; for ( xyz[2] = cropFrom[2]; xyz[2] < cropTo[2]; ++xyz[2], offset += nextPlane ) for ( xyz[1] = cropFrom[1]; xyz[1] < cropTo[1]; ++xyz[1], offset += nextRow ) for ( xyz[0] = cropFrom[0]; xyz[0] < cropTo[0]; ++xyz[0], ++offset ) { Types::DataItem value = 0; if ( ! data->Get( value, offset ) || (value < threshold) ) continue; for ( int i = 0; i < 3; ++i ) { newCropFrom[i] = std::min( newCropFrom[i], xyz[i] ); newCropTo[i] = std::max( newCropTo[i], xyz[i] ); } } for ( int dim = 0; dim < 3; ++dim ) { ++newCropTo[dim]; } if ( margin ) { for ( int dim = 0; dim < 3; ++dim ) { newCropFrom[dim] = std::max( 0, newCropFrom[dim] - margin ); newCropTo[dim] = std::min( this->m_Dims[dim], newCropTo[dim] + margin ); } } return (this->m_CropRegion = Self::RegionType( newCropFrom, newCropTo )); } void DataGrid::FillCropBackground( const Types::DataItem value ) { const Types::GridIndexType planeSize = this->m_Dims[0] * this->m_Dims[1]; Types::GridIndexType offset = this->m_CropRegion.From()[2] * planeSize; this->m_Data->BlockSet( value, 0, offset ); for ( Types::GridIndexType z = this->m_CropRegion.From()[2]; z < this->m_CropRegion.To()[2]; ++z ) { Types::GridIndexType planeOffset = offset + this->m_CropRegion.From()[1] * this->m_Dims[0]; this->m_Data->BlockSet( value, offset, planeOffset ); offset = planeOffset; for ( Types::GridIndexType y = this->m_CropRegion.From()[1]; y < this->m_CropRegion.To()[1]; ++y, offset += this->m_Dims[0] ) { this->m_Data->BlockSet( value, offset, offset+this->m_CropRegion.From()[0] ); this->m_Data->BlockSet( value, offset+this->m_CropRegion.To()[0], offset+this->m_Dims[0] ); } planeOffset = offset + (this->m_Dims[1]-this->m_CropRegion.To()[1]) * this->m_Dims[0]; this->m_Data->BlockSet( value, offset, planeOffset ); offset = planeOffset; } this->m_Data->BlockSet( value, this->m_CropRegion.To()[2] * planeSize, this->m_Dims[2] * planeSize ); } TypedArray::SmartPtr DataGrid::GetRegionData( const Self::RegionType& region ) const { const TypedArray* srcData = this->GetData(); if ( ! srcData ) return TypedArray::SmartPtr( NULL ); TypedArray::SmartPtr cropData = TypedArray::Create( srcData->GetType(), region.Size() ); const Types::GridIndexType lineLength = region.To()[0] - region.From()[0]; const Types::GridIndexType nextPlane = this->m_Dims[0] * (this->m_Dims[1] - (region.To()[1] - region.From()[1])); Types::GridIndexType toOffset = 0; Types::GridIndexType fromOffset = this->GetOffsetFromIndex( region.From() ); for ( Types::GridIndexType z = region.From()[2]; z < region.To()[2]; ++z, fromOffset += nextPlane ) for ( Types::GridIndexType y = region.From()[1]; y < region.To()[1]; ++y, fromOffset += this->m_Dims[0] ) { srcData->BlockCopy( *cropData, toOffset, fromOffset, lineLength ); toOffset += lineLength; } return cropData; } } // namespace cmtk cmtk-3.3.1/libs/Base/cmtkDataTypeTraits.h000066400000000000000000000310201276303427400202150ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2012 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4415 $ // // $LastChangedDate: 2012-06-04 14:57:12 -0700 (Mon, 04 Jun 2012) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkDataTypeTraits_h_included_ #define __cmtkDataTypeTraits_h_included_ #include #include #include #include namespace cmtk { /** \addtogroup Base */ //@{ /// Make signed data types. template class MakeSigned {}; template<> class MakeSigned { public: typedef signed long int Type; }; template<> class MakeSigned { public: typedef signed long int Type; }; template<> class MakeSigned { public: typedef signed int Type; }; template<> class MakeSigned { public: typedef signed int Type; }; template<> class MakeSigned { public: typedef signed short int Type; }; template<> class MakeSigned { public: typedef signed short int Type; }; template<> class MakeSigned { public: typedef signed char Type; }; template<> class MakeSigned { public: typedef signed char Type; }; template<> class MakeSigned { public: typedef float Type; }; template<> class MakeSigned { public: typedef double Type; }; /** Data type traits */ template class DataTypeTraits { public: /** Scalar type ID constant for this type. */ static const ScalarDataType DataTypeID = TYPE_NONE; }; /** Data type traits for single-precision floating point. */ template<> class DataTypeTraits { public: /** This class. */ typedef DataTypeTraits Self; /// The template value type. typedef float ValueType; /** Scalar type ID constant for this type. */ static const ScalarDataType DataTypeID = TYPE_FLOAT; /** Return given value converted (and rounded) to discrete type. */ template static inline Self::ValueType Convert ( const T value, const bool = false, const Self::ValueType = 0 ) { return static_cast( value ); } /** Return padding data value (i.e. Inf) for the given type. */ static inline Self::ValueType ChoosePaddingValue () { return std::numeric_limits::infinity(); } /// Return zero value for this type. static inline Self::ValueType Zero() { return 0.0; } /// Return one value (multiplicative identity) for this type. static inline Self::ValueType One() { return 1.0; } }; /** Data type traits for double-precision floating point. */ template<> class DataTypeTraits { public: /** This class. */ typedef DataTypeTraits Self; /// The template value type. typedef double ValueType; /** Scalar type ID constant for this type. */ static const ScalarDataType DataTypeID = TYPE_DOUBLE; /** Return given value converted (and rounded) to discrete type. */ template static inline Self::ValueType Convert ( const T value, const bool = false, const Self::ValueType = 0 ) { return static_cast( value ); } /** Return padding data value (i.e. Inf) for the given type. */ static inline Self::ValueType ChoosePaddingValue () { return std::numeric_limits::infinity(); } /// Return zero value for this type. static inline Self::ValueType Zero() { return 0.0; } /// Return one value (multiplicative identity) for this type. static inline Self::ValueType One() { return 1.0; } }; /** Data type traits for unsigned char (byte). */ template<> class DataTypeTraits { public: /** This class. */ typedef DataTypeTraits Self; /// The template value type. typedef byte ValueType; /** Scalar type ID constant for this type. */ static const ScalarDataType DataTypeID = TYPE_BYTE; /** Return given value converted (and rounded) to discrete type. */ template static inline Self::ValueType Convert ( const T value, const bool paddingFlag = false, const Self::ValueType paddingData = 0 ) { using namespace std; if ( MathUtil::IsFinite( value ) ) { return (Self::ValueType) ((value::min()) ? std::numeric_limits::min() : (value+0.5>std::numeric_limits::max()) ? std::numeric_limits::max() : floor(value+0.5)); } else { if ( paddingFlag ) return paddingData; else return ChoosePaddingValue(); } } /** Return padding data value for the given type. */ static inline Self::ValueType ChoosePaddingValue () { return 255; } /// Return zero value for this type. static inline Self::ValueType Zero() { return 0; } /// Return one value (multiplicative identity) for this type. static inline Self::ValueType One() { return 1; } }; /** Data type traits for signed char. */ template<> class DataTypeTraits { public: /** This class. */ typedef DataTypeTraits Self; /// The template value type. typedef char ValueType; /** Scalar type ID constant for this type. */ static const ScalarDataType DataTypeID = TYPE_CHAR; /** Return given value converted (and rounded) to discrete type. */ template static inline Self::ValueType Convert ( const T value, const bool paddingFlag = false, const Self::ValueType paddingData = 0 ) { using namespace std; if ( MathUtil::IsFinite( value ) ) { return (Self::ValueType) ((value::min()) ? std::numeric_limits::min() : (value+0.5>std::numeric_limits::max()) ? std::numeric_limits::max() : floor(value+0.5)); } else { if ( paddingFlag ) return paddingData; else return ChoosePaddingValue(); } } /** Return padding data value for the given type. */ static inline Self::ValueType ChoosePaddingValue () { return -1; } /// Return zero value for this type. static inline Self::ValueType Zero() { return 0; } /// Return one value (multiplicative identity) for this type. static inline Self::ValueType One() { return 1; } }; /** Data type traits for signed short. */ template<> class DataTypeTraits { public: /** This class. */ typedef DataTypeTraits Self; /// The template value type. typedef signed short ValueType; /** Scalar type ID constant for this type. */ static const ScalarDataType DataTypeID = TYPE_SHORT; /** Return given value converted (and rounded) to discrete type. */ template static inline Self::ValueType Convert ( const T value, const bool paddingFlag = false, const Self::ValueType paddingData = 0 ) { using namespace std; if ( MathUtil::IsFinite( value ) ) { return (Self::ValueType) (((value::min()) ? std::numeric_limits::min() : (value+0.5>std::numeric_limits::max()) ? std::numeric_limits::max() : floor(value+0.5))); } else { if ( paddingFlag ) return paddingData; else return ChoosePaddingValue(); } } /** Return padding data value for the given type. */ static inline Self::ValueType ChoosePaddingValue () { return -1; } /// Return zero value for this type. static inline Self::ValueType Zero() { return 0; } /// Return one value (multiplicative identity) for this type. static inline Self::ValueType One() { return 1; } }; /** Data type traits for unsigned short. */ template<> class DataTypeTraits { public: /** This class. */ typedef DataTypeTraits Self; /// The template value type. typedef unsigned short ValueType; /** Scalar type ID constant for this type. */ static const ScalarDataType DataTypeID = TYPE_USHORT; /** Return given value converted (and rounded) to discrete type. */ template static inline Self::ValueType Convert ( const T value, const bool paddingFlag = false, const Self::ValueType paddingData = 0 ) { using namespace std; if ( MathUtil::IsFinite( value ) ) { return (Self::ValueType) ((value::min()) ? std::numeric_limits::min() : (value+0.5>std::numeric_limits::max()) ? std::numeric_limits::max() : floor(value+0.5)); } else { if ( paddingFlag ) return paddingData; else return ChoosePaddingValue(); } } /** Return padding data value for the given type. */ static inline Self::ValueType ChoosePaddingValue () { return std::numeric_limits::max(); } /// Return zero value for this type. static inline Self::ValueType Zero() { return 0; } /// Return one value (multiplicative identity) for this type. static inline Self::ValueType One() { return 1; } }; /** Data type traits for int. */ template<> class DataTypeTraits { public: /** This class. */ typedef DataTypeTraits Self; /// The template value type. typedef int ValueType; /** Scalar type ID constant for this type. */ static const ScalarDataType DataTypeID = TYPE_INT; /** Return given value converted (and rounded) to discrete type. */ template static inline Self::ValueType Convert ( const T value, const bool paddingFlag = false, const Self::ValueType paddingData = 0 ) { using namespace std; if ( MathUtil::IsFinite( value ) ) { return (Self::ValueType) ((value::min()) ? std::numeric_limits::min() : (value+0.5>std::numeric_limits::max()) ? std::numeric_limits::max() : floor(value+0.5)); } else { if ( paddingFlag ) return paddingData; else return ChoosePaddingValue(); } } /** Return padding data value for the given type. */ static inline Self::ValueType ChoosePaddingValue () { return -1; } /// Return zero value for this type. static inline Self::ValueType Zero() { return 0; } /// Return one value (multiplicative identity) for this type. static inline Self::ValueType One() { return 1; } }; /** Data type traits for unsigned int. */ template<> class DataTypeTraits { public: /** This class. */ typedef DataTypeTraits Self; /// The template value type. typedef unsigned int ValueType; /** Scalar type ID constant for this type. */ static const ScalarDataType DataTypeID = TYPE_UINT; /** Return given value converted (and rounded) to discrete type. */ template static inline Self::ValueType Convert ( const T value, const bool paddingFlag = false, const Self::ValueType paddingData = 0 ) { using namespace std; if ( MathUtil::IsFinite( value ) ) { return (Self::ValueType) ((static_cast( value ) < std::numeric_limits::min()) ? std::numeric_limits::min() : (value+0.5>std::numeric_limits::max()) ? std::numeric_limits::max() : floor(value+0.5)); } else { if ( paddingFlag ) return paddingData; else return ChoosePaddingValue(); } } /** Return padding data value for the given type. */ static inline Self::ValueType ChoosePaddingValue () { return static_cast( -1 ); } /// Return zero value for this type. static inline Self::ValueType Zero() { return 0; } /// Return one value (multiplicative identity) for this type. static inline Self::ValueType One() { return 1; } }; //@} } // namespace cmtk #endif // #ifndef __cmtkDataTypeTraits_h_included_ cmtk-3.3.1/libs/Base/cmtkDeformationField.cxx000066400000000000000000000113761276303427400211150ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2012 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4540 $ // // $LastChangedDate: 2012-10-02 15:39:49 -0700 (Tue, 02 Oct 2012) $ // // $LastChangedBy: torstenrohlfing $ // */ #include "cmtkDeformationField.h" namespace cmtk { /** \addtogroup Base */ //@{ void DeformationField::InitControlPoints( const AffineXform* affineXform ) { this->m_ParameterVector->Clear(); if ( affineXform ) { Types::Coordinate *ofs = this->m_Parameters; Self::SpaceVectorType p; p[2] = this->m_Offset[2]; for ( int z = 0; z < this->m_Dims[2]; ++z, p[2] += this->m_Spacing[2] ) { p[1] = this->m_Offset[1]; for ( int y = 0; y < this->m_Dims[1]; ++y, p[1] += this->m_Spacing[1] ) { p[0] = this->m_Offset[0]; for ( int x = 0; x < this->m_Dims[0]; ++x, p[0] += this->m_Spacing[0], ofs+=3 ) { const Self::SpaceVectorType q( affineXform->Apply( p ) - p ); ofs[0] = q[0]; ofs[1] = q[1]; ofs[2] = q[2]; } } } this->m_InverseAffineScaling = affineXform->GetScales(); this->m_GlobalScaling = affineXform->GetGlobalScaling(); } else { this->m_InverseAffineScaling[0] = this->m_InverseAffineScaling[1] = this->m_InverseAffineScaling[2] = this->m_GlobalScaling = 1.0; } } DeformationField::SpaceVectorType DeformationField ::GetTransformedGrid ( const int idxX, const int idxY, const int idxZ ) const { const Types::Coordinate* coeff = this->m_Parameters + nextI * idxX + nextJ * idxY + nextK * idxZ; return (this->GetOriginalControlPointPosition( idxX, idxY, idxZ ) + Self::SpaceVectorType::FromPointer( coeff )); } void DeformationField::GetTransformedGridRow ( Self::SpaceVectorType *const vIn, const int numPoints, const int idxX, const int idxY, const int idxZ ) const { Self::SpaceVectorType *v = vIn; const Types::Coordinate* coeff = this->m_Parameters + 3 * (idxX + nextJ * (idxY + nextK * idxZ )); const Types::Coordinate Y = this->m_Offset[1] + this->m_Spacing[1] * idxY; const Types::Coordinate Z = this->m_Offset[2] + this->m_Spacing[2] * idxZ; for ( int n = 0; n < numPoints; ++n, ++v, coeff += 3 ) { v[n][0] = this->m_Offset[0] + this->m_Spacing[0] * idxX + coeff[0]; v[n][1] = Y + coeff[1]; v[n][2] = Z + coeff[2]; } } DeformationField::SpaceVectorType DeformationField::Apply ( const Self::SpaceVectorType& v ) const { Self::SpaceVectorType vTransformed( v ); Types::Coordinate r[3], f[3]; int grid[3]; // Do some precomputations. for ( int dim = 0; dim<3; ++dim ) { // This is the (real-valued) index of the control point grid cell the // given location is in. r[dim] = this->m_InverseSpacing[dim] * ( v[dim] - this->m_Offset[dim] ); // This is the actual cell index. grid[dim] = std::min( static_cast( r[dim] ), this->m_Dims[dim]-2 ); // And here's the relative position within the cell. f[dim] = r[dim] - grid[dim]; } // Create a pointer to the front-lower-left corner of the c.p.g. cell. Types::Coordinate* coeff = this->m_Parameters + 3 * ( grid[0] + this->m_Dims[0] * (grid[1] + this->m_Dims[1] * grid[2]) ); for ( int dim = 0; dim<3; ++dim ) { Types::Coordinate mm = 0; Types::Coordinate *coeff_mm = coeff; // Loop over 4 c.p.g. planes in z-direction. for ( int m = 0; m < 2; ++m ) { Types::Coordinate ll = 0; Types::Coordinate *coeff_ll = coeff_mm; // Loop over 4 c.p.g. planes in y-direction. for ( int l = 0; l < 2; ++l ) { Types::Coordinate kk = 0; Types::Coordinate *coeff_kk = coeff_ll; // Loop over 4 c.p.g. planes in x-direction. for ( int k = 0; k < 2; ++k, coeff_kk+=3 ) { kk += ( k ? f[0] : 1-f[0] ) * (*coeff_kk); } ll += ( l ? f[1] : 1-f[1] ) * kk; coeff_ll += nextJ; } mm += ( m ? f[2] : 1-f[2] ) * ll; coeff_mm += nextK; } vTransformed[dim] += mm; ++coeff; } return vTransformed; } } // namespace cmtk cmtk-3.3.1/libs/Base/cmtkDeformationField.h000066400000000000000000000121751276303427400205400ustar00rootroot00000000000000/* // // Copyright 2016 Google, Inc. // // Copyright 1997-2010 Torsten Rohlfing // // Copyright 2004-2012, 2014 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 5418 $ // // $LastChangedDate: 2016-01-20 20:15:12 -0800 (Wed, 20 Jan 2016) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkDeformationField_h_included_ #define __cmtkDeformationField_h_included_ #include #include #include #include #include #include #include namespace cmtk { /** \addtogroup Base */ //@{ /** Class for pixel-wise deformation field. * *\author Torsten Rohlfing */ class DeformationField : /// Inherit generic grid-based nonrigid transformation interface. public WarpXform { public: /// This class. typedef DeformationField Self; /// Parent class. typedef WarpXform Superclass; /// Smart pointer to DeformationField typedef SmartPointer SmartPtr; /// Smart pointer to const DeformationField typedef SmartConstPointer SmartConstPtr; public: /// Constructor. DeformationField( const UniformVolume* volume ) { this->InitGrid( volume->m_Size, volume->m_Dims ); this->m_Offset = volume->m_Offset; } /// Constructor. DeformationField( const FixedVector<3,Types::Coordinate>& domain, const DataGrid::IndexType& dims, const Types::Coordinate* offset = NULL ) { this->InitGrid( domain, dims ); if ( offset ) { for ( int dim = 0; dim < 3; ++dim ) this->m_Offset[dim] = offset[dim]; } } /// Destructor. virtual ~DeformationField () {} /// Initialized internal data structures for new control point grid. virtual void InitGrid( const FixedVector<3,Types::Coordinate>& domain, const Self::ControlPointIndexType& dims ) { this->Superclass::InitGrid( domain, dims ); for ( int dim = 0; dim < 3; ++dim ) { if ( dims[dim] > 1 ) this->m_Spacing[dim] = domain[dim] / (dims[dim]-1); else this->m_Spacing[dim] = 1.0; this->m_InverseSpacing[dim] = 1.0 / this->m_Spacing[dim]; } this->m_InverseAffineScaling[0] = this->m_InverseAffineScaling[1] = this->m_InverseAffineScaling[2] = this->m_GlobalScaling = 1.0; } /// Initialize control point positions, potentially with affine displacement. void InitControlPoints( const AffineXform* affineXform = NULL ); /// Apply transformation to vector in-place. virtual Self::SpaceVectorType Apply ( const Self::SpaceVectorType& ) const; /** Return origin of warped vector. */ virtual bool ApplyInverse ( const Self::SpaceVectorType&, Self::SpaceVectorType&, const Types::Coordinate = 0.01 ) const { // not implemented return false; } /** Get the deformed position of a transformation control point. *\note This function does not necessarily return the shifted control point position, * but rather it applies the current transformation to the given control * point. */ virtual Self::SpaceVectorType GetDeformedControlPointPosition( const int idxX, const int idxY, const int idxZ ) const { return this->GetTransformedGrid( idxX, idxY, idxZ ); } /// Get a grid point from the deformed grid. virtual Self::SpaceVectorType GetTransformedGrid( const int idxX, const int idxY, const int idxZ ) const; /// Get a sequence of grid points from the deformed grid. virtual void GetTransformedGridRow( Self::SpaceVectorType *const v, const int numPoints, const int idxX, const int idxY, const int idxZ ) const; /// Get Jacobian matrix. virtual const CoordinateMatrix3x3 GetJacobian( const Self::SpaceVectorType& v ) const; /// Compute Jacobian determinant at a certain location. virtual Types::Coordinate GetJacobianDeterminant ( const Self::SpaceVectorType& v ) const { return this->GetJacobian( v ).Determinant(); } /// Return 1.0 since deformation field DOFs are always direct deformations in space units. virtual Types::Coordinate GetParamStep( const size_t, const Self::SpaceVectorType&, const Types::Coordinate mmStep = 1 ) const { return mmStep; } protected: /** Clone transformation. *\todo This still needs to be implemented. */ virtual Self* CloneVirtual () const { return NULL; } }; //@} } // namespace cmtk #endif // #ifndef __cmtkDeformationField_h_included_ cmtk-3.3.1/libs/Base/cmtkDeformationField_Jacobian.cxx000066400000000000000000000063411276303427400226770ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2012, 2014 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 5160 $ // // $LastChangedDate: 2014-01-12 14:33:52 -0800 (Sun, 12 Jan 2014) $ // // $LastChangedBy: torsten_at_home $ // */ #include "cmtkDeformationField.h" #include namespace cmtk { /** \addtogroup Base */ //@{ const CoordinateMatrix3x3 DeformationField::GetJacobian ( const Self::SpaceVectorType& v ) const { Types::Coordinate r[3], f[3]; int grid[3]; for ( int dim = 0; dim<3; ++dim ) { r[dim] = this->m_InverseSpacing[dim] * (v[dim] - this->m_Offset[dim]); grid[dim] = static_cast( r[dim]-1 ); if ( (grid[dim] < 0) || (grid[dim] >= this->m_Dims[dim]-3) ) { return CoordinateMatrix3x3::Zero(); } f[dim] = r[dim] - grid[dim] - 1; } CoordinateMatrix3x3 J = CoordinateMatrix3x3::Identity(); // note that deformation field is offset vector field, thus start with identity matrix, not zero matrix const Types::Coordinate* coeff = this->m_Parameters + 3 * ( grid[0] + this->m_Dims[0] * (grid[1] + this->m_Dims[1] * grid[2]) ); // loop over the three components of the coordinate transformation function, // x, y, z. for ( int dim = 0; dim<3; ++dim, ++coeff ) { const Types::Coordinate *coeff_mm = coeff; for ( int m = 0; m < 4; ++m, coeff_mm += nextK ) { Types::Coordinate ll[3] = { 0, 0, 0 }; const Types::Coordinate *coeff_ll = coeff_mm; for ( int l = 0; l < 4; ++l, coeff_ll += nextJ ) { Types::Coordinate kk[3] = { 0, 0, 0 }; const Types::Coordinate *coeff_kk = coeff_ll; for ( int k = 0; k < 4; ++k, coeff_kk+=3 ) { // dT / dx kk[0] += CubicSpline::DerivInterpSpline( k, f[0] ) * (*coeff_kk); // dT / dy const Types::Coordinate tmp = CubicSpline::InterpSpline( k, f[0] ) * (*coeff_kk); kk[1] += tmp; // dT / dz kk[2] += tmp; } // dT / dx const Types::Coordinate tmp = CubicSpline::InterpSpline( l, f[1] ); ll[0] += tmp * kk[0]; // dT / dy ll[1] += CubicSpline::DerivInterpSpline( l, f[1] ) * kk[1]; // dT / dz ll[2] += tmp * kk[2]; } // dT / dx const Types::Coordinate tmp = CubicSpline::InterpSpline( m, f[2] ); J[dim][0] += tmp * ll[0]; // dT / dy J[dim][1] += tmp * ll[1]; // dT / dz J[dim][2] += CubicSpline::DerivInterpSpline( m, f[2] ) * ll[2]; } } return J; } } // namespace cmtk cmtk-3.3.1/libs/Base/cmtkDetectedPhantomMagphanEMR051.h000066400000000000000000000115541276303427400224630ustar00rootroot00000000000000/* // // Copyright 2012-2014 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 5355 $ // // $LastChangedDate: 2014-05-09 16:55:27 -0700 (Fri, 09 May 2014) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkDetectedPhantomMagphanEMR051_h_included_ #define __cmtkDetectedPhantomMagphanEMR051_h_included_ #include #include #include #include #include #include #include #include namespace cmtk { /** \addtogroup Base */ //@{ /// Description of a detected Magphan EMR051 structural imaging phantom (a.k.a. ADNI Phantom) in an actual image. class DetectedPhantomMagphanEMR051 { public: /// This class. typedef DetectedPhantomMagphanEMR051 Self; /// Smart pointer to this class. typedef SmartPointer SmartPtr; /// Smart pointer to a constant object of this class. typedef SmartConstPointer SmartConstPtr; /// Constructor. DetectedPhantomMagphanEMR051( const AffineXform& linearFitXform /*!< Fitted linear (including shear and scale) transformation */ ) : m_EstimatedSNR( 0.0 ), m_MaxDimming( 0.0 ), m_LinearFitXform( linearFitXform ) {} /// Add expected and actual location of a detected phantom landmark. void AddLandmarkPair( const std::string& name /*!< Name of this sphere. */, const LandmarkPair::SpaceVectorType& expected /*!< Expected landmark position in physical image coordinates based on linear (not necessarily rigid) fit. */, const LandmarkPair::SpaceVectorType& actual /*!< Actual, detected landmark position in physical image coordinates in the image. */, const Types::Coordinate residual /*!< Residual of landmark fit. */, const bool reliable /*!< If true, this landmark is considered reliable, i.e., its expected position by phantom manufacturing is sufficiently precise to be used as a landmark. */ ) { this->m_LandmarkPairs.push_back( LandmarkPair( name, expected, actual, residual, reliable ) ); } /** Apply a transformation to all landmarks. * The purpose of this function is mainly to bring all landmark images into a new image space, e.g., * to transform them from phantom image physical space to unwarp image standard space. */ void ApplyXformToLandmarks( const Xform& xform ) { for ( std::list::iterator it = this->m_LandmarkPairs.begin(); it != this->m_LandmarkPairs.end(); ++it ) { it->m_Location = xform.Apply( it->m_Location ); it->m_TargetLocation = xform.Apply( it->m_TargetLocation ); } } /// Get list of landmark pairs. const std::list& LandmarkPairsList() const { return this->m_LandmarkPairs; } /// Estimated image signal-to-noise ratio. Types::DataItem m_EstimatedSNR; /// Estimated maximum dimming (i.e., minimum fraction of intensity of a 10mm sphere relative to maximum) Types::DataItem m_MaxDimming; /// Estimated image contrast-to-noise ratio. FixedVector<4,Types::DataItem> m_EstimatedCNR; /// Estimated linear transformation fitted to landmarks. AffineXform m_LinearFitXform; /// Estimated image contrast-to-noise ratio. FixedVector<3,Types::Coordinate> m_EstimatedNonLinear; /// Vector of landmark pairs. std::list m_LandmarkPairs; /// Class for status flags that cover a variety of internal conditions. class StatusFlags { public: /// Default constructor. StatusFlags() : m_FallbackOrientationCNR( false ), m_FallbackCentroidCNR( false ), m_DistanceSNRtoCNR( 0.0 ) {} /// Flag for using CNR orientation as a fallback for missing/undetected 15mm spheres. bool m_FallbackOrientationCNR; /// Flag for using CNR center of mass as a fallback for SNR sphere centroid bool m_FallbackCentroidCNR; /// Distance between SNR center and CNR centroid. Types::Coordinate m_DistanceSNRtoCNR; }; /// The status flags for the detected phantom result. Self::StatusFlags m_StatusFlags; }; //@} } // namespace cmtk #endif // #ifndef __cmtkDetectedPhantomMagphanEMR051_h_included_ cmtk-3.3.1/libs/Base/cmtkDirectionSet.cxx000066400000000000000000000036041276303427400202710ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2011 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 3095 $ // // $LastChangedDate: 2011-04-06 12:54:02 -0700 (Wed, 06 Apr 2011) $ // // $LastChangedBy: torstenrohlfing $ // */ #include "cmtkDirectionSet.h" namespace cmtk { /** \addtogroup Base */ //@{ void DirectionSet::NormalizeMaxNorm( const double value ) { // For Each Direction for( size_t index = 0; index < this->GetNumberOfDirections(); index++ ) { CoordinateVector::SmartPtr direction = (*this)[index]; (*direction) *= value / direction->MaxNorm(); } } void DirectionSet::NormalizeEuclidNorm(const double value) { // For Each Direction for( size_t index = 0; index < this->GetNumberOfDirections(); index++) { CoordinateVector::SmartPtr direction = (*this)[index]; // Find Euclidean Norm Types::Coordinate euclid = direction->EuclidNorm(); // Divide Every Component by the Euclidean Norm // and multiply by normalization value.. *direction *= value / euclid; } } } // namespace cmtk cmtk-3.3.1/libs/Base/cmtkDirectionSet.h000066400000000000000000000045771276303427400177300ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2010 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 2398 $ // // $LastChangedDate: 2010-10-05 14:54:37 -0700 (Tue, 05 Oct 2010) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkDirectionSet_h_included_ #define __cmtkDirectionSet_h_included_ #include #include #include #include #include namespace cmtk { /** \addtogroup Base */ //@{ /// A set of directions in n-dimensional space. class DirectionSet : /// This is a vector of coordinate vectors. public std::vector { public: /// Smart pointer to DirectionSet. typedef SmartPointer SmartPtr; /// Get dimension of direction space. unsigned int GetDimension() const { return Dimension; } /// Get number of directions. unsigned int GetNumberOfDirections() const { return this->size(); } /** Normalizes each direction vector to have max norm = value. */ void NormalizeMaxNorm( const double value = 1.0 ); /** Normalizes each direction vector to have euclid norm = value. */ void NormalizeEuclidNorm( const double value = 1.0 ); /// Default constructor. DirectionSet() { Dimension = 0; } /// Allocation constructor. DirectionSet( const unsigned int dimension ) { Dimension = dimension; } /// Destructor. ~DirectionSet() {} private: /// Dimension of direction space. unsigned int Dimension; }; //@} } // namespace cmtk #endif // #ifndef __cmtkDirectionSet_h_included_ cmtk-3.3.1/libs/Base/cmtkDistanceMap.h000066400000000000000000000064501276303427400175140ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2012 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 3945 $ // // $LastChangedDate: 2012-02-29 11:33:37 -0800 (Wed, 29 Feb 2012) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkDistanceMap_h_included_ #define __cmtkDistanceMap_h_included_ #include namespace cmtk { /** \addtogroup Base */ //@{ /** Base class for distance map template class. * This class provides typedefs and static constants that do not depend on the * template parameter of the derived class template. */ class DistanceMap { public: /// Constant used to mark unprocessed pixels. static const long int EDT_MAX_DISTANCE_SQUARED = 2147329548; /** Enumeration with binary flags that control distance map computation. * The defined values can be combined by arithmetic "or". */ typedef enum { /** No special functions. * This flag will create a distance-from-feature map where any non-zero * voxel in the feature image will be considered. */ DEFAULT = 0, /** Compute distance-from-background map. * The resulting distance values will be non-zero at all feature voxels, * specifying the distance to the nearest non-feature voxel. */ INSIDE = 1, /** Compute "inside", rather than "outside" map. Ths essentially inverts * the mask. */ VALUE_EXACT = 2, /** Use specific feature value. * If this flag is set, only voxels in the feature image with values equal * to or larger than a given constant will be considered feature voxels. * All voxels with lower values will be considered background voxels. */ VALUE_WINDOW = 4, /** Use window around specific feature value. * If this flag is set, only voxels in the feature image with values that are * within a range from a given constant will be considered feature voxels. * All voxels with lower values will be considered background voxels. */ VALUE_THRESHOLD = 8, /** Compute signed distance map. * The "INSIDE" flag determines whether negative distance values are assigned to * pixels inside (flag off) or outside (flag on) the labelled region. */ SIGNED = 16, /** Compute squared distance - do not apply final sqrt() operator. * This can increase efficiency if the outside code wants the squared distance in the first place. */ SQUARED = 32 } Flags; }; //@} } // namespace cmtk #endif // #ifndef __cmtkDistanceMap_h_included_ cmtk-3.3.1/libs/Base/cmtkEigenSystemSymmetricMatrix.h000066400000000000000000000051221276303427400226350ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2012 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4721 $ // // $LastChangedDate: 2013-05-12 16:49:38 -0700 (Sun, 12 May 2013) $ // // $LastChangedBy: torsten_at_home $ // */ #ifndef __cmtkEigenSystemSymmetricMatrix_h_included_ #define __cmtkEigenSystemSymmetricMatrix_h_included_ #include #include #include #include namespace cmtk { /** \addtogroup Base */ //@{ /** Compute the eigenvectors and eigenvalues of a symmetric, square matrix of arbitrary size. */ template class EigenSystemSymmetricMatrix { public: /// This class. typedef EigenSystemSymmetricMatrix Self; /// Constructor: compute eigensystem of given matrix. EigenSystemSymmetricMatrix( const SymmetricMatrix& matrix /*!< Symmetric matrix for which we are computing the eigenvalues and eigenvectors.*/ ); /// Get n-th eigenvector. const Vector GetNthEigenvector( const size_t n ) const { return Vector( this->m_Eigenvectors[n] ); } /// Get n-th eigenvector. const TFloat EigenvectorElement( const size_t n, const size_t i ) const { return this->m_Eigenvectors[n][i]; } /// Get n-th eigenvalue. TFloat GetNthEigenvalue( const size_t n ) const { return this->m_Eigenvalues[n]; } /// Get vector of eigenvalues. std::vector GetEigenvalues() const { return this->m_Eigenvalues; } private: /// Eigenvector matrix. std::vector< Vector > m_Eigenvectors; /// Eigenvalues vector. std::vector m_Eigenvalues; }; //@} } // namespace cmtk #include "cmtkEigenSystemSymmetricMatrix.txx" #endif // #ifndef __cmtkEigenSystemSymmetricMatrix_h_included_ cmtk-3.3.1/libs/Base/cmtkEigenSystemSymmetricMatrix.txx000066400000000000000000000046231276303427400232360ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2012 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 1317 $ // // $LastChangedDate: 2010-03-10 14:08:16 -0800 (Wed, 10 Mar 2010) $ // // $LastChangedBy: torstenrohlfing $ // */ namespace cmtk { /** \addtogroup Base */ //@{ template EigenSystemSymmetricMatrix ::EigenSystemSymmetricMatrix( const SymmetricMatrix& matrix ) : m_Eigenvectors( matrix.Dim() ), m_Eigenvalues( matrix.Dim() ) { const int n = static_cast( matrix.Dim() ); /* Convert SymmetricMatrix to ap::real_2d_array */ ap::real_2d_array apMatrix; apMatrix.setbounds(0, n-1, 0, n-1 ); for (int j = 0; j < n; j++) for (int i = 0; i < n; i++) apMatrix(i,j) = static_cast( matrix(i,j) ); ap::real_1d_array apEigenvalues; apEigenvalues.setbounds( 0, n-1 ); ap::real_2d_array apEigenvectors; apEigenvectors.setbounds( 0, n-1, 0, n-1 ); /* Run AlgLib eigensystem computation */ if ( ! smatrixevd(apMatrix, (int)n, 1 /*upper storage*/, true /*eigenvectors needed*/, apEigenvalues, apEigenvectors) ) { StdErr << "WARNING: smatrixevd did not converge\n"; } /* Convert ap::real_1d_array and ap::real_2d_array * back to our data types. */ for (int j = 0; j < n; j++) { this->m_Eigenvectors[j].SetDim( matrix.Dim() ); for (int i = 0; i < n; i++) this->m_Eigenvectors[j][i] = static_cast( apEigenvectors(j,i) ); } for (int i = 0; i < n; i++) this->m_Eigenvalues[i] = static_cast( apEigenvalues(i) ); } } // namespace cmtk cmtk-3.3.1/libs/Base/cmtkEigenSystemSymmetricMatrix3x3.h000066400000000000000000000060021276303427400231710ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2012 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4427 $ // // $LastChangedDate: 2012-06-12 11:23:22 -0700 (Tue, 12 Jun 2012) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkEigenSystemSymmetricMatrix3x3_h_included_ #define __cmtkEigenSystemSymmetricMatrix3x3_h_included_ #include #include #include namespace cmtk { /** \addtogroup Base */ //@{ /** Compute the eigenvectors and eigenvalues of a symmetric 3x3 matrix. * (Eigen decomposition code for symmetric 3x3 matrices, copied from the public * domain Java Matrix library JAMA by Connelly Barnes. ) Eigenvectors and eigenvalues * are returned in sorted order, by ascending absolute values of the eigenvalues. */ template class EigenSystemSymmetricMatrix3x3 { public: /// Constructor: compute eigensystem of given matrix. EigenSystemSymmetricMatrix3x3( const Matrix3x3& matrix, /*!< Symmetric 3x3 matrix for which we are computing the eigenvalues and eigenvectors.*/ const bool sortAbsolute = true /*!< Flag for sorting by absolute eigenvalues (default) vs. sorting by actual eigenvalues.*/ ); /// Get n-th eigenvector. const FixedVector<3,TFloat> GetNthEigenvector( const size_t n ) const { return FixedVector<3,TFloat>::FromPointer( this->m_Eigenvectors[n] ); } /// Get n-th eigenvalue. TFloat GetNthEigenvalue( const size_t n ) const { return this->m_Eigenvalues[n]; } protected: /// Eigenvector matrix. TFloat m_Eigenvectors[3][3]; /// Eigenvalues vector. TFloat m_Eigenvalues[3]; private: /// Helper function that computes the Euclidean length of (x,y). static TFloat hypot2( const TFloat& x, const TFloat& y); /** Symmetric Householder reduction to tridiagonal form. */ static void tred2(TFloat V[3][3], TFloat d[3], TFloat e[3]); /* Symmetric tridiagonal QL algorithm. */ static void tql2(TFloat V[3][3], TFloat d[3], TFloat e[3], const bool sortAbsolute = true ); }; //@} } // namespace cmtk #include "cmtkEigenSystemSymmetricMatrix3x3.txx" #endif // #ifndef __cmtkEigenSystemSymmetricMatrix3x3_h_included_ cmtk-3.3.1/libs/Base/cmtkEigenSystemSymmetricMatrix3x3.txx000066400000000000000000000157201276303427400235740ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2009, 2013 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4870 $ // // $LastChangedDate: 2013-09-24 11:01:11 -0700 (Tue, 24 Sep 2013) $ // // $LastChangedBy: torstenrohlfing $ // */ namespace cmtk { /** \addtogroup Base */ //@{ template EigenSystemSymmetricMatrix3x3 ::EigenSystemSymmetricMatrix3x3( const Matrix3x3& matrix, const bool sortAbsolute ) { TFloat e[3]; for (int i = 0; i < 3; i++) { for (int j = 0; j < 3; j++) { this->m_Eigenvectors[i][j] = matrix[i][j]; } } tred2( this->m_Eigenvectors, this->m_Eigenvalues, e); tql2( this->m_Eigenvectors, this->m_Eigenvalues, e, sortAbsolute ); } template TFloat EigenSystemSymmetricMatrix3x3 ::hypot2( const TFloat& x, const TFloat& y) { return sqrt(x*x+y*y); } template void EigenSystemSymmetricMatrix3x3 ::tred2( TFloat V[3][3], TFloat d[3], TFloat e[3] ) { // This is derived from the Algol procedures tred2 by // Bowdler, Martin, Reinsch, and Wilkinson, Handbook for // Auto. Comp., Vol.ii-Linear Algebra, and the corresponding // Fortran subroutine in EISPACK. for (int j = 0; j < 3; j++) { d[j] = V[2][j]; } // Householder reduction to tridiagonal form. for (int i = 2; i > 0; i--) { // Scale to avoid under/overflow. TFloat scale = 0.0; TFloat h = 0.0; for (int k = 0; k < i; k++) { scale = scale + fabs(d[k]); } if (scale == 0.0) { e[i] = d[i-1]; for (int j = 0; j < i; j++) { d[j] = V[i-1][j]; V[i][j] = 0.0; V[j][i] = 0.0; } } else { // Generate Householder vector. for (int k = 0; k < i; k++) { d[k] /= scale; h += d[k] * d[k]; } TFloat f = d[i-1]; TFloat g = sqrt(h); if (f > 0) { g = -g; } e[i] = scale * g; h = h - f * g; d[i-1] = f - g; for (int j = 0; j < i; j++) { e[j] = 0.0; } // Apply similarity transformation to remaining columns. for (int j = 0; j < i; j++) { f = d[j]; V[j][i] = f; g = e[j] + V[j][j] * f; for (int k = j+1; k <= i-1; k++) { g += V[k][j] * d[k]; e[k] += V[k][j] * f; } e[j] = g; } f = 0.0; for (int j = 0; j < i; j++) { e[j] /= h; f += e[j] * d[j]; } TFloat hh = f / (h + h); for (int j = 0; j < i; j++) { e[j] -= hh * d[j]; } for (int j = 0; j < i; j++) { f = d[j]; g = e[j]; for (int k = j; k <= i-1; k++) { V[k][j] -= (f * e[k] + g * d[k]); } d[j] = V[i-1][j]; V[i][j] = 0.0; } } d[i] = h; } // Accumulate transformations. for (int i = 0; i < 2; i++) { V[2][i] = V[i][i]; V[i][i] = 1.0; TFloat h = d[i+1]; if (h != 0.0) { for (int k = 0; k <= i; k++) { d[k] = V[k][i+1] / h; } for (int j = 0; j <= i; j++) { TFloat g = 0.0; for (int k = 0; k <= i; k++) { g += V[k][i+1] * V[k][j]; } for (int k = 0; k <= i; k++) { V[k][j] -= g * d[k]; } } } for (int k = 0; k <= i; k++) { V[k][i+1] = 0.0; } } for (int j = 0; j < 3; j++) { d[j] = V[2][j]; V[2][j] = 0.0; } V[2][2] = 1.0; e[0] = 0.0; } template void EigenSystemSymmetricMatrix3x3 ::tql2(TFloat V[3][3], TFloat d[3], TFloat e[3], const bool sortAbsolute) { // This is derived from the Algol procedures tql2, by // Bowdler, Martin, Reinsch, and Wilkinson, Handbook for // Auto. Comp., Vol.ii-Linear Algebra, and the corresponding // Fortran subroutine in EISPACK. for (int i = 1; i < 3; i++) { e[i-1] = e[i]; } e[2] = 0.0; TFloat f = 0.0; TFloat tst1 = 0.0; TFloat eps = pow(2.0,-52.0); for (int l = 0; l < 3; l++) { // Find small subdiagonal element tst1 = ( tst1 > fabs( d[l]) + fabs(e[l]) ) ? tst1 : fabs( d[l]) + fabs(e[l]) ; int m = l; while (m < 3) { if (fabs(e[m]) <= eps*tst1) { break; } m++; } // If m == l, d[l] is an eigenvalue, // otherwise, iterate. if (m > l) { do { // Compute implicit shift TFloat g = d[l]; TFloat p = (d[l+1] - g) / (2.0 * e[l]); TFloat r = hypot2(p,1.0); if (p < 0) { r = -r; } d[l] = e[l] / (p + r); d[l+1] = e[l] * (p + r); TFloat dl1 = d[l+1]; TFloat h = g - d[l]; for (int i = l+2; i < 3; i++) { d[i] -= h; } f = f + h; // Implicit QL transformation. p = d[m]; TFloat c = 1.0; TFloat c2 = c; TFloat c3 = c; TFloat el1 = e[l+1]; TFloat s = 0.0; TFloat s2 = 0.0; for (int i = m-1; i >= l; i--) { c3 = c2; c2 = c; s2 = s; g = c * e[i]; h = c * p; r = hypot2(p,e[i]); e[i+1] = s * r; s = e[i] / r; c = p / r; p = c * d[i] - s * g; d[i+1] = h + s * (c * g + s * d[i]); // Accumulate transformation. for (int k = 0; k < 3; k++) { h = V[k][i+1]; V[k][i+1] = s * V[k][i] + c * h; V[k][i] = c * V[k][i] - s * h; } } p = -s * s2 * c3 * el1 * e[l] / dl1; e[l] = s * p; d[l] = c * p; // Check for convergence. } while (fabs(e[l]) > eps*tst1); } d[l] = d[l] + f; e[l] = 0.0; } // Sort eigenvalues and corresponding vectors. for (int i = 0; i < 2; i++) { int k = i; TFloat p = d[i]; for (int j = i+1; j < 3; j++) { const bool swap = sortAbsolute ? (fabs(d[j]) < fabs(p)) : (d[j] < p); if ( swap ) { k = j; p = d[j]; } } if (k != i) { d[k] = d[i]; d[i] = p; for (int j = 0; j < 3; j++) { p = V[j][i]; V[j][i] = V[j][k]; V[j][k] = p; } } } } } // namespace cmtk cmtk-3.3.1/libs/Base/cmtkEigenValuesSymmetricMatrix.h000066400000000000000000000043231276303427400226120ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2012 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4721 $ // // $LastChangedDate: 2013-05-12 16:49:38 -0700 (Sun, 12 May 2013) $ // // $LastChangedBy: torsten_at_home $ // */ #ifndef __cmtkEigenValuesSymmetricMatrix_h_included_ #define __cmtkEigenValuesSymmetricMatrix_h_included_ #include #include #include #include namespace cmtk { /** \addtogroup Base */ //@{ /** Compute the eigenvectors and eigenvalues of a symmetric, square matrix of arbitrary size. */ template class EigenValuesSymmetricMatrix { public: /// This class. typedef EigenValuesSymmetricMatrix Self; /// Constructor: compute eigensystem of given matrix. EigenValuesSymmetricMatrix( const SymmetricMatrix& matrix /*!< Symmetric matrix for which we are computing the eigenvalues and eigenvectors.*/ ); /// Get n-th eigenvalue. TFloat GetNthEigenvalue( const size_t n ) const { return this->m_Eigenvalues[n]; } /// Get vector of eigenvalues. std::vector GetEigenvalues() const { return this->m_Eigenvalues; } protected: /// Eigenvalues vector. std::vector m_Eigenvalues; }; //@} } // namespace cmtk #include "cmtkEigenValuesSymmetricMatrix.txx" #endif // #ifndef __cmtkEigenValuesSymmetricMatrix_h_included_ cmtk-3.3.1/libs/Base/cmtkEigenValuesSymmetricMatrix.txx000066400000000000000000000041771276303427400232150ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2012 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 1317 $ // // $LastChangedDate: 2010-03-10 14:08:16 -0800 (Wed, 10 Mar 2010) $ // // $LastChangedBy: torstenrohlfing $ // */ namespace cmtk { /** \addtogroup Base */ //@{ template EigenValuesSymmetricMatrix ::EigenValuesSymmetricMatrix( const SymmetricMatrix& matrix ) : m_Eigenvalues( matrix.Dim() ) { const int n = static_cast( matrix.Dim() ); /* Convert SymmetricMatrix to ap::real_2d_array */ ap::real_2d_array apMatrix; apMatrix.setbounds(0, n-1, 0, n-1 ); for (int j = 0; j < n; j++) for (int i = 0; i < n; i++) apMatrix(i,j) = static_cast( matrix(i,j) ); ap::real_1d_array apEigenvalues; apEigenvalues.setbounds( 0, n-1 ); ap::real_2d_array apEigenvectorsDummy; /* Run AlgLib eigensystem computation */ if ( ! smatrixevd(apMatrix, (int)n, 1 /*upper storage*/, false /*eigenvectors not needed*/, apEigenvalues, apEigenvectorsDummy) ) { StdErr << "WARNING: smatrixevd did not converge\n"; } /* Convert ap::real_1d_array and ap::real_2d_array * back to our data types. */ for (int i = 0; i < n; i++) this->m_Eigenvalues[i] = static_cast( apEigenvalues(i) ); } } // namespace cmtk cmtk-3.3.1/libs/Base/cmtkFilterMask.h000066400000000000000000000134621276303427400173660ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2010, 2013 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4882 $ // // $LastChangedDate: 2013-09-27 15:16:36 -0700 (Fri, 27 Sep 2013) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkFilterMask_h_included_ #define __cmtkFilterMask_h_included_ #include #include #include #include #include #include #include #include namespace cmtk { /** \addtogroup Base */ //@{ /** Filter mask pixel entry. * This class handles a single entry in a pre-computed filter mask for * multidimensional images with relative coordinates and filter coeffiecient * for a single pixel. */ template class FilterMaskPixel { public: /// This class. typedef FilterMaskPixel Self; /// Smart pointer. typedef SmartPointer SmartPtr; /// Default constructor. FilterMaskPixel() {} /// Explicit constructor. FilterMaskPixel ( const FixedVector& location, const int relativeIndex, const Types::DataItem coefficient ) : Location( location ), RelativeIndex ( relativeIndex ), Coefficient( coefficient ), PixelIndex( 0 ), Valid( false ) {} /// Relative location of this pixel. FixedVector Location; /// Relative index of this pixel in source image from center of kernel. int RelativeIndex; /// Filter coefficient. Types::DataItem Coefficient; /// Cached source index of image pixel for this element. int PixelIndex; /** Active flag. * This flag can be used in client code to flag pixels in the filter mask as * either valid (i.e., inside domain) or invalid (outsside domain). This * eliminates repeated range checking in cases where there are multiple * iterations over the filter mask at one location. */ bool Valid; }; /** Filter mask. * This class handles pre-computed filter masks for multidimensional images * with relative pixel coordinates and filter coeffiecients. */ template class FilterMask : /// Inherit from STL container. public std::vector< FilterMaskPixel > { public: /// This class type. typedef FilterMask Self; /// Direct parent class. typedef std::vector< FilterMaskPixel > Superclass; /// Smert pointer to this class. typedef SmartPointer SmartPtr; /// Constructor. template FilterMask( const FixedVector& dims, const FixedVector& deltas, const Types::Coordinate radius, F filter ) { FixedVector pixel; FixedVector width; FixedVector position; for ( int dim = 0; dim < DIM; ++dim ) { pixel[dim] = - (width[dim] = 1+static_cast( radius / deltas[dim] )); position[dim] = pixel[dim] * deltas[dim]; } bool done = false; while ( ! done ) { // increment the DIM-digit pixel index counter including overflow. for ( int dim = 0; dim < DIM; ++dim ) { ++pixel[dim]; if ( pixel[dim] <= width[dim] ) { // no overflow, leave for loop since we're done dim = DIM; } else { if ( dim+1 == DIM ) // was this the last dimension? if so, leave while() loop done = true; else { // no, then reset this dimension and repeat loop to increment next pixel[dim] = -width[dim]; } } } // are we done with the kernel? if ( ! done ) { // no, then compute Euclidean distance from center Types::Coordinate distance = 0.0; for ( int dim = 0; dim < DIM; ++dim ) { position[dim] = pixel[dim] * deltas[dim]; distance += position[dim] * position[dim]; } distance = sqrt( distance ); // if distance is within radius then add a pixel to the filter mask if ( distance < radius ) { const int index = pixel[0] + dims[0] * (pixel[1] + dims[1] * pixel[2] ); this->push_back( FilterMaskPixel( pixel, index, filter( position ) ) ); } } } } /// Gaussian filter as an example of a concrete filter implementation. class Gaussian { public: /// Constructor. Gaussian( const Units::GaussianSigma& standardDeviation ) { InvStandardDeviation = 1.0 / standardDeviation.Value(); NormFactor = 1.0 / (sqrt(2.0 * M_PI) * standardDeviation.Value()); } /// Get filter coefficient at relative location from filter center. Types::DataItem operator() ( const FixedVector& relativePosition ) { Types::Coordinate distance = 0; for ( int i = 0; i < DIM; ++i ) distance += relativePosition[i] * relativePosition[i]; return static_cast( NormFactor * exp( -distance * MathUtil::Square( InvStandardDeviation ) / 2 ) ); } private: /// Standard deviation. Types::Coordinate InvStandardDeviation; /// Gaussian normalization factor. Types::Coordinate NormFactor; }; }; } // namespace cmtk #endif // #ifndef __cmtkFilterMask_h_included_ cmtk-3.3.1/libs/Base/cmtkFilterVolume.cxx000066400000000000000000000405401276303427400203120ustar00rootroot00000000000000/* // // Copyright 2016 Google, Inc. // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2011, 2013 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 5410 $ // // $LastChangedDate: 2016-01-16 14:57:41 -0800 (Sat, 16 Jan 2016) $ // // $LastChangedBy: torstenrohlfing $ // */ #include "cmtkFilterVolume.h" #include #include #include #include #include #include #ifdef _OPENMP # include #endif namespace cmtk { /** \addtogroup Base */ //@{ TypedArray::SmartPtr FilterVolume::GaussianFilter ( const UniformVolume* volume, const Units::GaussianSigma& kernelWidth, const Types::Coordinate radius, const TypedArray* maskData ) { const TypedArray* inputData = volume->GetData(); if ( ! inputData ) return TypedArray::SmartPtr( NULL ); TypedArray::SmartPtr filtered = TypedArray::Create( inputData->GetType(), inputData->GetDataSize() ); const DataGrid::IndexType& dims = volume->m_Dims; FilterMask<3> filter( dims, volume->Deltas(), radius, FilterMask<3>::Gaussian( kernelWidth ) ); const Types::GridIndexType dimsX = dims[AXIS_X]; const Types::GridIndexType dimsY = dims[AXIS_Y]; const Types::GridIndexType dimsZ = dims[AXIS_Z]; Progress::Begin( 0, dimsZ, 1, "Gaussian Filter" ); #pragma omp parallel for for ( Types::GridIndexType z = 0; z < dimsZ; ++z ) { size_t offset = z * dimsX * dimsY; Progress::SetProgress( z ); for ( Types::GridIndexType y = 0; y < dimsY; ++y ) for ( Types::GridIndexType x = 0; x < dimsX; ++x, ++offset ) { Types::DataItem average = 0.0, weight = 0.0; Types::DataItem maskValue = 0.0; if ( maskData ) { maskData->Get( maskValue, offset ); } else { maskValue = 1.0; } if ( maskValue ) { FilterMask<3>::const_iterator it = filter.begin(); while ( it != filter.end() ) { const Types::GridIndexType xx = x + it->Location[0]; const Types::GridIndexType yy = y + it->Location[1]; const Types::GridIndexType zz = z + it->Location[2]; if ( (xx>=0) && (yy>=0) && (zz>=0) && (xx < (Types::GridIndexType)dimsX) && (yy < (Types::GridIndexType)dimsY) && (zz < (Types::GridIndexType)dimsZ) ) { const size_t srcOffset = volume->GetOffsetFromIndex( xx, yy, zz ); Types::DataItem value; if ( inputData->Get( value, srcOffset ) ) { average += it->Coefficient * value; weight += it->Coefficient; } } ++it; } } if ( weight > 0.0 ) { filtered->Set( average / weight, offset ); } else { filtered->SetPaddingAt( offset ); } } } Progress::Done(); return filtered; } TypedArray::SmartPtr FilterVolume ::RohlfingFilter ( const UniformVolume* volume, const TypedArray* subjectData, const TypedArray* maskData, const Units::GaussianSigma& iFilterSigma, const Units::GaussianSigma& filterWidth, const Types::Coordinate filterRadius ) { const TypedArray* inputData = volume->GetData(); if ( ! inputData ) return TypedArray::SmartPtr( NULL ); const Types::DataItemRange rangeSubj = subjectData->GetRange(); const size_t numBins = 1024; #ifdef _OPENMP const size_t maxThreads = omp_get_max_threads(); std::vector::SmartPtr> histograms( maxThreads ); for ( size_t thread = 0; thread < maxThreads; ++thread ) { histograms[thread] = Histogram::SmartPtr( new Histogram( numBins ) ); histograms[thread]->SetRange( rangeSubj ); } #else // #ifdef _OPENMP Histogram histogram( numBins ); histogram.SetRange( rangeSubj ); #endif // #ifdef _OPENMP const size_t iKernelRadius = 1 + static_cast( 2 * iFilterSigma.Value() * numBins ); std::vector iKernel( iKernelRadius ); if ( iKernelRadius > 1 ) { const Types::DataItem normFactor = static_cast( 1.0/(sqrt(2*M_PI) * iFilterSigma.Value() * numBins) ); // not really necessary since we normalize during convolution for ( size_t i = 0; i < iKernelRadius; ++i ) { iKernel[i] = static_cast( normFactor * exp( -MathUtil::Square( 1.0 * i / (iFilterSigma.Value()*numBins) ) / 2 ) ); } } else { iKernel[0] = 1.0; } TypedArray::SmartPtr filtered = TypedArray::Create( inputData->GetType(), inputData->GetDataSize() ); const DataGrid::IndexType& dims = volume->GetDims(); FilterMask<3> filter( dims, volume->Deltas(), filterRadius, FilterMask<3>::Gaussian( filterWidth ) ); const Types::GridIndexType dimsX = dims[AXIS_X]; const Types::GridIndexType dimsY = dims[AXIS_Y]; const Types::GridIndexType dimsZ = dims[AXIS_Z]; Progress::Begin( 0, dimsZ, 1, "Rohlfing Intensity-Consistent Filter" ); #pragma omp parallel for for ( Types::GridIndexType z = 0; z < static_cast( dimsZ ); ++z ) { size_t offset = z * dimsX * dimsY; #ifdef _OPENMP const size_t threadIdx = omp_get_thread_num(); Histogram& histogram = *(histograms[threadIdx]); if ( ! threadIdx ) #endif // #ifdef _OPENMP Progress::SetProgress( z ); for ( Types::GridIndexType y = 0; y < dimsY; ++y ) for ( Types::GridIndexType x = 0; x < dimsX; ++x, ++offset ) { Types::DataItem average = 0.0, weight = 0.0; Types::DataItem maskValue = 1.0; if ( maskData ) maskData->Get( maskValue, offset ); Types::DataItem valueSubjCenter; if ( maskValue && subjectData->Get( valueSubjCenter, offset ) ) { histogram.Reset(); histogram.AddWeightedSymmetricKernel( histogram.ValueToBin( valueSubjCenter ), iKernelRadius, &(iKernel[0]) ); for ( FilterMask<3>::const_iterator it = filter.begin(); it != filter.end(); ++it ) { const Types::GridIndexType xx = x + it->Location[0]; const Types::GridIndexType yy = y + it->Location[1]; const Types::GridIndexType zz = z + it->Location[2]; if ( (xx>=0) && (yy>=0) && (zz>=0) && (xx < (Types::GridIndexType)dimsX) && (yy < (Types::GridIndexType)dimsY) && (zz < (Types::GridIndexType)dimsZ) ) { Types::DataItem value; const size_t srcOffset = it->RelativeIndex + offset; if ( inputData->Get( value, srcOffset ) ) { Types::DataItem valueSubj; if ( subjectData->Get( valueSubj, srcOffset ) ) { const size_t bin = histogram.ValueToBin( valueSubj ); const Types::DataItem prob = it->Coefficient * histogram[bin]; average += value * prob; weight += prob; } } } } } if ( weight > 0.0 ) { filtered->Set( average / weight, offset ); } else { filtered->SetPaddingAt( offset ); } } } Progress::Done(); return filtered; } TypedArray::SmartPtr FilterVolume::StudholmeFilter ( const UniformVolume* volume, const TypedArray* subjectData, const TypedArray* averageData, const TypedArray* maskData, std::list imgList, const Types::DataItem binWidth, const Units::GaussianSigma& filterWidth, const Types::Coordinate filterRadius ) { const TypedArray* inputData = volume->GetData(); if ( ! inputData ) return TypedArray::SmartPtr( NULL ); const Types::DataItemRange range = averageData->GetRange(); const size_t numBins = std::min( 128, 1 + static_cast((range.Width()) / binWidth) ); TypedArray::SmartPtr filtered = TypedArray::Create( inputData->GetType(), inputData->GetDataSize() ); const DataGrid::IndexType& dims = volume->GetDims(); const Types::GridIndexType dimsX = dims[AXIS_X]; const Types::GridIndexType dimsY = dims[AXIS_Y]; const Types::GridIndexType dimsZ = dims[AXIS_Z]; const Types::GridIndexType numberOfRows = dimsY * dimsZ; const size_t numberOfThreads = Threads::GetNumberOfThreads(); std::vector< JointHistogram > histogramByThread( numberOfThreads ); std::vector::SmartPtr> filterByThread( numberOfThreads ); for ( size_t idx = 0; idx < numberOfThreads; ++idx ) { histogramByThread[idx].Resize( numBins, numBins ); histogramByThread[idx].SetRangeX( range ); histogramByThread[idx].SetRangeY( range ); FilterMask<3>::SmartPtr filter( new FilterMask<3>( dims, volume->Deltas(), filterRadius, FilterMask<3>::Gaussian( filterWidth ) ) ); filterByThread[idx] = filter; } Progress::Begin( 0, numberOfRows, 1, "Studholme Intensity-Consistent Filter" ); #pragma omp parallel for for ( Types::GridIndexType row = 0; row < static_cast( numberOfRows ); ++row ) { const Types::GridIndexType y = row % dimsY; const Types::GridIndexType z = row / dimsY; Progress::SetProgress( z ); size_t offset = row * dimsX; #ifdef _OPENMP const int thread = omp_get_thread_num(); #else const int thread = 0; #endif JointHistogram& histogram = histogramByThread[thread]; FilterMask<3>& filter = *(filterByThread[thread]); for ( Types::GridIndexType x = 0; x < dimsX; ++x, ++offset ) { Types::DataItem average = 0.0, weight = 0.0; histogram.Reset(); Types::DataItem maskValue = 1.0; if ( maskData ) maskData->Get( maskValue, offset ); Types::DataItem valueAvg; if ( maskValue && averageData->Get( valueAvg, offset ) ) { // first iteration over filter: compute consistency histogram FilterMask<3>::iterator it = filter.begin(); for ( ; it != filter.end(); ++it ) { const Types::GridIndexType xx = x + it->Location[0]; const Types::GridIndexType yy = y + it->Location[1]; const Types::GridIndexType zz = z + it->Location[2]; if ( (xx>=0) && (yy>=0) && (zz>=0) && (xx < (Types::GridIndexType)dimsX) && (yy < (Types::GridIndexType)dimsY) && (zz < (Types::GridIndexType)dimsZ) ) { it->Valid = true; const size_t srcOffset = it->RelativeIndex + offset; it->PixelIndex = srcOffset; Types::DataItem valueAvgSrc, valueSubj; if ( averageData->Get( valueAvgSrc, srcOffset ) ) { const size_t binAvg = histogram.ValueToBinX( valueAvgSrc ); for ( std::list::iterator itImg = imgList.begin(); itImg != imgList.end(); ++itImg ) { if ( (*itImg)->Get( valueSubj, srcOffset ) ) { histogram.Increment( binAvg, histogram.ValueToBinY( valueSubj ) ); } } } } else { it->Valid = false; } } const size_t binX = histogram.ValueToBinX( valueAvg ); const Types::DataItem avgHistogramValueInv = static_cast( 1.0/histogram.ProjectToX( binX ) ); for ( it = filter.begin(); it != filter.end(); ++it ) { if ( it->Valid ) { Types::DataItem value; if ( inputData->Get( value, it->PixelIndex ) ) { Types::DataItem valueSubj; if ( subjectData->Get( valueSubj, it->PixelIndex ) ) { const size_t binY = histogram.ValueToBinY( valueSubj ); const Types::DataItem prob = static_cast( it->Coefficient * avgHistogramValueInv * histogram.GetBin( binX, binY ) ); average += value * prob; weight += prob; } } } } } if ( weight > 0.0 ) { filtered->Set( average / weight, offset ); } else { filtered->SetPaddingAt( offset ); } } } Progress::Done(); return filtered; } TypedArray::SmartPtr FilterVolume::StudholmeFilter ( const UniformVolume* volume, std::list subjectData, const TypedArray* averageData, const TypedArray* maskData, std::list imgList, const Types::DataItem binWidth, const Units::GaussianSigma& filterWidth, const Types::Coordinate filterRadius ) { const TypedArray* inputData = volume->GetData(); if ( ! inputData ) return TypedArray::SmartPtr( NULL ); const Types::DataItemRange range = averageData->GetRange(); const size_t numBins = std::min( 128, 1 + static_cast( range.Width() / binWidth ) ); JointHistogram histogram( numBins, numBins ); histogram.SetRangeX( range ); histogram.SetRangeY( range ); TypedArray::SmartPtr filtered = TypedArray::Create( inputData->GetType(), inputData->GetDataSize() ); const DataGrid::IndexType& dims = volume->GetDims(); FilterMask<3> filter( dims, volume->Deltas(), filterRadius, FilterMask<3>::Gaussian( filterWidth ) ); const Types::GridIndexType dimsX = dims[AXIS_X]; const Types::GridIndexType dimsY = dims[AXIS_Y]; const Types::GridIndexType dimsZ = dims[AXIS_Z]; Progress::Begin( 0, dimsZ, 1, "Studholme Intensity-Consistent Filter" ); size_t offset = 0; for ( Types::GridIndexType z = 0; z < dimsZ; ++z ) { Progress::SetProgress( z ); for ( Types::GridIndexType y = 0; y < dimsY; ++y ) for ( Types::GridIndexType x = 0; x < dimsX; ++x, ++offset ) { Types::DataItem average = 0.0, weight = 0.0; histogram.Reset(); Types::DataItem maskValue = 1.0; if ( maskData ) maskData->Get( maskValue, offset ); Types::DataItem valueAvg; if ( maskValue && averageData->Get( valueAvg, offset ) ) { // first iteration over filter: compute consistency histogram for ( FilterMask<3>::iterator it = filter.begin(); it != filter.end(); ++it ) { const Types::GridIndexType xx = x + it->Location[0]; const Types::GridIndexType yy = y + it->Location[1]; const Types::GridIndexType zz = z + it->Location[2]; if ( (xx < dimsX) && (yy < dimsY) && (zz < dimsZ) ) { it->Valid = true; // since xx, yy, zz are unsigned, we need not check // for >= 0; this is taken care of by overflow (we // hope ;) const size_t srcOffset = volume->GetOffsetFromIndex( xx, yy, zz ); Types::DataItem valueAvgSrc, valueSubj; if ( averageData->Get( valueAvgSrc, srcOffset ) ) { const size_t binAvg = histogram.ValueToBinX( valueAvgSrc ); for ( std::list::iterator itImg = imgList.begin(); itImg != imgList.end(); ++itImg ) { if ( (*itImg)->Get( valueSubj, srcOffset ) ) { histogram.Increment( binAvg, histogram.ValueToBinY( valueSubj ) ); } } } } } Histogram* avgHistogram = histogram.GetMarginalX(); const size_t binX = histogram.ValueToBinX( valueAvg ); for ( FilterMask<3>::iterator it = filter.begin(); it != filter.end(); ++it ) { const Types::GridIndexType xx = x + it->Location[0]; const Types::GridIndexType yy = y + it->Location[1]; const Types::GridIndexType zz = z + it->Location[2]; if ( it->Valid ) { it->Valid = false; // since xx, yy, zz are unsigned, we need not check for // >= 0; this is taken care of by overflow (we hope ;) const size_t srcOffset = volume->GetOffsetFromIndex( xx, yy, zz ); Types::DataItem value; if ( inputData->Get( value, srcOffset ) ) { float prob = static_cast( it->Coefficient ); std::list::iterator subjectIt = subjectData.begin(); while ( subjectIt != subjectData.end() ) { Types::DataItem valueSubj; if ( (*subjectIt)->Get( valueSubj, srcOffset ) ) { const size_t binY = histogram.ValueToBinY( valueSubj ); prob *= static_cast( histogram.GetBin( binX, binY ) / (*avgHistogram)[binX] ); } ++subjectIt; } average += value * prob; weight += prob; } } } delete avgHistogram; } if ( weight > 0.0 ) { filtered->Set( average / weight, offset ); } else { filtered->SetPaddingAt( offset ); } } } Progress::Done(); return filtered; } } // namespace cmtk cmtk-3.3.1/libs/Base/cmtkFilterVolume.h000066400000000000000000000153351276303427400177430ustar00rootroot00000000000000/* // // Copyright 2016 Google, Inc. // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2011 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 5418 $ // // $LastChangedDate: 2016-01-20 20:15:12 -0800 (Wed, 20 Jan 2016) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkFilterVolume_h_included_ #define __cmtkFilterVolume_h_included_ #include #include #include #include #include namespace cmtk { /** \addtogroup Base */ //@{ /// Class for filtering volume images. class FilterVolume { public: /** Apply Gaussian filter. *\param volume Input 3D image. *\param width Width (standard deviation) of the Gaussian kernel. *\param radius Filter radius in multiples of the filter width. Outside the * radius (Euclidean distance) the filter is truncated. *\param maskData Optional binary mask data array. *\return A newly allocated TypedArray object that can be used, for * example, to replace the one held by the input image. The data type of the * array is identical to the input array. */ static TypedArray::SmartPtr GaussianFilter( const UniformVolume* volume, const Units::GaussianSigma& width, const Types::Coordinate radius = 1.0, const TypedArray* maskData = NULL ); /** Apply Coupe denoising filter. *\param volume Input 3D image. *\param beta Smoothing adjustment parameter *\param windowRadius Distance from center voxel to outer edge of window *\return A newly allocated TypedArray object that can be used, for * example, to replace the one held by the input image. The data type of the * array is identical to the input array. */ static TypedArray::SmartPtr CoupeFilter ( const UniformVolume* volume, const int windowRadius, const float beta = 0.5 ); /** Apply Torsten Rohlfing's single-image intensity-consistent Gaussian filter. *\param volume Input 3D image. *\param subjectData Pixel array of the individual grey image from this * subject. *\param maskData Optional binary mask data array. *\param iFilterSigma Width (standard deviation of the Gaussian kernel. *\param filterWidth Width (standard deviation of the Gaussian kernel. *\param filterRadius Filter radius in multiples of the filter width. * Outside the radius (Euclidean distance) the filter is truncated. *\return A newly allocated TypedArray object that can be used, for * example, to replace the one held by the input image. The data type of the * array is identical to the input array. */ static TypedArray::SmartPtr RohlfingFilter ( const UniformVolume* volume, const TypedArray* subjectData, const TypedArray* maskData, const Units::GaussianSigma& iFilterSigma, const Units::GaussianSigma& filterWidth, const Types::Coordinate filterRadius ); /** Apply Colin Studholme's Gaussian filter with registration-based weights. *\param volume Input 3D image. *\param subjectData Pixel array of the individual grey image from this * subject. *\param averageData Pixel array of the population average grey image. *\param maskData Optional binary mask data array. *\param imgList List of pixel arrays from matched 3D images. The consistency * between these pixel arrays determines the relative weights of the * otherwise Gaussian kernel. Consult Colin's NeuroImage (2003) paper for * details. *\param binWidth Bin width of the intensity histogram used to quantify the * matching between all individuals. *\param filterWidth Width (standard deviation of the Gaussian kernel. *\param filterRadius Filter radius in multiples of the filter width. * Outside the radius (Euclidean distance) the filter is truncated. *\return A newly allocated TypedArray object that can be used, for * example, to replace the one held by the input image. The data type of the * array is identical to the input array. */ static TypedArray::SmartPtr StudholmeFilter ( const UniformVolume* volume, const TypedArray* subjectData, const TypedArray* averageData, const TypedArray* maskData, std::list imgList, const Types::DataItem binWidth, const Units::GaussianSigma& filterWidth, const Types::Coordinate filterRadius ); /** Apply Colin Studholme's Gaussian filter using multiple time points. *\param volume Input 3D image. *\param subjectData List of pixel arrays of the individual grey images from * this subject. Each list item corresponds to one time point. Ideally, there * are two items in the list, which correspond to the time points between * which the Jacobian map in "volume" was computed. *\param averageData Pixel array of the population average grey image. *\param maskData Optional binary mask data array. *\param imgList List of pixel arrays from matched 3D images. The consistency * between these pixel arrays determines the relative weights of the * otherwise Gaussian kernel. Consult Colin's NeuroImage (2003) paper for * details. *\param binWidth Bin width of the intensity histogram used to quantify the * matching between all individuals. *\param filterWidth Width (standard deviation of the Gaussian kernel. *\param filterRadius Filter radius in multiples of the filter width. * Outside the radius (Euclidean distance) the filter is truncated. *\return A newly allocated TypedArray object that can be used, for * example, to replace the one held by the input image. The data type of the * array is identical to the input array. */ static TypedArray::SmartPtr StudholmeFilter ( const UniformVolume* volume, std::list subjectData, const TypedArray* averageData, const TypedArray* maskData, std::list imgList, const Types::DataItem binWidth, const Units::GaussianSigma& filterWidth, const Types::Coordinate filterRadius ); }; //@} } // namespace cmtk #endif // #ifndef __cmtkFilterVolume_h_included_ cmtk-3.3.1/libs/Base/cmtkFitAffineToLandmarks.cxx000066400000000000000000000055131276303427400216710ustar00rootroot00000000000000/* // // Copyright 2012 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4427 $ // // $LastChangedDate: 2012-06-12 11:23:22 -0700 (Tue, 12 Jun 2012) $ // // $LastChangedBy: torstenrohlfing $ // */ #include "cmtkFitAffineToLandmarks.h" cmtk::FitAffineToLandmarks::FitAffineToLandmarks( const LandmarkPairList& landmarkPairs ) { // first, get the centroids in "from" and "to" space cmtk::FixedVector<3,cmtk::Types::Coordinate> cFrom( 0.0 ); cmtk::FixedVector<3,cmtk::Types::Coordinate> cTo( 0.0 ); size_t nLandmarks = 0; for ( LandmarkPairList::const_iterator it = landmarkPairs.begin(); it != landmarkPairs.end(); ++it ) { cFrom += it->m_Location; cTo += it->m_TargetLocation; ++nLandmarks; } cFrom /= nLandmarks; cTo /= nLandmarks; // now compute the transformation matrix for rotation, scale, shear, using the previously computed centroids for reference Matrix3x3 txT = Matrix3x3::Zero(); // "t" is the 3xN matrix of transformation vectors (after removing global translation) at the control points Matrix3x3 xxT = Matrix3x3::Zero(); // "x" is the 3xN matrix of control point grid coordinates // build the two 3x3 matrices of (t*xT)(x*xT) on the fly. for ( LandmarkPairList::const_iterator it = landmarkPairs.begin(); it != landmarkPairs.end(); ++it ) { const cmtk::FixedVector<3,cmtk::Types::Coordinate> x = it->m_Location - cFrom; const cmtk::FixedVector<3,cmtk::Types::Coordinate> t = it->m_TargetLocation - cTo; for ( size_t j = 0; j < 3; ++j ) { for ( size_t i = 0; i < 3; ++i ) { txT[i][j] += t[j] * x[i]; xxT[i][j] += x[j] * x[i]; } } } Matrix3x3 matrix = (xxT.GetInverse()*txT); // put everything together AffineXform::MatrixType matrix4x4( matrix ); this->m_AffineXform = AffineXform::SmartPtr( new AffineXform( matrix4x4 ) ); this->m_AffineXform->SetTranslation( (cTo - cFrom) ); this->m_AffineXform->SetCenter( cFrom ); } cmtk-3.3.1/libs/Base/cmtkFitAffineToLandmarks.h000066400000000000000000000036701276303427400213200ustar00rootroot00000000000000/* // // Copyright 2012 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4164 $ // // $LastChangedDate: 2012-04-10 15:01:37 -0700 (Tue, 10 Apr 2012) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkFitAffineToLandmarks_h_included_ #define __cmtkFitAffineToLandmarks_h_included_ #include #include #include #include namespace cmtk { /** \addtogroup Base */ //@{ /** Fit affine transformation to a set of landmark pairs. */ class FitAffineToLandmarks { public: /// This class. typedef FitAffineToLandmarks Self; /// Constructor. FitAffineToLandmarks( const LandmarkPairList& landmarkPairs ); /// Return the affine transformation. AffineXform::SmartPtr GetAffineXform() { return this->m_AffineXform; } /// Return the constant affine transformation. AffineXform::SmartConstPtr GetAffineXform() const { return this->m_AffineXform; } private: /// The fitted transformation. AffineXform::SmartPtr m_AffineXform; }; } // namespace #endif // #ifndef __cmtkFitAffineToLandmarks_h_included_ cmtk-3.3.1/libs/Base/cmtkFitAffineToWarpXform.cxx000066400000000000000000000075141276303427400217050ustar00rootroot00000000000000/* // // Copyright 2012 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4430 $ // // $LastChangedDate: 2012-06-12 13:55:02 -0700 (Tue, 12 Jun 2012) $ // // $LastChangedBy: torstenrohlfing $ // */ #include "cmtkFitAffineToWarpXform.h" #include #include #include cmtk::FitAffineToWarpXform::FitAffineToWarpXform( WarpXform::SmartConstPtr warp ) : m_WarpXform( warp ) { } cmtk::AffineXform::SmartPtr cmtk::FitAffineToWarpXform::Fit() { const WarpXform& warpXform = *(this->m_WarpXform); // bypass smart pointer for speed // first, get the centroids in "from" and "to" space cmtk::FixedVector<3,cmtk::Types::Coordinate> cFrom( 0.0 ); cmtk::FixedVector<3,cmtk::Types::Coordinate> cTo( 0.0 ); size_t valid = 0; for ( RegionIndexIterator it = warpXform.GetInsideControlPointsRegion(); it != it.end(); ++it ) { const WarpXform::SpaceVectorType v = warpXform.GetDeformedControlPointPosition( it.Index()[0], it.Index()[1], it.Index()[2] ); if ( MathUtil::IsFinite( v[0] ) ) { ++valid; cFrom += warpXform.GetOriginalControlPointPosition( it.Index()[0], it.Index()[1], it.Index()[2] ); cTo += v; } } cFrom /= valid; cTo /= valid; // now get the transformation matrix for rotation, scale, shear, using the previously computed centroids for reference Matrix3x3 matrix = Self::GetMatrix( *(this->m_WarpXform), cFrom, cTo ); // put everything together AffineXform::MatrixType matrix4x4( matrix ); AffineXform::SmartPtr result( new AffineXform( matrix4x4 ) ); result->SetTranslation( (cTo - cFrom) ); result->SetCenter( cFrom ); return result; } cmtk::Matrix3x3 cmtk::FitAffineToWarpXform::GetMatrix( const WarpXform& warpXform, const cmtk::FixedVector<3,cmtk::Types::Coordinate>& cFrom, const cmtk::FixedVector<3,cmtk::Types::Coordinate>& cTo ) { Matrix3x3 txT = Matrix3x3::Zero(); // "t" is the 3xN matrix of transformation vectors (after removing global translation) at the control points Matrix3x3 xxT = Matrix3x3::Zero(); // "x" is the 3xN matrix of control point grid coordinates // build the two 3x3 matrices of (t*xT)(x*xT) on the fly. for ( RegionIndexIterator it = warpXform.GetInsideControlPointsRegion(); it != it.end(); ++it ) { const cmtk::FixedVector<3,cmtk::Types::Coordinate> x = warpXform.GetOriginalControlPointPosition( it.Index()[0], it.Index()[1], it.Index()[2] ) - cFrom; const cmtk::FixedVector<3,cmtk::Types::Coordinate> t = warpXform.GetDeformedControlPointPosition( it.Index()[0], it.Index()[1], it.Index()[2] ) - cTo; if ( MathUtil::IsFinite( t[0] ) ) { for ( size_t j = 0; j < 3; ++j ) { for ( size_t i = 0; i < 3; ++i ) { txT[i][j] += t[j] * x[i]; xxT[i][j] += x[j] * x[i]; } } } } return (xxT.GetInverse()*txT); } cmtk-3.3.1/libs/Base/cmtkFitAffineToWarpXform.h000066400000000000000000000050251276303427400213250ustar00rootroot00000000000000/* // // Copyright 2012 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 3876 $ // // $LastChangedDate: 2012-02-15 15:47:19 -0800 (Wed, 15 Feb 2012) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkFitAffineToWarpXform_h_included_ #define __cmtkFitAffineToWarpXform_h_included_ #include #include #include #include namespace cmtk { /** \addtogroup Base */ //@{ /** Fit affine transformation to nonrigid (B-spline or deformation field) transformation. */ class FitAffineToWarpXform { public: /// This class. typedef FitAffineToWarpXform Self; /// Constructor. FitAffineToWarpXform( WarpXform::SmartConstPtr warp ); /// Fit affine transformation. AffineXform::SmartPtr Fit(); private: /// Input nonrigid warp transformation. WarpXform::SmartConstPtr m_WarpXform; /** Compute rotation, scale, and shear matrix using previously computed translation. * We are using simple pseudoinverse rather than procrustes because we do not care whether * the result is rigid (det = 1). In fact, if the underlying transformation is not * rigid but full affine, then that is exactly what we want the output to be. */ static Matrix3x3 GetMatrix( const WarpXform& warpXform /*!< Reference to current warp transformation.*/, const cmtk::FixedVector<3,cmtk::Types::Coordinate>& cFrom /*!< Centroid in "from" space previously computed by GetCentroids member function.*/, const cmtk::FixedVector<3,cmtk::Types::Coordinate>& cTo /*!< Centroid in "to" space previously computed by GetCentroids member function.*/ ); }; } // namespace #endif // #ifndef __cmtkFitAffineToWarpXform_h_included_ cmtk-3.3.1/libs/Base/cmtkFitAffineToXformList.cxx000066400000000000000000000130021276303427400216740ustar00rootroot00000000000000/* // // Copyright 2012 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4438 $ // // $LastChangedDate: 2012-06-14 11:52:10 -0700 (Thu, 14 Jun 2012) $ // // $LastChangedBy: torstenrohlfing $ // */ #include "cmtkFitAffineToXformList.h" #include #include cmtk::AffineXform::SmartPtr cmtk::FitAffineToXformList::Fit( const bool fitRigid ) { // first, get the centroids in "from" and "to" space cmtk::FixedVector<3,cmtk::Types::Coordinate> cFrom( 0.0 ); cmtk::FixedVector<3,cmtk::Types::Coordinate> cTo( 0.0 ); size_t numberOfValidPixels = 0; size_t ofs = 0; for ( RegionIndexIterator it = this->m_XformField.GetWholeImageRegion(); it != it.end(); ++it, ++ofs ) { if ( this->m_XformValidAt[ofs] ) { cFrom += this->m_XformField.GetGridLocation( it.Index() ); cTo += this->m_XformField[ofs]; ++numberOfValidPixels; } } cFrom /= numberOfValidPixels; cTo /= numberOfValidPixels; // now get the transformation matrix for rotation, scale, shear, using the previously computed centroids for reference Matrix3x3 matrix = fitRigid ? this->GetMatrixRigidSVD( cFrom, cTo ) : this->GetMatrixAffinePseudoinverse( cFrom, cTo ); // put everything together AffineXform::MatrixType matrix4x4( matrix ); AffineXform::SmartPtr result( new AffineXform( matrix4x4 ) ); result->SetTranslation( (cTo - cFrom) ); result->SetCenter( cFrom ); return result; } cmtk::Matrix3x3 cmtk::FitAffineToXformList::GetMatrixAffinePseudoinverse( const cmtk::FixedVector<3,cmtk::Types::Coordinate>& cFrom, const cmtk::FixedVector<3,cmtk::Types::Coordinate>& cTo ) { Matrix3x3 txT = Matrix3x3::Zero(); // "t" is the 3xN matrix of transformation vectors (after removing global translation) at the control points Matrix3x3 xxT = Matrix3x3::Zero(); // "x" is the 3xN matrix of control point grid coordinates // build the two 3x3 matrices of (t*xT)(x*xT) on the fly. size_t ofs = 0; for ( RegionIndexIterator it = this->m_XformField.GetWholeImageRegion(); it != it.end(); ++it, ++ofs ) { if ( this->m_XformValidAt[ofs] ) { const cmtk::FixedVector<3,cmtk::Types::Coordinate> x = this->m_XformField.GetGridLocation( it.Index() ) - cFrom; const cmtk::FixedVector<3,cmtk::Types::Coordinate> t = this->m_XformField[ofs] - cTo; for ( size_t j = 0; j < 3; ++j ) { for ( size_t i = 0; i < 3; ++i ) { txT[i][j] += t[j] * x[i]; xxT[i][j] += x[j] * x[i]; } } } } return (xxT.GetInverse()*txT); } cmtk::Matrix3x3 cmtk::FitAffineToXformList::GetMatrixRigidSVD( const cmtk::FixedVector<3,cmtk::Types::Coordinate>& cFrom, const cmtk::FixedVector<3,cmtk::Types::Coordinate>& cTo ) { // compute the transformation matrix for rotation, scale, shear, using the previously computed centroids for reference Matrix2D U( 3, 3 ); U.SetAllToZero(); // build the two 3x3 matrices of (t*xT)(x*xT) on the fly. size_t ofs = 0; for ( RegionIndexIterator it = this->m_XformField.GetWholeImageRegion(); it != it.end(); ++it, ++ofs ) { if ( this->m_XformValidAt[ofs] ) { const cmtk::FixedVector<3,cmtk::Types::Coordinate> x = this->m_XformField.GetGridLocation( it.Index() ) - cFrom; const cmtk::FixedVector<3,cmtk::Types::Coordinate> t = this->m_XformField[ofs] - cTo; for ( size_t j = 0; j < 3; ++j ) { for ( size_t i = 0; i < 3; ++i ) { U[i][j] += t[j] * x[i]; } } } } // use SVD to solve orthogonal procrustes problem Matrix2D V( 3, 3 ); std::vector W( 3 ); MathUtil::SVD( U, W, V ); Matrix3x3 matrix = Matrix3x3::Zero(); for ( size_t j = 0; j < 3; ++j ) { for ( size_t i = 0; i < 3; ++i ) { for ( size_t k = 0; k < 3; ++k ) { matrix[j][i] += V[i][k] * U[j][k]; } } } // if there is a flip, find zero singular value and flip its singular vector. if ( matrix.Determinant() < 0 ) { int minSV = -1; if (W[0] < W[1]) { if (W[0] < W[2]) { minSV = 0; } else { minSV = 2; } } else { if (W[1] < W[2]) { minSV = 1; } else { minSV = 2; } } for ( size_t j = 0; j < 3; ++j ) { V[j][minSV] *= -1; } for ( size_t j = 0; j < 3; ++j ) { for ( size_t i = 0; i < 3; ++i ) { matrix[j][i] = 0; for ( size_t k = 0; k < 3; ++k ) { matrix[j][i] += V[i][k] * U[j][k]; } } } } return matrix; } cmtk-3.3.1/libs/Base/cmtkFitAffineToXformList.h000066400000000000000000000066771276303427400213450ustar00rootroot00000000000000/* // // Copyright 2012 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4438 $ // // $LastChangedDate: 2012-06-14 11:52:10 -0700 (Thu, 14 Jun 2012) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkFitAffineToXformList_h_included_ #define __cmtkFitAffineToXformList_h_included_ #include #include #include #include namespace cmtk { /** \addtogroup Base */ //@{ /** Fit affine transformation to series of concatenated, possibly numerically inverted, transformations. */ class FitAffineToXformList : protected FitToXformListBase { public: /// This class. typedef FitAffineToXformList Self; /// Base class. typedef FitToXformListBase Superclass; /// Constructor. FitAffineToXformList( const UniformVolume& sampleGrid /*!< Discrete pixel grid where the spline transformation is sampled and residuals are minimized.*/, const XformList& xformList /*!< List of concatenated transformation that the spline transformation is fitted to.*/, const bool absolute = true /*!< Flag fitting absolute transformation vs. relative deformation field */ ) : Superclass( sampleGrid, xformList, absolute ) {} /// Fit affine transformation. AffineXform::SmartPtr Fit( const bool fitRigid = false /*!< If this flag is set, a rigid transformation is fitted, otherwise a full affine transformation */ ); private: /** Compute rotation, scale, and shear matrix by pseudinverse using previously computed centroid translation. * We are using simple pseudoinverse rather than procrustes because we do not care whether * the result is rigid (det = 1). In fact, if the underlying transformation is not * rigid but full affine, then that is exactly what we want the output to be. */ Matrix3x3 GetMatrixAffinePseudoinverse( const cmtk::FixedVector<3,cmtk::Types::Coordinate>& cFrom /*!< Centroid in "from" space previously computed by GetCentroids member function.*/, const cmtk::FixedVector<3,cmtk::Types::Coordinate>& cTo /*!< Centroid in "to" space previously computed by GetCentroids member function.*/ ); /** Compute rotation matrix by SVD using previously computed centroid translation. */ Matrix3x3 GetMatrixRigidSVD( const cmtk::FixedVector<3,cmtk::Types::Coordinate>& cFrom /*!< Centroid in "from" space previously computed by GetCentroids member function.*/, const cmtk::FixedVector<3,cmtk::Types::Coordinate>& cTo /*!< Centroid in "to" space previously computed by GetCentroids member function.*/ ); }; } // namespace #endif // #ifndef __cmtkFitAffineToXformList_h_included_ cmtk-3.3.1/libs/Base/cmtkFitPolynomialToLandmarks.cxx000066400000000000000000000100661276303427400226230ustar00rootroot00000000000000/* // // Copyright 2012, 2014 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 5147 $ // // $LastChangedDate: 2014-01-10 15:31:18 -0800 (Fri, 10 Jan 2014) $ // // $LastChangedBy: torstenrohlfing $ // */ #include "cmtkFitPolynomialToLandmarks.h" cmtk::FitPolynomialToLandmarks::FitPolynomialToLandmarks( const LandmarkPairList& landmarkPairs, const byte degree ) { // first, get the centroids in "from" and "to" space PolynomialXform::SpaceVectorType cFrom( 0.0 ); PolynomialXform::SpaceVectorType cTo( 0.0 ); size_t nLandmarks = 0; for ( LandmarkPairList::const_iterator it = landmarkPairs.begin(); it != landmarkPairs.end(); ++it ) { cFrom += it->m_Location; cTo += it->m_TargetLocation; ++nLandmarks; } cFrom /= nLandmarks; cTo /= nLandmarks; // put everything together this->m_PolynomialXform = PolynomialXform::SmartPtr( new PolynomialXform( degree ) ); this->m_PolynomialXform->SetCenter( cFrom ); // Fit incrementally - start with lower degrees. // For degree = 0 we actually get relative translation of the centers of mass of the landmarks in the two spaces for ( byte fitDegree = 0; fitDegree <= degree; ++fitDegree ) { // what is the first parameter at the current degree? const size_t firstParameter = PolynomialHelper::GetNumberOfMonomials( fitDegree-1 ); // how many parameters in addition to prior degree? const size_t nParameters = PolynomialHelper::GetNumberOfMonomials( fitDegree ) - firstParameter; // set up matrix for SVD-based linear regression Matrix2D U( nLandmarks, nParameters ); std::vector residuals( nLandmarks ); size_t lmIdx = 0; for ( LandmarkPairList::const_iterator it = landmarkPairs.begin(); it != landmarkPairs.end(); ++it, ++lmIdx ) { // current residual for this landmark residuals[lmIdx] = it->m_TargetLocation - this->m_PolynomialXform->Apply( it->m_Location ); // monomials for this landmark and each of the currently fitted parameters for ( size_t paramIdx = 0; paramIdx < nParameters; ++paramIdx ) { U[lmIdx][paramIdx] = this->m_PolynomialXform->GetMonomialAt( firstParameter+paramIdx, it->m_Location ); } } // do SVD of the monomial matrix Matrix2D V( nParameters, nParameters ); std::vector W( nParameters ); MathUtil::SVD( U, W, V ); // for each dimension separately, use the SVD to get parameters that minimize the residuals (keeping in mind that poly xform is additive, i.e., adding monomials makes an additive contribution to existing transformation) std::vector params; for ( int dim = 0; dim < 3; ++dim ) { // first, extract residuals for current spatial dimension, dim std::vector dimResidual( nLandmarks ); for ( size_t lmIdx = 0; lmIdx < nLandmarks; ++lmIdx ) dimResidual[lmIdx] = residuals[lmIdx][dim]; // now apply the previously computed SVD to get coefficients for the additional monomials at this level MathUtil::SVDLinearRegression( U, W, V, dimResidual, params ); for ( size_t paramIdx = 0; paramIdx < nParameters; ++paramIdx ) { this->m_PolynomialXform->m_Parameters[3*(firstParameter+paramIdx) + dim] = params[paramIdx]; } } } } cmtk-3.3.1/libs/Base/cmtkFitPolynomialToLandmarks.h000066400000000000000000000043611276303427400222510ustar00rootroot00000000000000/* // // Copyright 2012, 2014 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 5152 $ // // $LastChangedDate: 2014-01-10 16:21:09 -0800 (Fri, 10 Jan 2014) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkFitPolynomialToLandmarks_h_included_ #define __cmtkFitPolynomialToLandmarks_h_included_ #include #include #include #include namespace cmtk { /** \addtogroup Base */ //@{ /** Fit polynomial transformation to a set of landmark pairs. * The fitting algorithm uses SVD and works incrementally. That is, lower polynomial degrees are fitted first, followed by higher ones. */ class FitPolynomialToLandmarks { public: /// This class. typedef FitPolynomialToLandmarks Self; /// Constructor. FitPolynomialToLandmarks( const LandmarkPairList& landmarkPairs /*!< Landmark pairs to fit transformation to */, const byte degree /*!< Degree of the fitted polynomial */ ); /// Return the affine transformation. PolynomialXform::SmartPtr GetPolynomialXform() { return this->m_PolynomialXform; } /// Return the constant affine transformation. PolynomialXform::SmartConstPtr GetPolynomialXform() const { return this->m_PolynomialXform; } private: /// The fitted transformation. PolynomialXform::SmartPtr m_PolynomialXform; }; } // namespace #endif // #ifndef __cmtkFitPolynomialToLandmarks_h_included_ cmtk-3.3.1/libs/Base/cmtkFitRigidToLandmarks.cxx000066400000000000000000000070311276303427400215340ustar00rootroot00000000000000/* // // Copyright 2012 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4427 $ // // $LastChangedDate: 2012-06-12 11:23:22 -0700 (Tue, 12 Jun 2012) $ // // $LastChangedBy: torstenrohlfing $ // */ #include "cmtkFitRigidToLandmarks.h" #include #include cmtk::FitRigidToLandmarks::FitRigidToLandmarks( const LandmarkPairList& landmarkPairs ) { // first, get the centroids in "from" and "to" space cmtk::FixedVector<3,cmtk::Types::Coordinate> cFrom( 0.0 ); cmtk::FixedVector<3,cmtk::Types::Coordinate> cTo( 0.0 ); size_t nLandmarks = 0; for ( LandmarkPairList::const_iterator it = landmarkPairs.begin(); it != landmarkPairs.end(); ++it ) { cFrom += it->m_Location; cTo += it->m_TargetLocation; ++nLandmarks; } cFrom /= nLandmarks; cTo /= nLandmarks; // now compute the transformation matrix for rotation, scale, shear, using the previously computed centroids for reference Matrix2D U( 3, 3 ); U.SetAllToZero(); // build the two 3x3 matrices of (t*xT)(x*xT) on the fly. for ( LandmarkPairList::const_iterator it = landmarkPairs.begin(); it != landmarkPairs.end(); ++it ) { const cmtk::FixedVector<3,cmtk::Types::Coordinate> x = it->m_Location - cFrom; const cmtk::FixedVector<3,cmtk::Types::Coordinate> t = it->m_TargetLocation - cTo; for ( size_t j = 0; j < 3; ++j ) { for ( size_t i = 0; i < 3; ++i ) { U[i][j] += t[j] * x[i]; } } } // use SVD to solve orthogonal procrustes problem Matrix2D V( 3, 3 ); std::vector W( 3 ); MathUtil::SVD( U, W, V ); Matrix3x3 matrix = Matrix3x3::Zero(); for ( size_t j = 0; j < 3; ++j ) { for ( size_t i = 0; i < 3; ++i ) { for ( size_t k = 0; k < 3; ++k ) { matrix[j][i] += V[i][k] * U[j][k]; } } } // if there is a flip, find zero singular value and flip its singular vector. if ( matrix.Determinant() < 0 ) { int minSV = -1; if (W[0] < W[1]) { if (W[0] < W[2]) { minSV = 0; } else { minSV = 2; } } else { if (W[1] < W[2]) { minSV = 1; } else { minSV = 2; } } for ( size_t j = 0; j < 3; ++j ) { V[j][minSV] *= -1; } for ( size_t j = 0; j < 3; ++j ) { for ( size_t i = 0; i < 3; ++i ) { matrix[j][i] = 0; for ( size_t k = 0; k < 3; ++k ) { matrix[j][i] += V[i][k] * U[j][k]; } } } } // put everything together AffineXform::MatrixType matrix4x4( matrix ); this->m_RigidXform = AffineXform::SmartPtr( new AffineXform( matrix4x4 ) ); this->m_RigidXform->SetTranslation( (cTo - cFrom) ); this->m_RigidXform->SetCenter( cFrom ); } cmtk-3.3.1/libs/Base/cmtkFitRigidToLandmarks.h000066400000000000000000000036531276303427400211670ustar00rootroot00000000000000/* // // Copyright 2012 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4164 $ // // $LastChangedDate: 2012-04-10 15:01:37 -0700 (Tue, 10 Apr 2012) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkFitRigidToLandmarks_h_included_ #define __cmtkFitRigidToLandmarks_h_included_ #include #include #include #include namespace cmtk { /** \addtogroup Base */ //@{ /** Fit affine transformation to a set of landmark pairs. */ class FitRigidToLandmarks { public: /// This class. typedef FitRigidToLandmarks Self; /// Constructor. FitRigidToLandmarks( const LandmarkPairList& landmarkPairs ); /// Return the rigid transformation. AffineXform::SmartPtr GetRigidXform() { return this->m_RigidXform; } /// Return the constant rigid transformation. AffineXform::SmartConstPtr GetRigidXform() const { return this->m_RigidXform; } private: /// The fitted transformation. AffineXform::SmartPtr m_RigidXform; }; } // namespace #endif // #ifndef __cmtkFitRigidToLandmarks_h_included_ cmtk-3.3.1/libs/Base/cmtkFitSplineWarpToDeformationField.cxx000066400000000000000000000166671276303427400241000ustar00rootroot00000000000000/* // // Copyright 2012 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4660 $ // // $LastChangedDate: 2012-12-10 16:59:23 -0800 (Mon, 10 Dec 2012) $ // // $LastChangedBy: torstenrohlfing $ // */ #include "cmtkFitSplineWarpToDeformationField.h" #include #include #include #include cmtk::FitSplineWarpToDeformationField::FitSplineWarpToDeformationField( DeformationField::SmartConstPtr dfield, const bool absolute ) : m_FitAbsolute( absolute ), m_DeformationField( dfield ), m_DeformationFieldFOV( dfield->m_Offset, dfield->m_Domain ) { } void cmtk::FitSplineWarpToDeformationField::ComputeResiduals( const SplineWarpXform& splineWarp ) { const DataGrid::IndexType dims = this->m_DeformationField->m_Dims; this->m_Residuals.resize( dims.Product() ); #pragma omp parallel for for ( int z = 0; z < dims[2]; ++z ) { size_t ofs = z * dims[0] * dims[1]; for ( int y = 0; y < dims[1]; ++y ) { for ( int x = 0; x < dims[0]; ++x, ++ofs ) { const Xform::SpaceVectorType xT = this->m_DeformationField->GetShiftedControlPointPositionByOffset( ofs ); if ( MathUtil::IsFinite( xT[0] ) ) { this->m_Residuals[ofs] = xT - splineWarp.GetTransformedGrid( x, y, z ); if ( this->m_FitAbsolute ) this->m_Residuals[ofs] += this->m_DeformationField->GetOriginalControlPointPositionByOffset( ofs ); } else { this->m_Residuals[ofs] = Xform::SpaceVectorType( std::numeric_limits::quiet_NaN() ); } } } } } cmtk::SplineWarpXform::SmartPtr cmtk::FitSplineWarpToDeformationField::Fit( const SplineWarpXform::ControlPointIndexType& finalDims, const int nLevels, const AffineXform* affineXform ) { // we may have to adjust nLevels downwards int numberOfLevels = nLevels; SplineWarpXform::ControlPointIndexType initialDims = finalDims; for ( int level = 1; level < numberOfLevels; ++level ) { if ( (initialDims[0]&1) && (initialDims[1]&1) && (initialDims[2]&1) && // check that all dims are still odd numbers (initialDims.MinValue()>4) ) // check that at least 4 control points will be left in each dimension { // apply the inverse of the refinement formula used in SplineWarpXform::Refine() initialDims.AddScalar( +3 ); initialDims /= 2; } else { numberOfLevels = level; DebugOutput(2) << "INFO: adjusted number of levels to " << numberOfLevels << " from " << nLevels << " to ensure sufficient number of control points\n"; } } // initialize B-spline transformation AffineXform::SmartPtr initialAffine( affineXform ? new AffineXform( *affineXform ) : new AffineXform ); SplineWarpXform* splineWarp = new SplineWarpXform( this->m_DeformationField->m_Domain, initialDims, CoordinateVector::SmartPtr::Null(), initialAffine ); this->FitSpline( *splineWarp, numberOfLevels ); return cmtk::SplineWarpXform::SmartPtr( splineWarp ); } cmtk::SplineWarpXform::SmartPtr cmtk::FitSplineWarpToDeformationField::Fit( const Types::Coordinate finalSpacing, const int nLevels, const AffineXform* affineXform ) { // compute the start spacing of multi-level approximation by doubling final spacing until user-defined initial spacing is exceeded. Types::Coordinate spacing = finalSpacing * (1 << (nLevels-1)); // initialize B-spline transformation AffineXform::SmartPtr initialAffine( affineXform ? new AffineXform( *affineXform ) : new AffineXform ); SplineWarpXform* splineWarp = new SplineWarpXform( this->m_DeformationField->m_Domain, spacing, initialAffine ); this->FitSpline( *splineWarp, nLevels ); return cmtk::SplineWarpXform::SmartPtr( splineWarp ); } void cmtk::FitSplineWarpToDeformationField::FitSpline( SplineWarpXform& splineWarp, const int nLevels ) { // loop until final control point spacing for ( int level = 0; level < nLevels; ++level ) { DebugOutput( 5 ) << "Multi-resolution spline fitting level " << level+1 << " out of " << nLevels << "\n"; // refine control point grid unless this is first iteration if ( level ) { splineWarp.Refine(); } DebugOutput( 6 ) << " Control point grid is " << splineWarp.m_Dims[0] << "x" << splineWarp.m_Dims[1] << "x" << splineWarp.m_Dims[2] << "\n"; // compute residuals splineWarp.RegisterVolumePoints( this->m_DeformationField->m_Dims, this->m_DeformationField->m_Spacing, this->m_DeformationField->m_Offset ); this->ComputeResiduals( splineWarp ); // loop over all control points to compute deltas as the spline coefficients that fit current residuals std::vector< FixedVector<3,Types::Coordinate> > delta( splineWarp.m_NumberOfControlPoints, FixedVector<3,Types::Coordinate>( 0.0 ) ); std::vector< Types::Coordinate > weight( splineWarp.m_NumberOfControlPoints, 0.0 ); for ( RegionIndexIterator voxelIt( this->m_DeformationField->GetAllControlPointsRegion() ); voxelIt != voxelIt.end(); ++voxelIt ) { const DataGrid::IndexType voxelIdx = voxelIt.Index(); const Xform::SpaceVectorType residual = this->m_Residuals[this->m_DeformationField->GetOffsetFromIndex( voxelIdx )/3]; if ( MathUtil::IsFinite( residual[0] ) ) { Types::Coordinate sumOfSquares = 0; Types::Coordinate wklm[4][4][4], w2klm[4][4][4]; for ( int m = 0; m < 4; ++m ) { for ( int l = 0; l < 4; ++l ) { const Types::Coordinate wlm = splineWarp.m_GridSpline[1][4*voxelIdx[1]+l] * splineWarp.m_GridSpline[2][4*voxelIdx[2]+m]; for ( int k = 0; k < 4; ++k ) { sumOfSquares += (w2klm[m][l][k] = MathUtil::Square( wklm[m][l][k] = splineWarp.m_GridSpline[0][4*voxelIdx[0]+k] * wlm ) ); } } } for ( int m = 0; m < 4; ++m ) { const size_t mOfs = splineWarp.m_Dims[1] * ( splineWarp.m_GridIndexes[2][voxelIdx[2]] + m ); for ( int l = 0; l < 4; ++l ) { const size_t mlOfs = splineWarp.m_Dims[0] * ( splineWarp.m_GridIndexes[1][voxelIdx[1]] + l + mOfs ); for ( int k = 0; k < 4; ++k ) { const size_t cpOfs = splineWarp.m_GridIndexes[0][voxelIdx[0]] + k + mlOfs; delta[cpOfs] += w2klm[m][l][k] * wklm[m][l][k] / sumOfSquares * residual; weight[cpOfs] += w2klm[m][l][k]; } } } } } // apply delta #pragma omp parallel for for ( int cp = 0; cp < static_cast( splineWarp.m_NumberOfControlPoints ); ++cp ) { if ( weight[cp] != 0 ) { delta[cp] /= weight[cp]; splineWarp.SetShiftedControlPointPositionByOffset( splineWarp.GetShiftedControlPointPositionByOffset( cp ) + delta[cp], cp ); } else { // nothing to do - keep control point where it is. } } } } cmtk-3.3.1/libs/Base/cmtkFitSplineWarpToDeformationField.h000066400000000000000000000104171276303427400235100ustar00rootroot00000000000000/* // // Copyright 2012, 2013 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4794 $ // // $LastChangedDate: 2013-06-25 16:15:35 -0700 (Tue, 25 Jun 2013) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkFitSplineWarpToDeformationField_h_included_ #define __cmtkFitSplineWarpToDeformationField_h_included_ #include #include #include #include #include namespace cmtk { /** \addtogroup Base */ //@{ /** Fit B-spline-based free-form deformation to pixel-wise deformation field. *\see This class implements the algorithm from: N. J. Tustison, B. B. Avants, and J. C. Gee, "Directly manipulated free-form deformation image registration," IEEE Transactions on Image Processing, vol. 18, no. 3, pp. 624-635, 2009; * http://dx.doi.org/10.1109/TIP.2008.2010072 *\see The implementation itself is more closely following S. Lee, G. Wolberg, and S. Y. Shin, “Scattered data interpolation with multilevel B-splines,†IEEE Transactions on Visualization and Computer Graphics, * vol. 3, no. 3, pp. 228-244, 1997. http://dx.doi.org/10.1109/2945.620490 * *\todo It would be nice to have the same multi-iteration fitting options here as in cmtk::FitSplineWarpToLandmarks. */ class FitSplineWarpToDeformationField { public: /// This class. typedef FitSplineWarpToDeformationField Self; /// Constructor. FitSplineWarpToDeformationField( DeformationField::SmartConstPtr dfield, const bool absolute /*!< Flag fitting absolute transformation vs. relative deformation field */ ); /// Fit spline warp based on final grid dimensions. SplineWarpXform::SmartPtr Fit( const SplineWarpXform::ControlPointIndexType& finalDims /*!< Final spline control point grid dimensions.*/, const int nLevels /*!< Number of levels in the multi-resolution fitting.*/, const AffineXform* initialAffine = NULL /*!< Optional affine transformation to initialize the spline control points.*/ ); /// Fit spline warp based on final grid spacing. SplineWarpXform::SmartPtr Fit( const Types::Coordinate finalSpacing /*!< Final control point spacing of the fitted B-spline free-form deformation*/, const int nLevels = 1 /*!< Number of levels for optional multi-resolution fit (default: single-resolution fit)*/, const AffineXform* initialAffine = NULL /*!< Optional affine transformation to initialize the spline control points.*/ ); private: /** Flag for absolute vs. relative deformation fields. * If this is true, the spline is fitted to the absolute transformation defined by the deformation field. * If this is false, the spline is fitted to the relative deformation field itself. */ bool m_FitAbsolute; /// Input deformation field. DeformationField::SmartConstPtr m_DeformationField; /// Deformation field residuals, i.e., pixel-wise difference between B-spline transformation and deformation field. std::vector< FixedVector<3,Types::Coordinate> > m_Residuals; /// Deformation field coverage, i.e., field of fiew. Region<3,Types::Coordinate> m_DeformationFieldFOV; /// Compute residuals, i.e., pixel-wise difference between B-spline transformation and deformation field. void ComputeResiduals( const SplineWarpXform& splineWarp ); /// Fit spline warp based on initial warp object. void FitSpline( SplineWarpXform& splineWarp, const int nLevels ); }; } // namespace #endif // #ifndef __cmtkFitSplineWarpToDeformationField_h_included_ cmtk-3.3.1/libs/Base/cmtkFitSplineWarpToLandmarks.cxx000066400000000000000000000165321276303427400225700ustar00rootroot00000000000000/* // // Copyright 2012, 2013 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4791 $ // // $LastChangedDate: 2013-06-25 15:48:27 -0700 (Tue, 25 Jun 2013) $ // // $LastChangedBy: torstenrohlfing $ // */ #include "cmtkFitSplineWarpToLandmarks.h" #include #include #include cmtk::FitSplineWarpToLandmarks::FitSplineWarpToLandmarks( const LandmarkPairList& landmarkList ) : m_Landmarks( landmarkList.begin(), landmarkList.end() ) { } cmtk::Types::Coordinate cmtk::FitSplineWarpToLandmarks::ComputeResiduals( const SplineWarpXform& splineWarp ) { this->m_LandmarksGrid.resize( this->m_Landmarks.size() ); this->m_LandmarksSpline.resize( this->m_Landmarks.size() ); this->m_Residuals.resize( this->m_Landmarks.size() ); Types::Coordinate msResidual = 0; #pragma omp parallel for reduction(+:msResidual) for ( int i = 0; i < static_cast( this->m_Landmarks.size() ); ++i ) { splineWarp.PrecomputeLocationSpline( this->m_Landmarks[i].m_Location, this->m_LandmarksGrid[i], this->m_LandmarksSpline[i] ); this->m_Residuals[i] = this->m_Landmarks[i].m_TargetLocation - splineWarp.Apply( this->m_Landmarks[i].m_Location ); msResidual += this->m_Residuals[i].SumOfSquares(); } return sqrt( msResidual ); } cmtk::SplineWarpXform::SmartPtr cmtk::FitSplineWarpToLandmarks::Fit ( const SplineWarpXform::SpaceVectorType& domain, const SplineWarpXform::ControlPointIndexType& finalDims, const AffineXform* affineXform, const Self::Parameters& parameters ) { // we may have to adjust m_Levels downwards Self::Parameters actualParameters = parameters; SplineWarpXform::ControlPointIndexType initialDims = finalDims; for ( int level = 1; level < actualParameters.m_Levels; ++level ) { if ( (initialDims[0]&1) && (initialDims[1]&1) && (initialDims[2]&1) && // check that all dims are still odd numbers (initialDims.MinValue()>4) ) // check that at least 4 control points will be left in each dimension { // apply the inverse of the refinement formula used in SplineWarpXform::Refine() initialDims.AddScalar( +3 ); initialDims /= 2; } else { actualParameters.m_Levels = level; DebugOutput(2) << "INFO: adjusted number of levels to " << level << " from " << parameters.m_Levels << " to ensure sufficient number of control points\n"; } } // initialize B-spline transformation AffineXform::SmartPtr initialAffine( affineXform ? new AffineXform( *affineXform ) : new AffineXform ); SplineWarpXform* splineWarp = new SplineWarpXform( domain, initialDims, CoordinateVector::SmartPtr::Null(), initialAffine ); this->FitSpline( *splineWarp, actualParameters ); return cmtk::SplineWarpXform::SmartPtr( splineWarp ); } cmtk::SplineWarpXform::SmartPtr cmtk::FitSplineWarpToLandmarks::Fit ( const SplineWarpXform::SpaceVectorType& domain, const Types::Coordinate finalSpacing, const AffineXform* affineXform, const Self::Parameters& parameters ) { // compute the start spacing of multi-level approximation by doubling final spacing until user-defined initial spacing is exceeded. Types::Coordinate spacing = finalSpacing * (1 << (parameters.m_Levels-1)); // initialize B-spline transformation AffineXform::SmartPtr initialAffine( affineXform ? new AffineXform( *affineXform ) : new AffineXform ); SplineWarpXform* splineWarp = new SplineWarpXform( domain, spacing, initialAffine ); this->FitSpline( *splineWarp, parameters ); return cmtk::SplineWarpXform::SmartPtr( splineWarp ); } void cmtk::FitSplineWarpToLandmarks::FitSpline( SplineWarpXform& splineWarp, const Self::Parameters& parameters ) { // loop until final control point spacing for ( int level = 0; level < parameters.m_Levels; ++level ) { DebugOutput( 5 ) << "Multi-resolution spline fitting level " << level+1 << " out of " << parameters.m_Levels << "\n"; // refine control point grid unless this is first iteration if ( level ) { splineWarp.Refine(); } DebugOutput( 6 ) << " Control point grid is " << splineWarp.m_Dims[0] << "x" << splineWarp.m_Dims[1] << "x" << splineWarp.m_Dims[2] << "\n"; // compute residuals Types::Coordinate rmsResidualBefore = this->ComputeResiduals( splineWarp ); DebugOutput( 6 ) << " RMS residual before update is " << rmsResidualBefore << "\n"; for ( int iteration = 0; iteration < parameters.m_IterationsPerLevel; ++iteration ) { // loop over all control points to compute deltas as the spline coefficients that fit current residuals std::vector< FixedVector<3,Types::Coordinate> > delta( splineWarp.m_NumberOfControlPoints, FixedVector<3,Types::Coordinate>( 0.0 ) ); std::vector< Types::Coordinate > weight( splineWarp.m_NumberOfControlPoints, 0.0 ); for ( size_t i = 0; i < this->m_Landmarks.size(); ++i ) { Types::Coordinate sumOfSquares = 0; Types::Coordinate wklm[4][4][4], w2klm[4][4][4]; for ( int m = 0; m < 4; ++m ) { for ( int l = 0; l < 4; ++l ) { const Types::Coordinate wlm = this->m_LandmarksSpline[i][1][l] * this->m_LandmarksSpline[i][2][m]; for ( int k = 0; k < 4; ++k ) { sumOfSquares += (w2klm[m][l][k] = MathUtil::Square( wklm[m][l][k] = this->m_LandmarksSpline[i][0][k] * wlm ) ); } } } for ( int m = 0; m < 4; ++m ) { const size_t mOfs = splineWarp.m_Dims[1] * ( this->m_LandmarksGrid[i][2] + m ); for ( int l = 0; l < 4; ++l ) { const size_t mlOfs = splineWarp.m_Dims[0] * ( this->m_LandmarksGrid[i][1] + l + mOfs ); for ( int k = 0; k < 4; ++k ) { const size_t cpOfs = this->m_LandmarksGrid[i][0] + k + mlOfs; delta[cpOfs] += w2klm[m][l][k] * wklm[m][l][k] / sumOfSquares * this->m_Residuals[i]; weight[cpOfs] += w2klm[m][l][k]; } } } } // apply delta #pragma omp parallel for for ( int cp = 0; cp < static_cast( splineWarp.m_NumberOfControlPoints ); ++cp ) { if ( weight[cp] != 0 ) { delta[cp] /= weight[cp]; splineWarp.SetShiftedControlPointPositionByOffset( splineWarp.GetShiftedControlPointPositionByOffset( cp ) + delta[cp], cp ); } else { // nothing to do - keep control point where it is. } } Types::Coordinate rmsResidualAfter = this->ComputeResiduals( splineWarp ); DebugOutput( 6 ) << " RMS residual after update is " << rmsResidualAfter << "\n"; if ( ((rmsResidualBefore - rmsResidualAfter) / rmsResidualBefore) < parameters.m_ResidualThreshold ) break; rmsResidualBefore = rmsResidualAfter; } } } cmtk-3.3.1/libs/Base/cmtkFitSplineWarpToLandmarks.h000066400000000000000000000122231276303427400222060ustar00rootroot00000000000000/* // // Copyright 2012 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4791 $ // // $LastChangedDate: 2013-06-25 15:48:27 -0700 (Tue, 25 Jun 2013) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkFitSplineWarpToLandmarks_h_included_ #define __cmtkFitSplineWarpToLandmarks_h_included_ #include #include #include #include #include namespace cmtk { /** \addtogroup Base */ //@{ /** Fit B-spline-based free-form deformation to a list of matched landmarks. *\see This class implements the algorithm from: N. J. Tustison, B. B. Avants, and J. C. Gee, "Directly manipulated free-form deformation image registration," IEEE Transactions on Image Processing, vol. 18, no. 3, pp. 624-635, 2009; * http://dx.doi.org/10.1109/TIP.2008.2010072 *\see The implementation itself is more closely following S. Lee, G. Wolberg, and S. Y. Shin, “Scattered data interpolation with multilevel B-splines,†IEEE Transactions on Visualization and Computer Graphics, * vol. 3, no. 3, pp. 228-244, 1997. http://dx.doi.org/10.1109/2945.620490 * *\attention This class does not compile with Solaris Studio, unless STLport support is activated via the "-library=stlport4" compiler command line option. */ class FitSplineWarpToLandmarks { public: /// This class. typedef FitSplineWarpToLandmarks Self; /// Class for parameters to the fitting algorithm. class Parameters { public: /// Default constructor. Parameters() : m_Levels( 1 ), m_IterationsPerLevel( 100 ), m_ResidualThreshold( 0 ) {} /// Number of levels in the multi-resolution fitting. int m_Levels; /// Number of update iterations per level in the multi-resolution fitting. int m_IterationsPerLevel; /// Threshold for relative RMS residual improvement. Iteration terminates if (rmsAfterUpdate-rmsBeforeUpdate)/rmsBeforeUpdate < threshold. Types::Coordinate m_ResidualThreshold; }; /// Constructor. FitSplineWarpToLandmarks( const LandmarkPairList& landmarkList ); /// Fit spline warp based on final grid dimensions. SplineWarpXform::SmartPtr Fit( const SplineWarpXform::SpaceVectorType& domain /*!< Domain of the deformation field. This should be the size of the fixed image grid to be used with the resulting deformation */, const SplineWarpXform::ControlPointIndexType& finalDims /*!< Final spline control point grid dimensions.*/, const AffineXform* initialAffine = NULL /*!< Optional affine transformation to initialize the spline control points.*/, const Self::Parameters& parameters = Self::DefaultParameters /*!< Fitting parameters.*/ ); /// Fit spline warp based on final grid spacing. SplineWarpXform::SmartPtr Fit( const SplineWarpXform::SpaceVectorType& domain /*!< Domain of the deformation field. This should be the size of the fixed image grid to be used with the resulting deformation */, const Types::Coordinate finalSpacing /*!< Final control point spacing of the fitted B-spline free-form deformation*/, const AffineXform* initialAffine = NULL /*!< Optional affine transformation to initialize the spline control points.*/, const Self::Parameters& parameters = Self::DefaultParameters /*!< Fitting parameters.*/ ); private: /// Default parameters. static Self::Parameters DefaultParameters; /// Input landmarks. std::vector m_Landmarks; /// Spline grid index per landmark. std::vector< FixedVector<3,int> > m_LandmarksGrid; /// Spline coeffiecints per landmark. std::vector< FixedArray<3, FixedVector<4,Types::Coordinate> > > m_LandmarksSpline; /// Deformation field residuals, i.e., pixel-wise difference between B-spline transformation and deformation field. std::vector< SplineWarpXform::SpaceVectorType > m_Residuals; /** Compute residuals, i.e., pixel-wise difference between B-spline transformation and deformation field. *\return Root-of-mean-squared residual over all landmarks. */ Types::Coordinate ComputeResiduals( const SplineWarpXform& splineWarp ); /// Fit spline warp based on initial warp object. void FitSpline( SplineWarpXform& splineWarp /*!< Fitted spline warp is returned here.*/, const Self::Parameters& parameters = Self::DefaultParameters /*!< Fitting parameters.*/ ); }; } // namespace #endif // #ifndef __cmtkFitSplineWarpToLandmarks_h_included_ cmtk-3.3.1/libs/Base/cmtkFitSplineWarpToXformList.cxx000066400000000000000000000153131276303427400225770ustar00rootroot00000000000000/* // // Copyright 2012, 2014 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 5122 $ // // $LastChangedDate: 2014-01-07 15:09:57 -0800 (Tue, 07 Jan 2014) $ // // $LastChangedBy: torstenrohlfing $ // */ #include "cmtkFitSplineWarpToXformList.h" #include #include void cmtk::FitSplineWarpToXformList::ComputeResiduals( const SplineWarpXform& splineWarp ) { const DataGrid::IndexType dims = this->m_XformField.m_Dims; this->m_Residuals.resize( dims.Product() ); #pragma omp parallel for for ( int z = 0; z < dims[2]; ++z ) { size_t ofs = z * dims[0] * dims[1]; for ( int y = 0; y < dims[1]; ++y ) { for ( int x = 0; x < dims[0]; ++x, ++ofs ) { if ( this->m_XformValidAt[ofs] ) this->m_Residuals[ofs] = this->m_XformField[ofs] - splineWarp.GetTransformedGrid( x, y, z ); } } } } cmtk::SplineWarpXform::SmartPtr cmtk::FitSplineWarpToXformList::Fit( const SplineWarpXform::ControlPointIndexType& finalDims, const int nLevels, const bool fitAffineFirst ) { AffineXform::SmartPtr initialAffine; if ( fitAffineFirst ) { initialAffine = this->Superclass::Fit(); } else { initialAffine = AffineXform::SmartPtr( new AffineXform ); } // we may have to adjust nLevels downwards int numberOfLevels = nLevels; SplineWarpXform::ControlPointIndexType initialDims = finalDims; for ( int level = 1; level < numberOfLevels; ++level ) { if ( (initialDims[0]&1) && (initialDims[1]&1) && (initialDims[2]&1) && // check that all dims are still odd numbers (initialDims.MinValue()>4) ) // check that at least 4 control points will be left in each dimension { // apply the inverse of the refinement formula used in SplineWarpXform::Refine() initialDims.AddScalar( +3 ); initialDims /= 2; } else { numberOfLevels = level; DebugOutput(2) << "INFO: adjusted number of levels to " << numberOfLevels << " from " << nLevels << " to ensure sufficient number of control points\n"; } } SplineWarpXform* splineWarp = new SplineWarpXform( this->m_XformField.m_Size, initialDims, CoordinateVector::SmartPtr::Null(), initialAffine ); this->FitSpline( *splineWarp, numberOfLevels ); return cmtk::SplineWarpXform::SmartPtr( splineWarp ); } cmtk::SplineWarpXform::SmartPtr cmtk::FitSplineWarpToXformList::Fit( const Types::Coordinate finalSpacing, const int nLevels, const bool fitAffineFirst ) { AffineXform::SmartPtr initialAffine; if ( fitAffineFirst ) { initialAffine = this->Superclass::Fit(); } else { initialAffine = AffineXform::SmartPtr( new AffineXform ); } // compute the start spacing of multi-level approximation by doubling final spacing until user-defined initial spacing is exceeded. Types::Coordinate spacing = finalSpacing * (1 << (nLevels-1)); // initialize B-spline transformation SplineWarpXform* splineWarp = new SplineWarpXform( this->m_XformField.m_Size, spacing, initialAffine ); this->FitSpline( *splineWarp, nLevels ); return cmtk::SplineWarpXform::SmartPtr( splineWarp ); } void cmtk::FitSplineWarpToXformList::FitSpline( SplineWarpXform& splineWarp, const int nLevels ) { // loop until final control point spacing for ( int level = 0; level < nLevels; ++level ) { DebugOutput( 5 ) << "Multi-resolution spline fitting level " << level+1 << " out of " << nLevels << "\n"; // refine control point grid unless this is first iteration if ( level ) { splineWarp.Refine(); } DebugOutput( 6 ) << " Control point grid is " << splineWarp.m_Dims[0] << "x" << splineWarp.m_Dims[1] << "x" << splineWarp.m_Dims[2] << "\n"; // compute residuals splineWarp.RegisterVolume( this->m_XformField ); this->ComputeResiduals( splineWarp ); // loop over all control points to compute deltas as the spline coefficients that fit current residuals std::vector< FixedVector<3,Types::Coordinate> > delta( splineWarp.m_NumberOfControlPoints, FixedVector<3,Types::Coordinate>( 0.0 ) ); std::vector< Types::Coordinate > weight( splineWarp.m_NumberOfControlPoints, 0.0 ); for ( RegionIndexIterator voxelIt( this->m_XformField.GetWholeImageRegion() ); voxelIt != voxelIt.end(); ++voxelIt ) { const DataGrid::IndexType voxelIdx = voxelIt.Index(); const size_t voxelOfs = this->m_XformField.GetOffsetFromIndex( voxelIdx ); if ( this->m_XformValidAt[voxelOfs] ) { Types::Coordinate sumOfSquares = 0; Types::Coordinate wklm[4][4][4], w2klm[4][4][4]; for ( int m = 0; m < 4; ++m ) { for ( int l = 0; l < 4; ++l ) { const Types::Coordinate wlm = splineWarp.m_GridSpline[1][4*voxelIdx[1]+l] * splineWarp.m_GridSpline[2][4*voxelIdx[2]+m]; for ( int k = 0; k < 4; ++k ) { sumOfSquares += (w2klm[m][l][k] = MathUtil::Square( wklm[m][l][k] = splineWarp.m_GridSpline[0][4*voxelIdx[0]+k] * wlm ) ); } } } for ( int m = 0; m < 4; ++m ) { const size_t mOfs = splineWarp.m_Dims[1] * ( splineWarp.m_GridIndexes[2][voxelIdx[2]] + m ); for ( int l = 0; l < 4; ++l ) { const size_t mlOfs = splineWarp.m_Dims[0] * ( splineWarp.m_GridIndexes[1][voxelIdx[1]] + l + mOfs ); for ( int k = 0; k < 4; ++k ) { const size_t cpOfs = splineWarp.m_GridIndexes[0][voxelIdx[0]] + k + mlOfs; delta[cpOfs] += w2klm[m][l][k] * wklm[m][l][k] / sumOfSquares * this->m_Residuals[voxelOfs]; weight[cpOfs] += w2klm[m][l][k]; } } } } } // apply delta #pragma omp parallel for for ( int cp = 0; cp < static_cast( splineWarp.m_NumberOfControlPoints ); ++cp ) { if ( weight[cp] != 0 ) { delta[cp] /= weight[cp]; splineWarp.SetShiftedControlPointPositionByOffset( splineWarp.GetShiftedControlPointPositionByOffset( cp ) + delta[cp], cp ); } else { // nothing to do - keep control point where it is. } } } } cmtk-3.3.1/libs/Base/cmtkFitSplineWarpToXformList.h000066400000000000000000000103041276303427400222170ustar00rootroot00000000000000/* // // Copyright 2012, 2013 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4794 $ // // $LastChangedDate: 2013-06-25 16:15:35 -0700 (Tue, 25 Jun 2013) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkFitSplineWarpToXformList_h_included_ #define __cmtkFitSplineWarpToXformList_h_included_ #include #include #include #include #include #include namespace cmtk { /** \addtogroup Base */ //@{ /** Fit B-spline-based free-form deformation to list of concatenated transformations, sampled on a discrete grid. *\see This class implements the algorithm from: N. J. Tustison, B. B. Avants, and J. C. Gee, "Directly manipulated free-form deformation image registration," IEEE Transactions on Image Processing, vol. 18, no. 3, pp. 624-635, 2009; * http://dx.doi.org/10.1109/TIP.2008.2010072 *\see The implementation itself is more closely following S. Lee, G. Wolberg, and S. Y. Shin, “Scattered data interpolation with multilevel B-splines,†IEEE Transactions on Visualization and Computer Graphics, * vol. 3, no. 3, pp. 228-244, 1997. http://dx.doi.org/10.1109/2945.620490 * *\todo It would be nice to have the same multi-iteration fitting options here as in cmtk::FitSplineWarpToLandmarks. */ class FitSplineWarpToXformList : private FitAffineToXformList { public: /// This class. typedef FitSplineWarpToXformList Self; /// Base class. typedef FitAffineToXformList Superclass; /// Constructor. FitSplineWarpToXformList( const UniformVolume& sampleGrid /*!< Discrete pixel grid where the spline transformation is sampled and residuals are minimized.*/, const XformList& xformList /*!< List of concatenated transformation that the spline transformation is fitted to.*/, const bool absolute = true /*!< Flag fitting absolute transformation vs. relative deformation field */ ) : Superclass( sampleGrid, xformList, absolute ) {} /// Fit spline warp based on final grid dimensions. SplineWarpXform::SmartPtr Fit( const SplineWarpXform::ControlPointIndexType& finalDims /*!< Final spline control point grid dimensions.*/, const int nLevels /*!< Number of levels in the multi-resolution fitting.*/, const bool fitAffineFirst = true /*!< Flag for optional affine transformation to initialize the spline control points.*/ ); /// Fit spline warp based on final grid spacing. SplineWarpXform::SmartPtr Fit( const Types::Coordinate finalSpacing /*!< Final control point spacing of the fitted B-spline free-form deformation*/, const int nLevels = 1 /*!< Number of levels for optional multi-resolution fit (default: single-resolution fit)*/, const bool fitAffineFirst = true /*!< Flag for optional affine transformation to initialize the spline control points.*/ ); private: /// Deformation field residuals, i.e., pixel-wise difference between B-spline transformation and deformation field. std::vector< FixedVector<3,Types::Coordinate> > m_Residuals; /// Compute residuals, i.e., pixel-wise difference between B-spline transformation and deformation field. void ComputeResiduals( const SplineWarpXform& splineWarp ); /// Fit spline warp based on initial warp object. void FitSpline( SplineWarpXform& splineWarp, const int nLevels ); }; } // namespace #endif // #ifndef __cmtkFitSplineWarpToXformList_h_included_ cmtk-3.3.1/libs/Base/cmtkFitToXformListBase.cxx000066400000000000000000000046261276303427400213720ustar00rootroot00000000000000/* // // Copyright 2012 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4429 $ // // $LastChangedDate: 2012-06-12 13:13:20 -0700 (Tue, 12 Jun 2012) $ // // $LastChangedBy: torstenrohlfing $ // */ #include "cmtkFitToXformListBase.h" #include cmtk::FitToXformListBase::FitToXformListBase( const UniformVolume& sampleGrid, const XformList& xformList, const bool absolute ) : m_XformField( sampleGrid ) { this->m_XformValidAt.resize( this->m_XformField.GetNumberOfPixels() ); std::fill( this->m_XformValidAt.begin(), this->m_XformValidAt.end(), true ); const DataGrid::RegionType wholeImageRegion = this->m_XformField.GetWholeImageRegion(); #ifndef _OPENMP const DataGrid::RegionType region = wholeImageRegion; #else // _OPENMP const int sliceFrom = wholeImageRegion.From()[2]; const int sliceTo = wholeImageRegion.To()[2]; #pragma omp parallel for for ( int slice = sliceFrom; slice < sliceTo; ++slice ) { DataGrid::RegionType region = wholeImageRegion; region.From()[2] = slice; region.To()[2] = slice+1; #endif for ( RegionIndexIterator voxelIt( region ); voxelIt != voxelIt.end(); ++voxelIt ) { const size_t ofs = this->m_XformField.GetOffsetFromIndex( voxelIt.Index() ); const Xform::SpaceVectorType v = this->m_XformField.GetGridLocation( voxelIt.Index() ); Xform::SpaceVectorType u = v; if ( xformList.ApplyInPlace( u ) ) { if ( !absolute ) u -= v; this->m_XformField[ofs] = u; } else { this->m_XformValidAt[ofs] = false; } } #ifdef _OPENMP } #endif } cmtk-3.3.1/libs/Base/cmtkFitToXformListBase.h000066400000000000000000000043661276303427400210200ustar00rootroot00000000000000/* // // Copyright 2012 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4388 $ // // $LastChangedDate: 2012-05-30 15:40:18 -0700 (Wed, 30 May 2012) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkFitToXformListBase_h_included_ #define __cmtkFitToXformListBase_h_included_ #include #include #include #include #include #include namespace cmtk { /** \addtogroup Base */ //@{ /** Fit affine transformation to nonrigid (B-spline or deformation field) transformation. */ class FitToXformListBase { public: /// This class. typedef FitToXformListBase Self; /// Constructor. FitToXformListBase( const UniformVolume& sampleGrid /*!< Discrete pixel grid where the fitted transformation is sampled and residuals are minimized.*/, const XformList& xformList /*!< List of concatenated transformation that the affine transformation is fitted to.*/, const bool absolute = true /*!< Flag fitting absolute transformation vs. relative deformation field */ ); protected: /// Sampled transformation field. ImageTemplate m_XformField; /// Bit flags to mark pixels where the transformation is valid or not (e.g., due to failed numerical inversion). std::vector m_XformValidAt; }; } // namespace #endif // #ifndef __cmtkFitToXformListBase_h_included_ cmtk-3.3.1/libs/Base/cmtkFixedArray.h000066400000000000000000000100421276303427400173520ustar00rootroot00000000000000/* // // Copyright 2010-2012 SRI International // // Copyright 2010 Torsten Rohlfing // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4952 $ // // $LastChangedDate: 2013-10-08 14:55:24 -0700 (Tue, 08 Oct 2013) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkFixedArray_h_included_ #define __cmtkFixedArray_h_included_ #include #include #include #include #include #include namespace cmtk { /// Class for fixed-size n-dimensional array. template class FixedArray { public: /// This class. typedef FixedArray Self; /// Type of the stored values. typedef T ValueType; /// Smart pointer to this class. typedef SmartPointer SmartPtr; /// Smart pointer-to-const to this class. typedef SmartConstPointer SmartConstPtr; /// Return vector size. size_t Size() const { return NDIM; } /// Zero operator. static const Self Zero() { Self v; std::fill( v.begin(), v.end(), DataTypeTraits::Zero() ); return v; } /** Default constructor. * note for improved efficiency, this does NOT initialize the data array. */ FixedArray() {} /// Initialization constructor. explicit FixedArray( const T& initValue ) { std::fill( this->begin(), this->end(), initValue ); } /// Type conversion constructor template. template FixedArray( const FixedArray& rhs ) { for ( size_t i = 0; i < NDIM; ++i ) this->m_Data[i] = static_cast( rhs[i] ); } /// Make array from const pointer. template static Self FromPointer( const T2 *const ptr ) { Self v; for ( size_t i = 0; i < NDIM; ++i ) v[i] = ptr[i]; return v; } /// Get element reference. T& operator[]( const size_t i ) { return this->m_Data[i]; } /// Get const element reference. const T& operator[]( const size_t i ) const { return this->m_Data[i]; } /// Equality operator. bool operator==( const Self& rhs ) const { for ( size_t i = 0; im_Data[i] != rhs.m_Data[i] ) return false; return true; } /// Inequality operator. bool operator!=( const Self& rhs ) const { return !this->operator==( rhs ); } /// Pointer to first array element. T* begin() { return this->m_Data; } /// Pointer behind last array element. T* end() { return this->m_Data+NDIM; } /// Pointer to first array element. const T* begin() const { return this->m_Data; } /// Pointer behind last array element. const T* end() const { return this->m_Data+NDIM; } protected: /// The actual index array. T m_Data[NDIM]; }; /// Stream output operator. template std::ostream& operator<<( std::ostream& stream, const FixedArray& index ) { for ( size_t i = 0; i < NDIM; ++i ) stream << index[i] << " "; return stream; } /// Stream input operator. template std::istream& operator>>( std::istream& stream, FixedArray& index ) { for ( size_t i = 0; i < NDIM; ++i ) stream >> index[i]; return stream; } } // namespace cmtk #endif // #ifndef __cmtkFixedArray_h_included_ cmtk-3.3.1/libs/Base/cmtkFixedSquareMatrix.h000066400000000000000000000150701276303427400207270ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2012, 2014 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 5301 $ // // $LastChangedDate: 2014-04-08 11:12:00 -0700 (Tue, 08 Apr 2014) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkFixedSquareMatrix_h_included_ #define __cmtkFixedSquareMatrix_h_included_ #include #include #include #include #include namespace cmtk { /** \addtogroup Base */ //@{ /// Fixed-size rank-2 matrix. template class FixedSquareMatrix { public: /// This class. typedef FixedSquareMatrix Self; /// The scalar data type. typedef TSCALAR ScalarType; /// The matrix dimension. static const size_t Dimension = NDIM; protected: /** Default constructor. *\attention This creates an uninitialized matrix. */ FixedSquareMatrix() {} public: /** Construc matrix and set all elements to given constant value. */ FixedSquareMatrix( const typename Self::ScalarType& value ); /// Copy and submatrix constructor. template FixedSquareMatrix( const FixedSquareMatrix& other /*!< Source matrix object */, const size_t iOfs = 0 /*!< Sub-matrix offset in i index */, const size_t jOfs = 0 /*!< Sub-matrix offset in i index */ ); /** Array constructor. * If a NULL parameter is given, an uninitialized matrix is generated. This * is intended behaviour. */ FixedSquareMatrix( const typename Self::ScalarType *const values ) { if ( values ) this->Set( values ); } /// 2D array constructor. template FixedSquareMatrix( const T2 (&matrix)[NDIM][NDIM] ) { for ( size_t j = 0; j < NDIM; ++j ) for ( size_t i = 0; i < NDIM; ++i ) this->m_Matrix[j][i] = matrix[j][i]; } /// Set from array of entries. Self& Set( const typename Self::ScalarType *const values ); /// Set to constant value. Self& Fill( const typename Self::ScalarType& value ); /// Get multiplicative identity matrix. static const Self& Identity(); /// Get all-zero matrix. static const Self& Zero(); /// Exception thrown when trying to invert singular matrix. class SingularMatrixException : public Exception {}; /// Get inverse matrix. Self GetInverse() const; /// Get transpose matrix. Self GetTranspose() const; /// Index operator. typename Self::ScalarType* operator[]( const size_t i ) { return this->m_Matrix[i]; } /// Constant index operator. const typename Self::ScalarType* operator[]( const size_t i ) const { return this->m_Matrix[i]; } /// In-place multiplication operator. Self& operator*=( const Self& other ); /// In-place scalar multiplication operator. Self& operator*=( const typename Self::ScalarType& scalar ); /// Multiplication operator. const Self operator*( const Self& other ) const; /// Assignment operator. Self& operator=( const Self& other ); /// Addition operator. Self& operator+=( const Self& other ); /// Subtraction operator. Self& operator-=( const Self& other ); /// Get Frobenius norm. typename Self::ScalarType FrobeniusNorm() const; /// Get column vector. FixedVector GetColumnVector( const size_t i ) const { FixedVector v; for ( size_t j = 0; jm_Matrix[j][i]; } return v; } /// Get row vector. FixedVector GetRowVector( const size_t j ) const { FixedVector v; for ( size_t i = 0; im_Matrix[j][i]; } return v; } protected: /// The actual matrix. typename Self::ScalarType m_Matrix[NDIM][NDIM]; template friend FixedSquareMatrix operator+( const FixedSquareMatrix&, const FixedSquareMatrix& ); template friend FixedSquareMatrix operator-( const FixedSquareMatrix&, const FixedSquareMatrix& ); }; /// In-place vector-matrix multiplication operation. template FixedVector& operator*=( FixedVector& u, const FixedSquareMatrix& M ) { FixedVector v; for ( size_t i = 0; i FixedVector operator*( FixedVector u, const FixedSquareMatrix& M ) { return u *= M; } /// In-place homogeneous multiplication with vector operation. template FixedVector& operator*=( FixedVector& u, const FixedSquareMatrix& M ) { FixedVector v; for ( size_t i = 0; i FixedVector operator*( FixedVector u, const FixedSquareMatrix& M ) { return u *= M; } /// Output object to console. template inline Console& operator<< ( Console& stream, const FixedSquareMatrix& m ) { stream << NDIM << "x" << NDIM << " Matrix:\n"; for ( int i = 0; i < NDIM; ++i ) { for ( int j = 0; j < NDIM; ++j ) stream << m[i][j] << "\t"; stream << "\n"; } return stream; } //@} } // namespace cmtk #include "cmtkFixedSquareMatrix.txx" #endif // #ifndef __cmtkFixedSquareMatrix_h_included_ cmtk-3.3.1/libs/Base/cmtkFixedSquareMatrix.txx000066400000000000000000000176301276303427400213270ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2012, 2014 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 5287 $ // // $LastChangedDate: 2014-04-05 22:46:09 -0700 (Sat, 05 Apr 2014) $ // // $LastChangedBy: torsten_at_home $ // */ #include #include namespace cmtk { /// Copy and submatrix constructor. template template FixedSquareMatrix::FixedSquareMatrix( const FixedSquareMatrix& other, const size_t iOfs, const size_t jOfs ) { assert( NDIM+iOfs <= N2 ); assert( NDIM+jOfs <= N2 ); for ( size_t j = 0; j < Self::Dimension; ++j ) { for ( size_t i = 0; i < Self::Dimension; ++i ) { this->m_Matrix[i][j] = other[i+iOfs][j+jOfs]; } } } template FixedSquareMatrix::FixedSquareMatrix( const typename Self::ScalarType& value ) { for ( size_t i = 0; i < Self::Dimension; ++i ) for ( size_t j = 0; j < Self::Dimension; ++j ) this->m_Matrix[i][j] = value; } template FixedSquareMatrix& FixedSquareMatrix::Set( const typename Self::ScalarType *const values ) { memcpy( this->m_Matrix, values, sizeof( this->m_Matrix ) ); return *this; } template FixedSquareMatrix& FixedSquareMatrix::Fill( const typename Self::ScalarType& value ) { for ( size_t i = 0; i < Self::Dimension; ++i ) for ( size_t j = 0; j < Self::Dimension; ++j ) this->m_Matrix[i][j] = value; return *this; } template const FixedSquareMatrix& FixedSquareMatrix::Identity() { static Self identity; static bool initialized = false; if ( ! initialized ) { for ( size_t i = 0; i < Self::Dimension; ++i ) for ( size_t j = 0; j < Self::Dimension; ++j ) identity[i][j] = DataTypeTraits::Zero(); for ( size_t i = 0; i < Self::Dimension; ++i ) identity[i][i] = DataTypeTraits::One(); initialized = true; } return identity; } template const FixedSquareMatrix& FixedSquareMatrix::Zero() { static Self zero; static bool initialized = false; if ( ! initialized ) { for ( size_t i = 0; i < Self::Dimension; ++i ) for ( size_t j = 0; j < Self::Dimension; ++j ) zero[i][j] = DataTypeTraits::Zero(); initialized = true; } return zero; } template FixedSquareMatrix FixedSquareMatrix::GetTranspose() const { Self transpose; for ( size_t i = 0; i < Self::Dimension; ++i ) { for ( size_t j = 0; j < Self::Dimension; ++j ) transpose[i][j] = this->m_Matrix[j][i]; } return transpose; } template FixedSquareMatrix FixedSquareMatrix::GetInverse() const { Self inverse = Self::Identity(); typename Self::ScalarType matrix[NDIM][NDIM]; memcpy( matrix, this->m_Matrix, sizeof( matrix ) ); for ( size_t eliminate = 0; eliminate < Self::Dimension; ++eliminate ) { size_t pivIdx = eliminate; typename Self::ScalarType pivAbs = fabs( matrix[eliminate][eliminate] ); for ( size_t row = eliminate+1; row < Self::Dimension; ++row ) { const typename Self::ScalarType nextAbs = fabs( matrix[row][eliminate] ); if (nextAbs > pivAbs ) { pivIdx = row; pivAbs = nextAbs; } } if ( pivAbs == 0 ) throw typename Self::SingularMatrixException(); if ( eliminate != pivIdx ) { for ( size_t col = 0; col < Self::Dimension; ++col ) { std::swap( matrix[eliminate][col], matrix[pivIdx][col] ); std::swap( inverse[eliminate][col], inverse[pivIdx][col] ); } } for ( size_t col = 0; col < Self::Dimension; ++col ) { if ( col > eliminate ) matrix[eliminate][col] /= matrix[eliminate][eliminate]; inverse[eliminate][col] /= matrix[eliminate][eliminate]; } matrix[eliminate][eliminate] = DataTypeTraits::One(); for ( size_t row = 0; row < Self::Dimension; ++row ) { if (row != eliminate ) { for ( size_t col = 0; col < Self::Dimension; ++col ) { if ( col > eliminate ) matrix[row][col] -= matrix[row][eliminate] * matrix[eliminate][col]; inverse[row][col] -= matrix[row][eliminate] * inverse[eliminate][col]; } matrix[row][eliminate] = DataTypeTraits::Zero(); } } } return inverse; } template FixedSquareMatrix operator+( const FixedSquareMatrix& A, const FixedSquareMatrix& B ) { FixedSquareMatrix M; for ( size_t i = 0; i FixedSquareMatrix& FixedSquareMatrix::operator+=( const Self& other ) { return (*this = ((*this) + other)); } template FixedSquareMatrix operator-( const FixedSquareMatrix& A, const FixedSquareMatrix& B ) { FixedSquareMatrix M; for ( size_t i = 0; i FixedSquareMatrix& FixedSquareMatrix::operator-=( const Self& other ) { return (*this = ((*this) - other)); } template FixedSquareMatrix& FixedSquareMatrix::operator*=( const typename Self::ScalarType& scalar ) { for ( size_t j=0; j < Self::Dimension; ++j ) { for ( size_t i=0; i < Self::Dimension; ++i ) { this->m_Matrix[i][j] *= scalar; } } return *this; } template FixedSquareMatrix& FixedSquareMatrix::operator*=( const Self& other ) { return (*this = ((*this) * other)); } template const FixedSquareMatrix FixedSquareMatrix::operator* ( const Self& other ) const { Self result; for ( size_t j=0; j < Self::Dimension; ++j ) { for ( size_t i=0; i < Self::Dimension; ++i ) { result[i][j] = this->m_Matrix[i][0] * other.m_Matrix[0][j]; for ( size_t k=1; k < Self::Dimension; ++k ) result[i][j] += this->m_Matrix[i][k] * other.m_Matrix[k][j]; } } return result; } template FixedSquareMatrix& FixedSquareMatrix::operator=( const Self& other ) { memcpy( this->m_Matrix, other.m_Matrix, sizeof( this->m_Matrix ) ); return *this; } template TSCALAR FixedSquareMatrix::FrobeniusNorm() const { typename Self::ScalarType norm = DataTypeTraits::Zero(); for ( size_t i = 0; i < Self::Dimension; ++i ) { for ( size_t j = 0; j < Self::Dimension; ++j ) norm += MathUtil::Square( this->m_Matrix[i][j] ); } return sqrt( norm ); } } // namespace cmtk cmtk-3.3.1/libs/Base/cmtkFixedVector.h000066400000000000000000000233461276303427400175510ustar00rootroot00000000000000/* // // Copyright 2010-2013 SRI International // // Copyright 2010 Torsten Rohlfing // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4738 $ // // $LastChangedDate: 2013-05-14 14:10:49 -0700 (Tue, 14 May 2013) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkFixedVector_h_included_ #define __cmtkFixedVector_h_included_ #include #include #include #include #include namespace cmtk { /** Class for fixed-size n-dimensional numerical vector. * This class inherits from cmtk::FixedArray and, unlike its base class, is suitable only for element types that support arithmetic * operations. */ template class FixedVector : public FixedArray { public: /// This class. typedef FixedVector Self; /// Base class. typedef FixedArray Superclass; /// Type of the stored values. typedef T ValueType; /// Smart pointer to this class. typedef SmartPointer SmartPtr; /// Smart pointer-to-const to this class. typedef SmartConstPointer SmartConstPtr; /// Default constructor. FixedVector() {} /// Initialization constructor. explicit FixedVector( const T& initValue ) : Superclass( initValue ) {} /// Type conversion constructor template. template FixedVector( const FixedVector& rhs ) : Superclass( rhs ) {} /// Make vector from const pointer. template static Self FromPointer( const T2 *const ptr ) { Self v; for ( size_t i = 0; i < NDIM; ++i ) v[i] = ptr[i]; return v; } /// In-place addition operator. Self& operator+=( const Self& rhs ) { for ( size_t i = 0; im_Data[i] += rhs.m_Data[i]; return *this; } /// In-place subtraction operator. Self& operator-=( const Self& rhs ) { for ( size_t i = 0; im_Data[i] -= rhs.m_Data[i]; return *this; } /// Multiply by a scalar. Self& operator*= ( const T a ) { for ( size_t i=0; im_Data[i] *= a; return *this; } /// Elementwise multiplication with another vector. Self& operator*=( const Self& rhs ) { for ( size_t i=0; im_Data[i] *= rhs[i]; return *this; } /// Divide by a scalar. Self& operator/= ( const T a ) { for ( size_t i=0; im_Data[i] /= a; return *this; } /// Elementwise dvision with another vector. Self& operator/=( const Self& rhs ) { for ( size_t i=0; im_Data[i] /= rhs[i]; return *this; } /// Unary minus. const Self operator-() { Self minus; for ( size_t i=0; im_Data; return minus; } /// Add scalar value to all vector elements. Self& AddScalar( const T& value ) { for ( size_t i=0; im_Data[i] += value; return *this; } /// Maximum value. T MaxValue() const { return *(std::max_element( this->begin(), this->end() ) ); } /// Maximum element index. size_t MaxIndex() const { return std::max_element( this->begin(), this->end() ) - this->m_Data; } /// Minimum value. T MinValue() const { return *(std::min_element( this->begin(), this->end() ) ); } /// Minimum element index. size_t MinIndex() const { return std::min_element( this->begin(), this->end() ) - this->m_Data; } /// Calculate sum of vector elements. T Sum() const { T sum = this->m_Data[0]; for ( size_t i = 1; i < NDIM; ++i ) sum += this->m_Data[i]; return sum; } /// Calculate product of vector elements. T Product() const { T product = this->m_Data[0]; for ( size_t i = 1; i < NDIM; ++i ) product *= this->m_Data[i]; return product; } /// Calculate sum of squares of vector elements (that is, square of Euclid's norm). T SumOfSquares() const { T sq = 0; for ( size_t i = 0; i < NDIM; ++i ) sq += this->m_Data[i] * this->m_Data[i]; return sq; } /// Calculate maximum absolute value in the vector. T MaxAbsValue() const { T maxAbs = fabs( this->m_Data[0] ); for ( size_t i = 1; i < NDIM; ++i ) maxAbs = std::max( maxAbs, fabs( this->m_Data[i] ) ); return maxAbs; } /// Elementwise absolute-value operator. const Self Abs() const { FixedVector result; for ( size_t i = 0; i < NDIM; ++i ) result[i] = static_cast( fabs( (*this)[i] ) ); return result; } /// Shorthand for root of sum of squares (i.e., Euclid's norm) T RootSumOfSquares() const { return sqrt( this->SumOfSquares() ); } /// Get angle cosine between this and another vector. T GetAngleCosine( const Self& other ) const { return (*this * other) / ( this->RootSumOfSquares() * other.RootSumOfSquares() ); } }; /// Addition operator. template const FixedVector operator+( const FixedVector& lhs, const FixedVector& rhs ) { return FixedVector( lhs ) += rhs; } /// Subtraction operator. template const FixedVector operator-( const FixedVector& lhs, const FixedVector& rhs ) { return FixedVector( lhs ) -= rhs; } /// Componentwise division operator. template const FixedVector ComponentDivide( const FixedVector& lhs, const FixedVector& rhs ) { return FixedVector( lhs ) /= rhs; } /// Componentwise multiplication operator. template const FixedVector ComponentMultiply( const FixedVector& lhs, const FixedVector& rhs ) { return FixedVector( lhs ) *= rhs; } /// Scalar product operator. template T operator* ( const FixedVector &lhs, const FixedVector& rhs ) { T product = lhs[0] * rhs[0]; for ( size_t i = 1; i const FixedVector operator*( const T2 lhs, const FixedVector& rhs ) { FixedVector result( rhs ); for ( size_t i = 0; i( lhs ); return result; } /// Elementwise maximum operator. template const FixedVector Max( const FixedVector& lhs, const FixedVector& rhs ) { FixedVector result; for ( size_t i = 0; i < NDIM; ++i ) result[i] = std::max( lhs[i], rhs[i] ); return result; } /// Elementwise minimum operator. template const FixedVector Min( const FixedVector& lhs, const FixedVector& rhs ) { FixedVector result; for ( size_t i = 0; i < NDIM; ++i ) result[i] = std::min( lhs[i], rhs[i] ); return result; } /// Element-wise less-than operator. template bool operator<( const FixedVector& lhs, const FixedVector& rhs ) { for ( size_t i = 0; i < NDIM; ++i ) if ( ! (lhs[i] < rhs[i] ) ) return false; return true; } /// Element-wise less-than-or-equal operator. template bool operator<=( const FixedVector& lhs, const FixedVector& rhs ) { for ( size_t i = 0; i < NDIM; ++i ) if ( lhs[i] > rhs[i] ) return false; return true; } /// Element-wise greater-than operator. template bool operator>( const FixedVector& lhs, const FixedVector& rhs ) { for ( size_t i = 0; i < NDIM; ++i ) if ( ! (lhs[i] > rhs[i] ) ) return false; return true; } /// Element-wise greater-than-or-equal operator. template bool operator>=( const FixedVector& lhs, const FixedVector& rhs ) { for ( size_t i = 0; i < NDIM; ++i ) if ( lhs[i] < rhs[i] ) return false; return true; } /// Stream output operator. template std::ostream& operator<<( std::ostream& stream, const FixedVector& index ) { for ( size_t i = 0; i < NDIM; ++i ) stream << index[i] << " "; return stream; } /// Stream input operator. template std::istream& operator>>( std::istream& stream, FixedVector& index ) { for ( size_t i = 0; i < NDIM; ++i ) stream >> index[i]; return stream; } /// Fixed vector static initializer class template. template class FixedVectorStaticInitializer { }; /// Fixed vector static initializer class template. template class FixedVectorStaticInitializer<3,T> { public: /// The vector type. typedef FixedVector<3,T> VectorType; /// Static initializer for three-dimensional vector. static const VectorType Init( const T& x0, const T& x1, const T& x2 ) { VectorType v; v[0] = x0; v[1] = x1; v[2] = x2; return v; } }; } // namespace cmtk #endif // #ifndef __cmtkFixedVector_h_included_ cmtk-3.3.1/libs/Base/cmtkFunctional.cxx000066400000000000000000000040061276303427400177740ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2011 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 2731 $ // // $LastChangedDate: 2011-01-13 16:22:47 -0800 (Thu, 13 Jan 2011) $ // // $LastChangedBy: torstenrohlfing $ // */ #include "cmtkFunctional.h" namespace cmtk { /** \addtogroup Base */ //@{ Functional::ReturnType Functional::EvaluateWithGradient ( Self::ParameterVectorType& v, Self::ParameterVectorType& g, const Types::Coordinate step ) { const Self::ReturnType baseValue = this->EvaluateAt( v ); for ( size_t dim = 0; dim < this->VariableParamVectorDim(); ++dim ) { const Types::Coordinate stepScale = this->GetParamStep( dim, step ); if ( stepScale <= 0 ) { g[dim] = 0; } else { const Types::Coordinate v0 = v[dim]; v[dim] += stepScale; const Self::ReturnType upper = this->EvaluateAt( v ); v[dim] = v0 - stepScale; const Self::ReturnType lower = this->EvaluateAt( v ); v[dim] = v0; if ( (upper > baseValue) || (lower > baseValue) ) { g[dim] = upper-lower; } else { g[dim] = 0; } } } return baseValue; } } // namespace cmtk cmtk-3.3.1/libs/Base/cmtkFunctional.h000066400000000000000000000110421276303427400174170ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2011 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 3175 $ // // $LastChangedDate: 2011-04-25 09:22:49 -0700 (Mon, 25 Apr 2011) $ // // $LastChangedBy: torsten_at_home $ // */ #ifndef __cmtkFunctional_h_included_ #define __cmtkFunctional_h_included_ #include #include #include #include #include #ifdef HAVE_UNISTD_H # include #endif namespace cmtk { /** \addtogroup Base */ //@{ /// Class defining a real-valued functional on a multi-dimensional domain. class Functional { public: /// This class. typedef Functional Self; /// Smart pointer to Functional. typedef SmartPointer SmartPtr; /** Functional return type. * This is the higher resolution type of either data item or spatial coordinate type. */ typedef Types::Combined::Type ReturnType; /// Parameter vector type. typedef Vector ParameterVectorType; /// Functional return type. typedef Types::Coordinate ParameterType; /// Set parameter vector. virtual void SetParamVector ( ParameterVectorType& ) { StdErr << "ERROR: Functional::SetParamVector() was called but not implemented\n"; exit( 1 ); } /// Return parameter vector. virtual void GetParamVector ( ParameterVectorType& ) { StdErr << "ERROR: Functional::GetParamVector() was called but not implemented\n"; exit( 1 ); } /// Evaluate functional. virtual Self::ReturnType Evaluate() { return 0; } // cannot make this abstract because we need to instantiate this for generic initialization /// Evaluate functional with new parameter vector. virtual Self::ReturnType EvaluateAt( ParameterVectorType& v ) { this->SetParamVector( v ); return this->Evaluate(); } #ifdef CMTK_BUILD_DEMO /// Create a snapshot (to disk) of current functional result. virtual void SnapshotAt( ParameterVectorType& ) {} #endif /// Evaluate functional and also return its gradient. virtual Self::ReturnType EvaluateWithGradient( ParameterVectorType& v, ParameterVectorType& g, const Types::Coordinate step = 1 ); /// Return dimension of the parameter vector. virtual size_t ParamVectorDim() const = 0; /** Return dimension of the parmater vector's variable part. * For example, the rotation center of a rigid body transformation is not * considered variable. Therefore it should not be used for gradient * computation. By default this function returns the total parameter * vector length. */ virtual size_t VariableParamVectorDim() const { return this->ParamVectorDim(); } /// Virtual destructor. virtual ~Functional() {}; /** Get stepping for one parameter. * This function serves to make sure that optimizers optimize all parameters with * approximately equal impact. If a parameter has smaller impact than another, it should * return a smaller value here. */ virtual Types::Coordinate GetParamStep( const size_t, const Types::Coordinate mmStep = 1 ) const { return mmStep; } /** Wiggle a little. * If the functional is not time-invariant, e.g., due to randomization, * then the optimizer can tell it to re-organize itself for example for * a repeated search. *\return If this function returns true, then the functional did indeed * wiggle a little, i.e., potentially change. If the function returns * false, then the functional does not support this operation, and the * optimizer can assume that further evaluations will produce exactly * the same values as before. */ virtual bool Wiggle() { return false; } }; //@} } // namespace cmtk #endif // #ifndef _FUNCTIONAL_H_ cmtk-3.3.1/libs/Base/cmtkGaussianKernel.h000066400000000000000000000072141276303427400202360ustar00rootroot00000000000000/* // // Copyright 2010-2012 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 3978 $ // // $LastChangedDate: 2012-03-06 15:40:08 -0800 (Tue, 06 Mar 2012) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkGaussianKernel_h_included_ #define __cmtkGaussianKernel_h_included_ #include #include #include #include /** \addtogroup Base */ //@{ namespace cmtk { /// Utility class for generating Gaussian kernels. template class GaussianKernel { public: /// This class. typedef GaussianKernel Self; /// Get raw kernel value. static TFloat GetValue( const TFloat x, const TFloat mu, const TFloat sigma ) { return exp( -MathUtil::Square( (x-mu) / sigma ) / 2 ) / (sqrt(2*M_PI) * sigma); } /// Create symmetric kernel. static std::vector GetSymmetricKernel( const Units::GaussianSigma& sigma /*!< Sigma parameter (standard deviation) of the kernel */, const TFloat maxError = 1e-5 /*!< Maximum approximation error: the kernel radius is computed so that truncated elements are below this value */ ) { const TFloat normFactor = static_cast( 1.0/(sqrt(2*M_PI) * sigma.Value()) ); const size_t radius = static_cast( Self::GetRadius( sigma, normFactor, maxError ) ); std::vector kernel( 2 * radius + 1 ); for ( size_t i = 0; i <= radius; ++i ) { kernel[radius-i] = kernel[radius+i] = static_cast( normFactor * exp( -MathUtil::Square( 1.0 * i / sigma.Value() ) / 2 ) ); } return kernel; } /// Create half kernel, starting with center element. static std::vector GetHalfKernel( const Units::GaussianSigma& sigma /*!< Sigma parameter (standard deviation) of the kernel */, const TFloat maxError = 1e-5 /*!< Maximum approximation error: the kernel radius is computed so that truncated elements are below this value */ ) { const double normFactor = 1.0/(sqrt(2*M_PI) * sigma.Value()); const size_t radius = static_cast( Self::GetRadius( sigma, normFactor, maxError ) ); std::vector kernel( radius + 1 ); for ( size_t i = 0; i <= radius; ++i ) { kernel[i] = normFactor * exp( -MathUtil::Square( 1.0 * i / sigma.Value() ) / 2 ); } return kernel; } private: /// Compute kernel radius based on maximum approximation error and normalization factor. static TFloat GetRadius( const Units::GaussianSigma& sigma, const TFloat normFactor, const TFloat maxError ) { if ( maxError >= normFactor ) // if normFactor is less than max error, then we really need no kernel at all return 0; else return static_cast( sqrt( -2.0 * log( maxError / normFactor ) ) * sigma.Value() ); } }; } // namespace cmtk //@} #endif // #ifndef __cmtkGaussianKernel_h_included_ cmtk-3.3.1/libs/Base/cmtkGeneralLinearModel.cxx000066400000000000000000000214251276303427400213670ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2012 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4268 $ // // $LastChangedDate: 2012-04-27 14:46:49 -0700 (Fri, 27 Apr 2012) $ // // $LastChangedBy: torstenrohlfing $ // */ #include "cmtkGeneralLinearModel.h" #include #include #include #ifdef HAVE_IEEEFP_H # include #endif #include #include namespace cmtk { /** \addtogroup Base */ //@{ #define TOL 1.0e-5 GeneralLinearModel::GeneralLinearModel ( const size_t nParameters, const size_t nData, const double* designMatrix ) : NParameters( nParameters ), NData( nData ), DesignMatrix( nData, nParameters, designMatrix ), Up( nParameters ), Vp( nParameters ), Wp( nParameters ), VariableMean( nParameters ), VariableSD( nParameters ) { this->LeastSquares(); } void GeneralLinearModel::LeastSquares() { U = new Matrix2D( NData, NParameters ); V = new Matrix2D( NParameters, NParameters ); W = new std::vector( NParameters ); double wmax, thresh; std::vector data( this->NData ); for ( size_t j=0; j < NParameters; ++j ) { // set up data vector for parameter 'j' for ( size_t i=0; i < this->NData; ++i ) { data[i] = DesignMatrix[i][j]; (*U)[i][j] = DesignMatrix[i][j]; } // compute variance this->VariableMean[j] = MathUtil::Mean( data ); this->VariableSD[j] = MathUtil::Variance( data, this->VariableMean[j] ); // convert variance to standard deviation this->VariableSD[j] = sqrt( this->VariableSD[j] ); } // perform SVD of design matrix MathUtil::SVD( *(this->U), *(this->W), *(this->V) ); // prepare partial regressions, each with one of the parameters omitted for ( size_t p=0; p < this->NParameters; ++p) { Up[p] = new Matrix2D( NData, NParameters-1 ); Vp[p] = new Matrix2D( NParameters-1, NParameters-1 ); Wp[p] = new std::vector( NParameters-1 ); // create partial design matrix, omitting parameter 'p' for ( size_t i=0; i < this->NData; ++i ) { size_t jj = 0; for ( size_t j=0; j < this->NParameters; ++j ) { if ( j != p ) { (*(this->Up[p]))[i][jj] = DesignMatrix[i][j]; ++jj; } } } MathUtil::SVD( *(this->Up[p]), *(this->Wp[p]), *(this->Vp[p]) ); } wmax=0.0; for ( size_t j=0;j wmax) wmax=(*W)[j]; thresh=TOL*wmax; for ( size_t j=0;j* GeneralLinearModel::GetCorrelationMatrix() const { Matrix2D* CC = new Matrix2D( this->NParameters, this->NParameters ); std::vector pi( this->NData ); std::vector pj( this->NData ); for ( size_t i = 0; i < this->NParameters; ++i ) { for ( size_t n = 0; n < this->NData; ++n ) { pi[n] = this->DesignMatrix[n][i]; } for ( size_t j = 0; j < this->NParameters; ++j ) { if ( i <= j ) { for ( size_t n = 0; n < this->NData; ++n ) { pj[n] = this->DesignMatrix[n][j]; } (*CC)[i][j] = MathUtil::Correlation( pi, pj ); } else { (*CC)[i][j] = (*CC)[j][i]; } } } return CC; } GeneralLinearModel::~GeneralLinearModel() { for ( size_t p=0; p < this->NParameters; ++p) { delete this->Wp[p]; delete this->Vp[p]; delete this->Up[p]; } delete this->W; delete this->V; delete this->U; } void GeneralLinearModel::InitResults( const size_t nPixels ) { Model.clear(); TStat.clear(); for (size_t p = 0; (pNParameters); ++p ) { TypedArray::SmartPtr nextModel( TypedArray::Create( TYPE_FLOAT, nPixels ) ); Model.push_back( nextModel ); TypedArray::SmartPtr nextTStat( TypedArray::Create( TYPE_FLOAT, nPixels ) ); TStat.push_back( nextTStat ); } FStat = TypedArray::SmartPtr( TypedArray::Create( TYPE_FLOAT, nPixels ) ); } void GeneralLinearModel::FitModel ( std::vector& y, const bool normalizeParameters ) { assert( y.size() == this->NData ); const size_t nPixels = y[0]->GetDataSize(); this->InitResults( nPixels ); std::vector lm_params( this->NParameters ); std::vector b( this->NData ); std::vector valueYhat( this->NData ); // number of degrees of freedom for t-statistics // note: we omit "-1" because the constant in our model is either suppressed or an explicit parameter const int df = this->NData - this->NParameters; const size_t pixelUpdateIncrement = 10000; Progress::Begin( 0, nPixels, pixelUpdateIncrement, "Linear model fitting" ); for ( size_t n = 0; n < nPixels; ++n ) { if ( ! (n % pixelUpdateIncrement) ) if ( Progress::SetProgress( n ) != Progress::OK ) break; bool missing = false; Types::DataItem value; for (size_t i = 0; (iNData) && !missing; i++) if ( y[i]->Get( value, n ) && finite( value ) ) b[i] = value; else missing = true; if ( missing ) { for (size_t p = 0; (pNParameters); ++p ) { this->Model[p]->SetPaddingAt( n ); this->TStat[p]->SetPaddingAt( n ); } } else { // use SVD of design matrix to compute model parameters lm_params[] from data b[] MathUtil::SVDLinearRegression( *(this->U), *(this->W), *(this->V), b, lm_params ); // compute variance of data double varY, avgY; avgY = MathUtil::Mean( this->NData, &b[0] ); varY = MathUtil::Variance( this->NData, &b[0], avgY ); // copy model parameters into output for (size_t p = 0; (pNParameters); ++p ) { value = lm_params[p]; if ( normalizeParameters ) // Cohen & Cohen, Eq. (3.5.2) // Model[p]->Set( lm_params[p] * this->GetNormFactor( p ) / sqrt( varY ), n ); value *= this->GetNormFactor( p ); if ( finite( value ) ) this->Model[p]->Set( value, n ); else this->Model[p]->SetPaddingAt( n ); } // compute variance of approximated data using entire model double varYhat, avgYhat; for (size_t i = 0; iNData; i++) { valueYhat[i] = 0.0; for (size_t pi = 0; (piNParameters); ++pi ) valueYhat[i] += lm_params[pi] * this->DesignMatrix[i][pi]; } avgYhat = MathUtil::Mean( this->NData, &valueYhat[0] ); varYhat = MathUtil::Variance( this->NData, &valueYhat[0], avgYhat ); // compute multiple R square const double R2 = varYhat / varY; this->FStat->Set( (R2*df) / ((1-R2)*this->NParameters), n ); std::vector lm_params_P( this->NParameters-1 ); std::vector valueYhatp( this->NData ); // for each parameter, evaluate R^2_i for model without parameter Xi for (size_t p = 0; p < this->NParameters; ++p ) { // use SVD of partial design matrix to compute partial regression MathUtil::SVDLinearRegression( *(this->Up[p]), *(this->Wp[p]), *(this->Vp[p]), b, lm_params_P ); // compute variance of data for (size_t i = 0; i < this->NData; i++) { valueYhatp[i] = 0.0; size_t pip = 0; for (size_t pi = 0; pi < this->NParameters; ++pi ) { if ( p != pi ) { valueYhatp[i] += lm_params_P[pip] * this->DesignMatrix[i][pi]; ++pip; } } double varYhatp, avgYhatp; avgYhatp = MathUtil::Mean( valueYhatp ); varYhatp = MathUtil::Variance( valueYhatp, avgYhatp ); // copmpute R^2_p const double R2p = varYhatp / varY; // assert( (R2p >= 0) && (R2p < 1) ); // compute sr_p^2 from R^2 and R^2_p const double srp = sqrt( R2 - R2p ); // assert( (sr2p >= 0) && (sr2p <= 1 ) ); // compute T-statistics double tStat = static_cast( srp * sqrt( df / (1.0-R2) ) ); // export T-statistics (set to zero if NAN) if ( ! MathUtil::IsFinite( tStat ) ) tStat = 0; this->TStat[p]->Set( tStat, n ); } } } } Progress::Done(); } } // namespace cmtk cmtk-3.3.1/libs/Base/cmtkGeneralLinearModel.h000066400000000000000000000113701276303427400210120ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2010 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4220 $ // // $LastChangedDate: 2012-04-18 13:46:30 -0700 (Wed, 18 Apr 2012) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkGeneralLinearModel_h_included_ #define __cmtkGeneralLinearModel_h_included_ #include #include #include #include #include #include namespace cmtk { /** \addtogroup Base */ //@{ /** Pixelwise linear modeling and t statistics of data. * \note This class formerly contained a method for * getting the covariance matrix of an SVD. It has * been removed due to obsolete implementation * (Numerical Recipes) and un-use */ class GeneralLinearModel { public: /// This class. typedef GeneralLinearModel Self; /// Smart pointer type. typedef SmartPointer SmartPtr; /** Constructor. * This will take care of SVD of the design matrix and perform all necessary * pre-computations for the actual modeling. */ GeneralLinearModel( const size_t nParameters, const size_t nData, const double* designMatrix ); /// Destructor. ~GeneralLinearModel(); /** Get singular value of the SVD decomposition of the design matrix. *\param n Index of the model parameter [0..NParameters-1]. *\return The singular value for parameter n. */ double GetSingularValue( const size_t n ) const { return (*(this->W))[n]; } /** Get the parameter correlation matrix from design matrix. */ Matrix2D* GetCorrelationMatrix() const; /** Model y[] distribution and return model parameters a[]. *\param y A vector of TypedArray smart pointers. Each object in this * vector points to a pixel array from a different subject in a population. *\param normalizeParameters If this flag is set (default), then the * linear model parameters are normalized w.r.t. the maghnitudes of the * respective measurements. */ void FitModel( std::vector& y, const bool normalizeParameters = true ); /// Get pointer to n-th model parameter array. TypedArray::SmartPtr& GetModel( const size_t n ) { return this->Model[n]; } /// Get normalization factor for parameter number 'p'. double GetNormFactor( const size_t p ) { // do not normalize constant part if ( this->VariableSD[p] > 0 ) return this->VariableSD[p]; else return 1.0; } /// Get pointer to n-th parameter t statistics array. TypedArray::SmartPtr& GetTStat( const size_t n ) { return this->TStat[n]; } /// Get pointer to n-th parameter t statistics array. TypedArray::SmartPtr& GetFStat() { return this->FStat; } private: /// Solve least-squares problem. void LeastSquares(); /// Initialize results arrays with the correct number of pixels. void InitResults( const size_t nPixels ); /// Number of model parameters. size_t NParameters; /// Number of data items. size_t NData; /// Design matrix. Matrix2D DesignMatrix; /// Matrix U of the design matrix SVD. Matrix2D* U; /// Array of partial design matrices. std::vector< Matrix2D* > Up; /// Matrix V the design matrix SVD. Matrix2D* V; /// SVD of partial design matrices. std::vector< Matrix2D* > Vp; /// Vector W (workspace). std::vector* W; /// Workspace vectors for partial regressions. std::vector< std::vector* > Wp; /// Means of variables. std::vector VariableMean; /// Standard deviations of variables. std::vector VariableSD; /// Computed model coefficients. std::vector Model; /// Computed model t statistics coefficients. std::vector TStat; /// Computed model F statistics. TypedArray::SmartPtr FStat; }; //@} } // namespace cmtk #endif // #ifndef __cmtkGeneralLinearModel_h_included_ cmtk-3.3.1/libs/Base/cmtkHashMapSTL.h000066400000000000000000000047471276303427400172370ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2009, 2012 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 3889 $ // // $LastChangedDate: 2012-02-17 14:30:02 -0800 (Fri, 17 Feb 2012) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkHashMapSTL_h_included_ #define __cmtkHashMapSTL_h_included_ #include #define HAVE_STL_HASH_MAP #if defined(HAVE_UNORDERED_MAP) # include #elif defined(HAVE_HASH_MAP_H) # include #elif defined(HAVE_HASH_MAP) # include #else # undef HAVE_STL_HASH_MAP #endif #ifdef HAVE_STL_HASH_MAP namespace cmtk { /// Generic hash function for all integer types. template struct HashFunctionInteger { /// Simply cast integer key to size_t size_t operator()( const TKey x ) const { return static_cast( x ); } }; /** Wrapper class for STL hash_map or unordered_map classes. * This class will use whatever hash map implementation is provided by the * currently used STL. If both unordered_map and hash_map are provided, the * former is used as it is the future standard class. */ template< class TKey, class TValue, class THashFunc = HashFunctionInteger > class HashMapSTL : /// Inherit STL hash/unordered map. #if defined(_MSC_VER) public std::tr1::unordered_map #elif defined(HAVE_UNORDERED_MAP) public std::unordered_map #elif defined(__GNUC__) public __gnu_cxx::hash_map #else public std::hash_map #endif { }; } // namespace cmtk #endif // #ifdef HAVE_STL_HASH_MAP #endif // #ifndef __cmtkHashMapSTL_h_included_ cmtk-3.3.1/libs/Base/cmtkHistogram.cxx000066400000000000000000000070701276303427400176330ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2012 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4375 $ // // $LastChangedDate: 2012-05-30 13:01:18 -0700 (Wed, 30 May 2012) $ // // $LastChangedBy: torstenrohlfing $ // */ #include "cmtkHistogram.h" #include namespace cmtk { /** \addtogroup Base */ //@{ template size_t Histogram ::GetMaximumBinIndex () const { T maximum = this->m_Bins[0]; size_t maximumIndex = 0; for ( size_t i = 0; iGetNumberOfBins(); ++i ) { if ( this->m_Bins[ i ] > maximum ) { maximum = this->m_Bins[ i ]; maximumIndex = i; } } return maximumIndex; } template double Histogram ::GetEntropy() const { double H = 0; const T sampleCount = this->SampleCount(); if ( ! sampleCount ) return std::numeric_limits::signaling_NaN(); for ( size_t i=0; iGetNumberOfBins(); ++i ) { if ( this->m_Bins[i] ) { const double pX = ((double)this->m_Bins[i]) / sampleCount; H -= pX*log(pX); } } return H; } template void Histogram ::AddHistogram ( const Self& other ) { assert( this->GetNumberOfBins() == other.GetNumberOfBins() ); for ( size_t i = 0; iGetNumberOfBins(); ++i ) { this->m_Bins[i] += other.m_Bins[i]; } } template void Histogram ::RemoveHistogram ( const Self& other ) { assert( this->GetNumberOfBins() == other.GetNumberOfBins() ); for ( size_t i = 0; iGetNumberOfBins(); ++i ) { assert( this->m_Bins[i] >= other.m_Bins[i] ); this->m_Bins[i] -= other.m_Bins[i]; } } template void Histogram ::Normalize ( const T normalizeTo ) { T sampleCount = this->SampleCount(); for ( size_t i = 0; i < this->GetNumberOfBins(); ++i ) ( this->m_Bins[ i ] *= normalizeTo ) /= sampleCount; } template void Histogram ::NormalizeMaximum ( const T normalizeTo ) { T maximum = this->GetMaximumBinValue(); for ( size_t i = 0; i < this->GetNumberOfBins(); ++i ) ( this->m_Bins[i] *= normalizeTo ) /= maximum; } template Types::DataItem Histogram ::GetPercentile( const Types::DataItem percentile ) const { const Types::DataItem threshold = percentile * this->SampleCount(); Types::DataItem cumulative = 0; for ( size_t i = 0; i < this->GetNumberOfBins(); ++i ) { cumulative += (*this)[i]; if ( cumulative >= threshold ) return this->BinToValue( i ); } return this->m_BinsLowerBound + this->m_BinWidth * (this->GetNumberOfBins() - 1); } template class Histogram; template class Histogram; template class Histogram; template class Histogram; template class Histogram; } // namespace cmtk cmtk-3.3.1/libs/Base/cmtkHistogram.h000066400000000000000000000254361276303427400172660ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2012 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4156 $ // // $LastChangedDate: 2012-04-10 10:13:18 -0700 (Tue, 10 Apr 2012) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkHistogram_h_included_ #define __cmtkHistogram_h_included_ #include #include #include #include #include #include #include namespace cmtk { /** \addtogroup Base */ //@{ /** Histogram of a distribution with bins of arbitrary types. * This template is the base class for one-dimensional histograms that can * hold integer or real-valued bins, depending on the template parameter. *\param T Template parameter: the type of the histogram bins. Can be integral, * or double in case of fractional bins. */ template class Histogram : /// Inherit some non-template functions. public HistogramBase { public: /// This class. typedef Histogram Self; /// Parent class. typedef HistogramBase Superclass; /// Smart pointer. typedef SmartPointer SmartPtr; /// Bin type. typedef T BinType; /** Constructor. */ Histogram ( const size_t numBins = 0 ) : m_Bins( numBins ) {} /** Destructor. */ virtual ~Histogram () {} /// Resize and allocate histogram bins. virtual void Resize( const size_t numberOfBins, const bool reset = true ) { this->m_Bins.resize( numberOfBins ); if ( reset ) this->Reset(); } /// Make an identical copy of this object. typename Self::SmartPtr Clone () const { return typename Self::SmartPtr( this->CloneVirtual() ); } /// Return number of histogram bins. virtual size_t GetNumberOfBins() const { return this->m_Bins.size(); } /** Reset computation. * This function has to be called before any other computation made with an * object of this class. All bin counters are reset to zero, therefore * Reset() must also be called before any new computation performed using an * already previously used object. */ void Reset () { std::fill( this->m_Bins.begin(), this->m_Bins.end(), static_cast( 0 ) ); } /// Return number of values in a given bin using [] operator. const T operator[] ( const size_t index ) const { assert( index < this->GetNumberOfBins() ); return this->m_Bins[index]; } /// Return reference to given bin. T& operator[] ( const size_t index ) { assert( index < this->GetNumberOfBins() ); return this->m_Bins[index]; } /** Return total number of samples stored in the histogram. */ T SampleCount () const { T sampleCount = 0; for ( size_t i=0; im_Bins.size(); ++i ) sampleCount += this->m_Bins[i]; return sampleCount; } /** Return index of bin with highest value. */ size_t GetMaximumBinIndex () const; /** Return maximum number of samples stored in any bin. */ T GetMaximumBinValue () const { return this->m_Bins[ this->GetMaximumBinIndex() ]; } /** Compute entropy of distribution. * From the bin counts, the entropy of the distribution of values is * estimated. */ double GetEntropy() const; /** Get Kullback-Leibler divergence to other histogram. * The second histogram must have the same number of bins, because the function * assumes bin-to-bin correspondence between the two distributions. * *\note Any bin value ranges set in derived classes are ignored here! */ double GetKullbackLeiblerDivergence( const Self& other ) const; /** Increment the value of a histogram bin by 1. * The histogram field to increment is identified directly by its index; * no value-rescaling is done internally. *\param sample Index of histogram field. */ void Increment ( const size_t sample ) { ++this->m_Bins[sample]; } /** Add weighted symmetric kernel to histogram. */ void AddWeightedSymmetricKernel( const size_t bin /*!< Histogram bin index */, const size_t kernelRadius /*!< Kernel radius */, const T* kernel /* Pointer to kernel values */, const T factor = 1 /*!< Kernel multiplication factor */ ); /** Add weighted symmetric kernel to histogram, spreading contriubtions between adjacent bins. */ void AddWeightedSymmetricKernelFractional( const double bin /*!< Histogram bin index */, const size_t kernelRadius /*!< Kernel radius */, const T* kernel /* Pointer to kernel values */, const T factor = 1 /*!< Kernel multiplication factor */ ); /** Increment the value of a histogram bins by fractions of 1. * The histogram field to increment is identified directly by its index; * no value-rescaling is done internally. The index for this function can be * a fractional value, in which case the entry is linearly distributed among * neighbouring bins. *\note If the bin type of this template object is an integer type, then * only the lower of two candidate bins will be decremented by 1. *\param bin Fractional index of histogram bin. */ void IncrementFractional ( const double bin ) { const T relative = static_cast( bin - floor(bin) ); this->m_Bins[static_cast(bin)] += (1 - relative); if ( bin<(this->GetNumberOfBins()-1) ) this->m_Bins[static_cast(bin+1)] += relative; } /** Increment the value of a histogram bin by a given weight. * The histogram field to increment is identified directly by its index; * no value-rescaling is done internally. *\param sample Index of histogram field. *\param weight Weight of the current value, i.e., real value that the given * bin is incremented by. */ void Increment ( const size_t sample, const double weight ) { this->m_Bins[sample] += static_cast( weight ); } /** Decrement the value of a histogram bin by 1. * The histogram field to decrement is identified directly by its index; * no value-rescaling is done internally. Make sure that a value has actually * been added to this bin before - otherwise, the next entropy computation my * give some unexpected results. *\param sample Index of histogram field in direction. */ void Decrement ( const size_t sample ) { assert( this->m_Bins[sample] >= 1 ); --this->m_Bins[sample]; } /** Decrement the value of a histogram bins by fractions of 1. * The histogram field to increment is identified directly by its index; * no value-rescaling is done internally. The index for this function can be * a fractional value, in which case the entry is linearly distributed among * neighbouring bins. *\note If the bin type of this template object is an integer type, then * only the lower of two candidate bins will be decremented by 1. *\param bin Fractional index of histogram bin. */ void DecrementFractional ( const double bin ) { T relative = static_cast( bin - floor(bin) ); this->m_Bins[static_cast(bin)] -= (1 - relative); if ( bin<(this->GetNumberOfBins()-1) ) this->m_Bins[static_cast(bin+1)] -= relative; } /** Decrement the value of a histogram bin by given weight. * The histogram field to decrement is identified directly by its index; * no value-rescaling is done internally. Make sure that a value has actually * been added to this bin before - otherwise, the next entropy computation my * give some unexpected results. *\param sample Index of histogram field in direction. *\param weight Weight of the current value, i.e., real value that the given * bin is decremented by. */ void Decrement ( const size_t sample, const double weight ) { assert( this->m_Bins[sample] >= weight ); this->m_Bins[sample] -= static_cast( weight ); } /** Add values from another histogram. * Adding is done by corresponding bins. The caller has to make sure that * both histograms actually have the same number and arrangement of bins. * It is also a good idea to ensure that the data range of these bins is * the same in both objects. Both can be guaranteed if one histogram was * created from the other by a call to Clone() for example. *\param other A pointer to the other histogram. Its bin values are added to * this object's bins. */ void AddHistogram ( const Self& other ); /** Subtract bin values from another histogram. * Subtraction is done by corresponding bins. The caller has to make sure * that both histograms actually have the same number of * bins. It is also a good idea to ensure that the data ranges of these bins * are the same in both objects. Both can be guaranteed if one histogram was * created from the other by a call to Clone() for example. *\param other A pointer to the other histogram. Its bin values are * subtracted this object's bins. */ void RemoveHistogram ( const Self& other ); /// Convert this histogram to a cumulative histogram (in place). void ConvertToCumulative() { for ( size_t idx = 1; idx < this->GetNumberOfBins(); ++idx ) { this->m_Bins[idx] += this->m_Bins[idx-1]; } } /** Normalize histogram values by their total sum. *\param normalizeTo All histogram bins are scaled by a common factor so that * their sum matches the value of this parameter. */ void Normalize( const T normalizeTo = 1 ); /** Normalize histogram values by their maximum. *\param normalizeTo All histogram bins are scaled by a common factor so that * their maximum matches the value of this parameter. */ void NormalizeMaximum( const T normalizeTo = 1 ); /** Compute approximate percentile value from histogram. */ Types::DataItem GetPercentile( const Types::DataItem percentile /*!< The percentile to be computed. Value must be between 0 and 1.*/ ) const; protected: /// Make an identical copy of this object including derived class objects virtual Self* CloneVirtual() const { return new Self( *this ); } private: /// Array bins. std::vector m_Bins; }; //@} } // namespace cmtk #include "cmtkHistogram.txx" #endif // #ifndef __cmtkHistogram_h_included_ cmtk-3.3.1/libs/Base/cmtkHistogram.txx000066400000000000000000000061711276303427400176550ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2012 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4156 $ // // $LastChangedDate: 2012-04-10 10:13:18 -0700 (Tue, 10 Apr 2012) $ // // $LastChangedBy: torstenrohlfing $ // */ #include namespace cmtk { /** \addtogroup Base */ //@{ template void Histogram ::AddWeightedSymmetricKernel ( const size_t bin, const size_t kernelRadius, const T* kernel, const T factor ) { this->m_Bins[bin] += factor * kernel[0]; for ( size_t idx = 1; idx < kernelRadius; ++idx ) { const T increment = factor * kernel[idx]; if ( (bin + idx) < this->GetNumberOfBins() ) this->m_Bins[bin + idx] += increment; if ( bin >= idx ) this->m_Bins[bin - idx] += increment; } } template void Histogram ::AddWeightedSymmetricKernelFractional ( const double bin, const size_t kernelRadius, const T* kernel, const T factor ) { const T relative = static_cast( bin - floor(bin) ); const size_t binIdx = static_cast( bin ); if ( (binIdx > 0) && (binIdx+1 < this->GetNumberOfBins()) ) { this->m_Bins[binIdx] += (1-relative) * factor * kernel[0]; this->m_Bins[binIdx+1] += relative * factor * kernel[0]; } for ( size_t idx = 1; idx < kernelRadius; ++idx ) { const T increment = factor * kernel[idx]; const size_t upIdx = binIdx+idx+1; if ( upIdx < this->GetNumberOfBins() ) { this->m_Bins[upIdx-1] += (1-relative) * increment; this->m_Bins[upIdx ] += relative * increment; } const int dnIdx = binIdx-idx; if ( dnIdx >= 0 ) { this->m_Bins[dnIdx ] += (1-relative) * increment; this->m_Bins[dnIdx+1] += relative * increment; } } } template double Histogram ::GetKullbackLeiblerDivergence( const Self& other ) const { assert( this->GetNumberOfBins() == other.GetNumberOfBins() ); const T sampleCount = this->SampleCount(); const T sampleCountOther = other.SampleCount(); double dKL = 0; for ( size_t i=0; iGetNumberOfBins(); ++i ) { if ( this->m_Bins[i] ) { const double pX = ((double)this->m_Bins[i]) / sampleCount; const double qX = ((double)other.m_Bins[i]) / sampleCountOther; dKL += pX*log(pX/qX); } } return dKL; } } // namespace cmtk cmtk-3.3.1/libs/Base/cmtkHistogramBase.h000066400000000000000000000127461276303427400200610ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2012 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4156 $ // // $LastChangedDate: 2012-04-10 10:13:18 -0700 (Tue, 10 Apr 2012) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkHistogramBase_h_included_ #define __cmtkHistogramBase_h_included_ #include #include #include #include #include #ifndef CMTK_HISTOGRAM_AUTOBINS /// Constant for number of bins to be determined automatically. #define CMTK_HISTOGRAM_AUTOBINS 0 #endif /// Constant for affirmative fractional bin flag. #define CMTK_HISTOGRAM_FRACTIONAL true /// Constant for affirmative fractional bin flag. #define CMTK_HISTOGRAM_DISCRETE false /// Constant for resetting histogram bins on instantiation. #define CMTK_HISTOGRAM_RESET true /// Constant for not resetting histogram bins on instantiation. #define CMTK_HISTOGRAM_NORESET false /// Constant for copying histogram bin values on object duplication. #define CMTK_HISTOGRAM_COPY true /// Constant for not copying histogram bin values on object duplication. #define CMTK_HISTOGRAM_NOCOPY false namespace cmtk { /** \addtogroup Base */ //@{ /** Common (non-template) base class for all 1-D histograms. */ class HistogramBase { protected: /// Width of data bins. Types::DataItem m_BinWidth; /// Lower value bound of data bins. Types::DataItem m_BinsLowerBound; /// Upper value bound of data bins. Types::DataItem m_BinsUpperBound; public: /// This class. typedef HistogramBase Self; /// Default constructor. HistogramBase() { this->m_BinWidth = 1.0; this->m_BinsLowerBound = this->m_BinsUpperBound = 0.0; } /// Virtual destructor. virtual ~HistogramBase() {} /// Return number of histogram bins. virtual size_t GetNumberOfBins() const = 0; /** Set data range corresponding to this histogram. */ void SetRange ( const Types::DataItemRange& range ) { this->m_BinsLowerBound = range.m_LowerBound; this->m_BinsUpperBound = range.m_UpperBound; this->m_BinWidth = range.Width() / (this->GetNumberOfBins() - 1); } /** Set data range corresponding to this histogram with upper and lower bound centered in first and last bin. */ void SetRangeCentered( const Types::DataItemRange& range ) { this->m_BinWidth = range.Width() / (this->GetNumberOfBins() - 1); this->m_BinsLowerBound = static_cast( range.m_LowerBound - 0.5 * this->m_BinWidth ); this->m_BinsUpperBound = static_cast( range.m_UpperBound + 0.5 * this->m_BinWidth ); } /** Get value range of the distribution. */ const Types::DataItemRange GetRange() const { return Types::DataItemRange( this->m_BinsLowerBound, this->m_BinsUpperBound ); } /** Get value range of a given bin. */ virtual const Types::DataItemRange GetRangeBin( const size_t bin ) const { const Types::DataItem from = this->m_BinsLowerBound + this->m_BinWidth * bin; return Types::DataItemRange( from, from + this->m_BinWidth ); } /// Get bin width. Types::DataItem GetBinWidth() const { return this->m_BinWidth; } /** Return bin corresponding to a certain value of the distribution. *\param value A value from the distribution. *\return The index of the bin corresponding to the given value. */ virtual size_t ValueToBin ( const Types::DataItem value ) const { const size_t binIndex = static_cast( (value - this->m_BinsLowerBound) / this->m_BinWidth ); return std::max( 0, std::min( this->GetNumberOfBins()-1, binIndex ) ); } /** Return fractional bin corresponding to a value of the distribution. *\param value A value from the distribution. *\return The index of the fractional bin index corresponding to the given * value. This value is an integer if and only if the given value is * identical to the lower bound of a bin. */ virtual Types::DataItem ValueToBinFractional ( const Types::DataItem value ) const { const Types::DataItem binIndex = (value - this->m_BinsLowerBound) / this->m_BinWidth; return std::max( 0, std::min( static_cast( this->GetNumberOfBins()-1 ), binIndex)); } /** Return center of values represented by a certain bin. *\param bin Index of a bin from the distribution. *\return Average of upper and lower margin values of the given bin. */ virtual Types::DataItem BinToValue ( const size_t bin ) const { return static_cast( this->m_BinsLowerBound + (bin+0.5) * this->m_BinWidth ); } }; //@} } // namespace cmtk #endif // #ifndef __cmtkHistogramBase_h_included_ cmtk-3.3.1/libs/Base/cmtkHistogramOtsuThreshold.h000066400000000000000000000050711276303427400220070ustar00rootroot00000000000000/* // // Copyright 2012 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4306 $ // // $LastChangedDate: 2012-05-04 10:06:54 -0700 (Fri, 04 May 2012) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkHistogramOtsuThreshold_h_included_ #define __cmtkHistogramOtsuThreshold_h_included_ #include #include namespace cmtk { /** \addtogroup Base */ //@{ /** Class for computing binarization threshold from intensity histogram using Otsu's method. *\see https://en.wikipedia.org/wiki/Otsu%27s_method *\see N. Otsu (1979). "A threshold selection method from gray-level histograms." IEEE Trans. Sys., Man., Cyber. 9 (1): 62–66. * http://dx.doi.org/10.1109/TSMC.1979.4310076 *\see D.-Y. Huang and C.-H. Wang, "Optimal multi-level thresholding using a two-stage Otsu optimization approach," Pattern Recognition Letters, vol. 30, no. 3, * pp. 275-284, 2009. http://dx.doi.org/10.1016/j.patrec.2008.10.003 */ template class HistogramOtsuThreshold { public: /// This class. typedef HistogramOtsuThreshold Self; /// Smart pointer to this class. typedef SmartPointer SmartPtr; /// Smart pointer to const for this class. typedef SmartConstPointer SmartConstPtr; /// The histogram template parameter type. typedef THistogram HistogramType; /// Constructor: compute and store threshold. HistogramOtsuThreshold( const typename Self::HistogramType& histogram ); /// Get the computed threshold. Types::DataItem Get() const { return this->m_Threshold; } private: /// Computed threshold. Types::DataItem m_Threshold; }; //@} } // namespace cmtk #include "cmtkHistogramOtsuThreshold.txx" #endif // #ifndef __cmtkHistogramOtsuThreshold_h_included_ cmtk-3.3.1/libs/Base/cmtkHistogramOtsuThreshold.txx000066400000000000000000000047631276303427400224120ustar00rootroot00000000000000/* // // Copyright 2012 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4306 $ // // $LastChangedDate: 2012-05-04 10:06:54 -0700 (Fri, 04 May 2012) $ // // $LastChangedBy: torstenrohlfing $ // */ #include #include template cmtk::HistogramOtsuThreshold::HistogramOtsuThreshold( const typename Self::HistogramType& histogram ) { const size_t nBins = histogram.GetNumberOfBins(); std::vector cumulativeProb( nBins ); std::vector cumulativeMean( nBins ); // create cumulative mean and probability tables const Types::DataItem invTotal = 1.0 / histogram.SampleCount(); cumulativeProb[0] = invTotal * histogram[0]; cumulativeMean[0] = cumulativeProb[0] * histogram.BinToValue( 0 ); for ( size_t i = 1; i < nBins; ++i ) { const Types::DataItem p = invTotal * histogram[i]; cumulativeProb[i] = cumulativeProb[i-1] + p; cumulativeMean[i] = cumulativeMean[i-1] + (p * i); } // find threshold the maximizes inter-class variance Types::DataItem maxSigma = 0; size_t maxIndex = 0; for ( size_t i = 0; i < nBins-1; ++i ) { const Types::DataItem w1 = cumulativeProb[i]; const Types::DataItem w2 = 1-cumulativeProb[i]; const Types::DataItem mu1 = cumulativeMean[i] / w1; const Types::DataItem mu2 = (cumulativeMean[nBins-1] - cumulativeMean[i]) / w2; const Types::DataItem muT = cumulativeMean[nBins-1]; const Types::DataItem sigma = w1*MathUtil::Square(mu1-muT) + w2*MathUtil::Square(mu2-muT); if ( sigma > maxSigma ) { maxSigma = sigma; maxIndex = i; } } this->m_Threshold = histogram.BinToValue( maxIndex ); } cmtk-3.3.1/libs/Base/cmtkHistogramThresholdByVolume.h000066400000000000000000000044151276303427400226200ustar00rootroot00000000000000/* // // Copyright 2012 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4296 $ // // $LastChangedDate: 2012-05-01 15:21:41 -0700 (Tue, 01 May 2012) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkHistogramThresholdByVolume_h_included_ #define __cmtkHistogramThresholdByVolume_h_included_ #include #include namespace cmtk { /** \addtogroup Base */ //@{ /** Class for computing binarization threshold from intensity histogram based on desired volume. */ template class HistogramThresholdByVolume { public: /// This class. typedef HistogramThresholdByVolume Self; /// Smart pointer to this class. typedef SmartPointer SmartPtr; /// Smart pointer to const for this class. typedef SmartConstPointer SmartConstPtr; /// The histogram template parameter type. typedef THistogram HistogramType; /// Constructor: compute minimum threshold such that the number of samples above threshold is at least "volumeAbove". HistogramThresholdByVolume( const typename Self::HistogramType& histogram, const typename Self::HistogramType::BinType volumeAbove ); /// Get the computed threshold. Types::DataItem Get() const { return this->m_Threshold; } private: /// Computed threshold. Types::DataItem m_Threshold; }; //@} } // namespace cmtk #include "cmtkHistogramThresholdByVolume.txx" #endif // #ifndef __cmtkHistogramThresholdByVolume_h_included_ cmtk-3.3.1/libs/Base/cmtkHistogramThresholdByVolume.txx000066400000000000000000000036021276303427400232110ustar00rootroot00000000000000/* // // Copyright 2012 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4296 $ // // $LastChangedDate: 2012-05-01 15:21:41 -0700 (Tue, 01 May 2012) $ // // $LastChangedBy: torstenrohlfing $ // */ #include template cmtk::HistogramThresholdByVolume::HistogramThresholdByVolume( const typename Self::HistogramType& histogram, const typename Self::HistogramType::BinType volumeAbove ) { typename Self::HistogramType cumulativeHistogram = histogram; cumulativeHistogram.ConvertToCumulative(); const size_t nBins = cumulativeHistogram.GetNumberOfBins(); const typename Self::HistogramType::BinType volumeBelow = cumulativeHistogram[nBins-1] - volumeAbove; // reverse cumulative histogram - volume we want is above threshold, not below. for ( size_t i = 0; i < nBins; ++i ) { if ( cumulativeHistogram[i] >= volumeBelow ) { this->m_Threshold = histogram.BinToValue( i ); return; } } // as a fall-back, return upper end of value range this->m_Threshold = histogram.GetRange().m_UpperBound; } cmtk-3.3.1/libs/Base/cmtkHypothesisTests.cxx000066400000000000000000000307571276303427400210700ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2010, 2013 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4870 $ // // $LastChangedDate: 2013-09-24 11:01:11 -0700 (Tue, 24 Sep 2013) $ // // $LastChangedBy: torstenrohlfing $ // */ #include "cmtkHypothesisTests.h" #include #include #include #include namespace cmtk { /** \addtogroup Registration */ //@{ TypedArray::SmartPtr HypothesisTests::GetUnpairedTwoTailedTTest ( std::vector& dataX, std::vector& dataY, TypedArray::SmartPtr* tstatData, TypedArray::SmartPtr* avgXData, TypedArray::SmartPtr* avgYData, const TypedArray* mask ) { const unsigned int length = dataX[0]->GetDataSize(); TypedArray::SmartPtr probData = TypedArray::Create( TYPE_FLOAT, length ); if ( tstatData ) *tstatData = TypedArray::Create( TYPE_FLOAT, length ); if ( avgXData ) *avgXData = TypedArray::Create( TYPE_FLOAT, length ); if ( avgYData ) *avgYData = TypedArray::Create( TYPE_FLOAT, length ); const unsigned int dataXsize = dataX.size(); std::vector valuesX( dataXsize ); const unsigned int dataYsize = dataY.size(); std::vector valuesY( dataYsize ); Types::DataItem t = 0, prob = 0, avgX = 0, avgY = 0; for ( unsigned int idx = 0; idx < length; ++idx ) { Types::DataItem maskValue; if ( !mask || (mask->Get( maskValue, idx ) && (maskValue != 0)) ) { unsigned int actualSizeX = 0; for ( unsigned int i = 0; i < dataXsize; ++i ) if ( dataX[i]->Get( valuesX[actualSizeX], idx ) ) ++actualSizeX; unsigned int actualSizeY = 0; for ( unsigned int i = 0; i < dataYsize; ++i ) if ( dataY[i]->Get( valuesY[actualSizeY], idx ) ) ++actualSizeY; if ( actualSizeX && actualSizeY ) { prob = MathUtil::TTest( valuesX, valuesY, t, avgX, avgY ); if ( (prob < 0) || (prob>1) ) { fprintf( stderr, "t = %f\tp = %f\n", t, prob ); } prob = 1.0 - prob; // convert probability to significance } else { t = prob = 0; } if ( tstatData ) (*tstatData)->Set( t, idx ); if ( avgXData ) (*avgXData)->Set( avgX, idx ); if ( avgYData ) (*avgYData)->Set( avgY, idx ); if ( avgX > avgY ) probData->Set( prob, idx ); else probData->Set( -prob, idx ); } else { probData->SetPaddingAt( idx ); if ( tstatData ) (*tstatData)->SetPaddingAt( idx ); if ( avgXData ) (*avgXData)->SetPaddingAt( idx ); if ( avgYData ) (*avgYData)->SetPaddingAt( idx ); } } return probData; } TypedArray::SmartPtr HypothesisTests::GetPairedTwoTailedTTest ( std::vector& dataX, std::vector& dataY, TypedArray::SmartPtr* tstatData, TypedArray::SmartPtr* avgXData, TypedArray::SmartPtr* avgYData, const TypedArray* mask ) { if ( dataX.size() != dataY.size() ) { throw( Exception( "Cannot perform paired t-test if numbers of X and Y samples isn't equal" ) ); } const unsigned int length = dataX[0]->GetDataSize(); TypedArray::SmartPtr probData = TypedArray::Create( TYPE_FLOAT, length ); if ( tstatData ) *tstatData = TypedArray::Create( TYPE_FLOAT, length ); if ( avgXData ) *avgXData = TypedArray::Create( TYPE_FLOAT, length ); if ( avgYData ) *avgYData = TypedArray::Create( TYPE_FLOAT, length ); const unsigned int dataXsize = dataX.size(); std::vector valuesX( dataXsize ); const unsigned int dataYsize = dataY.size(); std::vector valuesY( dataYsize ); Types::DataItem t = 0, prob = 0, avgX = 0, avgY = 0; for ( unsigned int idx = 0; idx < length; ++idx ) { Types::DataItem maskValue; if ( !mask || (mask->Get( maskValue, idx ) && (maskValue != 0)) ) { valuesX.resize( dataXsize ); unsigned int actualSizeX = 0; for ( unsigned int i = 0; i < dataXsize; ++i ) if ( dataX[i]->Get( valuesX[actualSizeX], idx ) ) ++actualSizeX; valuesY.resize( dataYsize ); unsigned int actualSizeY = 0; for ( unsigned int i = 0; i < dataYsize; ++i ) if ( dataY[i]->Get( valuesY[actualSizeY], idx ) ) ++actualSizeY; if ( actualSizeX == actualSizeY ) { valuesX.resize( actualSizeX ); valuesY.resize( actualSizeY ); prob = MathUtil::PairedTTest( valuesX, valuesY, t, avgX, avgY ); if ( (prob < 0) || (prob>1) ) { fprintf( stderr, "t = %f\tp = %f\n", t, prob ); } prob = 1.0 - prob; // convert probability to significance } else { t = prob = 0; } if ( tstatData ) (*tstatData)->Set( t, idx ); if ( avgXData ) (*avgXData)->Set( avgX, idx ); if ( avgYData ) (*avgYData)->Set( avgY, idx ); if ( avgX > avgY ) probData->Set( prob, idx ); else probData->Set( -prob, idx ); } else { probData->SetPaddingAt( idx ); if ( tstatData ) (*tstatData)->SetPaddingAt( idx ); if ( avgXData ) (*avgXData)->SetPaddingAt( idx ); if ( avgYData ) (*avgYData)->SetPaddingAt( idx ); } } return probData; } TypedArray::SmartPtr HypothesisTests::GetPairedCorrelation ( std::vector& dataX, std::vector& dataY, TypedArray::SmartPtr* pData, const TypedArray* mask ) { if ( dataX.size() != dataY.size() ) { throw( Exception( "Cannot perform paired correlation if numbers of X and Y samples isn't equal" ) ); } const unsigned int length = dataX[0]->GetDataSize(); TypedArray::SmartPtr correlationData = TypedArray::Create( TYPE_FLOAT, length ); if ( pData ) *pData = TypedArray::Create( TYPE_FLOAT, length ); const unsigned int dataXsize = dataX.size(); std::vector valuesX( dataXsize ); const unsigned int dataYsize = dataY.size(); std::vector valuesY( dataYsize ); for ( unsigned int idx = 0; idx < length; ++idx ) { correlationData->SetPaddingAt( idx ); if ( pData ) (*pData)->SetPaddingAt( idx ); Types::DataItem maskValue; if ( !mask || (mask->Get( maskValue, idx ) && (maskValue != 0)) ) { valuesX.resize( dataXsize ); valuesY.resize( dataXsize ); unsigned int actualSize = 0; for ( unsigned int i = 0; i < dataXsize; ++i ) if ( dataX[i]->Get( valuesX[actualSize], idx ) && dataY[i]->Get( valuesY[actualSize], idx ) ) ++actualSize; if ( actualSize ) { valuesX.resize( actualSize ); valuesY.resize( actualSize ); Types::DataItem corr = MathUtil::Correlation( valuesX, valuesY ); correlationData->Set( corr, idx ); if ( pData ) (*pData)->Set( MathUtil::ProbabilityFromTStat( MathUtil::TStatFromCorrelation( corr, actualSize-2 ), actualSize-2 ), idx ); } } } return correlationData; } TypedArray::SmartPtr HypothesisTests::GetOneSampleTTest ( std::vector& dataX, TypedArray::SmartPtr* tstatData, TypedArray::SmartPtr* avgXData, const TypedArray* mask ) { const unsigned int length = dataX[0]->GetDataSize(); TypedArray::SmartPtr probData = TypedArray::Create( TYPE_FLOAT, length ); if ( tstatData ) *tstatData = TypedArray::Create( TYPE_FLOAT, length ); if ( avgXData ) *avgXData = TypedArray::Create( TYPE_FLOAT, length ); const unsigned int dataXsize = dataX.size(); std::vector valuesX( dataXsize ); Types::DataItem t = 0, prob = 0, avgX = 0; for ( unsigned int idx = 0; idx < length; ++idx ) { Types::DataItem maskValue; if ( !mask || (mask->Get( maskValue, idx ) && (maskValue != 0)) ) { valuesX.resize( dataXsize ); unsigned int actualSizeX = 0; for ( unsigned int i = 0; i < dataXsize; ++i ) if ( dataX[i]->Get( valuesX[actualSizeX], idx ) ) ++actualSizeX; if ( actualSizeX ) { valuesX.resize( actualSizeX ); prob = MathUtil::TTest( valuesX, t, avgX ); if ( (prob < 0) || (prob>1) ) { fprintf( stderr, "t = %f\tp = %f\n", t, prob ); } prob = 1.0 - prob; // convert probability to significance } else { t = prob = 0; } if ( tstatData ) (*tstatData)->Set( t, idx ); if ( avgXData ) (*avgXData)->Set( avgX, idx ); if ( avgX > 0 ) probData->Set( -prob, idx ); else probData->Set( +prob, idx ); } else { probData->SetPaddingAt( idx ); if ( tstatData ) (*tstatData)->SetPaddingAt( idx ); if ( avgXData ) (*avgXData)->SetPaddingAt( idx ); } } return probData; } TypedArray::SmartPtr HypothesisTests::GetZScores ( std::vector& dataX, std::vector& dataY, const TypedArray* mask ) { const size_t length = dataX[0]->GetDataSize(); TypedArray::SmartPtr outData = TypedArray::Create( TYPE_FLOAT, length ); const unsigned int dataXsize = dataX.size(); std::vector valuesX( dataXsize ); const unsigned int dataYsize = dataY.size(); std::vector valuesY( dataYsize ); Types::DataItem avgX, avgY, varX; for ( size_t idx = 0; idx < length; ++idx ) { Types::DataItem maskValue; if ( !mask || (mask->Get( maskValue, idx ) && (maskValue != 0)) ) { valuesX.resize( dataXsize ); unsigned int actualSizeX = 0; for ( unsigned int i = 0; i < dataXsize; ++i ) if ( dataX[i]->Get( valuesX[actualSizeX], idx ) ) ++actualSizeX; valuesY.resize( dataYsize ); unsigned int actualSizeY = 0; for ( unsigned int i = 0; i < dataYsize; ++i ) if ( dataY[i]->Get( valuesY[actualSizeY], idx ) ) ++actualSizeY; if ( actualSizeX && actualSizeY ) { valuesX.resize( actualSizeX ); avgX = MathUtil::Mean( valuesX ); valuesY.resize( actualSizeY ); avgY = MathUtil::Mean( valuesY ); varX = MathUtil::Variance( valuesX, avgX ); outData->Set( (avgY - avgX) / sqrt( varX ), idx ); } else { outData->SetPaddingAt( idx ); } } else { outData->SetPaddingAt( idx ); } } return outData; } TypedArray::SmartPtr HypothesisTests::GetGeneticCovariance ( std::vector& dataMZ, std::vector& dataDZ, const TypedArray* mask ) { const size_t length = dataMZ[0]->GetDataSize(); TypedArray::SmartPtr outData = TypedArray::Create( TYPE_FLOAT, length ); const unsigned int dataMZsize = dataMZ.size(); std::vector valuesMZ( dataMZsize ); const unsigned int dataDZsize = dataDZ.size(); std::vector valuesDZ( dataDZsize ); Types::DataItem avgMZ, avgDZ, varMZ, varDZ; for ( size_t idx = 0; idx < length; ++idx ) { Types::DataItem maskValue; if ( !mask || (mask->Get( maskValue, idx ) && (maskValue != 0)) ) { valuesMZ.resize( dataMZsize ); unsigned int actualSizeMZ = 0; for ( unsigned int i = 0; i < dataMZsize; ++i ) if ( dataMZ[i]->Get( valuesMZ[actualSizeMZ], idx ) ) ++actualSizeMZ; valuesDZ.resize( dataDZsize ); unsigned int actualSizeDZ = 0; for ( unsigned int i = 0; i < dataDZsize; ++i ) if ( dataDZ[i]->Get( valuesDZ[actualSizeDZ], idx ) ) ++actualSizeDZ; if ( actualSizeMZ && actualSizeDZ ) { valuesMZ.resize( actualSizeMZ ); avgMZ = MathUtil::Mean( valuesMZ ); varMZ = MathUtil::Variance( valuesMZ, avgMZ ); valuesDZ.resize( actualSizeDZ ); avgDZ = MathUtil::Mean( valuesDZ ); varDZ = MathUtil::Variance( valuesDZ, avgDZ ); outData->Set( varMZ / avgMZ - varDZ / avgDZ, idx ); } else { outData->SetPaddingAt( idx ); } } else { outData->SetPaddingAt( idx ); } } return outData; } } // namespace cmtk cmtk-3.3.1/libs/Base/cmtkHypothesisTests.h000066400000000000000000000065331276303427400205100ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2010, 2013 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4870 $ // // $LastChangedDate: 2013-09-24 11:01:11 -0700 (Tue, 24 Sep 2013) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkHypothesisTests_h_included_ #define __cmtkHypothesisTests_h_included_ #include #include #include #include #include #include namespace cmtk { /** \addtogroup Registration */ //@{ /// Statistical hypothesis testing between groups of images. class HypothesisTests { public: /// Test Jacobian maps of two populations for statistical independence. static TypedArray::SmartPtr GetUnpairedTwoTailedTTest ( std::vector& dataX, std::vector& dataY, TypedArray::SmartPtr* tstatData, TypedArray::SmartPtr* avgXData, TypedArray::SmartPtr* avgYData, const TypedArray* mask = NULL ); /// Test parameter maps of two populations for statistical independence. static TypedArray::SmartPtr GetPairedTwoTailedTTest ( std::vector& dataX, std::vector& dataY, TypedArray::SmartPtr* tstatData, TypedArray::SmartPtr* avgXData, TypedArray::SmartPtr* avgYData, const TypedArray* mask = NULL ); /// Get pixel-wise correlation between two sets of input images. static TypedArray::SmartPtr GetPairedCorrelation( std::vector& dataX, std::vector& dataY, TypedArray::SmartPtr* pData = NULL, const TypedArray* mask = NULL ); /// Test mean of Jacobian map of a single population for difference from zero. static TypedArray::SmartPtr GetOneSampleTTest( std::vector& dataX, TypedArray::SmartPtr* tstatData, TypedArray::SmartPtr* avgXData, const TypedArray* mask = NULL ); /** Get pixelwise z-scores. * The X distribution is taken as the "true" or "reference" distribution. * The Y distribution is taken as the "test" or "sample" distribution. */ static TypedArray::SmartPtr GetZScores( std::vector& dataX, std::vector& dataY, const TypedArray* mask = NULL ); /// Get pixelwise genetic covariance from MZ and DZ twin data. static TypedArray::SmartPtr GetGeneticCovariance( std::vector& dataMZ, std::vector& dataDZ, const TypedArray* mask = NULL ); }; //@} } // namespace cmtk #endif // #ifndef __cmtkHypothesisTests_h_included_ cmtk-3.3.1/libs/Base/cmtkImageOperation.cxx000066400000000000000000000022101276303427400205700ustar00rootroot00000000000000/* // // Copyright 2009-2010 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 2022 $ // // $LastChangedDate: 2010-07-21 15:26:03 -0700 (Wed, 21 Jul 2010) $ // // $LastChangedBy: torstenrohlfing $ // */ #include "cmtkImageOperation.h" std::list cmtk::ImageOperation::m_ImageOperationList; cmtk-3.3.1/libs/Base/cmtkImageOperation.h000066400000000000000000000047621276303427400202330ustar00rootroot00000000000000/* // // Copyright 2009-2012 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4390 $ // // $LastChangedDate: 2012-05-31 10:39:00 -0700 (Thu, 31 May 2012) $ // // $LastChangedBy: torsten_at_home $ // */ #ifndef __cmtkImageOperation_h_included_ #define __cmtkImageOperation_h_included_ #include #include #include #include namespace cmtk { /** \addtogroup Base */ //@{ /** Image operation base class. * Classes derived from this base class are used to implement an ordered sequence of operations * primarily for the "convertx" command line tool. * *\warning This class is not thread-safe in the sense that the costructed operation sequence is * stored in a static member field of this class, m_ImageOperationList. */ class ImageOperation { public: /// This class. typedef ImageOperation Self; /// Smart pointer. typedef cmtk::SmartPointer SmartPtr; /// Virtual destructor. virtual ~ImageOperation() {} /// Apply this operation to an image in place. virtual cmtk::UniformVolume::SmartPtr Apply( cmtk::UniformVolume::SmartPtr& volume ) { return volume; } /// Apply all operations in list. static cmtk::UniformVolume::SmartPtr ApplyAll( cmtk::UniformVolume::SmartPtr& volume ) { for ( std::list::iterator opIt = Self::m_ImageOperationList.begin(); opIt != Self::m_ImageOperationList.end(); ++opIt ) { volume = (*opIt)->Apply( volume ); } return volume; } protected: /// List of image operations. static std::list m_ImageOperationList; }; //@} } // namespace cmtk #endif // #ifndef __cmtkImageOperation_h_included_ cmtk-3.3.1/libs/Base/cmtkImageOperationBoundaryMap.h000066400000000000000000000047011276303427400223660ustar00rootroot00000000000000/* // // Copyright 2009-2011 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 2902 $ // // $LastChangedDate: 2011-02-24 12:12:46 -0800 (Thu, 24 Feb 2011) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkImageOperationBoundaryMap_h_included_ #define __cmtkImageOperationBoundaryMap_h_included_ #include #include #include namespace cmtk { /** \addtogroup Base */ //@{ /// Image operation: create binary or multi-valued boundary map. class ImageOperationBoundaryMap /// Inherit from image operation base class. : public ImageOperation { public: /// Constructor: ImageOperationBoundaryMap( const bool multiValued ) : m_MultiValued( multiValued ) {} /// Apply this operation to an image in place. virtual cmtk::UniformVolume::SmartPtr Apply( cmtk::UniformVolume::SmartPtr& volume ) { cmtk::DataGridMorphologicalOperators ops( volume ); volume->SetData( ops.GetBoundaryMap( this->m_MultiValued ) ); return volume; } /// Create new binary boundary map operation. static void New() { ImageOperation::m_ImageOperationList.push_back( SmartPtr( new ImageOperationBoundaryMap( false ) ) ); } /// Create new multi-valued boundary map operation. static void NewMulti() { ImageOperation::m_ImageOperationList.push_back( SmartPtr( new ImageOperationBoundaryMap( true ) ) ); } private: /// Multi-valued flag: if this is set, a multi-valued boundary map will be created, otherwise a binary map. bool m_MultiValued; }; //@} } // namespace cmtk #endif // #ifndef __cmtkImageOperationBoundaryMap_h_included_ cmtk-3.3.1/libs/Base/cmtkImageOperationConnectedComponents.h000066400000000000000000000041131276303427400241120ustar00rootroot00000000000000/* // // Copyright 2010 Torsten Rohlfing // // Copyright 2010 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 2398 $ // // $LastChangedDate: 2010-10-05 14:54:37 -0700 (Tue, 05 Oct 2010) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkImageOperationConnectedComponents_h_included_ #define __cmtkImageOperationConnectedComponents_h_included_ #include #include #include namespace cmtk { /// Image operation: create connected components map. class ImageOperationConnectedComponents /// Inherit from image operation base class. : public ImageOperation { public: /// Apply this operation to an image in place. virtual cmtk::UniformVolume::SmartPtr Apply( cmtk::UniformVolume::SmartPtr& volume ) { cmtk::DataGridMorphologicalOperators ops( volume ); volume->SetData( ops.GetBinaryConnectedComponents() ); volume->SetData( ops.GetRegionsRenumberedBySize() ); return volume; } /// Create new connected components operation. static void New() { ImageOperation::m_ImageOperationList.push_back( SmartPtr( new ImageOperationConnectedComponents() ) ); } }; } // namespace cmtk #endif // #ifndef __cmtkImageOperationConnectedComponents_h_included_ cmtk-3.3.1/libs/Base/cmtkImageOperationConvertType.h000066400000000000000000000075251276303427400224360ustar00rootroot00000000000000/* // // Copyright 2009-2011 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 2905 $ // // $LastChangedDate: 2011-02-24 12:29:02 -0800 (Thu, 24 Feb 2011) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkImageOperationConvertType_h_included_ #define __cmtkImageOperationConvertType_h_included_ #include #include namespace cmtk { /** \addtogroup Base */ //@{ /// Image operation: convert data type. class ImageOperationConvertType /// Inherit from image operation base class. : public ImageOperation { public: /// Constructor: ImageOperationConvertType( const cmtk::ScalarDataType newType ) : m_NewType( newType ) {} /// Apply this operation to an image in place. virtual cmtk::UniformVolume::SmartPtr Apply( cmtk::UniformVolume::SmartPtr& volume ) { switch ( this->m_NewType ) { case cmtk::TYPE_CHAR: case cmtk::TYPE_BYTE: case cmtk::TYPE_SHORT: case cmtk::TYPE_USHORT: case cmtk::TYPE_INT: case cmtk::TYPE_UINT: case cmtk::TYPE_FLOAT: case cmtk::TYPE_DOUBLE: if ( this->m_NewType != volume->GetData()->GetType() ) { volume->SetData( cmtk::TypedArray::SmartPtr( volume->GetData()->Convert( this->m_NewType ) ) ); } break; default: break; } return volume; } /// Create object to convert to "char" data. static void NewChar() { ImageOperation::m_ImageOperationList.push_back( SmartPtr( new ImageOperationConvertType( cmtk::TYPE_CHAR ) ) ); } /// Create object to convert to "byte" data. static void NewByte() { ImageOperation::m_ImageOperationList.push_back( SmartPtr( new ImageOperationConvertType( cmtk::TYPE_BYTE ) ) ); } /// Create object to convert to "short" data. static void NewShort() { ImageOperation::m_ImageOperationList.push_back( SmartPtr( new ImageOperationConvertType( cmtk::TYPE_SHORT ) ) ); } /// Create object to convert to "unsigned short" data. static void NewUShort() { ImageOperation::m_ImageOperationList.push_back( SmartPtr( new ImageOperationConvertType( cmtk::TYPE_USHORT ) ) ); } /// Create object to convert to "int" data. static void NewInt() { ImageOperation::m_ImageOperationList.push_back( SmartPtr( new ImageOperationConvertType( cmtk::TYPE_INT ) ) ); } /// Create object to convert to "unsigned int" data. static void NewUInt() { ImageOperation::m_ImageOperationList.push_back( SmartPtr( new ImageOperationConvertType( cmtk::TYPE_UINT ) ) ); } /// Create object to convert to "float" data. static void NewFloat() { ImageOperation::m_ImageOperationList.push_back( SmartPtr( new ImageOperationConvertType( cmtk::TYPE_FLOAT ) ) ); } /// Create object to convert to "double" data. static void NewDouble() { ImageOperation::m_ImageOperationList.push_back( SmartPtr( new ImageOperationConvertType( cmtk::TYPE_DOUBLE ) ) ); } private: /// New data type. cmtk::ScalarDataType m_NewType; }; //@} } // namespace cmtk #endif // #ifndef __cmtkImageOperationConvertType_h_included_ cmtk-3.3.1/libs/Base/cmtkImageOperationCropRegion.cxx000066400000000000000000000034271276303427400225730ustar00rootroot00000000000000/* // // Copyright 2010-2012 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4427 $ // // $LastChangedDate: 2012-06-12 11:23:22 -0700 (Tue, 12 Jun 2012) $ // // $LastChangedBy: torstenrohlfing $ // */ #include "cmtkImageOperationCropRegion.h" cmtk::UniformVolume::SmartPtr cmtk::ImageOperationCropRegion ::Apply( cmtk::UniformVolume::SmartPtr& volume ) { volume->SetCropRegion( this->m_Region ); return cmtk::UniformVolume::SmartPtr( volume->GetCroppedVolume() ); } void cmtk::ImageOperationCropRegion ::New( const char* arg ) { int from[3], to[3]; const bool okay = (6 == sscanf( arg, "%5d,%5d,%5d,%5d,%5d,%5d", &from[0], &from[1], &from[2], &to[0], &to[1], &to[2] ) ); if ( ! okay ) { throw "Expected six comma-separated integer values."; } ImageOperation::m_ImageOperationList.push_back( SmartPtr( new ImageOperationCropRegion( DataGrid::RegionType( DataGrid::IndexType::FromPointer(from), DataGrid::IndexType::FromPointer(to) ) ) ) ); } cmtk-3.3.1/libs/Base/cmtkImageOperationCropRegion.h000066400000000000000000000035771276303427400222260ustar00rootroot00000000000000/* // // Copyright 2009-2011 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 2902 $ // // $LastChangedDate: 2011-02-24 12:12:46 -0800 (Thu, 24 Feb 2011) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkImageOperationCropRegion_h_included_ #define __cmtkImageOperationCropRegion_h_included_ #include #include namespace cmtk { /** \addtogroup Base */ //@{ /// Image operation: crop to region. class ImageOperationCropRegion /// Inherit from image operation base class. : public ImageOperation { public: /// Constructor: ImageOperationCropRegion( const DataGrid::RegionType& region ) { this->m_Region = region; } /// Apply this operation to an image in place. virtual cmtk::UniformVolume::SmartPtr Apply( cmtk::UniformVolume::SmartPtr& volume ); /// Create a new downsampler. static void New( const char* arg ); private: /// Cropping region: x0,y0,z0,x1,y1,z1 DataGrid::RegionType m_Region; }; //@} } // namespace cmtk #endif // #ifndef __cmtkImageOperationCropRegion_h_included_ cmtk-3.3.1/libs/Base/cmtkImageOperationCropThreshold.cxx000066400000000000000000000036721276303427400233060ustar00rootroot00000000000000/* // // Copyright 2009-2011 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 2712 $ // // $LastChangedDate: 2011-01-12 15:01:24 -0800 (Wed, 12 Jan 2011) $ // // $LastChangedBy: torsten_at_home $ // */ #include "cmtkImageOperationCropThreshold.h" cmtk::UniformVolume::SmartPtr cmtk::ImageOperationCropThreshold::Apply( cmtk::UniformVolume::SmartPtr& volume ) { volume->AutoCrop( this->m_Threshold, true /*recrop*/ ); if ( this->m_WriteRegion ) { const DataGrid::RegionType& cropRegion = volume->CropRegion(); printf( "AutoCrop %d,%d,%d,%d,%d,%d\n", cropRegion.From()[0], cropRegion.From()[1], cropRegion.From()[2], cropRegion.To()[0], cropRegion.To()[1], cropRegion.To()[2] ); } if ( this->m_WriteXform ) { const UniformVolume::CoordinateRegionType& cropRegion = volume->GetHighResCropRegion(); fprintf( stdout, "! TYPEDSTREAM 1.1\n\naffine_xform {\n\txlate %lf %lf %lf\n\trotate 0 0 0\n\tscale 1 1 1\n\tshear 0 0 0\n\tcenter 0 0 0\n}\n", cropRegion.From()[0], cropRegion.From()[1], cropRegion.From()[2] ); } return cmtk::UniformVolume::SmartPtr( volume->GetCroppedVolume() ); } cmtk-3.3.1/libs/Base/cmtkImageOperationCropThreshold.h000066400000000000000000000053451276303427400227320ustar00rootroot00000000000000/* // // Copyright 2009-2011 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 2902 $ // // $LastChangedDate: 2011-02-24 12:12:46 -0800 (Thu, 24 Feb 2011) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkImageOperationCropThreshold_h_included_ #define __cmtkImageOperationCropThreshold_h_included_ #include #include namespace cmtk { /** \addtogroup Base */ //@{ /// Image operation: crop by threshold class ImageOperationCropThreshold /// Inherit from image operation base class. : public ImageOperation { public: /// Constructor. ImageOperationCropThreshold( const double threshold, const bool writeRegion = false, const bool writeXform = false ) : m_Threshold( threshold ), m_WriteRegion( writeRegion ), m_WriteXform( writeXform ) {} /// Apply this operation to an image in place. virtual cmtk::UniformVolume::SmartPtr Apply( cmtk::UniformVolume::SmartPtr& volume ); /// Create a new crop operation. static void New( const double threshold ) { ImageOperation::m_ImageOperationList.push_back( SmartPtr( new ImageOperationCropThreshold( threshold ) ) ); } /// Create a new crop operation with region output. static void NewWriteRegion( const double threshold ) { ImageOperation::m_ImageOperationList.push_back( SmartPtr( new ImageOperationCropThreshold( threshold, true, false ) ) ); } /// Create a new crop operation with transformation output. static void NewWriteXform( const double threshold ) { ImageOperation::m_ImageOperationList.push_back( SmartPtr( new ImageOperationCropThreshold( threshold, false, true ) ) ); } private: /// Cropping threshold. double m_Threshold; /// Flag for writing region to standard output. bool m_WriteRegion; /// Flag for writing transformation to standard output. bool m_WriteXform; }; //@} } // namespace cmtk #endif // #ifndef __cmtkImageOperationCropThreshold_h_included_ cmtk-3.3.1/libs/Base/cmtkImageOperationDistanceMap.cxx000066400000000000000000000033351276303427400227120ustar00rootroot00000000000000/* // // Copyright 2009-2012 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 3945 $ // // $LastChangedDate: 2012-02-29 11:33:37 -0800 (Wed, 29 Feb 2012) $ // // $LastChangedBy: torstenrohlfing $ // */ #include "cmtkImageOperationDistanceMap.h" cmtk::UniformVolume::SmartPtr cmtk::ImageOperationDistanceMap ::Apply( cmtk::UniformVolume::SmartPtr& volume ) { if ( this->m_SignedDistance ) { UniformVolume::SmartPtr iMap = DistanceMapType( *volume, DistanceMap::INSIDE ).Get(); UniformVolume::SmartPtr oMap = DistanceMapType( *volume ).Get(); const size_t nPixels = volume->GetNumberOfPixels(); #pragma omp parallel for for ( int n = 0; n < static_cast( nPixels ); ++n ) { Types::DataItem iValue = iMap->GetDataAt( n ); if ( iValue > 0 ) oMap->SetDataAt( -iValue, n ); } return oMap; } else { return DistanceMapType( *volume ).Get(); } } cmtk-3.3.1/libs/Base/cmtkImageOperationDistanceMap.h000066400000000000000000000046221276303427400223370ustar00rootroot00000000000000/* // // Copyright 2009-2012 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 3945 $ // // $LastChangedDate: 2012-02-29 11:33:37 -0800 (Wed, 29 Feb 2012) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkImageOperationDistanceMap_h_included_ #define __cmtkImageOperationDistanceMap_h_included_ #include #include #include #include #include namespace cmtk { /** \addtogroup Base */ //@{ /// Compute distance map. class ImageOperationDistanceMap /// Inherit generic image operation. : public ImageOperation { public: /// Distance map type. typedef UniformDistanceMap DistanceMapType; /// Constructor. ImageOperationDistanceMap( const bool signedDistance ) : m_SignedDistance( signedDistance ) {} /// Apply this operation to an image in place. virtual UniformVolume::SmartPtr Apply( UniformVolume::SmartPtr& volume ); /// Create new signed distance map operation. static void NewSigned() { ImageOperation::m_ImageOperationList.push_back( SmartPtr( new ImageOperationDistanceMap( true /*signedDistance*/) ) ); } /// Create new unsigned distance map operation. static void NewUnsigned() { ImageOperation::m_ImageOperationList.push_back( SmartPtr( new ImageOperationDistanceMap( false /*signedDistance*/) ) ); } private: /// Flag for signed (inside/outside) vs. unsigned (outside only) distance map. bool m_SignedDistance; }; //@} } // namespace cmtk #endif // #ifndef __cmtkImageOperationDistanceMap_h_included_ cmtk-3.3.1/libs/Base/cmtkImageOperationDownsample.cxx000066400000000000000000000042661276303427400226370ustar00rootroot00000000000000/* // // Copyright 2016 Google, Inc. // // Copyright 2009-2011 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 5398 $ // // $LastChangedDate: 2016-01-14 18:29:18 -0800 (Thu, 14 Jan 2016) $ // // $LastChangedBy: torstenrohlfing $ // */ #include "cmtkImageOperationDownsample.h" cmtk::UniformVolume::SmartPtr cmtk::ImageOperationDownsample ::Apply( cmtk::UniformVolume::SmartPtr& volume ) { const Types::GridIndexType factors[3] = { this->m_FactorX, this->m_FactorY, this->m_FactorZ }; if ( this->m_DoAverage ) return cmtk::UniformVolume::SmartPtr( volume->GetDownsampledAndAveraged( factors ) ); else return cmtk::UniformVolume::SmartPtr( volume->GetDownsampled( factors ) ); } void cmtk::ImageOperationDownsample ::NewGeneric( const bool doAverage, const char* arg ) { Types::GridIndexType factorsX = 1; Types::GridIndexType factorsY = 1; Types::GridIndexType factorsZ = 1; const size_t nFactors = sscanf( arg, "%5d,%5d,%5d", &factorsX, &factorsY, &factorsZ ); if ( nFactors == 1 ) { factorsZ = factorsY = factorsX; } else { if ( nFactors != 3 ) { cmtk::StdErr << "ERROR: downsampling factors must either be three integers, x,y,z, or a single integer\n"; exit( 1 ); } } ImageOperation::m_ImageOperationList.push_back( SmartPtr( new ImageOperationDownsample( doAverage, factorsX, factorsY, factorsZ ) ) ); } cmtk-3.3.1/libs/Base/cmtkImageOperationDownsample.h000066400000000000000000000050361276303427400222600ustar00rootroot00000000000000/* // // Copyright 2009-2011 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 3176 $ // // $LastChangedDate: 2011-04-26 11:02:44 -0700 (Tue, 26 Apr 2011) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkImageOperationDownsample_h_included_ #define __cmtkImageOperationDownsample_h_included_ #include #include namespace cmtk { /** \addtogroup Base */ //@{ /// Image operation: grid downsampling. class ImageOperationDownsample /// Inherit from image operation base class. : public ImageOperation { public: /// This class. typedef ImageOperationDownsample Self; /// Apply this operation to an image in place. virtual cmtk::UniformVolume::SmartPtr Apply( cmtk::UniformVolume::SmartPtr& volume ); /// Create a new selecting downsampler. static void NewSelect( const char* arg ) { Self::NewGeneric( false /*doAverage*/, arg ); } /// Create a new averaging downsampler. static void NewAverage( const char* arg ) { Self::NewGeneric( true /*doAverage*/, arg ); } private: /// Constructor: ImageOperationDownsample( const bool doAverage, const int factorX, const int factorY, const int factorZ ) : m_DoAverage( doAverage ), m_FactorX( factorX ), m_FactorY( factorY ), m_FactorZ( factorZ ) {} /// Create a new generic downsampler. static void NewGeneric( const bool doAverage, const char* arg ); /// Flag for averaging vs. selecting downsampling. bool m_DoAverage; /// Downsampling factor in X direction. int m_FactorX; /// Downsampling factor in Y direction. int m_FactorY; /// Downsampling factor in Z direction. int m_FactorZ; }; //@} } // namespace cmtk #endif // #ifndef __cmtkImageOperationDownsample_h_included_ cmtk-3.3.1/libs/Base/cmtkImageOperationErodeDilate.h000066400000000000000000000052161276303427400223300ustar00rootroot00000000000000/* // // Copyright 2009-2011 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4716 $ // // $LastChangedDate: 2013-05-12 16:39:59 -0700 (Sun, 12 May 2013) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkImageOperationErodeDilate_h_included_ #define __cmtkImageOperationErodaDilate_h_included_ #include #include #include namespace cmtk { /** \addtogroup Base */ //@{ /// Image operation: erode or dilate. class ImageOperationErodeDilate /// Inherit from image operation base class. : public ImageOperation { public: /// Constructor: ImageOperationErodeDilate( const int iterations ) : m_Iterations( iterations ) {} /// Apply this operation to an image in place. virtual cmtk::UniformVolume::SmartPtr Apply( cmtk::UniformVolume::SmartPtr& volume ) { if ( this->m_Iterations < 0 ) { cmtk::DataGridMorphologicalOperators ops( volume ); volume->SetData( ops.GetEroded( -this->m_Iterations ) ); } else { if ( this->m_Iterations > 0 ) { cmtk::DataGridMorphologicalOperators ops( volume ); volume->SetData( ops.GetDilated( this->m_Iterations ) ); } } return volume; } /// Create new dilation operation. static void NewDilate( const long int iterations ) { ImageOperation::m_ImageOperationList.push_back( SmartPtr( new ImageOperationErodeDilate( iterations ) ) ); } /// Create new erosion operation. static void NewErode( const long int iterations ) { ImageOperation::m_ImageOperationList.push_back( SmartPtr( new ImageOperationErodeDilate( -iterations ) ) ); } private: /// Number of iterations of erosion (if negative) or dilation (if positive). int m_Iterations; }; //@} } // namespace cmtk #endif // #ifndef __cmtkImageOperationErodeDilate_h_included_ cmtk-3.3.1/libs/Base/cmtkImageOperationErodeDilateDistance.h000066400000000000000000000063251276303427400240050ustar00rootroot00000000000000/* // // Copyright 2009-2011, 2014 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 5259 $ // // $LastChangedDate: 2014-03-27 16:28:47 -0700 (Thu, 27 Mar 2014) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkImageOperationErodeDilateDistance_h_included_ #define __cmtkImageOperationErodaDilateDistance_h_included_ #include #include #include namespace cmtk { /** \addtogroup Base */ //@{ /// Image operation: erode or dilate by distance (rather than pixels). class ImageOperationErodeDilateDistance /// Inherit from image operation base class. : public ImageOperation { public: /// Constructor: ImageOperationErodeDilateDistance( const double distance, const bool multiLabels = false ) : m_Distance( distance ), m_MultiLabels( multiLabels ) {} /// Apply this operation to an image in place. virtual cmtk::UniformVolume::SmartPtr Apply( cmtk::UniformVolume::SmartPtr& volume ) { if ( this->m_Distance < 0 ) { cmtk::UniformVolumeMorphologicalOperators ops( volume ); if ( this->m_MultiLabels ) volume->SetData( ops.GetErodedByDistanceMultiLabels( -this->m_Distance ) ); else volume->SetData( ops.GetErodedByDistance( -this->m_Distance ) ); } else { if ( this->m_Distance > 0 ) { cmtk::UniformVolumeMorphologicalOperators ops( volume ); volume->SetData( ops.GetDilatedByDistance( this->m_Distance ) ); } } return volume; } /// Create new dilation operation. static void NewDilate( const double distance ) { ImageOperation::m_ImageOperationList.push_back( SmartPtr( new ImageOperationErodeDilateDistance( distance ) ) ); } /// Create new erosion operation. static void NewErode( const double distance ) { ImageOperation::m_ImageOperationList.push_back( SmartPtr( new ImageOperationErodeDilateDistance( -distance ) ) ); } /// Create new erosion operation for multi-label; data. static void NewErodeMultiLabels( const double distance ) { ImageOperation::m_ImageOperationList.push_back( SmartPtr( new ImageOperationErodeDilateDistance( -distance, true ) ) ); } private: /// Distance of erosion (if negative) or dilation (if positive). double m_Distance; /// Operate on multi-label map. bool m_MultiLabels; }; //@} } // namespace cmtk #endif // #ifndef __cmtkImageOperationErodeDilateDistance_h_included_ cmtk-3.3.1/libs/Base/cmtkImageOperationFlip.h000066400000000000000000000043371276303427400210440ustar00rootroot00000000000000/* // // Copyright 2009-2011 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 2902 $ // // $LastChangedDate: 2011-02-24 12:12:46 -0800 (Thu, 24 Feb 2011) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkImageOperationFlip_h_included_ #define __cmtkImageOperationFlip_h_included_ namespace cmtk { /** \addtogroup Base */ //@{ /// Image operation: flip. class ImageOperationFlip /// Inherit from image operation base class. : public ImageOperation { public: /// Constructor: ImageOperationFlip( const int normalAxis ) : m_NormalAxis( normalAxis ) {} /// Apply this operation to an image in place. virtual cmtk::UniformVolume::SmartPtr Apply( cmtk::UniformVolume::SmartPtr& volume ) { volume->ApplyMirrorPlane( this->m_NormalAxis ); return volume; } /// Create x flip object. static void NewX() { ImageOperation::m_ImageOperationList.push_back( SmartPtr( new ImageOperationFlip( cmtk::AXIS_X ) ) ); } /// Create y flip object. static void NewY() { ImageOperation::m_ImageOperationList.push_back( SmartPtr( new ImageOperationFlip( cmtk::AXIS_Y ) ) ); } /// Create y flip object. static void NewZ() { ImageOperation::m_ImageOperationList.push_back( SmartPtr( new ImageOperationFlip( cmtk::AXIS_Z ) ) ); } private: /// The normal axis of the flip. int m_NormalAxis; }; //@} } // namespace cmtk #endif // #ifndef __cmtkImageOperationFlip_h_included_ cmtk-3.3.1/libs/Base/cmtkImageOperationGaussFilter.h000066400000000000000000000050171276303427400223760ustar00rootroot00000000000000/* // // Copyright 2009-2012 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4111 $ // // $LastChangedDate: 2012-03-30 14:30:21 -0700 (Fri, 30 Mar 2012) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkImageOperationGaussFilter_h_included_ #define __cmtkImageOperationGaussFilter_h_included_ #include #include #include namespace cmtk { /** \addtogroup Base */ //@{ /// Image operation: Gaussian smoothing filter. class ImageOperationGaussFilter /// Inherit from image operation base class. : public ImageOperation { public: /// Constructor: ImageOperationGaussFilter( const Units::GaussianSigma& sigma ) : m_Sigma( sigma ) {} /// Apply this operation to an image in place. virtual cmtk::UniformVolume::SmartPtr Apply( cmtk::UniformVolume::SmartPtr& volume ) { volume->SetData( UniformVolumeGaussianFilter( volume ).GetFiltered3D( this->m_Sigma, 0.001 /* kernel truncation approximation error threshold */ ) ); return volume; } /// Create a new filter based on sigma parameter. static void NewSigma( const double sigma ) { ImageOperation::m_ImageOperationList.push_back( SmartPtr( new ImageOperationGaussFilter( Units::GaussianSigma( sigma ) ) ) ); } /// Create a new filter based on full-width-at-half-maximum parameter. static void NewFWHM( const double fwhm ) { ImageOperation::m_ImageOperationList.push_back( SmartPtr( new ImageOperationGaussFilter( Units::GaussianFWHM( fwhm ) ) ) ); } private: /// Kernel with specified by coefficient sigma. Units::GaussianSigma m_Sigma; }; //@} } // namespace cmtk #endif // #ifndef __cmtkImageOperationGaussFilter_h_included_ cmtk-3.3.1/libs/Base/cmtkImageOperationHistogramEqualization.cxx000066400000000000000000000037151276303427400250470ustar00rootroot00000000000000/* // // Copyright 2009-2010 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 2398 $ // // $LastChangedDate: 2010-10-05 14:54:37 -0700 (Tue, 05 Oct 2010) $ // // $LastChangedBy: torstenrohlfing $ // */ #include "cmtkImageOperationHistogramEqualization.h" #include #include void cmtk::ImageOperationHistogramEqualization::New() { ImageOperation::m_ImageOperationList.push_back( SmartPtr( new ImageOperationHistogramEqualization( ImageOperationHistogramEqualization::DefaultNumberOfBins ) ) ); } void cmtk::ImageOperationHistogramEqualization::NewBins( const long int nBins ) { ImageOperation::m_ImageOperationList.push_back( SmartPtr( new ImageOperationHistogramEqualization( nBins ) ) ); } cmtk::UniformVolume::SmartPtr cmtk::ImageOperationHistogramEqualization::Apply( cmtk::UniformVolume::SmartPtr& volume ) { cmtk::TypedArray::SmartPtr volumeData = volume->GetData(); volumeData->ApplyFunctionObject( TypedArrayFunctionHistogramEqualization( *volumeData, this->m_NumberOfBins ) ); // volumeData->HistogramEqualization( this->m_NumberOfBins ); return volume; } cmtk-3.3.1/libs/Base/cmtkImageOperationHistogramEqualization.h000066400000000000000000000042731276303427400244740ustar00rootroot00000000000000/* // // Copyright 2010-2011 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 2902 $ // // $LastChangedDate: 2011-02-24 12:12:46 -0800 (Thu, 24 Feb 2011) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkImageOperationHistogramEqualization_h_included_ #define __cmtkImageOperationHistogramEqualization_h_included_ #include #include namespace cmtk { /** \addtogroup Base */ //@{ /// Image operation: histogram equalization with optional number of bins. class ImageOperationHistogramEqualization /// Inherit from image operation base class. : public ImageOperation { public: /// Default number of bins for histogram equalization. static const size_t DefaultNumberOfBins = 1024; /// Constructor. ImageOperationHistogramEqualization( const size_t nBins ) : m_NumberOfBins( nBins ) {} /// Apply this operation to an image in place. virtual cmtk::UniformVolume::SmartPtr Apply( cmtk::UniformVolume::SmartPtr& volume ); /// Create a histogram equalization operation with default number of bins. static void New(); /// Create a histogram equalization operation with user-supplied number of bins. static void NewBins( const long int nBins); private: /// Number of histogram bins. size_t m_NumberOfBins; }; //@} } // namespace cmtk #endif // #ifndef __cmtkImageOperationHistogramEqualization_h_included_ cmtk-3.3.1/libs/Base/cmtkImageOperationLaplaceFilter.h000066400000000000000000000037451276303427400226630ustar00rootroot00000000000000/* // // Copyright 2009-2012 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4110 $ // // $LastChangedDate: 2012-03-30 14:23:39 -0700 (Fri, 30 Mar 2012) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkImageOperationLaplaceFilter_h_included_ #define __cmtkImageOperationLaplaceFilter_h_included_ #include #include #include namespace cmtk { /** \addtogroup Base */ //@{ /// Image operation: Laplacian edge enhancement filter. class ImageOperationLaplaceFilter /// Inherit from image operation base class. : public ImageOperation { public: /// Constructor: ImageOperationLaplaceFilter() {} /// Apply this operation to an image in place. virtual cmtk::UniformVolume::SmartPtr Apply( cmtk::UniformVolume::SmartPtr& volume ) { volume->SetData( UniformVolumeLaplaceFilter( volume ).Get() ); return volume; } /// Create a new filter. static void New() { ImageOperation::m_ImageOperationList.push_back( SmartPtr( new ImageOperationLaplaceFilter ) ); } }; //@} } // namespace cmtk #endif // #ifndef __cmtkImageOperationLaplaceFilter_h_included_ cmtk-3.3.1/libs/Base/cmtkImageOperationMapValues.cxx000066400000000000000000000064321276303427400224200ustar00rootroot00000000000000/* // // Copyright 2010 Torsten Rohlfing // // Copyright 2011-2013 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4810 $ // // $LastChangedDate: 2013-09-09 12:56:48 -0700 (Mon, 09 Sep 2013) $ // // $LastChangedBy: torstenrohlfing $ // */ #include "cmtkImageOperationMapValues.h" #include cmtk::ImageOperationMapValues::ImageOperationMapValues( const char* mapping, const bool exclusive ) : m_Exclusive( exclusive ) { const char* rptr = mapping; while ( rptr ) { const char* comma = strchr( rptr, ',' ); const char* plus = strchr( rptr, '+' ); double value; std::vector fromValues; while ( comma && ( !plus || comma < plus) ) { if ( 1 == sscanf( rptr, "%20lf", &value ) ) fromValues.push_back( value ); rptr = comma+1; comma = strchr( rptr, ',' ); } double newValue; if ( 2 == sscanf( rptr, "%20lf:%20lf", &value, &newValue ) ) { fromValues.push_back( value ); for ( size_t i = 0; i < fromValues.size(); ++i ) { this->m_Mapping[fromValues[i]] = newValue; } } else { if ( 1 == sscanf( rptr, "%20lf", &value ) ) { fromValues.push_back( value ); for ( size_t i = 0; i < fromValues.size(); ++i ) { this->m_Mapping[fromValues[i]] = std::numeric_limits::signaling_NaN(); } } else { StdErr << "ERROR: could not parse mapping\n\t" << mapping << "\nwhich is supposed to be VAL0[,VAL1,...][:NEWVAL]\n"; } } // if more rules are following, separated by "+", then move to next one, otherwise terminate loop. if ( plus ) { rptr = plus + 1; } else rptr = NULL; } } cmtk::UniformVolume::SmartPtr cmtk::ImageOperationMapValues::Apply( cmtk::UniformVolume::SmartPtr& volume ) { TypedArray& volumeData = *(volume->GetData()); #pragma omp parallel for for ( int i = 0; i < static_cast( volumeData.GetDataSize() ); ++i ) { Types::DataItem value = 0; if ( volumeData.Get( value, i ) ) { std::map::const_iterator it = this->m_Mapping.find( value ); if ( it != this->m_Mapping.end() ) { const Types::DataItem newValue = it->second; if ( finite( newValue ) ) volumeData.Set( newValue, i ); else volumeData.SetPaddingAt( i ); } else { // value not explicitly mapped; see if we're in "exclusive" mode. if ( this->m_Exclusive ) { volumeData.SetPaddingAt( i ); } } } } return volume; } cmtk-3.3.1/libs/Base/cmtkImageOperationMapValues.h000066400000000000000000000063651276303427400220520ustar00rootroot00000000000000/* // // Copyright 2010 Torsten Rohlfing // // Copyright 2011 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 2902 $ // // $LastChangedDate: 2011-02-24 12:12:46 -0800 (Thu, 24 Feb 2011) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkImageOperationMapValues_h_included_ #define __cmtkImageOperationMapValues_h_included_ #include #include #ifdef HAVE_IEEEFP_H # include #endif #include namespace cmtk { /** \addtogroup Base */ //@{ /// Image operation: apply mapping function to replace image pixel values. class ImageOperationMapValues : public ImageOperation { public: /// This class. typedef ImageOperationMapValues Self; /// Superclass. typedef ImageOperation Superclass; /// Constructor. ImageOperationMapValues( const char* mapping /*!< Mapping function defined as 'VAL0[,VAL1,...][:NEWVAL]' to map values VAL0, VAL1, etc. to new value NEWVAL. If NEWVAL is not given, values are set to padding. */, const bool exclusive = false /*!< Exclusive mapping flag: if set, all pixels not explicitly mapped will be set to padding; if not set, such pixels will keep their original values. */ ); /// Apply this operation to an image in place. virtual cmtk::UniformVolume::SmartPtr Apply( cmtk::UniformVolume::SmartPtr& volume ); /// Create new operation to replace padded pixels. static void New( const char* mapping /*!< Mapping function defined as 'VAL0[,VAL1,...][:NEWVAL]' to map values VAL0, VAL1, etc. to new value NEWVAL. If NEWVAL is not given, values are set to padding.*/ ) { ImageOperation::m_ImageOperationList.push_back( SmartPtr( new Self( mapping ) ) ); } /// Create new operation to replace padded pixels and set all unmapped pixels to padding. static void NewExclusive( const char* mapping /*!< Mapping function defined as 'VAL0[,VAL1,...][:NEWVAL]' to map values VAL0, VAL1, etc. to new value NEWVAL. If NEWVAL is not given, values are set to padding.*/ ) { ImageOperation::m_ImageOperationList.push_back( SmartPtr( new Self( mapping, true /*exclusive*/ ) ) ); } private: /// Mapping. std::map m_Mapping; /** Exclusive mapping flag. * If set, all pixels not explicitly mapped will be set to padding; if not set, such pixels will keep their original values. */ bool m_Exclusive; }; //@} } // namespace cmtk #endif // #ifndef __cmtkImageOperationMapValues_h_included_ cmtk-3.3.1/libs/Base/cmtkImageOperationOtsuThreshold.cxx000066400000000000000000000032201276303427400233220ustar00rootroot00000000000000/* // // Copyright 2009-2012, 2014 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 5268 $ // // $LastChangedDate: 2014-03-28 11:41:16 -0700 (Fri, 28 Mar 2014) $ // // $LastChangedBy: torstenrohlfing $ // */ #include "cmtkImageOperationOtsuThreshold.h" #include #include cmtk::UniformVolume::SmartPtr cmtk::ImageOperationOtsuThreshold::Apply( cmtk::UniformVolume::SmartPtr& volume ) { cmtk::TypedArray& volumeData = *(volume->GetData()); const Types::DataItem threshold = HistogramOtsuThreshold< Histogram >( *(volumeData.GetHistogram( this->m_NumberOfBins )) ).Get(); DebugOutput( 5 ) << "INFO: Otsu binarization threshold = " << threshold << "\n"; volumeData.Binarize( threshold ); volumeData.SetDataClass( DATACLASS_LABEL ); return volume; } cmtk-3.3.1/libs/Base/cmtkImageOperationOtsuThreshold.h000066400000000000000000000045521276303427400227600ustar00rootroot00000000000000/* // // Copyright 2009-2012 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4156 $ // // $LastChangedDate: 2012-04-10 10:13:18 -0700 (Tue, 10 Apr 2012) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkImageOperationOtsuThreshold_h_included_ #define __cmtkImageOperationOtsuThreshold_h_included_ #include #include namespace cmtk { /** \addtogroup Base */ //@{ /// Image operation: Otsu threshold binarization class ImageOperationOtsuThreshold /// Inherit from image operation base class. : public ImageOperation { public: /// Constructor. ImageOperationOtsuThreshold( const size_t nBins = 1024 /*!< Number of histogram bins for threshold computation.*/ ) : m_NumberOfBins( nBins ) {} /// Apply this operation to an image in place. virtual cmtk::UniformVolume::SmartPtr Apply( cmtk::UniformVolume::SmartPtr& volume ); /// Create a new thresholding operation. static void New() { ImageOperation::m_ImageOperationList.push_back( SmartPtr( new ImageOperationOtsuThreshold() ) ); } /// Create a new thresholding operation with explicit number of histogram bins. static void NewBins( const long int nBins = 1024 /*!< Number of histogram bins for threshold computation.*/ ) { ImageOperation::m_ImageOperationList.push_back( SmartPtr( new ImageOperationOtsuThreshold( nBins ) ) ); } private: /// Number of histogram bins for threshold computation. size_t m_NumberOfBins; }; //@} } // namespace cmtk #endif // #ifndef __cmtkImageOperationOtsuThreshold_h_included_ cmtk-3.3.1/libs/Base/cmtkImageOperationPruneHistogram.cxx000066400000000000000000000024361276303427400234720ustar00rootroot00000000000000/* // // Copyright 2009-2011 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4721 $ // // $LastChangedDate: 2013-05-12 16:49:38 -0700 (Sun, 12 May 2013) $ // // $LastChangedBy: torsten_at_home $ // */ #include "cmtkImageOperationPruneHistogram.h" cmtk::UniformVolume::SmartPtr cmtk::ImageOperationPruneHistogram::Apply( cmtk::UniformVolume::SmartPtr& volume ) { volume->GetData()->PruneHistogram( this->m_High, this->m_Low, this->m_NumberOfBins ); return volume; } cmtk-3.3.1/libs/Base/cmtkImageOperationPruneHistogram.h000066400000000000000000000052541276303427400231200ustar00rootroot00000000000000/* // // Copyright 2009-2011 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4721 $ // // $LastChangedDate: 2013-05-12 16:49:38 -0700 (Sun, 12 May 2013) $ // // $LastChangedBy: torsten_at_home $ // */ #ifndef __cmtkImageOperationPruneHistogram_h_included_ #define __cmtkImageOperationPruneHistogram_h_included_ #include #include namespace cmtk { /** \addtogroup Base */ //@{ /// Image operation: thresholding class ImageOperationPruneHistogram /// Inherit from image operation base class. : public ImageOperation { public: /// Constructor. ImageOperationPruneHistogram( const long int numberOfBins, const bool high = false, const bool low = false ) : m_NumberOfBins( numberOfBins ), m_High( high ), m_Low( low ) {} /// Apply this operation to an image in place. virtual cmtk::UniformVolume::SmartPtr Apply( cmtk::UniformVolume::SmartPtr& volume ); /// Create an upper plus lower thresholding operation. static void New( const long int nBins ) { ImageOperation::m_ImageOperationList.push_back( SmartPtr( new ImageOperationPruneHistogram( nBins, true, true ) ) ); } /// Create an upper thresholding operation. static void NewHigh( const long int nBins ) { ImageOperation::m_ImageOperationList.push_back( SmartPtr( new ImageOperationPruneHistogram( nBins, true, false ) ) ); } /// Create a lower thresholding operation. static void NewLow( const long int nBins ) { ImageOperation::m_ImageOperationList.push_back( SmartPtr( new ImageOperationPruneHistogram( nBins, false, true ) ) ); } private: /// PruneHistogram. long int m_NumberOfBins; /// Flag for using the threshold as upper threshold. bool m_High; /// Flag for using the threshold as lower threshold. bool m_Low; }; //@} } // namespace cmtk #endif // #ifndef __cmtkImageOperationPruneHistogram_h_included_ cmtk-3.3.1/libs/Base/cmtkImageOperationRegionFilter.cxx000066400000000000000000000061551276303427400231160ustar00rootroot00000000000000/* // // Copyright 2009-2011 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 3646 $ // // $LastChangedDate: 2011-12-22 10:10:55 -0800 (Thu, 22 Dec 2011) $ // // $LastChangedBy: torstenrohlfing $ // */ #include "cmtkImageOperationRegionFilter.h" void cmtk::ImageOperationRegionFilter ::NewGeneric( const Self::OperatorEnum op, const char* arg ) { int radiusX = 1; int radiusY = 1; int radiusZ = 1; const size_t nRadii = sscanf( arg, "%10d,%10d,%10d", &radiusX, &radiusY, &radiusZ ); if ( nRadii == 1 ) { radiusZ = radiusY = radiusX; } else { if ( nRadii != 3 ) { cmtk::StdErr << "ERROR: downsampling radii must either be three integers, x,y,z, or a single integer\n"; exit( 1 ); } } ImageOperation::m_ImageOperationList.push_back( SmartPtr( new ImageOperationRegionFilter( op, radiusX, radiusY, radiusZ ) ) ); } cmtk::UniformVolume::SmartPtr cmtk::ImageOperationRegionFilter ::Apply( cmtk::UniformVolume::SmartPtr& volume ) { switch ( this->m_Operator ) { case MEDIAN: volume->SetData( DataGridFilter( volume ).GetDataMedianFiltered( this->m_RadiusX, this->m_RadiusY, this->m_RadiusZ ) ); break; case MEAN: volume->SetData( DataGridFilter( volume ).RegionMeanFilter( this->m_RadiusX, this->m_RadiusY, this->m_RadiusZ ) ); break; case FAST_MEAN: volume->SetData( DataGridFilter( volume ).FastRegionMeanFilter( this->m_RadiusX, this->m_RadiusY, this->m_RadiusZ ) ); break; case VARIANCE: volume->SetData( DataGridFilter( volume ).RegionVarianceFilter( this->m_RadiusX, this->m_RadiusY, this->m_RadiusZ ) ); break; case FAST_VARIANCE: volume->SetData( DataGridFilter( volume ).FastRegionVarianceFilter( this->m_RadiusX, this->m_RadiusY, this->m_RadiusZ ) ); break; case THIRD_MOMENT: volume->SetData( DataGridFilter( volume ).RegionThirdMomentFilter( this->m_RadiusX, this->m_RadiusY, this->m_RadiusZ ) ); break; case STANDARD_DEVIATION: volume->SetData( DataGridFilter( volume ).RegionStandardDeviationFilter( this->m_RadiusX, this->m_RadiusY, this->m_RadiusZ ) ); break; case SMOOTHNESS: volume->SetData( DataGridFilter( volume ).RegionSmoothnessFilter( this->m_RadiusX, this->m_RadiusY, this->m_RadiusZ ) ); break; } return volume; } cmtk-3.3.1/libs/Base/cmtkImageOperationRegionFilter.h000066400000000000000000000105071276303427400225370ustar00rootroot00000000000000/* // // Copyright 2009-2011 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 3342 $ // // $LastChangedDate: 2011-08-09 13:10:49 -0700 (Tue, 09 Aug 2011) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkImageOperationRegionFilter_h_included_ #define __cmtkImageOperationRegionFilter_h_included_ #include #include #include namespace cmtk { /** \addtogroup Base */ //@{ /// Image operation: region filtering. class ImageOperationRegionFilter /// Inherit from image operation base class. : public ImageOperation { public: /// This class. typedef ImageOperationRegionFilter Self; /// Types of filters supported by this class. typedef enum { MEDIAN, MEAN, FAST_MEAN, VARIANCE, FAST_VARIANCE, THIRD_MOMENT, STANDARD_DEVIATION, SMOOTHNESS } OperatorEnum; /// Constructor: ImageOperationRegionFilter( const Self::OperatorEnum op, const int radiusX, const int radiusY, const int radiusZ ) : m_Operator( op ), m_RadiusX( radiusX ), m_RadiusY( radiusY ), m_RadiusZ( radiusZ ) {} /// Apply this operation to an image in place. virtual cmtk::UniformVolume::SmartPtr Apply( cmtk::UniformVolume::SmartPtr& volume ); /// Create a new region median filter operation. static void NewMedian( const char* arg /*!< Region size argument: either "XYZ" or "X,Y,Z" */ ) { Self::NewGeneric( Self::MEDIAN, arg ); } /// Create a new region mean filter operation. static void NewMean( const char* arg /*!< Region size argument: either "XYZ" or "X,Y,Z" */ ) { Self::NewGeneric( Self::MEAN, arg ); } /// Create a new fast region mean filter operation. static void NewFastMean( const char* arg /*!< Region size argument: either "XYZ" or "X,Y,Z" */ ) { Self::NewGeneric( Self::FAST_MEAN, arg ); } /// Create a new region variance filter operation. static void NewVariance( const char* arg /*!< Region size argument: either "XYZ" or "X,Y,Z" */ ) { Self::NewGeneric( Self::VARIANCE, arg ); } /// Create a new fast region variance filter operation. static void NewFastVariance( const char* arg /*!< Region size argument: either "XYZ" or "X,Y,Z" */ ) { Self::NewGeneric( Self::FAST_VARIANCE, arg ); } /// Create a new region third moment filter operation. static void NewThirdMoment( const char* arg /*!< Region size argument: either "XYZ" or "X,Y,Z" */ ) { Self::NewGeneric( Self::THIRD_MOMENT, arg ); } /// Create a new region standard deviation filter operation. static void NewStandardDeviation( const char* arg /*!< Region size argument: either "XYZ" or "X,Y,Z" */ ) { Self::NewGeneric( Self::STANDARD_DEVIATION, arg ); } /// Create a new region smoothness filter operation. static void NewSmoothness( const char* arg /*!< Region size argument: either "XYZ" or "X,Y,Z" */ ) { Self::NewGeneric( Self::SMOOTHNESS, arg ); } private: /// Parse region size argument and create new filter object. static void NewGeneric( const Self::OperatorEnum op /*!< The operation to perform by the new object.*/, const char* arg /*!< Region size argument: either "XYZ" or "X,Y,Z" */ ); /// The operator this object will apply to its input. Self::OperatorEnum m_Operator; /// Downsampling radius in X direction. int m_RadiusX; /// Downsampling radius in Y direction. int m_RadiusY; /// Downsampling radius in Z direction. int m_RadiusZ; }; //@} } // namespace cmtk #endif // #ifndef __cmtkImageOperationRegionFilter_h_included_ cmtk-3.3.1/libs/Base/cmtkImageOperationReplace.h000066400000000000000000000065121276303427400215220ustar00rootroot00000000000000/* // // Copyright 2010 Torsten Rohlfing // // Copyright 2011 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 3097 $ // // $LastChangedDate: 2011-04-06 13:07:22 -0700 (Wed, 06 Apr 2011) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkImageOperationReplace_h_included_ #define __cmtkImageOperationReplace_h_included_ #include #include #ifdef HAVE_IEEEFP_H # include #endif namespace cmtk { /** \addtogroup Base */ //@{ /// Image operation: replace image pixel values. class ImageOperationReplace /// Inherit from image operation base class. : public ImageOperation { public: /// This class. typedef ImageOperationReplace Self; /// Superclass. typedef ImageOperation Superclass; /// Operation mode. typedef enum { /// Replace padded pixels. REPLACE_PADDING, /// Replace Inf and NaN pixels. REPLACE_INF_NAN } Mode; /// Constructor. ImageOperationReplace( const Self::Mode mode /*!< Operation mode.*/, const Types::DataItem value /*!< Replacement data value.*/ ) : m_Mode( mode ), m_ReplacementValue( value ) {} /// Apply this operation to an image in place. virtual cmtk::UniformVolume::SmartPtr Apply( cmtk::UniformVolume::SmartPtr& volume ) { TypedArray& volumeData = *(volume->GetData()); switch ( this->m_Mode ) { case Self::REPLACE_PADDING: volumeData.ReplacePaddingData( this->m_ReplacementValue ); break; case Self::REPLACE_INF_NAN: #pragma omp parallel for for ( int i = 0; i < static_cast( volumeData.GetDataSize() ); ++i ) { cmtk::Types::DataItem value = 0; if ( volumeData.Get( value, i ) ) { if ( !finite( value ) ) { volumeData.Set( this->m_ReplacementValue, i ); } } } break; } return volume; } /// Create new operation to replace padded pixels. static void NewReplacePadding( const double value /*!< Replacement value. */ ) { ImageOperation::m_ImageOperationList.push_back( SmartPtr( new Self( Self::REPLACE_PADDING, value ) ) ); } /// Create new operation to replace pixels with Inf or NaN values. static void NewReplaceInfNaN( const double value /*!< Replacement value. */ ) { ImageOperation::m_ImageOperationList.push_back( SmartPtr( new Self( Self::REPLACE_INF_NAN, value ) ) ); } private: /// Operation mode. Self::Mode m_Mode; /// Padding value. Types::DataItem m_ReplacementValue; }; //@} } // namespace cmtk #endif // #ifndef __cmtkImageOperationReplace_h_included_ cmtk-3.3.1/libs/Base/cmtkImageOperationResampleIsotropic.cxx000066400000000000000000000035031276303427400241630ustar00rootroot00000000000000/* // // Copyright 2009-2011, 2013 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 5242 $ // // $LastChangedDate: 2014-03-18 15:34:46 -0700 (Tue, 18 Mar 2014) $ // // $LastChangedBy: torstenrohlfing $ // */ #include "cmtkImageOperationResampleIsotropic.h" cmtk::UniformVolume::SmartPtr cmtk::ImageOperationResampleIsotropic ::Apply( cmtk::UniformVolume::SmartPtr& volume ) { if ( this->m_Exact ) return cmtk::UniformVolume::SmartPtr( volume->GetResampledExact( this->m_Resolution ) ); else return cmtk::UniformVolume::SmartPtr( volume->GetResampled( this->m_Resolution, true /*allowUpsampling*/ ) ); } void cmtk::ImageOperationResampleIsotropic ::New( const double resolution ) { ImageOperation::m_ImageOperationList.push_back( SmartPtr( new ImageOperationResampleIsotropic( resolution ) ) ); } void cmtk::ImageOperationResampleIsotropic ::NewExact( const double resolution ) { ImageOperation::m_ImageOperationList.push_back( SmartPtr( new ImageOperationResampleIsotropic( resolution, true ) ) ); } cmtk-3.3.1/libs/Base/cmtkImageOperationResampleIsotropic.h000066400000000000000000000042411276303427400236100ustar00rootroot00000000000000/* // // Copyright 2009-2011, 2013-2014 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 5242 $ // // $LastChangedDate: 2014-03-18 15:34:46 -0700 (Tue, 18 Mar 2014) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkImageOperationResampleIsotropic_h_included_ #define __cmtkImageOperationResampleIsotropic_h_included_ #include #include namespace cmtk { /** \addtogroup Base */ //@{ /// Image operation: anisotropic resampling class ImageOperationResampleIsotropic /// Inherit from image operation base class. : public ImageOperation { public: /// This class. typedef ImageOperationResampleIsotropic Self; /// Apply this operation to an image in place. virtual cmtk::UniformVolume::SmartPtr Apply( cmtk::UniformVolume::SmartPtr& volume ); /// Create a new resampler. static void New( const double resolution ); /// Create a new resampler. static void NewExact( const double resolution ); private: /// Constructor: ImageOperationResampleIsotropic( const double resolution, const bool exact = false ) : m_Resolution( resolution ), m_Exact( exact ) {} /// Anisotropic resampling resolution double m_Resolution; /// Flag for exact vs. approximate resampling. bool m_Exact; }; //@} } // namespace cmtk #endif // #ifndef __cmtkImageOperationResampleIsotropic_h_included_ cmtk-3.3.1/libs/Base/cmtkImageOperationRevert.cxx000066400000000000000000000026211276303427400217660ustar00rootroot00000000000000/* // // Copyright 2009-2010 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 2022 $ // // $LastChangedDate: 2010-07-21 15:26:03 -0700 (Wed, 21 Jul 2010) $ // // $LastChangedBy: torstenrohlfing $ // */ #include "cmtkImageOperationRevert.h" cmtk::UniformVolume::SmartPtr cmtk::ImageOperationRevert::Apply( cmtk::UniformVolume::SmartPtr& volume ) { const size_t nPixels = volume->GetNumberOfPixels(); for ( size_t n = 0; n < nPixels; ++n ) { if ( volume->GetDataAt( n ) ) volume->SetDataAt( 0, n ); else volume->SetDataAt( 1, n ); } return volume; } cmtk-3.3.1/libs/Base/cmtkImageOperationRevert.h000066400000000000000000000034621276303427400214170ustar00rootroot00000000000000/* // // Copyright 2009-2011 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 2902 $ // // $LastChangedDate: 2011-02-24 12:12:46 -0800 (Thu, 24 Feb 2011) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkImageOperationRevert_h_included_ #define __cmtkImageOperationRevert_h_included_ #include #include namespace cmtk { /** \addtogroup Base */ //@{ /// Image operation: revert binary mask. class ImageOperationRevert /// Inherit from image operation base class. : public ImageOperation { public: /// Constructor. ImageOperationRevert() {} /// Apply this operation to an image in place. virtual cmtk::UniformVolume::SmartPtr Apply( cmtk::UniformVolume::SmartPtr& volume ); /// Create a new lower reverting operation. static void New() { ImageOperation::m_ImageOperationList.push_back( SmartPtr( new ImageOperationRevert() ) ); } }; //@} } // namespace cmtk #endif // #ifndef __cmtkImageOperationRevert_h_included_ cmtk-3.3.1/libs/Base/cmtkImageOperationScaleToRange.cxx000066400000000000000000000034441276303427400230320ustar00rootroot00000000000000/* // // Copyright 2009-2011 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 3646 $ // // $LastChangedDate: 2011-12-22 10:10:55 -0800 (Thu, 22 Dec 2011) $ // // $LastChangedBy: torstenrohlfing $ // */ #include "cmtkImageOperationScaleToRange.h" #include void cmtk::ImageOperationScaleToRange::New( const char* range ) { double rangeFrom, rangeTo; if ( 2 == sscanf( range, "%20lf:%20lf", &rangeFrom, &rangeTo ) ) { ImageOperation::m_ImageOperationList.push_back( SmartPtr( new ImageOperationScaleToRange( Types::DataItemRange( rangeFrom, rangeTo ) ) ) ); } else { throw CommandLine::Exception( "Range must be given as two floating point numbers separated by ':', e.g., '0.5:1.0'" ); } } cmtk::UniformVolume::SmartPtr cmtk::ImageOperationScaleToRange::Apply( cmtk::UniformVolume::SmartPtr& volume ) { cmtk::TypedArray::SmartPtr volumeData = volume->GetData(); volumeData->RescaleToRange( this->m_ToRange ); return volume; } cmtk-3.3.1/libs/Base/cmtkImageOperationScaleToRange.h000066400000000000000000000036411276303427400224560ustar00rootroot00000000000000/* // // Copyright 2010-2011 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 2902 $ // // $LastChangedDate: 2011-02-24 12:12:46 -0800 (Thu, 24 Feb 2011) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkImageOperationScaleToRange_h_included_ #define __cmtkImageOperationScaleToRange_h_included_ #include #include namespace cmtk { /** \addtogroup Base */ //@{ /// Image operation: scale image values to given range. class ImageOperationScaleToRange /// Inherit from image operation base class. : public ImageOperation { public: /// Constructor. ImageOperationScaleToRange( const Types::DataItemRange& toRange ) : m_ToRange( toRange ) {} /// Apply this operation to an image in place. virtual cmtk::UniformVolume::SmartPtr Apply( cmtk::UniformVolume::SmartPtr& volume ); /// Create a new lower thresholding operation. static void New( const char* range ); private: /// Start of range we're scaling to. Types::DataItemRange m_ToRange; }; //@} } // namespace cmtk #endif // #ifndef __cmtkImageOperationScaleToRange_h_included_ cmtk-3.3.1/libs/Base/cmtkImageOperationSetDataClass.h000066400000000000000000000046561276303427400224710ustar00rootroot00000000000000/* // // Copyright 2010 Torsten Rohlfing // // Copyright 2011, 2014 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 2902 $ // // $LastChangedDate: 2011-02-24 12:12:46 -0800 (Thu, 24 Feb 2011) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkImageOperationSetDataClass_h_included_ #define __cmtkImageOperationSetDataClass_h_included_ #include #include #include namespace cmtk { /** \addtogroup Base */ //@{ /// Image operation: set padding flag and value. class ImageOperationSetDataClass /// Inherit from image operation base class. : public ImageOperation { public: /// Constructor. ImageOperationSetDataClass( const DataClass dataClass ) : m_DataClass( dataClass ) {} /// Apply this operation to an image in place. virtual cmtk::UniformVolume::SmartPtr Apply( cmtk::UniformVolume::SmartPtr& volume ) { volume->GetData()->SetDataClass( this->m_DataClass ); return volume; } /// Create new operation to set class to labels. static void NewLabels() { ImageOperation::m_ImageOperationList.push_back( SmartPtr( new ImageOperationSetDataClass( DATACLASS_LABEL ) ) ); } /// Create new operation to set class to grey values. static void NewGrey() { ImageOperation::m_ImageOperationList.push_back( SmartPtr( new ImageOperationSetDataClass( DATACLASS_GREY ) ) ); } private: /// Set flag: if this is set, padding is activated, otherwise it is deactivated (and m_DataClassValue is ignored). DataClass m_DataClass; }; //@} } // namespace cmtk #endif // #ifndef __cmtkImageOperationSetDataClass_h_included_ cmtk-3.3.1/libs/Base/cmtkImageOperationSetPadding.h000066400000000000000000000051151276303427400221670ustar00rootroot00000000000000/* // // Copyright 2010 Torsten Rohlfing // // Copyright 2011 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 5234 $ // // $LastChangedDate: 2014-03-18 11:18:48 -0700 (Tue, 18 Mar 2014) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkImageOperationSetPadding_h_included_ #define __cmtkImageOperationSetPadding_h_included_ #include #include namespace cmtk { /** \addtogroup Base */ //@{ /// Image operation: set padding flag and value. class ImageOperationSetPadding /// Inherit from image operation base class. : public ImageOperation { public: /// Constructor. ImageOperationSetPadding( const bool flag, const double value = 0 ) : m_PaddingFlag( flag ), m_PaddingValue( value ) {} /// Apply this operation to an image in place. virtual cmtk::UniformVolume::SmartPtr Apply( cmtk::UniformVolume::SmartPtr& volume ) { if ( this->m_PaddingFlag ) { volume->GetData()->SetPaddingValue( this->m_PaddingValue ); } else { volume->GetData()->ClearPaddingFlag(); } return volume; } /// Create new operation to set padding value. static void New( const double value ) { ImageOperation::m_ImageOperationList.push_back( SmartPtr( new ImageOperationSetPadding( true, value ) ) ); } /// Create new operation to reset padding flag. static void NewUnset() { ImageOperation::m_ImageOperationList.push_back( SmartPtr( new ImageOperationSetPadding( false ) ) ); } private: /// Set flag: if this is set, padding is activated, otherwise it is deactivated (and m_PaddingValue is ignored). bool m_PaddingFlag; /// Padding value. Types::DataItem m_PaddingValue; }; //@} } // namespace cmtk #endif // #ifndef __cmtkImageOperationSetPadding_h_included_ cmtk-3.3.1/libs/Base/cmtkImageOperationThreshold.cxx000066400000000000000000000032471276303427400224600ustar00rootroot00000000000000/* // // Copyright 2009-2010 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 2022 $ // // $LastChangedDate: 2010-07-21 15:26:03 -0700 (Wed, 21 Jul 2010) $ // // $LastChangedBy: torstenrohlfing $ // */ #include "cmtkImageOperationThreshold.h" cmtk::UniformVolume::SmartPtr cmtk::ImageOperationThreshold::Apply( cmtk::UniformVolume::SmartPtr& volume ) { cmtk::TypedArray::SmartPtr volumeData = volume->GetData(); if ( this->m_Binarize ) { volumeData->Binarize( this->m_Threshold ); } else { cmtk::Types::DataItemRange range = volumeData->GetRange(); if ( this->m_Above ) range.m_UpperBound = this->m_Threshold; else range.m_LowerBound = this->m_Threshold; if ( this->m_ToPadding ) volumeData->ThresholdToPadding( range ); else volumeData->Threshold( range ); } return volume; } cmtk-3.3.1/libs/Base/cmtkImageOperationThreshold.h000066400000000000000000000065471276303427400221130ustar00rootroot00000000000000/* // // Copyright 2009-2011 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 2902 $ // // $LastChangedDate: 2011-02-24 12:12:46 -0800 (Thu, 24 Feb 2011) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkImageOperationThreshold_h_included_ #define __cmtkImageOperationThreshold_h_included_ #include #include namespace cmtk { /** \addtogroup Base */ //@{ /// Image operation: thresholding class ImageOperationThreshold /// Inherit from image operation base class. : public ImageOperation { public: /// Constructor. ImageOperationThreshold( const double threshold, const bool above = false, const bool toPadding = false, const bool binarize = false ) : m_Threshold( threshold ), m_Above( above ), m_ToPadding( toPadding ), m_Binarize( binarize ) {} /// Apply this operation to an image in place. virtual cmtk::UniformVolume::SmartPtr Apply( cmtk::UniformVolume::SmartPtr& volume ); /// Create a new lower thresholding operation. static void NewBelow( const double threshold ) { ImageOperation::m_ImageOperationList.push_back( SmartPtr( new ImageOperationThreshold( threshold, false, false, false ) ) ); } /// Create a new upper thresholding operation. static void NewAbove( const double threshold ) { ImageOperation::m_ImageOperationList.push_back( SmartPtr( new ImageOperationThreshold( threshold, true, false, false ) ) ); } /// Create a new lower thresholding operation to padding. static void NewBelowToPadding( const double threshold ) { ImageOperation::m_ImageOperationList.push_back( SmartPtr( new ImageOperationThreshold( threshold, false, true, false ) ) ); } /// Create a new upper thresholding operation to padding. static void NewAboveToPadding( const double threshold ) { ImageOperation::m_ImageOperationList.push_back( SmartPtr( new ImageOperationThreshold( threshold, true, true, false ) ) ); } /// Create a new binarization thresholding operation. static void NewBinarize( const double threshold ) { ImageOperation::m_ImageOperationList.push_back( SmartPtr( new ImageOperationThreshold( threshold, false, false, true ) ) ); } private: /// Threshold. double m_Threshold; /// Flag for using the threshold as an upper, rather than lower, threshold. bool m_Above; /// Flag for setting values beyond threshold to padding, rather than to threshold value. bool m_ToPadding; /// Flag for binarization. bool m_Binarize; }; //@} } // namespace cmtk #endif // #ifndef __cmtkImageOperationThreshold_h_included_ cmtk-3.3.1/libs/Base/cmtkImageTemplate.h000066400000000000000000000063721276303427400200450ustar00rootroot00000000000000/* // // Copyright 1997-2010 Torsten Rohlfing // // Copyright 2004-2012 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4441 $ // // $LastChangedDate: 2012-06-18 11:02:46 -0700 (Mon, 18 Jun 2012) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkImageTemplate_h_included_ #define __cmtkImageTemplate_h_included_ #include #include #include #include #include namespace cmtk { /** \addtogroup Base */ //@{ /** Uniform volume template. * This class is a templated generalization of the UniformVolume class. Here, the type of pixel data is determined by template instantiation. */ template class ImageTemplate : /// Inherit from generic Volume class. public UniformVolume { public: /// Pixel data type. typedef TPixelType PixelType; /// This class. typedef ImageTemplate Self; /// Superclass. typedef UniformVolume Superclass; /// Smart pointer to ImageTemplate. typedef SmartPointer SmartPtr; /// Smart pointer to const ImageTemplate. typedef SmartConstPointer SmartConstPtr; /// Region type. typedef Superclass::CoordinateRegionType CoordinateRegionType; /// Index type. typedef Superclass::CoordinateVectorType CoordinateVectorType; /// Destructor. virtual ~ImageTemplate() {} /** Create volume "from scratch". *\param dims Number of grid elements for the three spatial dimensions. *\param size Size of the volume in real-world coordinates. */ ImageTemplate( const DataGrid::IndexType& dims, const typename Self::CoordinateVectorType& size ) : Superclass( dims, size ) { this->m_DataArray.resize( this->GetNumberOfPixels() ); } /** Create volume from base class instance. *\param dims Number of grid elements for the three spatial dimensions. *\param size Size of the volume in real-world coordinates. */ ImageTemplate( const Superclass& from ) : Superclass( from ) { this->m_DataArray.resize( this->GetNumberOfPixels() ); } /// Access operator. PixelType& operator[]( const size_t idx ) { return this->m_DataArray[idx]; } /// Const access operator. const PixelType& operator[]( const size_t idx ) const { return this->m_DataArray[idx]; } private: /// The actual data array. std::vector m_DataArray; }; //@} } // namespace cmtk #endif // #ifndef __cmtkImageTemplate_h_included_ cmtk-3.3.1/libs/Base/cmtkInterpolator.h000066400000000000000000000035401276303427400200030ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2010 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 2470 $ // // $LastChangedDate: 2010-10-20 11:35:16 -0700 (Wed, 20 Oct 2010) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkInterpolator_h_included_ #define __cmtkInterpolator_h_included_ #include namespace cmtk { /** \addtogroup Base */ //@{ /// Interpolation kernels to be used with UniformVolumeInterpolator. namespace Interpolators { /// Constants for interpolation modes. typedef enum { /// Nearest neighbour interpolation. NEAREST_NEIGHBOR, /// Partial volume interpolation. PARTIALVOLUME, /// (Tri-)linear interpolation. LINEAR, /// (Tri-)cubic interpolation. CUBIC, /// Sinc interpolation with cosine window. COSINE_SINC, /// Sinc interpolation with Hamming window. HAMMING_SINC, /// Default interpolation: decide automatically DEFAULT } InterpolationEnum; } // namespace Interpolators } // namespace cmtk #endif // #ifndef __cmtkInterpolator_h_included_ cmtk-3.3.1/libs/Base/cmtkJointHistogram.cxx000066400000000000000000000057571276303427400206510ustar00rootroot00000000000000/* // // Copyright 2016 Google, Inc. // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2010, 2013 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 5400 $ // // $LastChangedDate: 2016-01-14 19:42:40 -0800 (Thu, 14 Jan 2016) $ // // $LastChangedBy: torstenrohlfing $ // */ #include "cmtkJointHistogram.h" #include namespace cmtk { /** \addtogroup Base */ //@{ template void JointHistogram::GetMarginalEntropies ( double& HX, double& HY ) const { const T sampleCount = this->SampleCount(); if ( sampleCount > 0 ) { HX = HY = 0; for ( size_t i=0; iProjectToX( i ); if ( project ) { const double pX = project / sampleCount; HX -= pX * log(pX); } } for ( size_t j=0; jProjectToY( j ); if ( project ) { const double pY = project / sampleCount; HY -= pY * log(pY); } } } else { HX = HY = 0.0; } } template double JointHistogram::GetJointEntropy() const { double HXY = 0; const T sampleCount = this->SampleCount(); if ( sampleCount > 0 ) { for ( size_t idx = 0; idx < this->m_TotalNumberOfBins; ++idx ) { if ( JointBins[idx] ) { const double pXY = ((double)JointBins[idx]) / sampleCount; HXY -= pXY * log(pXY); } } } return HXY; } template Histogram* JointHistogram::GetMarginalX() const { Histogram* marg = new Histogram( NumBinsX ); marg->SetRange( this->GetRangeX() ); for ( size_t i = 0; i < NumBinsX; ++i ) (*marg)[i] = this->ProjectToX( i ); return marg; } template Histogram* JointHistogram::GetMarginalY() const { Histogram* marg = new Histogram( NumBinsY ); marg->SetRange( this->GetRangeY() ); for ( size_t i = 0; i < NumBinsY; ++i ) (*marg)[i] = this->ProjectToY( i ); return marg; } template class JointHistogram; template class JointHistogram; template class JointHistogram; template class JointHistogram; template class JointHistogram; } // namespace cmtk cmtk-3.3.1/libs/Base/cmtkJointHistogram.h000066400000000000000000000433371276303427400202720ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2012 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4407 $ // // $LastChangedDate: 2012-06-01 14:49:55 -0700 (Fri, 01 Jun 2012) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkJointHistogram_h_included_ #define __cmtkJointHistogram_h_included_ #include #include #include #include #include #include #include #include #include #include namespace cmtk { /** \addtogroup Base */ //@{ /** Joint histogram of two distributions. *\param T Template parameter: the type of the histogram bins. Can be integral, * or float in case of fractional bins. */ template class JointHistogram : /// Inherit from non-templated base class. public JointHistogramBase { protected: /// Number of reference data bins. size_t NumBinsX; /// Width of reference data bins. Types::DataItem BinWidthX; /// Lower value bound of reference data bins. Types::DataItem BinOffsetX; /// Number of model data bins. size_t NumBinsY; /// Width of model data bins. Types::DataItem BinWidthY; /// Lower value bound of model data bins. Types::DataItem BinOffsetY; /// Array of cross-modality (joint) bins. std::vector JointBins; /// Total number of bins, ie., NumBinsX*NumBinsY. size_t m_TotalNumberOfBins; public: /// This class. typedef JointHistogram Self; /// Smart pointer. typedef SmartPointer SmartPtr; /** Default constructor. */ JointHistogram () { this->m_TotalNumberOfBins = NumBinsX = NumBinsY = 0; BinWidthX = BinWidthY = 1.0; BinOffsetX = BinOffsetY = 0.0; } /** Constructor. */ JointHistogram( const size_t numBinsX, const size_t numBinsY, const bool reset = true ) { this->m_TotalNumberOfBins = NumBinsX = NumBinsY = 0; BinWidthX = BinWidthY = 1.0; BinOffsetX = BinOffsetY = 0.0; this->Resize( numBinsX, numBinsY, reset ); } /** Make an identical copy of this object. *\return The newly created copy of this object. */ Self* Clone () const { return new Self( *this ); } /// Resize and allocate histogram bins. void Resize( const size_t numberOfBinsX, const size_t numberOfBinsY, const bool reset = true ) { this->NumBinsX = numberOfBinsX; this->NumBinsY = numberOfBinsY; this->m_TotalNumberOfBins = this->NumBinsX * this->NumBinsY; this->JointBins.resize( this->m_TotalNumberOfBins ); if ( reset ) this->Reset(); } /// Return number of histogram bins in X direction. size_t GetNumBinsX() const { return this->NumBinsX; } /// Return number of histogram bins in Y direction. size_t GetNumBinsY() const { return this->NumBinsY; } /** Set value range of the X distribution. */ void SetRangeX( const Types::DataItemRange& range ) { this->BinOffsetX = range.m_LowerBound; this->BinWidthX = range.Width() / ( this->NumBinsX - 1 ); } /** Set value range of the Y distribution. */ void SetRangeY( const Types::DataItemRange& range ) { this->BinOffsetY = range.m_LowerBound; this->BinWidthY = range.Width() / ( this->NumBinsY - 1 ); } /** Set value range of the X distribution where upper and lower bound are centered in first and last histogram bins. */ void SetRangeCenteredX( const Types::DataItemRange& range ) { this->BinWidthX = range.Width() / (this->NumBinsX - 1); this->BinOffsetX = - this->BinWidthX / 2; } /** Set value range of the Y distribution where upper and lower bound are centered in first and last histogram bins. */ void SetRangeCenteredY( const Types::DataItemRange& range ) { this->BinWidthY = range.Width() / (this->NumBinsY - 1); this->BinOffsetY = - this->BinWidthY / 2; } /** Get value range of the X distribution. */ const Types::DataItemRange GetRangeX() const { return Types::DataItemRange( this->BinOffsetX, this->BinOffsetX + this->BinWidthX * ( this->NumBinsX - 1) ); } /** Get value range of the Y distribution. */ const Types::DataItemRange GetRangeY() const { return Types::DataItemRange( this->BinOffsetY, this->BinOffsetY + this->BinWidthY * ( this->NumBinsY - 1) ); } /** Reset computation. * This function has to be called before any other computation made with an * object of this class. All bin counters are reset to zero, therefore * Reset() must also be called before any new computation performed using an * already previously used object. */ void Reset () { std::fill( this->JointBins.begin(), this->JointBins.end(), static_cast( 0 ) ); } /** Return bin corresponding to a certain value of the X distribution. *\param value A value from the X distribution. *\return The index of the X bin corresponding to the given value. */ size_t ValueToBinX ( const Types::DataItem value ) const { const size_t binIndex = static_cast( (value - BinOffsetX) / BinWidthX ); return std::max( 0, std::min( NumBinsX-1, binIndex ) ); } /** Return bin corresponding to a certain value of the Y distribution. *\param value A value from the Y distribution. *\return The index of the Y bin corresponding to the given value. */ size_t ValueToBinY ( const Types::DataItem value ) const { const size_t binIndex = static_cast( (value - BinOffsetY) / BinWidthY ); return std::max( 0, std::min( NumBinsY-1, binIndex ) ); } /** Return center of values represented by a certain X distribution bin. *\param bin Index of a bin from the X distribution. *\return Average of upper and lower margin values of the given bin. */ Types::DataItem BinToValueX ( const size_t bin ) const { return BinOffsetX + (bin+0.5) * BinWidthX; } /** Return center of values represented by a certain Y distribution bin. *\param bin Index of a bin from the Y distribution. *\return Average of upper and lower margin values of the given bin. */ Types::DataItem BinToValueY ( const size_t bin ) const { return BinOffsetY + (bin+0.5) * BinWidthY; } /** Return projection of 2D distribution to X. * This function can be used to reconstruct the marginal distribution X from * the 2D histogram without explicitly stored 1D distributions. *\param indexX A bin index for the X distribution. *\return Projection of 2D histogram onto X distribution. This is the sum * of all bins in Y direction for the given index in X direction. */ T ProjectToX ( const size_t indexX ) const { T project = 0; for ( size_t j=0; j < NumBinsY; ++j ) project += this->JointBins[indexX + j * NumBinsX]; return project; } /** Return projection of 2D distribution to Y. * This function can be used to reconstruct the marginal distribution Y from * the 2D histogram without explicitly stored 1D distributions. *\param indexY A bin index for the Y distribution. *\return Projection of 2D histogram onto X distribution. This is the sum * of all bins in X direction for the given index in X direction. */ T ProjectToY ( const size_t indexY ) const { T project = 0; size_t offset = indexY * NumBinsX; for ( size_t i = 0; i < NumBinsX; ++i ) project += this->JointBins[i + offset]; return project; } /// Get marginal X distribution as 1D histogram. Histogram* GetMarginalX() const; /// Get marginal Y distribution as 1D histogram. Histogram* GetMarginalY() const; /** Return a bin value. */ T GetBin( const size_t indexX, const size_t indexY ) const { return this->JointBins[ indexX + NumBinsX * indexY ]; } /** Return total number of samples stored in the histogram. */ T SampleCount () const { T sampleCount = 0; for ( size_t idx = 0; idx < this->m_TotalNumberOfBins; ++idx ) { sampleCount += this->JointBins[idx]; } return sampleCount; } /** Return maximum number of samples stored in any bin. */ T GetMaximumBinValue () const { T maximum = 0; size_t idx = 0; for ( size_t i=0; iJointBins[idx] ); } return maximum; } /** Compute marginal entropies. * From the bin counts, the marginal entropies of both, reference and * model data are estimated. *\param HX Upon return, this reference holds the estimated marginal entropy * of the X random variable, i.e. the reference image. *\param HY Upon return, this reference holds the estimated marginal entropy * of the Y random variable, i.e. the model image. */ void GetMarginalEntropies ( double& HX, double& HY ) const; /** Compute joint entropy. * From the joint bins, an estimate to the joint entropy of both, * reference and model image is computed. *\return The estimated joint entropy. */ double GetJointEntropy() const; /** Increment the value of a histogram bin by 1. * The histogram field to increment is identified directly by its index; * no value-rescaling is done internally. *\param sampleX Index of histogram field in x-direction. *\param sampleY Index of histogram field in y-direction. */ void Increment ( const size_t sampleX, const size_t sampleY ) { ++this->JointBins[sampleX+sampleY*NumBinsX]; } /** Increment the value of a histogram bin by an arbitrary value. * The histogram field to increment is identified directly by its index; * no value-rescaling is done internally. *\param sampleX Index of histogram field in x-direction. *\param sampleY Index of histogram field in y-direction. *\param weight Value to increment the given histogram bin by. */ void Increment ( const size_t sampleX, const size_t sampleY, const T weight ) { this->JointBins[ sampleX + sampleY * NumBinsX ] += weight; } /** Decrement the value of a histogram bin by 1. * The histogram field to decrement is identified directly by its index; * no value-rescaling is done internally. Make sure that a value has actually * been added to this bin before - otherwise, the next entropy computation my * give some unexpected results. *\param sampleX Index of histogram field in x-direction. *\param sampleY Index of histogram field in y-direction. */ void Decrement ( const size_t sampleX, const size_t sampleY ) { --this->JointBins[sampleX+sampleY*NumBinsX]; } void Decrement ( const size_t sampleX, const size_t sampleY, const Types::DataItem weight ) { this->JointBins[ sampleX + sampleY * NumBinsX ] -= static_cast( weight ); } /** Add values from another histogram. * Adding is done by corresponding bins. The caller has to make sure that * both histograms actually have the same number and arrangement of bins. * It is also a good idea to ensure that the data ranges of these bins are * the same in both objects. Both can be guaranteed if one histogram was * created from the other by a call to Clone() for example. *\param other A pointer to the other histogram. Its bin values are added to * this object's bins. */ void AddJointHistogram ( const Self& other ) { for ( size_t idx = 0; idx < this->m_TotalNumberOfBins; ++idx ) this->JointBins[idx] += other.JointBins[idx]; } /** Add values from another 1D histogram in X-direction. * Adding is done by corresponding bins. The caller has to make sure that * both histograms actually have the same number and arrangement of bins. * It is also a good idea to ensure that the data ranges of these bins are * the same in both objects. *\param other A pointer to the other histogram. Its bin values are added to * this object's bins. *\param sampleY Index of the histogram row to which the 1D histogram is to * be added. *\param weight Multiplicative weight factor with which the other histogram is added to this one. */ void AddHistogramRow( const Histogram& other, const size_t sampleY, const float weight = 1 ) { size_t idx = this->NumBinsX * sampleY; for ( size_t i = 0; iJointBins[idx] += static_cast( weight * other[i] ); } } /** Add values from another 1D histogram in Y-direction. * Adding is done by corresponding bins. The caller has to make sure that * both histograms actually have the same number and arrangement of bins. * It is also a good idea to ensure that the data ranges of these bins are * the same in both objects. *\param sampleX Index of the histogram column to which the 1D histogram is * to be added. *\param other A pointer to the other histogram. Its bin values are added to * this object's bins. *\param weight Multiplicative weight factor with which the other histogram is added to this one. */ void AddHistogramColumn( const size_t sampleX, const Histogram& other, const float weight = 1 ) { size_t idx = sampleX; for ( size_t j = 0; jJointBins[idx] += static_cast( weight * other[j] ); } /** Subtract bin values from another histogram. * Subtraction is done by corresponding bins. The caller has to make sure * that both histograms actually have the same number and arrangement of * bins. It is also a good idea to ensure that the data ranges of these bins * are the same in both objects. Both can be guaranteed if one histogram was * created from the other by a call to Clone() for example. *\param other A pointer to the other histogram. Its bin values are * subtracted this object's bins. */ void RemoveJointHistogram ( const Self& other ) { for ( size_t idx = 0; idx < this->m_TotalNumberOfBins; ++idx ) { this->JointBins[idx] -= other.JointBins[idx]; } } /** Normalize histogram values over X dimension. *\param normalizeTo All histogram bins in every row of the histogram are * scaled by a common factor so that their sum matches the value of this * parameter. */ void NormalizeOverX( const double normalizeTo = 1.0 ) { for ( size_t j = 0; j < NumBinsY; ++j ) { const T project = this->ProjectToY( j ); if ( project > 0 ) { const double factor = normalizeTo / project; for ( size_t i = 0; i < NumBinsX; ++i ) this->JointBins[ i + NumBinsX * j ] = static_cast( this->JointBins[ i + NumBinsX * j ] * factor ); } } } /** Normalize histogram values over Y dimension. *\param normalizeTo All histogram bins in every column of the histogram are * scaled by a common factor so that their sum matches the value of this * parameter. */ void NormalizeOverY( const double normalizeTo = 1.0 ) { for ( size_t i = 0; i < NumBinsX; ++i ) { const T project = this->ProjectToX( i ); if ( project > 0 ) { const double factor = normalizeTo / project; for ( size_t j = 0; j < NumBinsY; ++j ) this->JointBins[ i + NumBinsX * j ] = static_cast( this->JointBins[ i + NumBinsX * j ] * factor ); } } } /* Return the index of the bin with the maximum value for one row. *\param j Index of the row. *\return The index of the bin with the maximum value in row j. */ size_t GetMaximumBinIndexOverX( const size_t j ) const { size_t offset = j * NumBinsX; size_t maxIndex = 0; T maxValue = this->JointBins[ offset ]; for ( size_t i = 1; i < NumBinsX; ++i ) { offset++; if ( this->JointBins[ offset ] > maxValue ) { maxValue = this->JointBins[ offset ]; maxIndex = i; } } return maxIndex; } /* Return the index of the bin with the maximum value for one column. *\param j Index of the column. *\return The index of the bin with the maximum value in column j. */ size_t GetMaximumBinIndexOverY( const size_t i ) const { size_t offset = i; size_t maxIndex = 0; T maxValue = this->JointBins[ offset ]; for ( size_t j = 1; j < NumBinsY; ++j ) { offset += NumBinsX; if ( this->JointBins[ offset ] > maxValue ) { maxValue = this->JointBins[ offset ]; maxIndex = j; } } return maxIndex; } /** Compute the (normalized) mutual information of this histogram. *\param normalized If this parameter is true, then the "normalized" version * of Mutual Information as introduced by Studholme et al. [Pattern Analysis * 1999] is computed. If this flag is false, the original formula of Viola * [IEEE Trans Med Imaging 1997] and Maes is used instead. */ double GetMutualInformation( const bool normalized = false ) const { double hX, hY, hXY; this->GetMarginalEntropies( hX, hY ); hXY = this->GetJointEntropy(); if ( hXY > 0 ) if ( normalized ) return (hX + hY) / hXY; else return (hX + hY) - hXY; else return 0; } }; //@} } // namespace cmtk #endif // #ifndef __cmtkJointHistogram_h_included_ cmtk-3.3.1/libs/Base/cmtkJointHistogramBase.cxx000066400000000000000000000032631276303427400214320ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2010 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 3346 $ // // $LastChangedDate: 2011-08-09 15:19:36 -0700 (Tue, 09 Aug 2011) $ // // $LastChangedBy: torstenrohlfing $ // */ #include "cmtkJointHistogramBase.h" size_t cmtk::JointHistogramBase::CalcNumBins ( const size_t numberOfSamples, const Types::DataItemRange& valueRange ) { const size_t side = static_cast( sqrt( static_cast( numberOfSamples )) ); const size_t dataRange = static_cast( valueRange.Width() + 1 ); const int upperLimit = std::min( std::min( dataRange, 128), side ); return std::max( 8, upperLimit ); } size_t cmtk::JointHistogramBase::CalcNumBins ( const UniformVolume* volume ) { return Self::CalcNumBins( volume->CropRegion().Size(), volume->GetData()->GetRange() ) ; } cmtk-3.3.1/libs/Base/cmtkJointHistogramBase.h000066400000000000000000000036401276303427400210560ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2010 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 2398 $ // // $LastChangedDate: 2010-10-05 14:54:37 -0700 (Tue, 05 Oct 2010) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkJointHistogramBase_h_included_ #define __cmtkJointHistogramBase_h_included_ #include #include #include namespace cmtk { /** \addtogroup Base */ //@{ /// Basic (non-template) 2-D histogram functions. class JointHistogramBase { public: /// This class. typedef JointHistogramBase Self; /// Smart pointer. typedef SmartPointer SmartPtr; /// Calculate optimum number of histogram bins. static size_t CalcNumBins( const size_t numberOfSamples /*!< Number of data values. */, const Types::DataItemRange& valueRange /*!< Range of values in the data.*/ ); /// Calculate optimum number of histogram bins for given volume. static size_t CalcNumBins ( const UniformVolume* volume ); }; //@} } // namespace cmtk #endif // #ifndef __cmtkJointHistogramBase_h_included_ cmtk-3.3.1/libs/Base/cmtkLandmark.cxx000066400000000000000000000024371276303427400174310ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2012 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4093 $ // // $LastChangedDate: 2012-03-27 13:05:25 -0700 (Tue, 27 Mar 2012) $ // // $LastChangedBy: torstenrohlfing $ // */ #include "cmtkLandmark.h" namespace cmtk { /** \addtogroup Base */ //@{ Landmark::Landmark( const std::string& name, const Self::SpaceVectorType& location ) : m_Name( name ), m_Location( location ) {} } // namespace cmtk-3.3.1/libs/Base/cmtkLandmark.h000066400000000000000000000037551276303427400170620ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2012 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4282 $ // // $LastChangedDate: 2012-04-30 15:10:55 -0700 (Mon, 30 Apr 2012) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkLandmark_h_included_ #define __cmtkLandmark_h_included_ #include #include #include #include #include #include #include namespace cmtk { /** \addtogroup Base */ //@{ /// Coordinates of an (anatomical) landmark. class Landmark { public: /// This class. typedef Landmark Self; /// Space vector type. typedef FixedVector<3,Types::Coordinate> SpaceVectorType; /// Default constructor. Landmark() {}; /// Explicit constructor. Landmark( const std::string& name /*!< Name of this landmark */, const Self::SpaceVectorType& location /*!< Location of this landmark */ ); /// Name of this landmark. std::string m_Name; /// Coordinates of this landmark. Self::SpaceVectorType m_Location; }; //@} } // namespace cmtk #endif // #ifndef __cmtkLandmarks_h_included_ cmtk-3.3.1/libs/Base/cmtkLandmarkList.cxx000066400000000000000000000032441276303427400202620ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2012 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4142 $ // // $LastChangedDate: 2012-04-06 15:35:21 -0700 (Fri, 06 Apr 2012) $ // // $LastChangedBy: torstenrohlfing $ // */ #include "cmtkLandmarkList.h" namespace cmtk { /** \addtogroup Base */ //@{ LandmarkList::Iterator LandmarkList::FindByName( const std::string& name ) { Self::Iterator it = this->begin(); while ( it != this->end() ) { if ( it->m_Name == name ) { return it; } ++it; } return it; } LandmarkList::ConstIterator LandmarkList::FindByName( const std::string& name ) const { Self::ConstIterator it = this->begin(); while ( it != this->end() ) { if ( it->m_Name == name ) { return it; } ++it; } return it; } } // namespace cmtk cmtk-3.3.1/libs/Base/cmtkLandmarkList.h000066400000000000000000000040711276303427400177060ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2012 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4142 $ // // $LastChangedDate: 2012-04-06 15:35:21 -0700 (Fri, 06 Apr 2012) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkLandmarkList_h_included_ #define __cmtkLandmarkList_h_included_ #include #include #include #include namespace cmtk { /** \addtogroup Base */ //@{ /// List of landmarks. class LandmarkList : /// Inherit STL list container. public std::list { public: /// This class. typedef LandmarkList Self; /// List iterator. typedef std::list::iterator Iterator; /// List const iterator. typedef std::list::const_iterator ConstIterator; /// Smart pointer to LandmarkList. typedef SmartPointer SmartPtr; /// Smart pointer to const LandmarkList. typedef SmartConstPointer SmartConstPtr; /// Find landmark by name. Self::Iterator FindByName( const std::string& name ); /// Find landmark by name and return constant pointer. Self::ConstIterator FindByName( const std::string& name ) const; }; //@} } // namespace cmtk #endif // #ifndef __cmtkLandmarkList_h_included_ cmtk-3.3.1/libs/Base/cmtkLandmarkPair.cxx000066400000000000000000000027151276303427400202440ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2012 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4208 $ // // $LastChangedDate: 2012-04-17 14:28:55 -0700 (Tue, 17 Apr 2012) $ // // $LastChangedBy: torstenrohlfing $ // */ #include "cmtkLandmarkPair.h" std::ostream& operator<<( std::ostream& stream, const cmtk::LandmarkPair& pair ) { stream << pair.m_Location << "\t" << pair.m_TargetLocation << "\t" << pair.m_Name << std::endl; return stream; } std::istream& operator>>( std::istream& stream, cmtk::LandmarkPair& pair ) { stream >> pair.m_Location >> pair.m_TargetLocation >> pair.m_Name; return stream; } cmtk-3.3.1/libs/Base/cmtkLandmarkPair.h000066400000000000000000000064271276303427400176750ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2013 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4778 $ // // $LastChangedDate: 2013-05-30 13:30:32 -0700 (Thu, 30 May 2013) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkLandmarkPair_h_included_ #define __cmtkLandmarkPair_h_included_ #include #include #include namespace cmtk { /** \addtogroup Base */ //@{ /// Matched landmark (landmark with source and target location). class LandmarkPair : /// Inherit single landmark. public Landmark { public: /// This class. typedef LandmarkPair Self; /// Smart pointer to LandmarkPair. typedef SmartPointer SmartPtr; /// Smart pointer to const LandmarkPair. typedef SmartConstPointer SmartConstPtr; /// Constructor. LandmarkPair( const Landmark& landmark, const Self::SpaceVectorType& target, const Types::Coordinate residual = -1 /*!< Landmark matching residual (e.g., wqith respect to linear fit) */, const bool precise = true /*!< Flag whether this landmark is "precise" enough for use in registration etc. */ ) : Landmark( landmark ), m_TargetLocation( target ), m_Residual( residual ), m_Precise( precise ) {} /// Constructor. LandmarkPair( const std::string& name, const Self::SpaceVectorType& source, const Self::SpaceVectorType& target, const Types::Coordinate residual = -1 /*!< Landmark matching residual (e.g., wqith respect to linear fit) */, const bool precise = true /*!< Flag whether this landmark is "precise" enough for use in registration etc. */ ) : Landmark( name, source ), m_TargetLocation( target ), m_Residual( residual ), m_Precise( precise ) {} /// Get same pair with source and target swapped. Self GetSwapSourceTarget() const { return Self( this->m_Name, this->m_TargetLocation, this->m_Location, this->m_Residual, this->m_Precise ); } /// Coordinates of this landmark. Self::SpaceVectorType m_TargetLocation; /// Fitting residual (negative if unknown). Types::Coordinate m_Residual; /// Precision flag. Only landmarks with this flag set should be used for registration. bool m_Precise; }; /// Stream output operator. std::ostream& operator<<( std::ostream& stream, const LandmarkPair& pair ); /// Stream input operator. std::istream& operator>>( std::istream& stream, LandmarkPair& pair ); //@} } // namespace cmtk #endif // #ifndef __cmtkLandmarkPair_h_included_ cmtk-3.3.1/libs/Base/cmtkLandmarkPairList.cxx000066400000000000000000000046141276303427400211000ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2012 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4208 $ // // $LastChangedDate: 2012-04-17 14:28:55 -0700 (Tue, 17 Apr 2012) $ // // $LastChangedBy: torstenrohlfing $ // */ #include "cmtkLandmarkPairList.h" namespace cmtk { /** \addtogroup Base */ //@{ void LandmarkPairList::AddLandmarkLists ( const LandmarkList& sourceList, const LandmarkList& targetList ) { LandmarkList::ConstIterator it = sourceList.begin(); while ( it != sourceList.end() ) { const LandmarkList::ConstIterator targetLM = targetList.FindByName( it->m_Name ); if ( targetLM != targetList.end() ) { this->push_back( LandmarkPair( *it, targetLM->m_Location ) ); } ++it; } } LandmarkPairList::ConstIterator LandmarkPairList::FindByName( const std::string& name ) const { for ( const_iterator it = this->begin(); it != this->end(); ++it ) { if ( it->m_Name == name ) { return it; } } return this->end(); } LandmarkPairList::Iterator LandmarkPairList::FindByName( const std::string& name ) { for ( Self::Iterator it = this->begin(); it != this->end(); ++it ) { if ( it->m_Name == name ) { return it; } } return this->end(); } std::ostream& operator<<( std::ostream& stream, const LandmarkPairList& pairList ) { for ( LandmarkPairList::ConstIterator it = pairList.begin(); it != pairList.end(); ++it ) stream << it->m_Location << "\t" << it->m_TargetLocation << "\t" << it->m_Name << std::endl; return stream; } } // namespace cmtk cmtk-3.3.1/libs/Base/cmtkLandmarkPairList.h000066400000000000000000000052521276303427400205240ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2012 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4208 $ // // $LastChangedDate: 2012-04-17 14:28:55 -0700 (Tue, 17 Apr 2012) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkLandmarkPairList_h_included_ #define __cmtkLandmarkPairList_h_included_ #include #include #include #include #include #include namespace cmtk { /** \addtogroup Base */ //@{ /// List of matched landmarks in 3-D. class LandmarkPairList : /// Inherit STL list container. public std::list { public: /// This class. typedef LandmarkPairList Self; /// Smart pointer to LandmarkPairList. typedef SmartPointer SmartPtr; /// Smart pointer to const LandmarkPairList. typedef SmartConstPointer SmartConstPtr; /// List iterator. typedef std::list::iterator Iterator; /// List const iterator. typedef std::list::const_iterator ConstIterator; /// Default constructor. LandmarkPairList() {} /// Initialize from two separate landmark lists. LandmarkPairList( const LandmarkList& sourceList, const LandmarkList& targetList ) { this->AddLandmarkLists( sourceList, targetList ); } /// Initialize from two separate landmark lists. void AddLandmarkLists( const LandmarkList& sourceList, const LandmarkList& targetList ); /// Find landmark by name. Self::Iterator FindByName( const std::string& name ); /// Find landmark by name and return constant pointer. Self::ConstIterator FindByName( const std::string& name ) const; }; /// Stream output operator. std::ostream& operator<<( std::ostream& stream, const LandmarkPairList& pairList ); //@} } // namespace cmtk #endif // #ifndef __cmtkLandmarkPairList_h_included_ cmtk-3.3.1/libs/Base/cmtkLeastSquares.h000066400000000000000000000061731276303427400177420ustar00rootroot00000000000000/* // // Copyright 2012 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4268 $ // // $LastChangedDate: 2012-04-27 14:46:49 -0700 (Fri, 27 Apr 2012) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkLeastSquares_h_included_ #define __cmtkLeastSquares_h_included_ #include #include #include namespace cmtk { /** \addtogroup Base */ //@{ /** Solve linear least-squares problem. * This class solves a least-squares problem of the form Ax=y for x, where A is a n-by-m design matrix, y is an n-dimensional measurement vector, * and x is the m-dimensional parameter vector. * * Solution of the least-squares problem is implemented via Singular Value Decomposition of the design matrix. Multiple problems using the same * design matrix but different measurement vectors can be solved without repeating the SVD. */ template class LeastSquares { public: /// This class. typedef LeastSquares Self; /// The scalar data type. typedef TScalar ScalarType; /// Constructor. LeastSquares( const Matrix2D& designMatrix ) : m_MatrixU( designMatrix ), m_MatrixV( designMatrix.NumberOfColumns(), designMatrix.NumberOfColumns() ), m_VectorW( designMatrix.NumberOfColumns() ) { MathUtil::SVD( this->m_MatrixU, this->m_VectorW, this->m_MatrixV ); } /** Compute parameter vector for a given measurement data vector. *\return Parameter vector that minimizes the least squares fitting error. */ std::vector Solve( const std::vector& measurements /*!< The n-dimensional measurement data vector.*/ ) const { std::vector parameters( this->m_MatrixU.NumberOfRows() ); MathUtil::SVDLinearRegression( this->m_MatrixU, this->m_VectorW, this->m_MatrixV, measurements, parameters ); return parameters; } private: /// Matrix U returned from singular value decomposition. Matrix2D m_MatrixU; /// Matrix V returned from singular value decomposition. Matrix2D m_MatrixV; /// Vector W returned from singular value decomposition. std::vector m_VectorW; }; //@} } // namespace cmtk #endif // #ifndef __cmtkLeastSquares_h_included_ cmtk-3.3.1/libs/Base/cmtkLinearInterpolator.h000066400000000000000000000037421276303427400211420ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2009, 2014 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 5240 $ // // $LastChangedDate: 2014-03-18 14:30:29 -0700 (Tue, 18 Mar 2014) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkLinearInterpolator_h_included_ #define __cmtkLinearInterpolator_h_included_ #include namespace cmtk { /** \addtogroup Base */ //@{ namespace Interpolators { /// Linear interpolator. class Linear { public: /// Size of the interpolation region in grid points to the left and right. static const int RegionSizeLeftRight = 1; /// Flag whether this interpolator is suitable for labels. static const bool SuitableForLabels = false; /// Get specific interpolation weight for relative coordinate. static Types::Coordinate GetWeight( const int weight, const Types::Coordinate x ) { switch (weight) { case 0: return 1 - x; case 1: return x; default: #ifdef DEBUG std::cerr << "weight=" << weight << " shouldn't happen!" << std::endl; exit( 1 ); #endif break; } return 0; } }; } // namespace Interpolators //@} } // namespace cmtk #endif cmtk-3.3.1/libs/Base/cmtkLogHistogram.h000066400000000000000000000103031276303427400177130ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2012 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4156 $ // // $LastChangedDate: 2012-04-10 10:13:18 -0700 (Tue, 10 Apr 2012) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkLogHistogram_h_included_ #define __cmtkLogHistogram_h_included_ #include #include #include #include namespace cmtk { /** \addtogroup Base */ //@{ /** Histogram of log intensities. */ template class LogHistogram : /// Inherit from non-log histogram.. public Histogram { public: /// This class. typedef LogHistogram Self; /// Smart pointer. typedef SmartPointer SmartPtr; /// Parent class. typedef Histogram Superclass; /** Constructor. */ LogHistogram ( const size_t numBins = 0 ) : Superclass( numBins ), m_LogNumBins( log( static_cast( numBins ) ) ) {} /** Destructor. * All bin arrays and the precomputed data bin index arrays are * de-allocated. */ virtual ~LogHistogram() {} /// Resize and allocate histogram bins. virtual void Resize( const size_t numberOfBins, const bool reset = true ) { this->Superclass::Resize( numberOfBins, reset ); this->m_LogNumBins = log( static_cast( numberOfBins ) ); } /// Make an identical copy of this object. typename Self::SmartPtr Clone () const { return typename Self::SmartPtr( this->CloneVirtual() ); } /** Return bin corresponding to a certain value of the distribution. *\param value A value from the distribution. *\return The index of the bin corresponding to the given value. */ virtual size_t ValueToBin ( const Types::DataItem value ) const { return static_cast( this->ValueToBinFractional( value ) ); } /** Return fractional bin corresponding to a value of the distribution. *\param value A value from the distribution. *\return The index of the fractional bin index corresponding to the given * value. This value is an integer if and only if the given value is * identical to the lower bound of a bin. */ virtual Types::DataItem ValueToBinFractional ( const Types::DataItem value ) const { const Types::DataItem binIndex = this->Superclass::ValueToBinFractional( value ); return (this->GetNumberOfBins()-1) * std::max( 0.0, std::min( 1.0, log( 1+binIndex ) / this->m_LogNumBins ) ); } /** Get value range of a given bin. */ virtual const Types::DataItemRange GetRangeBin( const size_t bin ) const { return Types::DataItemRange( this->BinToValue( bin ), this->BinToValue( bin+1 ) ); } /** Return center of values represented by a certain bin. *\param bin Index of a bin from the distribution. *\return Average of upper and lower margin values of the given bin. */ virtual Types::DataItem BinToValue ( const size_t bin ) const { return this->Superclass::BinToValue( static_cast( exp( static_cast( bin ) / (this->GetNumberOfBins()-1) * this->m_LogNumBins ) - 1 ) ); } protected: /// Make an identical copy of this object including derived class objects virtual Self* CloneVirtual() const { return new Self( *this ); } private: /// Pre-computed log of number of bins. double m_LogNumBins; }; //@} } // namespace cmtk #endif // #ifndef __cmtkLogHistogram_h_included_ cmtk-3.3.1/libs/Base/cmtkMacros.h000066400000000000000000000152401276303427400165450ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2010 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 2398 $ // // $LastChangedDate: 2010-10-05 14:54:37 -0700 (Tue, 05 Oct 2010) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkMacros_h_included_ #define __cmtkMacros_h_included_ #include #include #include #include #include /** Trap for abstract class member functions. * In DEBUG mode (that is, when compiled with DEBUG defined, a call to this * macro will terminate the application. Without DEBUG mode, it has no effect. * The purpose of this is to detect calls to member functions of otherwise * purely virtual classes. That is, we are trying to detect calls to member of * a class that should never have any instances anyway. */ inline void abstract() { #ifdef DEBUG abort(); #endif } /** Macro to define a protected scalar class parameter and public read and * write access member functions. */ #define cmtkGetSetMacro(type,name) \ public: type m_##name; \ void Set##name( const type& v ) { this->m_##name = v; } \ const type& Get##name() const { return this->m_##name; } \ type& Get##name() { return this->m_##name; } \ void Get##name( type& to ) { to = this->m_##name; } /** Macro to define a protected scalar class parameter and public read and * write access member functions. */ #define cmtkGetSetMacroDefault(type,name,default) \ public: type m_##name; \ void Set##name( const type v = default ) { this->m_##name = v; } \ const type& Get##name() const { return this->m_##name; } \ type& Get##name() { return this->m_##name; } \ void Get##name( type& to ) { to = this->m_##name; } /** Macro to define a protected string class parameter and public read and * write access member functions. */ #define cmtkGetSetMacroString(name) \ protected: char *m_##name; \ public: void Set##name( const char* v ) { \ if ( (this->m_##name != NULL) ) { if ( v != NULL ) \ if ( !strcmp( this->m_##name, v ) ) return; \ free(this->m_##name); this->m_##name = NULL; \ } else { if ( v == NULL ) return; } \ if ( v != NULL ) { this->m_##name = strdup(v); } } \ char* Get##name() { return this->m_##name; } \ const char* Get##name() const { return this->m_##name; } \ void Get##name( char*& to ) { to = this->m_##name; } /** Macro to define a protected pointer class parameter and public read and * write access member functions. */ #define cmtkGetSetMacroPtr(t,name) \ public: t *m_##name; \ void Set##name( t *const v ) { this->m_##name = v; } \ t* Get##name() { return this->m_##name; } \ const t* Get##name() const { return this->m_##name; } \ void Get##name( t*& to ) { to = this->m_##name; } /** Macro to define a protected 2D array class parameter and public read and * write access member functions. */ #define cmtkGetSetMacro2Array(type,name) \ public: type m_##name[2]; \ void Set##name( const type v0, const type v1 ) \ { this->m_##name[0] = v0; this->m_##name[1] = v1; } \ void SetByIndex##name( const int axis, const type v ) \ { this->m_##name[axis] = v; } \ void Set##name( const type* v ) \ { this->m_##name[0] = v[0]; this->m_##name[1] = v[1]; } \ void Get##name( type &v0, type &v1 ) const \ { v0 = this->m_##name[0]; v1 = this->m_##name[1]; } \ void Get##name( type *const v ) const \ { v[0] = this->m_##name[0]; v[1] = this->m_##name[1]; } \ const type* Get##name() const { return this->m_##name; } \ type* Get##name() { return this->m_##name; } \ type Get##name( const int i ) const { return this->m_##name[i]; } /** Macro to define a protected 3D array class parameter and public read and * write access member functions. */ #define cmtkGetSetMacro3Array(type,name) \ public: type m_##name[3]; \ void Set##name( const type v0, const type v1, const type v2 ) \ { this->m_##name[0] = v0; this->m_##name[1] = v1; this->m_##name[2] = v2; } \ void SetByIndex##name( const int axis, const type v ) \ { this->m_##name[axis] = v; } \ void Set##name( const type* v ) \ { this->m_##name[0] = v[0]; this->m_##name[1] = v[1]; this->m_##name[2] = v[2]; } \ void Get##name( type &v0, type &v1, type &v2 ) const \ { v0 = this->m_##name[0]; v1 = this->m_##name[1]; v2 = this->m_##name[2]; } \ void Get##name( type *const v ) const \ { v[0] = this->m_##name[0]; v[1] = this->m_##name[1]; v[2] = this->m_##name[2]; } \ const type* Get##name() const { return this->m_##name; } \ type* Get##name() { return this->m_##name; } \ type Get##name( const int i ) const { return this->m_##name[i]; } /** Macro to define a protected 3D array class parameter and public read and * write access member functions. */ #define cmtkGetMacro3Array(type,name) \ public: type m_##name[3]; \ void Get##name( type &v0, type &v1, type &v2 ) const \ { v0 = this->m_##name[0]; v1 = this->m_##name[1]; v2 = this->m_##name[2]; } \ void Get##name( type *const v ) const \ { v[0] = this->m_##name[0]; v[1] = this->m_##name[1]; v[2] = this->m_##name[2]; } \ const type* Get##name() const { return this->m_##name; } \ type* Get##name() { return this->m_##name; } \ type Get##name( const int i ) const { return this->m_##name[i]; } //@} #endif // #ifndef __cmtkMacros_h_included_ cmtk-3.3.1/libs/Base/cmtkMagphanEMR051.cxx000066400000000000000000000376651276303427400200600ustar00rootroot00000000000000/* // // Copyright 2012, 2013 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4974 $ // // $LastChangedDate: 2013-10-11 13:50:11 -0700 (Fri, 11 Oct 2013) $ // // $LastChangedBy: torstenrohlfing $ // */ #include "cmtkMagphanEMR051.h" /* * Measurements were derived manually from the following document: http://www.phantomlab.com/library/pdf/magphan_adni_manual.pdf * They can, therefore, be used without reference to ADNI publications. */ const cmtk::MagphanEMR051::SphereEntryType cmtk::MagphanEMR051::SphereTable[cmtk::MagphanEMR051::NumberOfSpheres] = { // // LICENSING EXCEPTION // Unlike the remainder of this file, the table of phantom sphere coordinates // is licensed under the CC BY 3.0 license (https://creativecommons.org/licenses/by/3.0/us/) // // 1x 6.0cm SNR sphere { "SNR", 60, { 0.0, 0.0, 0.0 }, 0.820, 282, Self::SPHERE_COLOR_NONE }, //#000 // 2x 1.5cm spheres { "15mm@90mm", 15, { 89.0, -2.9, 0.0 }, 0.820, 282, Self::SPHERE_COLOR_NONE }, { "15mm@60mm", 15, { 0.0, -2.9, -60.0 }, 0.820, 282, Self::SPHERE_COLOR_NONE }, // 4x 3.0cm spheres { "CNR-Orange", 30, { 60.0, 20.0, 0 }, 0.590, 450, Self::SPHERE_COLOR_ORANGE }, // y coordinate estimated - not marked in construction drawing { "CNR-Red", 30, { -60.0, 20.0, 0 }, 0.430, 600, Self::SPHERE_COLOR_RED }, // y coordinate estimated - not marked in construction drawing { "CNR-Yellow", 30, { -60.0, -20.0, 0 }, 0.295, 750, Self::SPHERE_COLOR_YELLOW }, // y coordinate estimated - not marked in construction drawing { "CNR-Green", 30, { 60.0, -20.0, 0 }, 0.220, 900, Self::SPHERE_COLOR_GREEN }, // y coordinate estimated - not marked in construction drawing // 158x 1.0cm spheres { "10mm_0_23", 10, { -30.0, 0.0, 30.0 }, 0.820, 282, Self::SPHERE_COLOR_NONE }, { "10mm_0_24", 10, { -30.0, 0.0, -30.0 }, 0.820, 282, Self::SPHERE_COLOR_NONE }, { "10mm_0_25", 10, { 30.0, 0.0, 30.0 }, 0.820, 282, Self::SPHERE_COLOR_NONE }, { "10mm_0_26", 10, { 30.0, 0.0, -30.0 }, 0.820, 282, Self::SPHERE_COLOR_NONE }, { "10mm_1_01", 10, { 0.0, -30.0, 40.0 }, 0.820, 282, Self::SPHERE_COLOR_NONE }, { "10mm_1_02", 10, { 0.0, -30.0, -40.0 }, 0.820, 282, Self::SPHERE_COLOR_NONE }, { "10mm_1b_13", 10, { 40.0, 29.1, 15.0 }, 0.820, 282, Self::SPHERE_COLOR_NONE }, { "10mm_1b_14", 10, { 40.0, 29.1, -15.0 }, 0.820, 282, Self::SPHERE_COLOR_NONE }, { "10mm_1b_15", 10, { -40.0, 29.1, 15.0 }, 0.820, 282, Self::SPHERE_COLOR_NONE }, { "10mm_1b_16", 10, { -40.0, 29.1, -15.0 }, 0.820, 282, Self::SPHERE_COLOR_NONE }, { "10mm_1_15", 10, { 30.0, -30.0, 30.0 }, 0.820, 282, Self::SPHERE_COLOR_NONE }, { "10mm_1_16", 10, { 30.0, -30.0, -30.0 }, 0.820, 282, Self::SPHERE_COLOR_NONE }, { "10mm_1_17", 10, { -30.0, -30.0, 30.0 }, 0.820, 282, Self::SPHERE_COLOR_NONE }, { "10mm_1_18", 10, { -30.0, -30.0, -30.0 }, 0.820, 282, Self::SPHERE_COLOR_NONE }, { "10mm_1b_01", 10, { 15.0, 29.1, 45.0 }, 0.820, 282, Self::SPHERE_COLOR_NONE }, { "10mm_1b_04", 10, { 15.0, 29.1, -45.0 }, 0.820, 282, Self::SPHERE_COLOR_NONE }, { "10mm_1b_07", 10, { -15.0, 29.1, 45.0 }, 0.820, 282, Self::SPHERE_COLOR_NONE }, { "10mm_1b_10", 10, { -15.0, 29.1, -45.0 }, 0.820, 282, Self::SPHERE_COLOR_NONE }, { "10mm_0_27", 10, { 0.0, 0.0, 60.0 }, 0.820, 282, Self::SPHERE_COLOR_NONE }, { "10mm_2b_01", 10, { 15.0, 59.1, 15.0 }, 0.820, 282, Self::SPHERE_COLOR_NONE }, { "10mm_2b_02", 10, { 15.0, 59.1, -15.0 }, 0.820, 282, Self::SPHERE_COLOR_NONE }, { "10mm_2b_03", 10, { -15.0, 59.1, 15.0 }, 0.820, 282, Self::SPHERE_COLOR_NONE }, { "10mm_2b_04", 10, { -15.0, 59.1, -15.0 }, 0.820, 282, Self::SPHERE_COLOR_NONE }, { "10mm_2_01", 10, { 15.0, -60.0, 15.0 }, 0.820, 282, Self::SPHERE_COLOR_NONE }, { "10mm_2_02", 10, { 15.0, -60.0, -15.0 }, 0.820, 282, Self::SPHERE_COLOR_NONE }, { "10mm_2_03", 10, { -15.0, -60.0, 15.0 }, 0.820, 282, Self::SPHERE_COLOR_NONE }, { "10mm_2_04", 10, { -15.0, -60.0, -15.0 }, 0.820, 282, Self::SPHERE_COLOR_NONE }, { "10mm_1_05", 10, { 0.0, -30.0, 60.0 }, 0.820, 282, Self::SPHERE_COLOR_NONE }, { "10mm_1_06", 10, { 0.0, -30.0, -60.0 }, 0.820, 282, Self::SPHERE_COLOR_NONE }, { "10mm_0_15", 10, { -30.0, 0.0, 60.0 }, 0.820, 282, Self::SPHERE_COLOR_NONE }, { "10mm_0_16", 10, { -30.0, 0.0, -60.0 }, 0.820, 282, Self::SPHERE_COLOR_NONE }, { "10mm_0_17", 10, { 30.0, 0.0, 60.0 }, 0.820, 282, Self::SPHERE_COLOR_NONE }, { "10mm_0_18", 10, { 30.0, 0.0, -60.0 }, 0.820, 282, Self::SPHERE_COLOR_NONE }, { "10mm_0_19", 10, { -60.0, 0.0, 30.0 }, 0.820, 282, Self::SPHERE_COLOR_NONE }, { "10mm_0_20", 10, { -60.0, 0.0, -30.0 }, 0.820, 282, Self::SPHERE_COLOR_NONE }, { "10mm_0_21", 10, { 60.0, 0.0, 30.0 }, 0.820, 282, Self::SPHERE_COLOR_NONE }, { "10mm_0_22", 10, { 60.0, 0.0, -30.0 }, 0.820, 282, Self::SPHERE_COLOR_NONE }, { "10mm_1b_17", 10, { 45.0, 29.1, 45.0 }, 0.820, 282, Self::SPHERE_COLOR_NONE }, { "10mm_1b_18", 10, { 45.0, 29.1, -45.0 }, 0.820, 282, Self::SPHERE_COLOR_NONE }, { "10mm_1b_19", 10, { -45.0, 29.1, 45.0 }, 0.820, 282, Self::SPHERE_COLOR_NONE }, { "10mm_1b_20", 10, { -45.0, 29.1, -45.0 }, 0.820, 282, Self::SPHERE_COLOR_NONE }, { "10mm_1b_02", 10, { 15.0, 29.1, 65.0 }, 0.820, 282, Self::SPHERE_COLOR_NONE }, { "10mm_1b_05", 10, { 15.0, 29.1, -65.0 }, 0.820, 282, Self::SPHERE_COLOR_NONE }, { "10mm_1b_08", 10, { -15.0, 29.1, 65.0 }, 0.820, 282, Self::SPHERE_COLOR_NONE }, { "10mm_1b_11", 10, { -15.0, 29.1, -65.0 }, 0.820, 282, Self::SPHERE_COLOR_NONE }, { "10mm_1_11", 10, { 30.0, -30.0, 60.0 }, 0.820, 282, Self::SPHERE_COLOR_NONE }, { "10mm_1_12", 10, { 30.0, -30.0, -60.0 }, 0.820, 282, Self::SPHERE_COLOR_NONE }, { "10mm_1_13", 10, { -30.0, -30.0, 60.0 }, 0.820, 282, Self::SPHERE_COLOR_NONE }, { "10mm_1_14", 10, { -30.0, -30.0, -60.0 }, 0.820, 282, Self::SPHERE_COLOR_NONE }, { "10mm_1_19", 10, { 60.0, -30.0, 30.0 }, 0.820, 282, Self::SPHERE_COLOR_NONE }, { "10mm_1_20", 10, { 60.0, -30.0, -30.0 }, 0.820, 282, Self::SPHERE_COLOR_NONE }, { "10mm_1_21", 10, { -60.0, -30.0, 30.0 }, 0.820, 282, Self::SPHERE_COLOR_NONE }, { "10mm_1_22", 10, { -60.0, -30.0, -30.0 }, 0.820, 282, Self::SPHERE_COLOR_NONE }, { "10mm_2b_05", 10, { 15.0, 59.1, 45.0 }, 0.820, 282, Self::SPHERE_COLOR_NONE }, { "10mm_2b_06", 10, { 15.0, 59.1, -45.0 }, 0.820, 282, Self::SPHERE_COLOR_NONE }, { "10mm_2b_07", 10, { -15.0, 59.1, 45.0 }, 0.820, 282, Self::SPHERE_COLOR_NONE }, { "10mm_2b_08", 10, { -15.0, 59.1, -45.0 }, 0.820, 282, Self::SPHERE_COLOR_NONE }, { "10mm_2b_13", 10, { 45.0, 59.1, 15.0 }, 0.820, 282, Self::SPHERE_COLOR_NONE }, { "10mm_2b_14", 10, { 45.0, 59.1, -15.0 }, 0.820, 282, Self::SPHERE_COLOR_NONE }, { "10mm_2b_15", 10, { -45.0, 59.1, 15.0 }, 0.820, 282, Self::SPHERE_COLOR_NONE }, { "10mm_2b_16", 10, { -45.0, 59.1, -15.0 }, 0.820, 282, Self::SPHERE_COLOR_NONE }, { "10mm_2_05", 10, { 15.0, -60.0, 45.0 }, 0.820, 282, Self::SPHERE_COLOR_NONE }, { "10mm_2_06", 10, { 15.0, -60.0, -45.0 }, 0.820, 282, Self::SPHERE_COLOR_NONE }, { "10mm_2_07", 10, { -15.0, -60.0, 45.0 }, 0.820, 282, Self::SPHERE_COLOR_NONE }, { "10mm_2_08", 10, { -15.0, -60.0, -45.0 }, 0.820, 282, Self::SPHERE_COLOR_NONE }, { "10mm_2_13", 10, { 45.0, -60.0, 15.0 }, 0.820, 282, Self::SPHERE_COLOR_NONE }, { "10mm_2_14", 10, { 45.0, -60.0, -15.0 }, 0.820, 282, Self::SPHERE_COLOR_NONE }, { "10mm_2_15", 10, { -45.0, -60.0, 15.0 }, 0.820, 282, Self::SPHERE_COLOR_NONE }, { "10mm_2_16", 10, { -45.0, -60.0, -15.0 }, 0.820, 282, Self::SPHERE_COLOR_NONE }, { "10mm_1b_25", 10, { 64.5, 29.1, -45.0 }, 0.820, 282, Self::SPHERE_COLOR_NONE }, { "10mm_1b_26", 10, { -64.5, 29.1, 45.0 }, 0.820, 282, Self::SPHERE_COLOR_NONE }, { "10mm_1b_21", 10, { 45.0, 29.1, 65.0 }, 0.820, 282, Self::SPHERE_COLOR_NONE }, { "10mm_1b_22", 10, { -45.0, 29.1, -65.0 }, 0.820, 282, Self::SPHERE_COLOR_NONE }, { "10mm_3b_09", 10, { 0.0, 88.6, 0.0 }, 0.820, 282, Self::SPHERE_COLOR_NONE }, { "10mm_3_09", 10, { 0.0, -89.5, 0.0 }, 0.820, 282, Self::SPHERE_COLOR_NONE }, { "10mm_3b_01", 10, { 28.3, 81.4, 28.3 }, 0.820, 282, Self::SPHERE_COLOR_NONE }, { "10mm_3b_02", 10, { 28.3, 81.4, -28.3 }, 0.820, 282, Self::SPHERE_COLOR_NONE }, { "10mm_3b_03", 10, { -28.3, 81.4, 28.3 }, 0.820, 282, Self::SPHERE_COLOR_NONE }, { "10mm_3b_04", 10, { -28.3, 81.4, -28.3 }, 0.820, 282, Self::SPHERE_COLOR_NONE }, { "10mm_3b_05", 10, { 0.0, 87.2, 25.0 }, 0.820, 282, Self::SPHERE_COLOR_NONE }, { "10mm_3b_06", 10, { 0.0, 87.2, -25.0 }, 0.820, 282, Self::SPHERE_COLOR_NONE }, { "10mm_3b_07", 10, { 25.0, 87.2, 0.0 }, 0.820, 282, Self::SPHERE_COLOR_NONE }, { "10mm_3b_08", 10, { -25.0, 87.2, 0.0 }, 0.820, 282, Self::SPHERE_COLOR_NONE }, { "10mm_2b_17", 10, { 48.7, 59.1, 48.7 }, 0.820, 282, Self::SPHERE_COLOR_NONE }, { "10mm_2b_18", 10, { 48.7, 59.1, -48.7 }, 0.820, 282, Self::SPHERE_COLOR_NONE }, { "10mm_2b_19", 10, { -48.7, 59.1, 48.7 }, 0.820, 282, Self::SPHERE_COLOR_NONE }, { "10mm_2b_20", 10, { -48.7, 59.1, -48.7 }, 0.820, 282, Self::SPHERE_COLOR_NONE }, { "10mm_2b_09", 10, { 15.0, 59.1, 67.3 }, 0.820, 282, Self::SPHERE_COLOR_NONE }, { "10mm_2b_10", 10, { 15.0, 59.1, -67.3 }, 0.820, 282, Self::SPHERE_COLOR_NONE }, { "10mm_2b_11", 10, { -15.0, 59.1, 67.3 }, 0.820, 282, Self::SPHERE_COLOR_NONE }, { "10mm_2b_12", 10, { -15.0, 59.1, -67.3 }, 0.820, 282, Self::SPHERE_COLOR_NONE }, { "10mm_2b_21", 10, { 67.3, 59.1, 15.0 }, 0.820, 282, Self::SPHERE_COLOR_NONE }, { "10mm_2b_22", 10, { 67.3, 59.1, -15.0 }, 0.820, 282, Self::SPHERE_COLOR_NONE }, { "10mm_2b_23", 10, { -67.3, 59.1, 15.0 }, 0.820, 282, Self::SPHERE_COLOR_NONE }, { "10mm_2b_24", 10, { -67.3, 59.1, -15.0 }, 0.820, 282, Self::SPHERE_COLOR_NONE }, { "10mm_1b_03", 10, { 15.0, 29.1, 85.2 }, 0.820, 282, Self::SPHERE_COLOR_NONE }, { "10mm_1b_06", 10, { 15.0, 29.1, -85.2 }, 0.820, 282, Self::SPHERE_COLOR_NONE }, { "10mm_1b_09", 10, { -15.0, 29.1, 85.2 }, 0.820, 282, Self::SPHERE_COLOR_NONE }, { "10mm_1b_12", 10, { -15.0, 29.1, -85.2 }, 0.820, 282, Self::SPHERE_COLOR_NONE }, { "10mm_1b_29", 10, { 85.2, 29.1, 15.0 }, 0.820, 282, Self::SPHERE_COLOR_NONE }, { "10mm_1b_30", 10, { 85.2, 29.1, -15.0 }, 0.820, 282, Self::SPHERE_COLOR_NONE }, { "10mm_1b_31", 10, { -85.2, 29.1, 15.0 }, 0.820, 282, Self::SPHERE_COLOR_NONE }, { "10mm_1b_32", 10, { -85.2, 29.1, -15.0 }, 0.820, 282, Self::SPHERE_COLOR_NONE }, { "10mm_1b_23", 10, { 45.0, 29.1, -73.9 }, 0.820, 282, Self::SPHERE_COLOR_NONE }, { "10mm_1b_24", 10, { -45.0, 29.1, 73.9 }, 0.820, 282, Self::SPHERE_COLOR_NONE }, { "10mm_1b_27", 10, { 73.9, 29.1, 45.0 }, 0.820, 282, Self::SPHERE_COLOR_NONE }, { "10mm_1b_28", 10, { -73.9, 29.1, -45.0 }, 0.820, 282, Self::SPHERE_COLOR_NONE }, { "10mm_1_23", 10, { 61.0, -30.0, 61.0 }, 0.820, 282, Self::SPHERE_COLOR_NONE }, { "10mm_1_24", 10, { 61.0, -30.0, -61.0 }, 0.820, 282, Self::SPHERE_COLOR_NONE }, { "10mm_1_25", 10, { -61.0, -30.0, 61.0 }, 0.820, 282, Self::SPHERE_COLOR_NONE }, { "10mm_1_26", 10, { -61.0, -30.0, -61.0 }, 0.820, 282, Self::SPHERE_COLOR_NONE }, { "10mm_2_17", 10, { 48.7, -60.0, 48.7 }, 0.820, 282, Self::SPHERE_COLOR_NONE }, { "10mm_2_18", 10, { 48.7, -60.0, -48.7 }, 0.820, 282, Self::SPHERE_COLOR_NONE }, { "10mm_2_19", 10, { -48.7, -60.0, 48.7 }, 0.820, 282, Self::SPHERE_COLOR_NONE }, { "10mm_2_20", 10, { -48.7, -60.0, -48.7 }, 0.820, 282, Self::SPHERE_COLOR_NONE }, { "10mm_2_09", 10, { 15.0, -60.0, 67.3 }, 0.820, 282, Self::SPHERE_COLOR_NONE }, { "10mm_2_10", 10, { 15.0, -60.0, -67.3 }, 0.820, 282, Self::SPHERE_COLOR_NONE }, { "10mm_2_11", 10, { -15.0, -60.0, 67.3 }, 0.820, 282, Self::SPHERE_COLOR_NONE }, { "10mm_2_12", 10, { -15.0, -60.0, -67.3 }, 0.820, 282, Self::SPHERE_COLOR_NONE }, { "10mm_2_21", 10, { 67.3, -60.0, 15.0 }, 0.820, 282, Self::SPHERE_COLOR_NONE }, { "10mm_2_22", 10, { 67.3, -60.0, -15.0 }, 0.820, 282, Self::SPHERE_COLOR_NONE }, { "10mm_2_23", 10, { -67.3, -60.0, 15.0 }, 0.820, 282, Self::SPHERE_COLOR_NONE }, { "10mm_2_24", 10, { -67.3, -60.0, -15.0 }, 0.820, 282, Self::SPHERE_COLOR_NONE }, { "10mm_3_01", 10, { 28.3, -82.2, 28.3 }, 0.820, 282, Self::SPHERE_COLOR_NONE }, { "10mm_3_02", 10, { 28.3, -82.2, -28.3 }, 0.820, 282, Self::SPHERE_COLOR_NONE }, { "10mm_3_03", 10, { -28.3, -82.2, 28.3 }, 0.820, 282, Self::SPHERE_COLOR_NONE }, { "10mm_3_04", 10, { -28.3, -82.2, -28.3 }, 0.820, 282, Self::SPHERE_COLOR_NONE }, { "10mm_0_09", 10, { -30.0, 0.0, 86.4 }, 0.820, 282, Self::SPHERE_COLOR_NONE }, { "10mm_0_10", 10, { -30.0, 0.0, -86.4 }, 0.820, 282, Self::SPHERE_COLOR_NONE }, { "10mm_0_11", 10, { 30.0, 0.0, 86.4 }, 0.820, 282, Self::SPHERE_COLOR_NONE }, { "10mm_0_12", 10, { 30.0, 0.0, -86.4 }, 0.820, 282, Self::SPHERE_COLOR_NONE }, { "10mm_0_01", 10, { -86.4, 0.0, -30.0 }, 0.820, 282, Self::SPHERE_COLOR_NONE }, { "10mm_0_02", 10, { -86.4, 0.0, 30.0 }, 0.820, 282, Self::SPHERE_COLOR_NONE }, { "10mm_0_03", 10, { 86.4, 0.0, -30.0 }, 0.820, 282, Self::SPHERE_COLOR_NONE }, { "10mm_0_04", 10, { 86.4, 0.0, 30.0 }, 0.820, 282, Self::SPHERE_COLOR_NONE }, { "10mm_3_05", 10, { 0.0, -88.0, 25.0 }, 0.820, 282, Self::SPHERE_COLOR_NONE }, { "10mm_3_06", 10, { 0.0, -88.0, -25.0 }, 0.820, 282, Self::SPHERE_COLOR_NONE }, { "10mm_3_07", 10, { 25.0, -88.0, 0.0 }, 0.820, 282, Self::SPHERE_COLOR_NONE }, { "10mm_3_08", 10, { -25.0, -88.0, 0.0 }, 0.820, 282, Self::SPHERE_COLOR_NONE }, { "10mm_0_05", 10, { -64.7, 0.0, 64.7 }, 0.820, 282, Self::SPHERE_COLOR_NONE }, { "10mm_0_06", 10, { -64.7, 0.0, -64.7 }, 0.820, 282, Self::SPHERE_COLOR_NONE }, { "10mm_0_07", 10, { 64.7, 0.0, 64.7 }, 0.820, 282, Self::SPHERE_COLOR_NONE }, { "10mm_0_08", 10, { 64.7, 0.0, -64.7 }, 0.820, 282, Self::SPHERE_COLOR_NONE }, { "10mm_0_13", 10, { 0.0, 0.0, 91.5 }, 0.820, 282, Self::SPHERE_COLOR_NONE }, { "10mm_0_14", 10, { 0.0, 0.0, -91.5 }, 0.820, 282, Self::SPHERE_COLOR_NONE }, { "10mm_0_28", 10, { -91.5, 0.0, 0.0 }, 0.820, 282, Self::SPHERE_COLOR_NONE }, { "10mm_1_07", 10, { 30.0, -30.0, 81.1 }, 0.820, 282, Self::SPHERE_COLOR_NONE }, { "10mm_1_08", 10, { 30.0, -30.0, -81.1 }, 0.820, 282, Self::SPHERE_COLOR_NONE }, { "10mm_1_09", 10, { -30.0, -30.0, 81.1 }, 0.820, 282, Self::SPHERE_COLOR_NONE }, { "10mm_1_10", 10, { -30.0, -30.0, -81.1 }, 0.820, 282, Self::SPHERE_COLOR_NONE }, { "10mm_1_27", 10, { 81.1, -30.0, 30.0 }, 0.820, 282, Self::SPHERE_COLOR_NONE }, { "10mm_1_28", 10, { 81.1, -30.0, -30.0 }, 0.820, 282, Self::SPHERE_COLOR_NONE }, { "10mm_1_29", 10, { -81.1, -30.0, 30.0 }, 0.820, 282, Self::SPHERE_COLOR_NONE }, { "10mm_1_30", 10, { -81.1, -30.0, -30.0 }, 0.820, 282, Self::SPHERE_COLOR_NONE }, { "10mm_1_03", 10, { 0.0, -30.0, 86.5 }, 0.820, 282, Self::SPHERE_COLOR_NONE }, { "10mm_1_04", 10, { 0.0, -30.0, -86.5 }, 0.820, 282, Self::SPHERE_COLOR_NONE }, { "10mm_1_31", 10, { 86.5, -30.0, 0.0 }, 0.820, 282, Self::SPHERE_COLOR_NONE }, { "10mm_1_32", 10, { -86.5, -30.0, 0.0 }, 0.820, 282, Self::SPHERE_COLOR_NONE } // // END LICENSING EXCEPTION // }; cmtk-3.3.1/libs/Base/cmtkMagphanEMR051.h000066400000000000000000000101601276303427400174620ustar00rootroot00000000000000/* // // Copyright 2012 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4721 $ // // $LastChangedDate: 2013-05-12 16:49:38 -0700 (Sun, 12 May 2013) $ // // $LastChangedBy: torsten_at_home $ // */ #ifndef __cmtkMagphanEMR051_h_included_ #define __cmtkMagphanEMR051_h_included_ #include #include #include namespace cmtk { /** \addtogroup Base */ //@{ /// Description of the Magphan EMR051 structural imaging phantom (a.k.a. ADNI Phantom). class MagphanEMR051 { public: /// This class. typedef MagphanEMR051 Self; /// Number of phantom spheres. static const unsigned int NumberOfSpheres = 165; /// Enumeration type for sphere colors. typedef enum { /// No color. SPHERE_COLOR_NONE, /// Green sphere. SPHERE_COLOR_GREEN, /// Yellow sphere. SPHERE_COLOR_YELLOW, /// Red sphere. SPHERE_COLOR_RED, /// Orange sphere. SPHERE_COLOR_ORANGE } SphereColorType; /// Data structure for phantom sphere location table. typedef struct __SphereEntryType { /// Sphere name (this has been "arbitrarily" assigned for CMTK). const char* m_Name; /// Sphere diameter in milimeters. Types::Coordinate m_Diameter; /// Sphere center location in "RAS" coordinate. Phantom center is the coordinate space origin. Types::Coordinate m_CenterLocation[3]; /// Grams of Copper Sulfate Penta Hydrate per liter double m_GramsPerLiterCSPH; /** Estimated imaging T1. * For the 3.0cm spheres, this value is given in the phantom manual. For the remaining * spheres, we derive it from an exponential fit as T1=exp(-1.83364*CSPH+7.18582). * * We store only integer values because no fractional values are given in the phantom manual. */ int m_EstimatedT1; /// Sphere color. SphereColorType m_Color; } SphereEntryType; /** Table of phantom sphere locations and properties. * Measurements were derived manually from the following document: http://www.phantomlab.com/library/pdf/magphan_adni_manual.pdf * They can, therefore, be used without reference to ADNI publications. */ static const Self::SphereEntryType SphereTable[Self::NumberOfSpheres]; /// Convenience access function - get sphere radius. static Types::Coordinate SphereRadius( const size_t i ) { return 0.5 * Self::SphereTable[i].m_Diameter; } /// Convenience access function - get sphere radius. static const char* SphereName( const size_t i ) { return Self::SphereTable[i].m_Name; } /// Convenience access function - get sphere center as 3D vector. static FixedVector<3,Types::Coordinate> SphereCenter( const size_t i ) { return FixedVector<3,Types::Coordinate>::FromPointer( Self::SphereTable[i].m_CenterLocation ); } /// Create a simulated T1-weighted image of the phantom spheres. static UniformVolume::SmartPtr GetPhantomImage( const Types::Coordinate resolution = 1.0 /*!< Pixel size of the output image; number of pixels is determined by this and the FOV needed to cover the entire phantom. */, const bool labels = false /*!< If this is set, each sphere is drawn with its intensity equal to the index in the marker table; otherwise, estimated T1 is used. */ ); }; //@} } // namespace cmtk #endif // #ifndef __cmtkMagphanEMR051_h_included_ cmtk-3.3.1/libs/Base/cmtkMagphanEMR051_GetPhantomImage.cxx000066400000000000000000000044331276303427400231340ustar00rootroot00000000000000/* // // Copyright 2012 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4427 $ // // $LastChangedDate: 2012-06-12 11:23:22 -0700 (Tue, 12 Jun 2012) $ // // $LastChangedBy: torstenrohlfing $ // */ #include "cmtkMagphanEMR051.h" #include #include cmtk::UniformVolume::SmartPtr cmtk::MagphanEMR051::GetPhantomImage( const cmtk::Types::Coordinate resolution, const bool labels ) { const int npx = 1 + static_cast( 200.0 / resolution ); const int dims[3] = { npx, npx, npx }; UniformVolume::SmartPtr result( new UniformVolume( DataGrid::IndexType::FromPointer( dims ), resolution, resolution, resolution ) ); result->SetMetaInfo( cmtk::META_SPACE, cmtk::AnatomicalOrientation::ORIENTATION_STANDARD ); result->SetMetaInfo( cmtk::META_SPACE_ORIGINAL, cmtk::AnatomicalOrientation::ORIENTATION_STANDARD ); result->CreateDataArray( TYPE_SHORT ); const Types::Coordinate offset[3] = { -100, -100, -100 }; result->m_Offset = UniformVolume::CoordinateVectorType::FromPointer( offset ); UniformVolumePainter painter( result, UniformVolumePainter::COORDINATES_ABSOLUTE ); for ( int idx = 0; idx < 165; ++idx ) { const Types::DataItem value = ( labels ) ? (idx+1) : Self::SphereTable[idx].m_EstimatedT1; painter.DrawSphere( UniformVolume::CoordinateVectorType::FromPointer( Self::SphereTable[idx].m_CenterLocation ), Self::SphereTable[idx].m_Diameter / 2, value ); } return result; } cmtk-3.3.1/libs/Base/cmtkMathFunctionWrappers.cxx000066400000000000000000000033321276303427400220160ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2011 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 3342 $ // // $LastChangedDate: 2011-08-09 13:10:49 -0700 (Tue, 09 Aug 2011) $ // // $LastChangedBy: torstenrohlfing $ // */ #include "cmtkMathFunctionWrappers.h" namespace cmtk { namespace Wrappers { double Log( const double x ) { return log( x ); } double Exp( const double x ) { return exp( x ); } double Sqrt( const double x ) { return sqrt( x ); } double Abs( const double x ) { return fabs( x ); } double Trunc( const double x ) { #ifdef _MSC_VER return static_cast( static_cast( x ) ); #else return trunc( x ); #endif } double Square( const double x ) { return x*x; } double Logit( const double x ) { return log(x / (1.0-x)); } double Logistic( const double x ) { return 1.0/(1.0+exp(-x)); } } // namespace Wrappers } // namespace cmtk cmtk-3.3.1/libs/Base/cmtkMathFunctionWrappers.h000066400000000000000000000037541276303427400214530ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2011 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 3342 $ // // $LastChangedDate: 2011-08-09 13:10:49 -0700 (Tue, 09 Aug 2011) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkMathFunctionWrappers_h_included_ #define __cmtkMathFunctionWrappers_h_included_ #include #include namespace cmtk { /** Wrappers for math.h functions. * These are necessary because on some platforms (e.g., Solaris), the * math functions are declared extern "C"; so until we find a better way * to address this, we wrap these functions. */ namespace Wrappers { /// Log function. double Log( const double x ); /// Exponential function. double Exp( const double x ); /// Square root function. double Sqrt( const double x ); /// Abs function. double Abs( const double x ); /// Trunc function. double Trunc( const double x ); /// Square function. double Square( const double x ); /// Logit function. double Logit( const double x ); /// Logistic function. double Logistic( const double x ); } // namespace Wrappers } // namespace cmtk #endif // #ifndef __cmtkMathFunctionWrappers_h_included_ cmtk-3.3.1/libs/Base/cmtkMathUtil.h000066400000000000000000000261061276303427400170530ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2012 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4510 $ // // $LastChangedDate: 2012-09-14 14:34:59 -0700 (Fri, 14 Sep 2012) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkMathUtil_h_included_ #define __cmtkMathUtil_h_included_ #include #include #include #ifdef HAVE_STDINT_H # include #else # ifdef _MSC_VER typedef unsigned int uint32_t; # endif // _MSC_VER #endif #include #include #include #include #ifdef HAVE_IEEEFP_H # include #endif #ifndef M_PI #define M_PI 3.14159265358979323846 #endif #ifdef _MSC_VER /* Some useful constants taken from SGI's math.h */ #define M_E 2.7182818284590452354 #define M_LOG2E 1.4426950408889634074 #define M_LOG10E 0.43429448190325182765 #define M_LN2 0.69314718055994530942 #define M_LN10 2.30258509299404568402 #define M_PI_2 1.57079632679489661923 #define M_PI_4 0.78539816339744830962 #define M_1_PI 0.31830988618379067154 #define M_2_PI 0.63661977236758134308 #define M_2_SQRTPI 1.12837916709551257390 #define M_SQRT2 1.41421356237309504880 #define M_SQRT1_2 0.70710678118654752440 #endif namespace cmtk { /** \addtogroup Base */ //@{ /** General-purpose mathematical functions and function templates. */ class MathUtil { private: /// This class. typedef MathUtil Self; public: /// Portable test for "not a number" values. template static bool IsNaN( const T value ) { return isnan( value ); } /// Portable test for "finite" values. template static bool IsFinite( const T value ) { return finite( value ) != 0; } /// Unit-safe sin() function. static double Sin( const Units::Radians& radians ) { return sin( radians.Value() ); } /// Unit-safe cos() function. static double Cos( const Units::Radians& radians ) { return cos( radians.Value() ); } /// Unit-safe tan() function. static double Tan( const Units::Radians& radians ) { return tan( radians.Value() ); } /// Unit-safe asin() function. static const Units::Radians ArcSin( const double value ) { return Units::Radians( asin( value ) ); } /// Unit-safe acos() function. static const Units::Radians ArcCos( const double value ) { return Units::Radians( acos( value ) ); } /// Unit-safe atan() function. static const Units::Radians ArcTan( const double value ) { return Units::Radians( atan( value ) ); } /// Unit-safe atan2() function. static const Units::Radians ArcTan2( const double y, const double x ) { return Units::Radians( atan2( y, x ) ); } /// Return square of a float value. template static T Square ( const T a) { return a*a; } /** Return minimum of an array of ordered values. */ template static T Min ( const int nValues, const T* Values ) { T Result = Values[0]; for ( int idx=1; idx static T Max ( const int nValues, const T* Values ) { T Result = Values[0]; for ( int idx=1; idx static T Intersect ( const T aMin, const T aMax, const T bMin, const T bMax ) { return ( std::min( aMax, bMax ) - std::max( aMin, bMin ) ); } /// Round float value to the nearest integer. template static int Round ( const T a ) { return (int)floor(a+0.5); } /** Return sign of float value. *\return -1 if a<0, 1 if a>0, 0 if a==0. */ template static int Sign ( const T a ) { return (a<0)?-1:((a==0)?0:1); } /** Check if some float value is within a range. *\return 0 if value is in range, -1 if value is below minumum, 1 if value * is above maximum. */ template static int CheckRange ( const T value, const T a, const T b ) { const int sigA = Self::Sign(value-a); return (sigA == Self::Sign(value-b))?sigA:0; } /// Compute p*log(p) for a single value. template static double plogp( const T p ) { return (p>0) ? p * log( p ) : 0.0; } /** Computes average of an array of float values. */ template static T Mean ( const unsigned int nValues, const T* values ); /** Computes average of a vector of float values. */ template static T Mean ( const std::vector& values ); /** Computes variance of an array of float values. *\param nValues Number of values in "values" array. *\param values The array of values to compute the variance of. *\param mean Previously calculated mean of the array values. *\param unbiased If this flag is set (default: unset), then the variance * will be computed over nValues-1; otherwise over nValues. */ template static T Variance ( const unsigned int nValues, const T* values, T mean, const bool unbiased = false ); /** Computes variance of a vector of float values. *\param values Vector of values to compute variance from. *\param mean Previously computed mean of vector values. *\param unbiased If this flag is set (default: unset), then the variance * will be computed over nValues-1; otherwise over nValues. */ template static T Variance ( const std::vector& values, T mean, const bool unbiased = false ); /** Normalized correlation coefficient between two float vectors. */ template static T Correlation( const std::vector& x, const std::vector& y ); /// Compute t-statistic from coefficient of correlation. static double TStatFromCorrelation( const double r /*!< Coefficient of correlation as computed by MathUtil::Correlation function.*/, const size_t df /*!< Number of degrees of freedom*/ ); /// Compute probability from T-statistic. static double ProbabilityFromTStat( const double t /*!< T-statistic as returned for example from MathUtil::TStatFromCorrelation function.*/, const size_t df /*!< Number of degrees of freedom.*/ ); /** Performs two-tailed unpaired t-test on two distributions. */ template static T TTest ( const std::vector& valuesX, const std::vector& valuesY, T& t ); /** Performs two-tailed unpaired t-test on two distributions. * Also return average value for each distribution. */ template static T TTest ( const std::vector& valuesX, const std::vector& valuesY, T& t, T& avgX, T& avgY ); /** Performs two-tailed paired t-test on two distributions. * Also return average value for each distribution. */ template static T PairedTTest ( const std::vector& valuesX, const std::vector& valuesY, T& t, T& avgX, T& avgY ); /** Performs one-sample t-test on distribution to test for zero mean. * Also return average value for each distribution. */ template static T TTest ( const std::vector& valuesX, T& t, T& avgX ); /// Beta-i function. static double Betai( const double a, const double b, const double x ); /// Beta-Cf function. static double BetaCf( const double a, const double b, const double x ); /// GammaLn function. static double GammaLn( const double xx ); /// Singular Value Decomposition static void SVD( Matrix2D& U, std::vector& W, Matrix2D& V ); /// Linear Regression using SVD results static void SVDLinearRegression( const Matrix2D& U, const std::vector& W, const Matrix2D& V, const std::vector& b, std::vector& lm_params ); /// Function that compares two floats; to be used in qsort(). static inline int CompareFloat( const void *a, const void *b ) { const float* A = static_cast( a ); const float* B = static_cast( b ); if ( *A > *B ) return +1; if ( *A < *B ) return -11; return 0; } /// Function that compares two doubles; to be used in qsort(). static inline int CompareDouble( const void *a, const void *b ) { const double A = *(static_cast( a )); const double B = *(static_cast( b )); if ( A > B ) return +1; if ( A < B ) return -1; return 0; } /** Generate normally distributed random numbers. * This function uses the Box-Muller method to transform a pair of uniformly * distributed random numbers into a pair of normally (ie., Gaussian) * distributed random numbers. One of the two generated numbers is returned * while the other is stored so that, when this function is called the next * time, the previously computed value can be returned without further * computational expense. *\param sigma Standard deviation of the resulting distribution. */ static inline double NormalRandom( const double sigma ) { static bool secondNumberReady = false; static double secondNumber = 0; if ( secondNumberReady ) { secondNumberReady = false; return secondNumber; } double x1, x2, w; do { x1 = 2.0 * (random()&0xffffff)/0x1000000 - 1.0; x2 = 2.0 * (random()&0xffffff)/0x1000000 - 1.0; w = x1 * x1 + x2 * x2; } while ( w >= 1.0 ); w = sqrt( (-2.0 * log( w ) ) / w ); secondNumber = x1 * w * sigma; return x2 * w * sigma; } /** Generate normally distributed random numbers with explicit seed. *\param sigma Standard deviation of the resulting distribution. *\param seed Random seed given to srandom() function. */ static inline double NormalRandom( const double sigma, const unsigned int seed ) { srandom( seed ); return NormalRandom( sigma ); } /** Uniform random number generator. *\return A random number from a uniform distribution over the interval [0,1). */ static double UniformRandom(); /// Determinant of an n x n square matrix. template static T CholeskyDeterminant( const Matrix2D& matrix, int n); }; //@} } // namespace cmtk #include "cmtkMathUtil_Statistics.txx" #endif // #ifndef __cmtkMathUtil_h_included_ cmtk-3.3.1/libs/Base/cmtkMathUtil_LinAlg.cxx000066400000000000000000000102021276303427400206420ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2013 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4870 $ // // $LastChangedDate: 2013-09-24 11:01:11 -0700 (Tue, 24 Sep 2013) $ // // $LastChangedBy: torstenrohlfing $ // */ #include #include #include #include "Numerics/sevd.h" #include "Numerics/spddet.h" #include "Numerics/svd.h" #include #include #include namespace cmtk { void MathUtil::SVD( Matrix2D& U, std::vector& W, Matrix2D& V ) { const size_t m = U.NumberOfRows(); const size_t n = U.NumberOfColumns(); W.resize( n ); V.Resize( n, n ); ap::real_2d_array apA; apA.setbounds(0, m-1, 0, n-1); for (size_t j = 0; j < n; j++) for (size_t i = 0; i < m; i++) apA(i,j) = U[i][j]; ap::real_1d_array w; ap::real_2d_array u; ap::real_2d_array vt; rmatrixsvd( apA, m, n, true /* U needed */, true /* V needed */, 2 /*max-level memory usage */, w, u, vt); /* Put u in U */ for (size_t j = 0; j < n; j++) for (size_t i = 0; i < m; i++) U[i][j] = u(i,j); /* Put w in W */ for (size_t i = 0; i < n; i++) W[i] = w(i); /* Un-transpose vt and put it in V */ for (size_t j = 0; j < n; j++) for (size_t i = 0; i < n; i++) V[i][j] = vt(j,i); } /** TODO: move this someplace more logical than the linear-algebra module */ void MathUtil::SVDLinearRegression( const Matrix2D& U, const std::vector& W, const Matrix2D& V, const std::vector& b, std::vector& lm_params ) { const size_t m = U.NumberOfRows(); const size_t n = U.NumberOfColumns(); lm_params.resize( n ); // From alglib linear regression: // Take the inverses of the singular values, setting the inverse // to 0 if the sv is close to 0 (tolerance controlled by epstol) double epstol = 1000; ap::real_1d_array svi; svi.setbounds( 0, n-1 ); for( size_t i = 0; i < n; i++ ) if( W[i] > epstol*ap::machineepsilon * W[0] ) svi(i) = 1 / W[i]; else svi(i) = 0; // Calculate linear model parameters following Heath, Ch. 3.6 // (Scientific Computing: An Introductory Survey, 2nd Ed., 2002) for ( size_t i = 0; i < n; i++ ) lm_params[i] = 0.0; for ( size_t i = 0; i < n; i++ ) { double ut_times_b = 0.0; for ( size_t j = 0; j < m; j++ ) ut_times_b += U[j][i] * b[j]; ut_times_b *= svi(i); for ( size_t j = 0; j < n; j++ ) lm_params[j] += ut_times_b * V[j][i]; } } ///////////////////////////////////////////////////////////////////// // HELPERS ///////////////////////////////////////////////////////////////////// template T MathUtil::CholeskyDeterminant (const Matrix2D& matrix, int n) { ap::real_2d_array apMatrix; apMatrix.setbounds(0, n-1, 0, n-1); for (int j = 0; j < n; j++) for (int i = 0; i < n; i++) apMatrix(i,j) = (double)(1.0 * matrix[i][j]); spdmatrixcholesky( apMatrix, n, false ); T determinant = (T) spdmatrixcholeskydet( apMatrix, n ); return determinant; } template double MathUtil::CholeskyDeterminant(const Matrix2D& matrix, int n); template float MathUtil::CholeskyDeterminant(const Matrix2D& matrix, int n); } // namespace cmtk cmtk-3.3.1/libs/Base/cmtkMathUtil_Random.cxx000066400000000000000000000025741276303427400207310ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2010 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 2426 $ // // $LastChangedDate: 2010-10-07 16:14:37 -0700 (Thu, 07 Oct 2010) $ // // $LastChangedBy: torstenrohlfing $ // */ #include "cmtkMathUtil.h" #include "Numerics/randomc.h" #include namespace cmtk { /** \addtogroup Base */ //@{ double MathUtil::UniformRandom() { static const int seed = (int)time(0) + (int)( 1000 * rand() ); static CRandomMersenne randGen( seed ); return randGen.Random(); } } // namespace cmtk cmtk-3.3.1/libs/Base/cmtkMathUtil_Statistics.cxx000066400000000000000000000031501276303427400216320ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2010 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 2425 $ // // $LastChangedDate: 2010-10-07 16:14:23 -0700 (Thu, 07 Oct 2010) $ // // $LastChangedBy: torstenrohlfing $ // */ #include "cmtkMathUtil.h" #include #include #include "Numerics/ibetaf.h" namespace cmtk { /** \addtogroup Base */ //@{ double MathUtil::TStatFromCorrelation ( const double r, const size_t df ) { return r * sqrt( df / (1-r*r) ); } double MathUtil::ProbabilityFromTStat ( const double t, const size_t df ) { double stat; if ( df == 0.0 ) stat = 0.0; else if ( t == 0.0 ) stat = 1.0; else stat = df/(df+t*t); return alglib::incompletebeta( 0.5*df, 0.5, stat ); } } // namespace cmtk cmtk-3.3.1/libs/Base/cmtkMathUtil_Statistics.txx000066400000000000000000000126631276303427400216640ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2010, 2013 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4887 $ // // $LastChangedDate: 2013-09-27 21:16:46 -0700 (Fri, 27 Sep 2013) $ // // $LastChangedBy: torsten_at_home $ // */ #include "Numerics/sevd.h" #include "Numerics/studentttests.h" namespace cmtk { /** \addtogroup Base */ //@{ template T MathUtil::Mean ( const unsigned int nValues, const T* values ) { T mean = 0.0; for ( unsigned int j = 0; j < nValues; j++ ) mean += values[j]; mean /= nValues; return mean; } template T MathUtil::Variance ( const unsigned int nValues, const T* values, const T mean, const bool unbiased ) { T sumOfSquares = 0.0; T sum = 0.0; for ( unsigned int j = 0; j < nValues; j++ ) { const T s = values[j] - mean; sum += s; sumOfSquares += s*s; } if ( unbiased && (nValues>1) ) return (sumOfSquares - sum*sum/nValues) / (nValues-1); else if ( nValues > 0 ) return (sumOfSquares - sum*sum/nValues) / (nValues); return 0; } template T MathUtil::Mean ( const std::vector& values ) { const size_t nValues = values.size(); T mean = 0.0; for ( size_t j = 0; j < nValues; j++ ) mean += values[j]; mean /= nValues; return mean; } template T MathUtil::Variance ( const std::vector& values, const T mean, const bool unbiased ) { const size_t nValues = values.size(); T sumOfSquares = 0.0; T sum = 0.0; for ( size_t j = 0; j < nValues; j++ ) { const T s = values[j] - mean; sum += s; sumOfSquares += s*s; } if ( unbiased && (nValues>1) ) return (sumOfSquares - sum*sum/nValues) / (nValues-1); else if ( nValues > 0 ) return (sumOfSquares - sum*sum/nValues) / (nValues); return 0; } template T MathUtil::Correlation ( const std::vector& x, const std::vector& y ) { const size_t n = std::min( x.size(), y.size() ); // compute means T meanx = 0, meany = 0; for ( size_t i = 0; i < n; ++i ) { meanx += x[i]; meany += y[i]; } meanx /= n; meany /= n; // compute parameter correlations T c = 0, xSq = 0, ySq = 0; T dx, dy; for ( size_t i = 0; i < n; ++i ) { c += (dx=x[i]-meanx) * (dy=y[i]-meany); xSq += dx * dx; ySq += dy * dy; } return static_cast( c / (sqrt(xSq*ySq)+1e-20) ); } template T MathUtil::TTest ( const std::vector& valuesX, const std::vector& valuesY, T& t ) { T averageX, averageY; return TTest( valuesX, valuesY, t, averageX, averageY ); } template T MathUtil::PairedTTest ( const std::vector& valuesX, const std::vector& valuesY, T& t, T& avgX, T& avgY ) { const size_t nValues = valuesX.size(); avgX = Mean( valuesX ); avgY = Mean( valuesY ); T SSD = 0; for ( size_t i = 0; i < nValues; ++i ) SSD += Square( (valuesX[i]-avgX) - (valuesY[i]-avgY) ); t = (avgX - avgY) * sqrt( (nValues * (nValues-1)) / SSD ); double s = alglib::studenttdistribution(nValues-1, t); double p1 = 2 * ap::minreal(s, 1-s); return (T) p1; // probability } /** Two-sample t-test */ template T MathUtil::TTest ( const std::vector& valuesX, const std::vector& valuesY, T& t, T& avgX, T& avgY ) { const size_t nValuesX = valuesX.size(); const size_t nValuesY = valuesY.size(); ap::real_1d_array apValuesX; apValuesX.setbounds( 0, nValuesX-1 ); for (unsigned int i = 0; i < nValuesX; i++) apValuesX(i) = (double)(1.0 * valuesX[i]); ap::real_1d_array apValuesY; apValuesY.setbounds( 0, nValuesY-1 ); for (unsigned int i = 0; i < nValuesY; i++) apValuesY(i) = (double)(1.0 * valuesY[i]); ap::real_value_type t_temp, p1, p2, p3; avgX = MathUtil::Mean( valuesX ); avgY = MathUtil::Mean( valuesY ); alglib::studentttest2( apValuesX, nValuesX, apValuesY, nValuesY, t_temp, p1, p2, p3 ); t = static_cast( t_temp ); return static_cast( p1 ); // probability } /** One-sample t-test */ template T MathUtil::TTest ( const std::vector& valuesX, T& t, T& avgX ) { const size_t nValuesX = valuesX.size(); avgX = Mean( valuesX ); const T varianceX = Variance( valuesX, avgX, true /*unbiased*/ ); t = avgX * nValuesX / sqrt( varianceX ); const double s = alglib::studenttdistribution(nValuesX-1, t); const double p1 = 2 * ap::minreal(s, 1-s); return (T) p1; // probability } } // namespace cmtk cmtk-3.3.1/libs/Base/cmtkMatrix.h000066400000000000000000000207231276303427400165670ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2013 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4840 $ // // $LastChangedDate: 2013-09-13 11:02:03 -0700 (Fri, 13 Sep 2013) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkMatrix_h_included_ #define __cmtkMatrix_h_included_ #include #include #include #include #include #include namespace cmtk { /** \addtogroup Base */ //@{ /// Rekursive matrix template. template class Matrix { public: /// This class. typedef Matrix Self; /// Superclass. typedef Matrix Superclass; /// Public constructor. Matrix( const size_t (&dims)[NDim] ) : m_SubMatrixArray( dims[0] ) { } /// Destructor. ~Matrix() {}; /// Element pointer type. typedef typename Superclass::ElementPointerType* ElementPointerType; typename Self::ElementPointerType& operator[]( const size_t idx ) { return this->m_SubMatrixArray[idx]; } const typename Self::ElementPointerType& operator[]( const size_t idx ) const { return this->m_SubMatrixArray[idx]; } protected: /// Recursive constructor. Matrix() {}; private: /// Vector of pointers to lower-dimensional sub-matrices. std::vector m_SubMatrixArray; }; // class Matrix template class Matrix { }; /// Two-dimensional matrix template. template class Matrix2D : /// For access, make this a vector of pointers. public std::vector { public: /// Superclass. typedef std::vector Superclass; /// This class. typedef Matrix2D Self; /// Smart pointer to this class. typedef SmartPointer SmartPtr; /// Row vector type. typedef std::vector RowVectorType; /// Default constructor. Matrix2D() : Superclass( 1 ) { this->m_NumberOfColumns = 0; this->m_NumberOfRows = 0; this->m_NumberOfElements = 0; (*this)[0] = NULL; } /// Constructor: allocate and create cross-references. Matrix2D( const size_t dims1, const size_t dims0, const T* dataPtr = NULL ) : Superclass( dims1 ) { this->m_NumberOfColumns = dims0; this->m_NumberOfRows = dims1; this->m_NumberOfElements = dims0 * dims1; (*this)[0] = Memory::ArrayC::Allocate( this->m_NumberOfElements ); for ( size_t i = 1; i < this->m_NumberOfRows; ++i ) (*this)[i] = (*this)[i-1] + this->m_NumberOfColumns; if ( dataPtr ) memcpy( (*this)[0], dataPtr, this->m_NumberOfElements * sizeof( T ) ); } /// Copy constructor. Matrix2D( const Matrix2D& other ) : Superclass( other.size() ) { this->m_NumberOfColumns = other.m_NumberOfColumns; this->m_NumberOfRows = other.m_NumberOfRows; this->m_NumberOfElements = other.m_NumberOfElements; (*this)[0] = Memory::ArrayC::Allocate( this->m_NumberOfElements ); for ( size_t i = 1; i < this->m_NumberOfRows; ++i ) (*this)[i] = (*this)[i-1] + this->m_NumberOfColumns; memcpy( (*this)[0], other[0], this->m_NumberOfElements * sizeof( T ) ); } /// Destructor: free allocated array. ~Matrix2D() { if ( (*this)[0] ) { Memory::ArrayC::Delete( (*this)[0] ); (*this)[0] = NULL; } } /// Get number of rows. size_t NumberOfRows() const { return this->m_NumberOfRows; } /** Get number of columns. * Get this from underlying Array. */ size_t NumberOfColumns() const { return this->m_NumberOfColumns; } /// Resize the matrix. void Resize( const size_t numberOfRows, const size_t numberOfColumns ) { if ( (numberOfColumns != this->m_NumberOfColumns) || (numberOfRows != this->m_NumberOfRows) ) { if ( (*this)[0] ) { Memory::ArrayC::Delete( (*this)[0] ); (*this)[0] = NULL; } this->m_NumberOfColumns = numberOfColumns; this->m_NumberOfRows = numberOfRows; this->m_NumberOfElements = numberOfColumns * numberOfRows; this->Superclass::resize( numberOfRows ); (*this)[0] = Memory::ArrayC::Allocate( this->m_NumberOfElements ); for ( size_t i = 1; i < numberOfRows; ++i ) (*this)[i] = (*this)[i-1] + numberOfColumns; } } /// Reset all values to zero. void SetAllToZero() { memset( (*this)[0], 0, this->m_NumberOfElements * sizeof( T ) ); } /// Set all values. void SetAll( const T value) { for ( size_t i = 0; i < this->m_NumberOfElements; ++i ) { (*this)[0][i] = value; } } /// Copy another matrix. Matrix2D& operator= ( const Matrix2D& other ) { this->Resize( other.NumberOfColumns(), other.NumberOfRows() ); memcpy( (*this)[0], other[0], this->m_NumberOfElements * sizeof( T ) ); return *this; } private: /// Size of the allocated array. size_t m_NumberOfElements; /// Number of rows. size_t m_NumberOfColumns; /// Number of rows. size_t m_NumberOfRows; }; /// Three-dimensional matrix template. template class Matrix3D : /// For access, make this a 2-D matrix of pointers. public Matrix2D { public: /// This class. typedef Matrix3D Self; /// Smart pointer. typedef SmartPointer SmartPtr; /// Superclass. typedef Matrix2D Superclass; /// Constructor: allocate and create cross-references. Matrix3D ( const size_t dims2, const size_t dims1, const size_t dims0 ) : Matrix2D( dims2, dims1 ) { this->m_NumberOfPlanes = dims0; this->m_NumberOfElements = dims0 * dims1 * dims2; (*this)[0][0] = Memory::ArrayC::Allocate( this->m_NumberOfElements ); for ( size_t j = 0; j < this->NumberOfRows(); ++j ) for ( size_t i = 0; i < this->NumberOfColumns(); ++i ) if ( i && j ) { (*this)[i][j] = (*this)[0][0] + this->NumberOfRows() * ( i + this->NumberOfColumns() * j ); } } /// Return number of planes size_t NumberOfPlanes() const { return this->m_NumberOfPlanes; } /// Resize the matrix. void Resize( const size_t numberOfRows, const size_t numberOfColumns, const size_t numberOfPlanes ) { if ( ( numberOfColumns != this->NumberOfColumns() ) || ( numberOfRows != this->NumberOfRows() ) || ( numberOfPlanes != this->NumberOfPlanes() ) ) { if ( (*this)[0][0] ) { Memory::ArrayC::Delete( (*this)[0][0] ); (*this)[0][0] = NULL; } this->m_NumberOfPlanes = numberOfPlanes; this->m_NumberOfElements = numberOfPlanes * numberOfRows * numberOfColumns; this->Superclass::Resize( numberOfRows, numberOfColumns ); (*this)[0][0] = Memory::ArrayC::Allocate( this->m_NumberOfElements ); for ( size_t j = 0; j < this->NumberOfRows(); ++j ) for ( size_t i = 0; i < this->NumberOfColumns(); ++i ) if ( i && j ) { (*this)[i][j] = (*this)[0][0] + this->NumberOfPlanes() * ( i + this->NumberOfColumns() * j ); } } } /// Reset all values to zero. void SetAllToZero() { memset( (*this)[0][0], 0, this->m_NumberOfElements * sizeof( T ) ); } /// Set all values. void SetAll( const T value) { for ( size_t i = 0; i < this->m_NumberOfElements; ++i ) { (*this)[0][0][i] = value; } } /// Copy another matrix. Matrix2D& operator= ( const Matrix2D& other ) { this->Resize( other.NumberOfColumns(), other.NumberOfRows(), other.NumberOfPlanes() ); memcpy( (*this)[0], other[0], this->m_NumberOfElements * sizeof( T ) ); return *this; } private: /// Planes in the 3D matrix. size_t m_NumberOfPlanes; /// Number of matrix elements. size_t m_NumberOfElements; }; //@} } // namespace cmtk #endif // #ifndef __cmtkMatrix_h_included_ cmtk-3.3.1/libs/Base/cmtkMatrix3x3.cxx000066400000000000000000000205411276303427400174760ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2013 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4831 $ // // $LastChangedDate: 2013-09-11 15:41:00 -0700 (Wed, 11 Sep 2013) $ // // $LastChangedBy: torsten_at_home $ // */ #include "cmtkMatrix3x3.h" #include #include #include #include #include namespace cmtk { template Matrix3x3& Matrix3x3::Compose( const typename Self::ElementType params[8] ) { const Units::Radians alpha = Units::Degrees( params[2] ); (*this)[0][0] = static_cast( MathUtil::Cos( alpha ) * params[3] ); (*this)[0][1] = static_cast( -MathUtil::Sin( alpha ) * params[3] ); (*this)[0][2] = static_cast( 0.0 ); (*this)[1][0] = static_cast( MathUtil::Sin( alpha ) * params[4] ); (*this)[1][1] = static_cast( MathUtil::Cos( alpha ) * params[4] ); (*this)[1][2] = static_cast( 0.0 ); (*this)[2][0] = static_cast( 0.0 ); (*this)[2][1] = static_cast( 0.0 ); (*this)[2][2] = static_cast( 1.0 ); // generate shears Self shearMatrix = Self::Identity(); shearMatrix[0][1] = static_cast( params[5] ); *this *= shearMatrix; // transform rotation center typename Self::ElementType cM[2] = { params[6]*(*this)[0][0] + params[7]*(*this)[1][0], params[6]*(*this)[0][1] + params[7]*(*this)[1][1], }; // set translations (*this)[2][0] = static_cast( params[0] - cM[0] + params[6] ); (*this)[2][1] = static_cast( params[1] - cM[1] + params[7] ); return *this; } /**\todo Currently, we cannot correctly recover the parameters of a * transformation that includes a mirroring. Rotation also gets messed up * in this case. A possible solution may involve using the determinant to * detect the mirror and then come up with something that respects this. * Shear is not even supported yet. */ template bool Matrix3x3::Decompose ( typename Self::ElementType params[8], const typename Self::ElementType *center ) const { // make a working copy of the matrix for step-by-step decomposition typename Self::ElementType matrix[3][3]; memcpy( matrix, this->m_Matrix, sizeof( matrix ) ); // translation entries params[0] = matrix[2][0]; params[1] = matrix[2][1]; if ( center ) { typename Self::ElementType cM[2] = { center[0]*matrix[0][0] + center[1]*matrix[1][0], center[0]*matrix[0][1] + center[1]*matrix[1][1] }; params[0] += cM[0] - center[0]; params[1] += cM[1] - center[1]; memcpy( params+6, center, 2*sizeof( typename Self::ElementType ) ); } else { memset( params+6, 0, 2*sizeof( typename Self::ElementType ) ); } for ( int i=0; i<2; ++i ) { // scale params[3+i] = sqrt( MathUtil::Square( matrix[i][0] ) + MathUtil::Square( matrix[i][1] ) ); // report error on singular matrices. if ( fabs(params[3+i]) < std::numeric_limits::epsilon() ) { throw typename Self::SingularMatrixException(); } } // rotation // first rotate about y axis double x2 = matrix[0][0] / params[3]; double y2 = -matrix[0][1] / params[3]; double dot = x2 * x2 + y2 * y2; double d1 = sqrt (dot); double cosTheta, sinTheta; // if (d1 < std::numeric_limits::epsilon() ) if (d1 < 1e-8 ) { cosTheta = 1.0; sinTheta = 0.0; } else { cosTheta = x2 / d1; sinTheta = y2 / d1; } params[2] = static_cast( Units::Degrees( MathUtil::ArcTan2 (sinTheta, cosTheta) ).Value() ); return true; } template void Matrix3x3::ComputeEigenvalues( T (&lambda)[3] ) const { const double M11 = (*this)[0][0]; const double M12 = (*this)[0][1]; const double M13 = (*this)[0][2]; const double M22 = (*this)[1][1]; const double M23 = (*this)[1][2]; const double M33 = (*this)[2][2]; // // --------------------------------------------------------------------------- // // Copyright (c) 2000-2003 TargetJr Consortium // GE Corporate Research and Development (GE CRD) // 1 Research Circle // Niskayuna, NY 12309 // All Rights Reserved // Reproduction rights limited as described below. // // Permission to use, copy, modify, distribute, and sell this software // and its documentation for any purpose is hereby granted without fee, // provided that (i) the above copyright notice and this permission // notice appear in all copies of the software and related documentation, // (ii) the name TargetJr Consortium (represented by GE CRD), may not be // used in any advertising or publicity relating to the software without // the specific, prior written permission of GE CRD, and (iii) any // modifications are clearly marked and summarized in a change history // log. // // THE SOFTWARE IS PROVIDED "AS IS" AND WITHOUT WARRANTY OF ANY KIND, // EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY // WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. // IN NO EVENT SHALL THE TARGETJR CONSORTIUM BE LIABLE FOR ANY SPECIAL, // INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND OR ANY // DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, // WHETHER OR NOT ADVISED OF THE POSSIBILITY OF SUCH DAMAGES, OR ON // ANY THEORY OF LIABILITY ARISING OUT OF OR IN CONNECTION WITH THE // USE OR PERFORMANCE OF THIS SOFTWARE. // // --------------------------------------------------------------------------- // // Characteristic eqtn |M - xI| = 0 // x^3 + b x^2 + c x + d = 0 const double b = -M11-M22-M33; const double c = M11*M22 +M11*M33 +M22*M33 -M12*M12 -M13*M13 -M23*M23; const double d = M11*M23*M23 +M12*M12*M33 +M13*M13*M22 -2.0*M12*M13*M23 -M11*M22*M33; // Using a numerically tweaked version of the real cubic solver http://www.1728.com/cubic2.htm const double b_3 = b/3.0; const double f = b_3*b_3 - c/3.0 ; const double g = b*c/6.0 - b_3*b_3*b_3 - 0.5*d; if (f == 0.0 && g == 0.0) { lambda[0] = lambda[1] = lambda[2] = static_cast( - b_3 ); return; } const double f3 = f*f*f; const double g2 = g*g; const double sqrt_f = -sqrt(f); // deal explicitly with repeated root and treat // complex conjugate roots as numerically inaccurate repeated roots. // first check we are not too numerically innacurate // assert((g2 - f3) / vnl_math_sqr(b*b*b) < 1e-8); if (g2 >= f3) { if (g < 0.0) { lambda[0] = static_cast( 2.0 * sqrt_f - b_3 ); lambda[1] = lambda[2] = static_cast( - sqrt_f - b_3 ); } else { lambda[0] = lambda[1] = static_cast( sqrt_f - b_3 ); lambda[2] = static_cast( -2.0 * sqrt_f - b_3 ); } return; } const double sqrt_f3 = sqrt_f * sqrt_f * sqrt_f; const double k = acos(g / sqrt_f3) / 3.0; const double j = 2.0 * sqrt_f; lambda[0] = static_cast( j * cos(k) - b_3 ); lambda[1] = static_cast( j * cos(k + M_PI * 2.0 / 3.0) - b_3 ); lambda[2] = static_cast( j * cos(k - M_PI * 2.0 / 3.0) - b_3 ); T tmp; if (lambda[1] < lambda[0]) { tmp = lambda[1]; lambda[1] = lambda[0]; lambda[0] = tmp; } if (lambda[2] < lambda[1]) { tmp = lambda[1]; lambda[1] = lambda[2]; lambda[2] = tmp; if (lambda[1] < lambda[0]) { tmp = lambda[1]; lambda[1] = lambda[0]; lambda[0] = tmp; } } } template class Matrix3x3; template class Matrix3x3; } // namespace cmtk cmtk-3.3.1/libs/Base/cmtkMatrix3x3.h000066400000000000000000000057641276303427400171350ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2013 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 5129 $ // // $LastChangedDate: 2014-01-09 13:48:15 -0800 (Thu, 09 Jan 2014) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkMatrix3x3_h_included_ #define __cmtkMatrix3x3_h_included_ #include #include #include #include namespace cmtk { /** \addtogroup Base */ //@{ /// Homogeneous 3x3 transformation matrix. template class Matrix3x3 : public FixedSquareMatrix<3,T> { public: /// This class. typedef Matrix3x3 Self; /// The floating point element type. typedef T ElementType; /// Parent class. typedef FixedSquareMatrix<3,T> Superclass; /// Default constructor. Matrix3x3() {} /// Copy constructor. Matrix3x3( const Superclass& other ) : Superclass( other ) {} /** Array constructor. * If a NULL parameter is given, an uninitialized matrix is generated. This * is intended behaviour. */ Matrix3x3( const typename Self::ElementType *const values ) : Superclass( values ) {} /// 2D array constructor. template Matrix3x3( const T2 (&matrix)[3][3] ) : Superclass( matrix ) {} /// Compose from canonical parameters. Self& Compose( const typename Self::ElementType params[8] ); /// Decompose into affine parameters. bool Decompose( typename Self::ElementType params[8], const typename Self::ElementType *center = NULL ) const; /// Get determinant. typename Self::ElementType Determinant() const { return ( (*this)[0][0]*(*this)[1][1]*(*this)[2][2] + (*this)[0][1]*(*this)[1][2]*(*this)[2][0] + (*this)[0][2]*(*this)[1][0]*(*this)[2][1] - (*this)[0][2]*(*this)[1][1]*(*this)[2][0] - (*this)[0][0]*(*this)[1][2]*(*this)[2][1] - (*this)[0][1]*(*this)[1][0]*(*this)[2][2] ); } /// Compute eigenvalues. void ComputeEigenvalues( typename Self::ElementType (&lambda)[3] ) const; }; /// Define coordinate matrix. typedef Matrix3x3 CoordinateMatrix3x3; //@} } // namespace cmtk #endif // #ifndef __cmtkMatrix3x3_h_included_ cmtk-3.3.1/libs/Base/cmtkMatrix4x4.cxx000066400000000000000000000302401276303427400174750ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2013 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 5160 $ // // $LastChangedDate: 2014-01-12 14:33:52 -0800 (Sun, 12 Jan 2014) $ // // $LastChangedBy: torsten_at_home $ // */ #include "cmtkMatrix4x4.h" #include #include #include #include #include #include #include namespace cmtk { template Matrix4x4::Matrix4x4( const Matrix3x3& other ) { for ( int j=0; j<3; ++j ) { for ( int i=0; i<3; ++i ) { this->m_Matrix[i][j] = other[i][j]; } } for ( int j=0; j<3; ++j ) { this->m_Matrix[3][j] = this->m_Matrix[j][3] = 0.0; } this->m_Matrix[3][3] = 1.0; } template const Matrix3x3 Matrix4x4::GetTopLeft3x3() const { Matrix3x3 m3; for ( int j=0; j<3; ++j ) { for ( int i=0; i<3; ++i ) { m3[i][j] = this->m_Matrix[i][j]; } } return m3; } template Matrix4x4& Matrix4x4::Compose ( const Types::Coordinate params[15], const bool logScaleFactors ) { const Units::Radians alpha = Units::Degrees( params[3] ); const Units::Radians theta = Units::Degrees( params[4] ); const Units::Radians phi = Units::Degrees( params[5] ); const double cos0 = MathUtil::Cos(alpha), sin0 = MathUtil::Sin(alpha); const double cos1 = MathUtil::Cos(theta), sin1 = MathUtil::Sin(theta); const double cos2 = MathUtil::Cos( phi), sin2 = MathUtil::Sin( phi); const double sin0xsin1 = sin0 * sin1; const double cos0xsin1 = cos0 * sin1; Self rotation = Self::Identity(); rotation[0][0] = static_cast( cos1*cos2 ); rotation[0][1] = static_cast( -cos1*sin2 ); rotation[0][2] = static_cast( -sin1 ); rotation[1][0] = static_cast( (sin0xsin1*cos2 + cos0*sin2) ); rotation[1][1] = static_cast( (-sin0xsin1*sin2 + cos0*cos2) ); rotation[1][2] = static_cast( sin0*cos1 ); rotation[2][0] = static_cast( (cos0xsin1*cos2 - sin0*sin2) ); rotation[2][1] = static_cast( (-cos0xsin1*sin2 - sin0*cos2) ); rotation[2][2] = static_cast( cos0*cos1 ); // generate shears Self scaleShear = Self::Identity(); for ( int i = 0; i < 3; ++i ) { scaleShear[i][i] = (logScaleFactors) ? exp( params[6+i] ) : params[6+i]; scaleShear[(i/2)+(i%2)+1][i/2] = params[9+i]; } *this = scaleShear * rotation; // transform rotation center const Types::Coordinate cM[3] = { params[12]*this->m_Matrix[0][0] + params[13]*this->m_Matrix[1][0] + params[14]*this->m_Matrix[2][0], params[12]*this->m_Matrix[0][1] + params[13]*this->m_Matrix[1][1] + params[14]*this->m_Matrix[2][1], params[12]*this->m_Matrix[0][2] + params[13]*this->m_Matrix[1][2] + params[14]*this->m_Matrix[2][2] }; // set translations this->m_Matrix[3][0] = params[0] - cM[0] + params[12]; this->m_Matrix[3][1] = params[1] - cM[1] + params[13]; this->m_Matrix[3][2] = params[2] - cM[2] + params[14]; return *this; } template bool Matrix4x4::Decompose ( Types::Coordinate params[15], const Types::Coordinate *center, const bool logScaleFactor ) const { // translation entries params[0] = this->m_Matrix[3][0]; params[1] = this->m_Matrix[3][1]; params[2] = this->m_Matrix[3][2]; if ( center ) { const Types::Coordinate cM[3] = { center[0]*this->m_Matrix[0][0] + center[1]*this->m_Matrix[1][0] + center[2]*this->m_Matrix[2][0], center[0]*this->m_Matrix[0][1] + center[1]*this->m_Matrix[1][1] + center[2]*this->m_Matrix[2][1], center[0]*this->m_Matrix[0][2] + center[1]*this->m_Matrix[1][2] + center[2]*this->m_Matrix[2][2], }; params[0] += cM[0] - center[0]; params[1] += cM[1] - center[1]; params[2] += cM[2] - center[2]; if ( center != params+12) { // sometimes we may get a pointer to our own parameters memcpy( params+12, center, 3*sizeof( Types::Coordinate ) ); } } else { memset( params+12, 0, 3*sizeof( Types::Coordinate ) ); } // use QR decomposition to separate rotation (Q) from shear and scales (R) Matrix2D matrix2d( 3, 3 ); for ( int i = 0; i < 3; ++i ) { for ( int j = 0; j < 3; ++j ) { // We use QR decomposition, but because we do left-multiplication of coordinate vector times transformation matrix, // the matrix is actually (RQ)^T, and we instead need to decompose the transformation matrix transpose. matrix2d[i][j] = this->m_Matrix[j][i]; } } QRDecomposition qr( matrix2d ); const Matrix2D R = qr.GetR(); const Matrix2D Q = qr.GetQ(); for ( int k=0; k<3; ++k ) { // if scale is negative, make positive and correct Q and R accordingly (we will figure out later if the overall transformation is a true rotation or has a negative determinant) if ( R[k][k] < 0 ) { for ( int i=0; i<3; ++i ) { R[k][i] = -R[k][i]; Q[i][k] = -Q[i][k]; } } // scale params[6 + k] = R[k][k]; // report error on singular matrices. if ( params[6+k] < std::numeric_limits::epsilon() ) { throw typename Self::SingularMatrixException(); } // shear const int i = k / 2; // i.e. i := { 0, 0, 1 } const int j = i + (k%2) + 1; // i.e. j := { 1, 2, 2 } -- so i,j index the upper triangle of aMat, which is R from QR params[9+k] = R[i][j]; } /*========================================================================= THE FOLLOWING CODE WAS ADOPTED AND MODIFIED FROM VTK, The Visualization Toolkit. Program: Visualization Toolkit Language: C++ Thanks: Thanks to David G. Gobbi who developed this class. Copyright (c) 1993-2001 Ken Martin, Will Schroeder, Bill Lorensen All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither name of Ken Martin, Will Schroeder, or Bill Lorensen nor the names of any contributors may be used to endorse or promote products derived from this software without specific prior written permission. * Modified source versions must be plainly marked as such, and must not be misrepresented as being the original software. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. =========================================================================*/ double d; double d1; double d2; double dot; double cosPhi, sinPhi; double cosTheta, sinTheta; double cosAlpha, sinAlpha; double x2, y2, z2; double x3, y3, z3; double x3p, y3p; const Types::Coordinate determinant = this->m_Matrix[0][0]*this->m_Matrix[1][1]*this->m_Matrix[2][2] + this->m_Matrix[0][1]*this->m_Matrix[1][2]*this->m_Matrix[2][0] + this->m_Matrix[0][2]*this->m_Matrix[1][0]*this->m_Matrix[2][1] - this->m_Matrix[0][2]*this->m_Matrix[1][1]*this->m_Matrix[2][0] - this->m_Matrix[0][0]*this->m_Matrix[1][2]*this->m_Matrix[2][1] - this->m_Matrix[0][1]*this->m_Matrix[1][0]*this->m_Matrix[2][2]; // if negative determinant, this is not a true rotation if ( determinant < 0 ) { // negate x scale params[6] = -params[6]; // also negative shears related to x params[9] = -params[9]; params[10] = -params[10]; } // Now deal with the rotation Q: // First rotate about y axis x2 = Q[1][0] / params[6]; y2 = Q[2][0] / params[6]; z2 = Q[0][0] / params[6]; x3 = Q[1][2] / params[8]; y3 = Q[2][2] / params[8]; z3 = Q[0][2] / params[8]; dot = x2 * x2 + z2 * z2; d1 = sqrt (dot); // if (d1 < std::numeric_limits::epsilon()) if (d1 < 1e-8) { cosTheta = 1.0; sinTheta = 0.0; } else { cosTheta = z2 / d1; sinTheta = x2 / d1; } params[5] = Units::Degrees( -MathUtil::ArcTan2( sinTheta, cosTheta ) ).Value(); // theta // now rotate about x axis dot = x2 * x2 + y2 * y2 + z2 * z2; d = sqrt (dot); // if (d < std::numeric_limits::epsilon()) if (d < 1e-8) { sinPhi = 0.0; cosPhi = 1.0; } else if (d1 < std::numeric_limits::epsilon()) { sinPhi = y2 / d; cosPhi = z2 / d; } else { sinPhi = y2 / d; cosPhi = ( x2 * x2 + z2 * z2) / (d1 * d); } params[4] = Units::Degrees( -MathUtil::ArcTan2( sinPhi, cosPhi ) ).Value(); // phi // finally, rotate about z x3p = x3 * cosTheta - z3 * sinTheta; y3p = - sinPhi * sinTheta * x3 + cosPhi * y3 - sinPhi * cosTheta * z3; dot = x3p * x3p + y3p * y3p; d2 = sqrt (dot); // if (d2 < std::numeric_limits::epsilon()) if (d2 < 1e-8) { cosAlpha = 1.0; sinAlpha = 0.0; } else { cosAlpha = y3p / d2; sinAlpha = x3p / d2; } params[3] = Units::Degrees( -MathUtil::ArcTan2( sinAlpha, cosAlpha ) ).Value(); // alpha if ( logScaleFactor ) { for ( int i = 6; i < 9; ++i ) params[i] = log( params[i] ); } return true; /** END OF ADOPTED VTK CODE **/ } template Matrix4x4& Matrix4x4::ChangeCoordinateSystem ( const FixedVector<3,T>& newX, const FixedVector<3,T>& newY ) { // rotate x axis to match new coordinate system Self rotate = RotateX( -atan2( newX[1], newX[2] ) ); rotate *= RotateY( acos( newX[0] ) ); // rotate previously rotated y axis further to match new coordinate system const FixedVector<3,T> newYrot = newY * rotate; rotate *= RotateX( atan2( newYrot[2], newYrot[1] ) ); // z axis now matches automatically // apply rotation matrix to previous transformation to apply change of // coordinate systems. *this *= rotate; *this = rotate.GetInverse() * *this; return *this; } template Matrix4x4 Matrix4x4::RotateX( const T angle ) { Self rot = Self::Identity(); rot[1][1] = rot[2][2] = cos( angle ); rot[1][2] = -1.0 * (rot[2][1] = sin( angle ) ); return rot; } template Matrix4x4 Matrix4x4::RotateY( const T angle ) { Self rot = Self::Identity(); rot[0][0] = rot[2][2] = cos( angle ); rot[0][2] = -1.0 * (rot[2][0] = sin( angle ) ); return rot; } template Matrix4x4 Matrix4x4::RotateZ( const T angle ) { Self rot = Self::Identity(); rot[0][0] = rot[1][1] = cos( angle ); rot[0][1] = -1.0 * (rot[1][0] = sin( angle ) ); return rot; } template class Matrix4x4; } // namespace cmtk cmtk-3.3.1/libs/Base/cmtkMatrix4x4.h000066400000000000000000000061161276303427400171270ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2013 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 5160 $ // // $LastChangedDate: 2014-01-12 14:33:52 -0800 (Sun, 12 Jan 2014) $ // // $LastChangedBy: torsten_at_home $ // */ #ifndef __cmtkMatrix4x4_h_included_ #define __cmtkMatrix4x4_h_included_ #include #include #include #include #include #include #include namespace cmtk { /** \addtogroup Base */ //@{ /// Homogeneous 4x4 transformation matrix. template class Matrix4x4 : public FixedSquareMatrix<4,T> { public: /// This type instance. typedef Matrix4x4 Self; /// Base class.. typedef FixedSquareMatrix<4,T> Superclass; /// Smart pointer. typedef SmartPointer SmartPtr; /// Default constructor. Matrix4x4() {} /// Copy-from-baseclass constructor. Matrix4x4( const Superclass& other ) : Superclass( other ) {} /// Top left submatrix copy constructor. Matrix4x4( const Matrix3x3& other ); /** Array constructor. * If a NULL parameter is given, an uninitialized matrix is generated. This * is intended behaviour. */ Matrix4x4( const T *const values ) : Superclass( values ) {} /// 2D array constructor. template Matrix4x4( const T2 (&matrix)[4][4] ) : Superclass( matrix ) {} /// Compose from canonical parameters. Self& Compose( const Types::Coordinate params[15], const bool logScaleFactors = false ); /// Decompose into affine parameters. bool Decompose( Types::Coordinate params[15], const Types::Coordinate *center = NULL, const bool logScaleFactors = false ) const; /// Get top-left 3x3 submatrix. const Matrix3x3 GetTopLeft3x3() const; /** Change reference coordinate system. */ Self& ChangeCoordinateSystem( const FixedVector<3,T>& newX, const FixedVector<3,T>& newY ); /// Return rotation around x axis. static Self RotateX( const T angle ); /// Return rotation around y axis. static Self RotateY( const T angle ); /// Return rotation around z axis. static Self RotateZ( const T angle ); }; //@} } // namespace cmtk #endif // #ifndef __cmtkMatrix4x4_h_included_ cmtk-3.3.1/libs/Base/cmtkMetaInformationObject.cxx000066400000000000000000000034141276303427400221170ustar00rootroot00000000000000/* // // Copyright 2010-2011 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 3503 $ // // $LastChangedDate: 2011-10-21 21:05:37 -0700 (Fri, 21 Oct 2011) $ // // $LastChangedBy: torsten_at_home $ // */ #include "cmtkMetaInformationObject.h" const std::string& cmtk::MetaInformationObject::GetMetaInfo( const std::string& key, const std::string& defaultVal ) const { Self::KeyValueMapType::const_iterator it = this->m_MetaInformation.find( key ); if ( it != this->m_MetaInformation.end() ) return it->second; else return defaultVal; } void cmtk::MetaInformationObject::SetMetaInfo( const std::string& key, const std::string& value ) { this->m_MetaInformation[key] = value; } void cmtk::MetaInformationObject::CopyMetaInfo( const Self& other, const std::string& key ) { Self::KeyValueMapType::const_iterator it = other.m_MetaInformation.find( key ); if ( it != other.m_MetaInformation.end() ) this->SetMetaInfo( it->first, it->second ); } cmtk-3.3.1/libs/Base/cmtkMetaInformationObject.h000066400000000000000000000124531276303427400215470ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2012 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 5220 $ // // $LastChangedDate: 2014-03-04 13:19:27 -0800 (Tue, 04 Mar 2014) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkMetaInformationObject_h_included_ #define __cmtkMetaInformationObject_h_included_ #include #include #include namespace cmtk { const char* const META_FS_PATH = "FILESYSTEM_PATH"; const char* const META_FILEFORMAT_ORIGINAL = "FILEFORMAT_ORIGINAL"; const char* const META_SPACE = "SPACE"; const char* const META_SPACE_ORIGINAL = "SPACE_ORIGINAL"; const char* const META_SPACE_UNITS_STRING = "SPACE_UNITS_STRING"; const char* const META_EXTERNAL_SPACE_ID = "SPACE_ID_EXTERNAL"; const char* const META_IMAGE_ORIENTATION = "IMAGE_ORIENTATION"; const char* const META_IMAGE_ORIENTATION_ORIGINAL = "IMAGE_ORIENTATION_ORIGINAL"; const char* const META_IMAGE_DESCRIPTION = "IMAGE_DESCRIPTION"; const char* const META_IMAGE_DIRECTION_VECTORS = "IMAGE_DIRECTION_VECTORS"; // Slice order (ascending/descending, sequential/interleaved) const char* const META_IMAGE_SLICEORDER = "IMAGE_SLICEORDER"; const char* const META_IMAGE_SLICEORDER_SI = "SEQ-INC"; const char* const META_IMAGE_SLICEORDER_SD = "SEQ-DEC"; const char* const META_IMAGE_SLICEORDER_AI = "ALT-INC"; const char* const META_IMAGE_SLICEORDER_AD = "ALT-DEC"; const char* const META_IMAGE_SLICEORDER_AI2 = "ALT-INC2"; const char* const META_IMAGE_SLICEORDER_AD2 = "ALT-DEC2"; // Slice time (duration for one slice; this should be TR/nSlices) const char* const META_IMAGE_SLICEDURATION = "IMAGE_SLICEDURATION"; // Phase encode direction within slice - this should be either "COL" (y) or "ROW" (x), as delivered by DICOM tag InPlanePhaseEncodingDirection const char* const META_IMAGE_SLICE_PEDIRECTION = "IMAGE_SLICE_PEDIRECTION"; const char* const META_XFORM_FIXED_IMAGE_PATH = "XFORM_FIXED_IMAGE_PATH"; const char* const META_XFORM_MOVING_IMAGE_PATH = "XFORM_MOVING_IMAGE_PATH"; /** \addtogroup Base */ //@{ /// Meta-information associated with library objects. class MetaInformationObject { public: /// This class. typedef MetaInformationObject Self; /// Default constructor: do nothing. MetaInformationObject() : m_XML( NULL ) {} /// Copy constructor: copy meta information when copying higher-level objects. MetaInformationObject( const MetaInformationObject& other ) : m_MetaInformation( other.m_MetaInformation ), m_XML( NULL ) {} /// Virtual destructor template. virtual ~MetaInformationObject() { if ( this->m_XML ) { mxmlDelete( this->m_XML ); } } /// Check whether a key exists. bool MetaKeyExists( const std::string& key ) const { return this->m_MetaInformation.find( key ) != this->m_MetaInformation.end(); } /// Return a meta info value. const std::string& GetMetaInfo( const std::string& key, const std::string& defaultVal = "" /*!< This string is returned if the key is not found.*/ ) const; /// Set a meta info value. void SetMetaInfo( const std::string& key, const std::string& value ); /** Copy a meta key/value pair from another meta information object. * If the key does not exist in the source object, it will not be created in this * object either. */ void CopyMetaInfo( const Self& other, const std::string& key ); /** Copy all meta key/value pairs from another meta information object. */ void CopyMetaInfo( const Self& other ) { this->m_MetaInformation = other.m_MetaInformation; } /// Check if object has XML data. bool HasXML() const { return this->m_XML != NULL; } /// Set XML data (delete existing data, if any). void SetXML( mxml_node_t *const xml ) { if ( this->m_XML ) { mxmlDelete( this->m_XML ); } this->m_XML = xml; } /// Get XML data (or NULL, if no XML data exists). const mxml_node_t* XML( const char* path = NULL /*!< Optional path to the requested element in the XML tree; return root if this is NULL */ ) const { if ( (path != NULL) && (this->m_XML != NULL) ) { return mxmlFindPath( this->m_XML, path ); } else { return this->m_XML; } } private: /// The map type used for representation of the key/value map. typedef std::map KeyValueMapType; /// The actual table of meta data: maps keys to values. Self::KeyValueMapType m_MetaInformation; /// Optional xml sidecar file contents. mxml_node_t* m_XML; }; //@} } // namespace cmtk #endif cmtk-3.3.1/libs/Base/cmtkNearestNeighborInterpolator.h000066400000000000000000000040271276303427400230040ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2009, 2014 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 5240 $ // // $LastChangedDate: 2014-03-18 14:30:29 -0700 (Tue, 18 Mar 2014) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkNearestNeighborInterpolator_h_included_ #define __cmtkNearestNeighborInterpolator_h_included_ #include namespace cmtk { /** \addtogroup Base */ //@{ namespace Interpolators { /// NearestNeighbor interpolator. class NearestNeighbor { public: /// Size of the interpolation region in grid points to the left and right. static const int RegionSizeLeftRight = 1; /// Flag whether this interpolator is suitable for labels. static const bool SuitableForLabels = true; /// Get specific interpolation weight for relative coordinate. static Types::Coordinate GetWeight( const int weight, const Types::Coordinate x ) { switch (weight) { case 0: return (x<=0.5) ? 1 : 0; case 1: return (x>0.5) ? 1 : 0; default: #ifdef DEBUG std::cerr << "weight=" << weight << " shouldn't happen!" << std::endl; exit( 1 ); #endif break; } return 0; } }; } // namespace Interpolators } // namespace cmtk #endif cmtk-3.3.1/libs/Base/cmtkParametricPlane.cxx000066400000000000000000000110541276303427400207420ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2012 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4427 $ // // $LastChangedDate: 2012-06-12 11:23:22 -0700 (Tue, 12 Jun 2012) $ // // $LastChangedBy: torstenrohlfing $ // */ #include "cmtkParametricPlane.h" #include #include namespace cmtk { /** \addtogroup Base */ //@{ ParametricPlane::ParametricPlane() : Rho( 0 ), Theta( 0 ), Phi( 0 ) { this->m_Origin = Self::CoordinateVectorType( 0.0 ); this->Update(); } void ParametricPlane::Update() { Normal[0] = MathUtil::Cos( Theta ) * MathUtil::Sin( Phi ); Normal[1] = MathUtil::Sin( Theta ) * MathUtil::Sin( Phi ); Normal[2] = MathUtil::Cos( Phi ); this->SquareNormal = Normal * Normal; } void ParametricPlane::SetNormal( const Self::CoordinateVectorType& normal ) { this->Normal = (1.0 / normal.RootSumOfSquares()) * normal; this->Phi = MathUtil::ArcCos( this->Normal[2] ); const Types::Coordinate sinPhi = MathUtil::Sin( this->Phi ); if ( sinPhi != 0 ) this->Theta = MathUtil::ArcSin( this->Normal[1] / sinPhi ); else this->Theta = Units::Degrees( 0 ); this->SquareNormal = this->Normal * this->Normal; } AffineXform* ParametricPlane::GetAlignmentXform( const byte normalAxis ) const { Types::Coordinate angles[3] = { 0, 0, 0 }; Types::Coordinate xlate[3] = { 0, 0, 0 }; AffineXform *alignment = new AffineXform; switch ( normalAxis ) { // YZ plane, i.e., normal to X case 0: { // first make y component zero. angles[2] = -1.0 * Units::Degrees( MathUtil::ArcTan2( Normal[1], Normal[0] ) ).Value(); // compute x component of normal vector after first rotation; remember that y component will be zero after this rotation. const Types::Coordinate newNormal0 = MathUtil::Sign( Normal[0] ) * sqrt( 1 - Normal[2]*Normal[2] ); angles[1] = -1.0 * Units::Degrees( MathUtil::ArcTan2( Normal[2], newNormal0 ) ).Value(); break; } // XZ plane, normal to Y case 1: { // first make x component zero. angles[2] = -1.0 * Units::Degrees( MathUtil::ArcTan2( Normal[0], Normal[1] ) ).Value(); // compute y component of normal vector after first rotation; remember that x component will be zero after this rotation. const Types::Coordinate newNormal1 = MathUtil::Sign( Normal[1] ) * sqrt( 1 - Normal[2]*Normal[2] ); angles[0] = -1.0 * Units::Degrees( MathUtil::ArcTan2( Normal[2], newNormal1 ) ).Value(); break; } // XY plane, normal to Z case 2: { // first make x component zero. angles[1] = -1.0 * Units::Degrees( MathUtil::ArcTan2( Normal[0], Normal[2] ) ).Value(); // compute z component of normal vector after first rotation; remember that x component will be zero after this rotation. const Types::Coordinate newNormal2 = MathUtil::Sign( Normal[2] ) * sqrt( 1 - Normal[1]*Normal[1] ); angles[0] = -1.0 * Units::Degrees( MathUtil::ArcTan2( Normal[1], newNormal2 ) ).Value(); break; } } alignment->ChangeCenter( this->GetOrigin() ); alignment->SetAngles( angles ); xlate[normalAxis] = Rho; alignment->SetXlate( xlate ); return alignment; } AffineXform::MatrixType ParametricPlane::GetMirrorXformMatrix() const { // put together zero-offset mirror matrix AffineXform::MatrixType M = AffineXform::MatrixType::Identity(); for ( int i = 0; i < 3; ++i ) { for ( int j = 0; j < 3; ++j ) { M[i][j] -= 2.0 * this->Normal[i]*this->Normal[j] / this->SquareNormal; } } FixedVector<3,Types::Coordinate> mo = this->m_Origin; mo *= M; for ( int j = 0; j < 3; ++j ) { M[3][j] = this->m_Origin[j] - mo[j] + 2 * this->Rho * this->Normal[j] / this->SquareNormal; } return M; } } // namespace cmtk cmtk-3.3.1/libs/Base/cmtkParametricPlane.h000066400000000000000000000152231276303427400203710ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2011 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 2752 $ // // $LastChangedDate: 2011-01-17 11:33:31 -0800 (Mon, 17 Jan 2011) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkParametricPlane_h_included_ #define __cmtkParametricPlane_h_included_ #include #include #include #include #include #include namespace cmtk { /** \addtogroup Base */ //@{ /** Class for parameterized infinite planes. */ class ParametricPlane { public: /// This class. typedef ParametricPlane Self; /// Smart pointer. typedef SmartPointer SmartPtr; /// Coordinate vector. typedef FixedVector<3,Types::Coordinate> CoordinateVectorType; /// Default constructor. ParametricPlane(); /** Origin of the coordinate system the plane parameters refer to. * This is NOT the origin of the plane. In fact, this coordinate does not * even need to be on the plane itself. It is merely the coordinate in space * relative to which the plane is parameterized. */ cmtkGetSetMacro(Self::CoordinateVectorType,Origin); /// Set parameter Rho. void SetRho( const Types::Coordinate rho ) { Rho = rho; this->Update(); } /// Get parameter Rho. Types::Coordinate GetRho() const { return Rho; } /// Set parameter Theta. void SetTheta( const Units::Degrees& theta ) { Theta = theta; this->Update(); } /// Get parameter Theta. const Units::Degrees GetTheta() const { return Theta; } /// Set parameter Phi. void SetPhi( const Units::Degrees& phi ) { Phi = phi; this->Update(); } /// Get parameter Phi. const Units::Degrees GetPhi() const { return Phi; } /// Set normal vector and determine rotation angles from it. void SetNormal( const Self::CoordinateVectorType& normal ); /// Get all parameters. void GetParameters( CoordinateVector& v ) const { v.SetDim( 6 ); v[0] = Rho; v[1] = Theta.Value(); v[2] = Phi.Value(); v[3] = this->m_Origin[0]; v[4] = this->m_Origin[1]; v[5] = this->m_Origin[2]; } /// Set all parameters. void SetParameters( const CoordinateVector& v ) { Rho = v[0]; Theta = Units::Degrees( v[1] ); Phi = Units::Degrees( v[2] ); this->m_Origin[0] = v[3]; this->m_Origin[1] = v[4]; this->m_Origin[2] = v[5]; this->Update(); } /** Determine which side of the plane a point is on. *\return 0, if given point is on the plane; +1 if point is on the same side * as Origin; -1, if point is on the other side, seen from Origin. */ char GetWhichSide( const Self::CoordinateVectorType& point ) const { // move given origin to coordinate origin Self::CoordinateVectorType p = point; p -= this->m_Origin; // compute line parameter of orthogonal projection of "point" onto this plane const Types::Coordinate intersect = Normal*p - Rho; return (intersect < 0) ? -1 : (intersect > 0) ? 1 : 0; } /// Mirror point with respect to plane. void Mirror( Self::CoordinateVectorType& toPoint, const Self::CoordinateVectorType& fromPoint ) const { toPoint = fromPoint; this->MirrorInPlace( toPoint ); } /// Mirror point in-place with respect to plane. void MirrorInPlace( Self::CoordinateVectorType& point ) const { // move given origin to coordinate origin point -= this->m_Origin; // compute line parameter of orthogonal projection of "point" onto // this plane and multiply by two to get parameter of mirrored point const Types::Coordinate intersect = 2 * (( Normal * point - Rho ) / SquareNormal); // compute mirrored point for ( int dim = 0; dim < 3; ++dim ) point[dim] -= intersect * Normal[dim]; // move given origin back to its given location point += this->m_Origin; } /// Project point onto plane. void Project( Self::CoordinateVectorType& toPoint, const Self::CoordinateVectorType& fromPoint ) const { toPoint = fromPoint; this->ProjectInPlace( toPoint ); } /// Project point onto plane in-place. void ProjectInPlace( Self::CoordinateVectorType& point ) const { // move given origin to coordinate origin point -= this->m_Origin; // compute line parameter of orthogonal projection of "point" onto // this plane const Types::Coordinate intersect = ( Normal * point - Rho ) / SquareNormal; // compute projected point for ( int dim = 0; dim < 3; ++dim ) point[dim] -= intersect * Normal[dim]; // move given origin back to its given location point += this->m_Origin; } /** Get transformation that aligns this plane with the coordinate system. * The object returned by this function represents a rigid transformation * that aligns the normal vector of this object with one of the coordinate * axes. *\param normalAxis The index of the axis to align the normal vector with (0, 1, or * 2 for x, y, or z, respectively). */ AffineXform* GetAlignmentXform( const byte normalAxis = 0 ) const; /// Get affine transformation matrix of the mirror transform. AffineXform::MatrixType GetMirrorXformMatrix() const; /** Return normal vector. */ const Self::CoordinateVectorType& GetNormal() const { return Normal; } private: /// Radius of tangent sphere with center at Origin. Types::Coordinate Rho; /// Rotation angle of tangent point around z axis through Origin. Units::Degrees Theta; /// Elevation angle of tangent point over x/y plane through Origin. Units::Degrees Phi; /// Plane normal. Self::CoordinateVectorType Normal; /// Square norm of plane normal. Types::Coordinate SquareNormal; /// Update internal fields after change of parameters. void Update(); }; //@} } // namespace cmtk #endif // #ifndef __cmtkParametricPlane_h_included_ cmtk-3.3.1/libs/Base/cmtkPolynomial.h000066400000000000000000000353111276303427400174450ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2014 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 5146 $ // // $LastChangedDate: 2014-01-10 15:30:51 -0800 (Fri, 10 Jan 2014) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkPolynomial_h_included_ #define __cmtkPolynomial_h_included_ #include #include namespace cmtk { /** \addtogroup Base */ //@{ /** Generic class template for polynomials of arbitrary degrees. * This must be implemented by partial specialization for each degree that is * needed in the program. */ template class Polynomial { public: /// This class. typedef Polynomial Self; /// Real value type. typedef TRealType RealValueType; /// Number of monomials in x, y, and z of degree up to NDegree. enum { NumberOfMonomials = 0 }; /// Evaluate the idx'th monomial at (x,y,z). static TRealType EvaluateMonomialAt( const size_t idx, const TRealType x, const TRealType y, const TRealType z ) { return 0.0; } /** Evaluate all monomials at one point. * This is more efficient than calling EvaluateMonomialAt() repeatedly, because the * computation can proceed incrementally and save most multiplications in the process. */ static void EvaluateAllMonomials( TRealType *const mvec, const TRealType x, const TRealType y, const TRealType z ) { } }; /// Generic class template for polynomials of degree 1. template class Polynomial<0,TRealType> { public: /// This class. typedef Polynomial<0,TRealType> Self; /// Real value type. typedef TRealType RealValueType; /// Number of monomials in x, y, and z of degree 0. enum { NumberOfMonomials = 1 }; /// Evaluate the idx'th monomial at (x,y,z). static TRealType EvaluateMonomialAt( const size_t idx, const TRealType x, const TRealType y, const TRealType z ) { if ( idx == 0 ) return 1.0; else return 0.0; } /** Evaluate all monomials at one point. * This is more efficient than calling EvaluateMonomialAt() repeatedly, because the * computation can proceed incrementally and save most multiplications in the process. */ static void EvaluateAllMonomials( TRealType *const, const TRealType, const TRealType, const TRealType ) {} }; /// Generic class template for polynomials of degree 1. template class Polynomial<1,TRealType> { public: /// This class. typedef Polynomial<1,TRealType> Self; /// Real value type. typedef TRealType RealValueType; /// Number of monomials in x, y, and z of degree up to 1. enum { NumberOfMonomials = 4 }; /// Evaluate the idx'th monomial at (x,y,z). static TRealType EvaluateMonomialAt( const size_t idx, const TRealType x, const TRealType y, const TRealType z ) { switch ( idx ) { case 0 : return 1.0; case 1 : return x; case 2 : return y; case 3 : return z; } return 0.0; } /** Evaluate all monomials at one point. * This is more efficient than calling EvaluateMonomialAt() repeatedly, because the * computation can proceed incrementally and save most multiplications in the process. */ static void EvaluateAllMonomials( TRealType *const mvec, const TRealType x, const TRealType y, const TRealType z ) { mvec[0] = 1.0; mvec[1] = x; mvec[2] = y; mvec[3] = z; } }; /// Generic class template for polynomials of degree 2. template class Polynomial<2,TRealType> { public: /// This class. typedef Polynomial<2,TRealType> Self; /// Real value type. typedef TRealType RealValueType; /// Number of monomials in x, y, and z of degree up to 2. enum { NumberOfMonomials = 10 }; /// Evaluate the idx'th monomial at (x,y,z). static TRealType EvaluateMonomialAt( const size_t idx, const TRealType x, const TRealType y, const TRealType z ) { switch ( idx ) { case 0 : return 1.0; case 1 : return x; case 2 : return y; case 3 : return z; case 4 : return x*x; case 5 : return x*y; case 6 : return x*z; case 7 : return y*y; case 8 : return y*z; case 9 : return z*z; } return 0.0; } /** Evaluate all monomials at one point. * This is more efficient than calling EvaluateMonomialAt() repeatedly, because the * computation can proceed incrementally and save most multiplications in the process. */ static void EvaluateAllMonomials( TRealType *const mvec, const TRealType x, const TRealType y, const TRealType z ) { Polynomial<1,TRealType>::EvaluateAllMonomials( mvec, x, y, z ); mvec[4] = mvec[1]*x; mvec[5] = mvec[1]*y; mvec[6] = mvec[1]*z; mvec[7] = mvec[2]*y; mvec[8] = mvec[2]*z; mvec[9] = mvec[3]*z; } }; /// Generic class template for polynomials of degree 3. template class Polynomial<3,TRealType> { public: /// This class. typedef Polynomial<3,TRealType> Self; /// Real value type. typedef TRealType RealValueType; /// Number of monomials in x, y, and z of degree up to 3. enum { NumberOfMonomials = 20 }; /// Evaluate the idx'th monomial at (x,y,z). static TRealType EvaluateMonomialAt( const size_t idx, const TRealType x, const TRealType y, const TRealType z ) { switch ( idx ) { case 0 : return 1.0; case 1 : return x; case 2 : return y; case 3 : return z; case 4 : return x*x; case 5 : return x*y; case 6 : return x*z; case 7 : return y*y; case 8 : return y*z; case 9 : return z*z; case 10 : return x*x*x; case 11 : return x*x*y; case 12 : return x*x*z; case 13 : return x*y*y; case 14 : return x*y*z; case 15 : return x*z*z; case 16 : return y*y*y; case 17 : return y*y*z; case 18 : return y*z*z; case 19 : return z*z*z; } return 0.0; } /** Evaluate all monomials at one point. * This is more efficient than calling EvaluateMonomialAt() repeatedly, because the * computation can proceed incrementally and save most multiplications in the process. */ static void EvaluateAllMonomials( TRealType *const mvec, const TRealType x, const TRealType y, const TRealType z ) { Polynomial<2,TRealType>::EvaluateAllMonomials( mvec, x, y, z ); mvec[10] = mvec[4]*x; mvec[11] = mvec[4]*y; mvec[12] = mvec[4]*z; mvec[13] = mvec[5]*y; mvec[14] = mvec[5]*z; mvec[15] = mvec[6]*z; mvec[16] = mvec[7]*y; mvec[17] = mvec[7]*z; mvec[18] = mvec[8]*z; mvec[19] = mvec[9]*z; } }; /// Generic class template for polynomials of degree 4. template class Polynomial<4,TRealType> { public: /// This class. typedef Polynomial<4,TRealType> Self; /// Real value type. typedef TRealType RealValueType; /// Number of monomials in x, y, and z of degree up to 4. enum { NumberOfMonomials = 35 }; /// Evaluate the idx'th monomial at (x,y,z). static TRealType EvaluateMonomialAt( const size_t idx, const TRealType x, const TRealType y, const TRealType z ) { switch ( idx ) { case 0 : return 1.0; case 1 : return x; case 2 : return y; case 3 : return z; case 4 : return x*x; case 5 : return x*y; case 6 : return x*z; case 7 : return y*y; case 8 : return y*z; case 9 : return z*z; case 10 : return x*x*x; case 11 : return x*x*y; case 12 : return x*x*z; case 13 : return x*y*y; case 14 : return x*y*z; case 15 : return x*z*z; case 16 : return y*y*y; case 17 : return y*y*z; case 18 : return y*z*z; case 19 : return z*z*z; case 20 : return x*x*x*x; case 21 : return x*x*x*y; case 22 : return x*x*x*z; case 23 : return x*x*y*y; case 24 : return x*x*y*z; case 25 : return x*x*z*z; case 26 : return x*y*y*y; case 27 : return x*y*y*z; case 28 : return x*y*z*z; case 29 : return x*z*z*z; case 30 : return y*y*y*y; case 31 : return y*y*y*z; case 32 : return y*y*z*z; case 33 : return y*z*z*z; case 34 : return z*z*z*z; } return 0.0; } /// Evaluate the derivative of idx'th monomial w.r.t. x at (x,y,z). static TRealType EvaluateMonomialDXAt( const size_t idx, const TRealType x, const TRealType y, const TRealType z ) { switch ( idx ) { case 0 : return 0; case 1 : return 1; case 2 : return 0; case 3 : return 0; case 4 : return 2*x; case 5 : return y; case 6 : return z; case 7 : return 0; case 8 : return 0; case 9 : return 0; case 10 : return 3*x*x; case 11 : return 2*x*y; case 12 : return 2*x*z; case 13 : return y*y; case 14 : return y*z; case 15 : return z*z; case 16 : return 0; case 17 : return 0; case 18 : return 0; case 19 : return 0; case 20 : return 4*x*x*x; case 21 : return 3*x*x*y; case 22 : return 3*x*x*z; case 23 : return 2*x*y*y; case 24 : return 2*x*y*z; case 25 : return 2*x*z*z; case 26 : return y*y*y; case 27 : return y*y*z; case 28 : return y*z*z; case 29 : return z*z*z; case 30 : return 0; case 31 : return 0; case 32 : return 0; case 33 : return 0; case 34 : return 0; } return 0.0; } /// Evaluate the derivative of idx'th monomial w.r.t. y at (x,y,z). static TRealType EvaluateMonomialDYAt( const size_t idx, const TRealType x, const TRealType y, const TRealType z ) { switch ( idx ) { case 0 : return 0; case 1 : return 0; case 2 : return 1; case 3 : return 0; case 4 : return 0; case 5 : return x; case 6 : return 0; case 7 : return 2*y; case 8 : return z; case 9 : return 0; case 10 : return 0; case 11 : return y; case 12 : return 0; case 13 : return 2*x*y; case 14 : return x*z; case 15 : return 0; case 16 : return 3*y*y; case 17 : return 2*y*z; case 18 : return z*z; case 19 : return 0; case 20 : return 0; case 21 : return x*x*x; case 22 : return 0; case 23 : return 2*y*x*x; case 24 : return x*x*z; case 25 : return x*x*z*z; case 26 : return x*3*y*y; case 27 : return x*2*y*z; case 28 : return x*z*z; case 29 : return 0; case 30 : return 4*y*y*y; case 31 : return 3*y*y*z; case 32 : return 2*y*z*z; case 33 : return z*z*z; case 34 : return 0; } return 0.0; } /// Evaluate the derivative of idx'th monomial w.r.t. z at (x,y,z). static TRealType EvaluateMonomialDZAt( const size_t idx, const TRealType x, const TRealType y, const TRealType z ) { switch ( idx ) { case 0 : return 0; case 1 : return 0; case 2 : return 0; case 3 : return 1; case 4 : return 0; case 5 : return 0; case 6 : return x; case 7 : return 0; case 8 : return y; case 9 : return 2*z; case 10 : return 0; case 11 : return 0; case 12 : return x*x; case 13 : return 0; case 14 : return x*y; case 15 : return x*2*z; case 16 : return 0; case 17 : return y*y; case 18 : return y*2*z; case 19 : return 3*z*z; case 20 : return 0; case 21 : return 0; case 22 : return x*x*x; case 23 : return 0; case 24 : return x*x*y; case 25 : return x*x*2*z; case 26 : return 0; case 27 : return x*y*y; case 28 : return x*y*2*z; case 29 : return x*3*z*z; case 30 : return y*y*y*y; case 31 : return y*y*y; case 32 : return y*y*2*z; case 33 : return y*3*z*z; case 34 : return 3*z*z*z; } return 0.0; } /** Evaluate all monomials at one point. * This is more efficient than calling EvaluateMonomialAt() repeatedly, because the * computation can proceed incrementally and save most multiplications in the process. */ static void EvaluateAllMonomials( TRealType *const mvec, const TRealType x, const TRealType y, const TRealType z ) { Polynomial<3,TRealType>::EvaluateAllMonomials( mvec, x, y, z ); mvec[20] = mvec[10] * x; mvec[21] = mvec[10] * y; mvec[22] = mvec[10] * z; mvec[23] = mvec[11] * y; mvec[24] = mvec[11] * z; mvec[25] = mvec[12] * z; mvec[26] = mvec[13] * y; mvec[27] = mvec[13] * z; mvec[28] = mvec[14] * z; mvec[29] = mvec[15] * z; mvec[30] = mvec[16] * y; mvec[31] = mvec[16] * z; mvec[32] = mvec[17] * z; mvec[33] = mvec[18] * z; mvec[34] = mvec[19] * z; } }; /// Polynomial helper class. class PolynomialHelper { public: /// This class. typedef PolynomialHelper Self; /// Exception class thrown when unsupported degree is used. class DegreeUnsupported : public Exception { public: /// Constructor: take a message and had over to parent class. DegreeUnsupported( const std::string& msg = "", const void *const fromObject = NULL ) : Exception( msg, fromObject ) {} }; /// Get number of monomials in a polynomial of given degree. static unsigned int GetNumberOfMonomials( const int degree ) { switch ( degree ) { case -1: return 0; // this is only here so we can compute "relative" number of monomials easily, i.e., additional monomials at degree n on top of those at degree n-1 case 0: return Polynomial<0>::NumberOfMonomials; case 1: return Polynomial<1>::NumberOfMonomials; case 2: return Polynomial<2>::NumberOfMonomials; case 3: return Polynomial<3>::NumberOfMonomials; case 4: return Polynomial<4>::NumberOfMonomials; default: throw Self::DegreeUnsupported( "Supported degrees are 0 through 4" ); } } }; //@} } // namespace cmtk #endif // #ifndef __cmtkPolynomial_h_included_ cmtk-3.3.1/libs/Base/cmtkPolynomialXform.cxx000066400000000000000000000067261276303427400210440ustar00rootroot00000000000000/* // // Copyright 2016 Google, Inc. // // Copyright 2014 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 5419 $ // // $LastChangedDate: 2016-01-20 23:09:13 -0800 (Wed, 20 Jan 2016) $ // // $LastChangedBy: torstenrohlfing $ // */ #include "cmtkPolynomialXform.h" namespace cmtk { /** \addtogroup Base */ //@{ bool PolynomialXform::ApplyInverse ( const Self::SpaceVectorType& v, Self::SpaceVectorType& u, const Types::Coordinate accuracy ) const { return this->ApplyInverseWithInitial( v, u, v*this->GetGlobalAffineMatrix().GetInverse(), accuracy ); } const CoordinateMatrix3x3 PolynomialXform::GetJacobian( const Self::SpaceVectorType& v ) const { const Self::SpaceVectorType& vRel = v - this->m_Center; CoordinateMatrix3x3 J = CoordinateMatrix3x3::Identity(); size_t paramIdx = 0; for ( size_t monomialIdx = 0; monomialIdx < this->m_NumberOfMonomials; ++monomialIdx ) { const Types::Coordinate monomialValueDX = Polynomial<4,Types::Coordinate>::EvaluateMonomialDXAt( monomialIdx, vRel[0], vRel[1], vRel[2] ); const Types::Coordinate monomialValueDY = Polynomial<4,Types::Coordinate>::EvaluateMonomialDYAt( monomialIdx, vRel[0], vRel[1], vRel[2] ); const Types::Coordinate monomialValueDZ = Polynomial<4,Types::Coordinate>::EvaluateMonomialDZAt( monomialIdx, vRel[0], vRel[1], vRel[2] ); for ( size_t i = 0; i < 3; ++i, ++paramIdx ) { J[0][i] += this->m_Parameters[paramIdx] * monomialValueDX; J[1][i] += this->m_Parameters[paramIdx] * monomialValueDY; J[2][i] += this->m_Parameters[paramIdx] * monomialValueDZ; } } return J; } const CoordinateMatrix3x3 PolynomialXform::GetLinearMatrix() const { CoordinateMatrix3x3 m3 = CoordinateMatrix3x3::Identity(); // There is a linear matrix only of degree is non-zero, i.e., the // polynomial transform includes at least the linear monomials. if (m_Degree > 0) { size_t paramIdx = 3; for ( size_t j = 0; j < 3; ++j ) { for ( size_t i = 0; i < 3; ++i, ++paramIdx ) { m3[j][i] += this->m_Parameters[paramIdx]; } } } return m3; } const AffineXform::MatrixType PolynomialXform::GetGlobalAffineMatrix() const { // initialize top-left 3x3 as the linear matrix. CoordinateMatrix3x3 m3( this->GetLinearMatrix() ); // transform center using linear matrix const Self::SpaceVectorType cM = this->m_Center * m3; // initialize top-left 3x3 as the linear matrix. AffineXform::MatrixType m4x4( this->GetLinearMatrix() ); // fill in translation parameters, accounting for center. for ( size_t i = 0; i < 3; ++i ) { m4x4[3][i] = this->m_Parameters[i] - cM[i] + this->m_Center[i]; } return m4x4; } //@} } // namespace cmtk cmtk-3.3.1/libs/Base/cmtkPolynomialXform.h000066400000000000000000000136101276303427400204570ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2012, 2014 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 5163 $ // // $LastChangedDate: 2014-01-12 15:40:23 -0800 (Sun, 12 Jan 2014) $ // // $LastChangedBy: torsten_at_home $ // */ #ifndef __cmtkPolynomialXform_h_included_ #define __cmtkPolynomialXform_h_included_ #include #include #include #include #include #include namespace cmtk { /** \addtogroup Base */ //@{ /** 3D polynomial coordinate transformation. */ class PolynomialXform : /// Inherit from meta data information container. public Xform { public: /// This class. typedef PolynomialXform Self; /// This class. typedef Xform Superclass; /// Smart pointer to this class. typedef SmartPointer SmartPtr; /// Smart pointer-to-const for this class. typedef SmartConstPointer SmartConstPtr; /// Copy constructor. PolynomialXform( const PolynomialXform& other ) : Xform( other ), m_Degree( other.m_Degree ), m_Center( other.m_Center ), m_NumberOfMonomials( other.m_NumberOfMonomials ) { } /// Default constructor. PolynomialXform( const byte degree = 0 /*!< Polynomial degree - 0 through 4 are supported. */ ) : m_Degree( degree ) { // Sort out how many monomials a polynomial of the given degree has. this->m_NumberOfMonomials = PolynomialHelper::GetNumberOfMonomials( this->m_Degree ); // Allocate one set per spatial dimension this->AllocateParameterVector( 3 * this->m_NumberOfMonomials ); } /// Clone and return smart pointer. Self::SmartPtr Clone () const { return Self::SmartPtr( this->CloneVirtual() ); } /// Virtual destructor. virtual ~PolynomialXform() {} /// Get degree of the polynomial. byte Degree() const { return this->m_Degree; } /** Set center of the transformation. *\warning This will change the meaning of the transformation. */ void SetCenter( const Self::SpaceVectorType& center ) { this->m_Center = center; } /// Get transformation center. const Self::SpaceVectorType& Center() const { return this->m_Center; } /// Apply transformation to vector. virtual Self::SpaceVectorType Apply ( const Self::SpaceVectorType& v ) const { // initialize result vector as input vector (polynomial xform is a relative transformation) Self::SpaceVectorType result = v; // now apply actual monomials size_t paramIdx = 0; for ( size_t monomialIdx = 0; monomialIdx < this->m_NumberOfMonomials; ++monomialIdx ) { const Types::Coordinate monomialValue = this->GetMonomialAt( monomialIdx, v ); for ( size_t dim = 0; dim < 3; ++dim, ++paramIdx ) result[dim] += this->m_Parameters[paramIdx] * monomialValue; } return result; } /// Get monomial at coordinate. Types::Coordinate GetMonomialAt( const size_t idx /*!< Index of the monomial to get. */, const Self::SpaceVectorType& v ) const { // remove "center" from input const Self::SpaceVectorType vRel = v - this->m_Center; return Polynomial<4,Types::Coordinate>::EvaluateMonomialAt( idx, vRel[0], vRel[1], vRel[2] ); } /** Return inverse-transformed vector. * This uses the Jacobian-based search algorithm inherited from cmtk::Xform, with the * initial estimate of the iverse derived from the inverse of the affine sub-transformation * within this polynomial. */ virtual bool ApplyInverse ( const Self::SpaceVectorType& v, Self::SpaceVectorType& u, const Types::Coordinate = 0.01 ) const; /// Get local Jacobian. virtual const CoordinateMatrix3x3 GetJacobian( const Self::SpaceVectorType& v ) const; /// Compute Jacobian determinant at a certain location. virtual Types::Coordinate GetJacobianDeterminant ( const Self::SpaceVectorType& v ) const { return this->GetJacobian( v ).Determinant() ; } /** Get global linear transformation matrix. * The global linear matrix comprises the first-order components of the polynomial transformation, i.e., the rotational/scale/shear components. */ const CoordinateMatrix3x3 GetLinearMatrix() const; /** Get global affine sub-transformation matrix. * The global affine sub-transformation comprises the zero- and first-order * components of the polynomial transformation, i.e., the translational as well * as rotational/scale/shear components. */ const AffineXform::MatrixType GetGlobalAffineMatrix() const; /// Get global scaling factor. virtual Types::Coordinate GetGlobalScaling() const { return this->GetGlobalAffineMatrix().GetTopLeft3x3().Determinant(); } protected: /// Polynomial degree. byte m_Degree; /// Center of source coordinate space (all coordinates relative to this) Self::SpaceVectorType m_Center; /// Number of monomials (per spatial dimension) size_t m_NumberOfMonomials; /// Actual virtual clone constructor function. virtual Self* CloneVirtual () const { return new Self( *this ); } }; //@} } // namespace cmtk #endif // #ifdef __cmtkPolynomialXform_h_included_ cmtk-3.3.1/libs/Base/cmtkProbeInfo.h000066400000000000000000000067421276303427400172130ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2012 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4354 $ // // $LastChangedDate: 2012-05-21 15:37:08 -0700 (Mon, 21 May 2012) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkProbeInfo_h_included_ #define __cmtkProbeInfo_h_included_ #include #include #include namespace cmtk { /** \addtogroup Base */ //@{ /** Information on volume node data. * This class represents all information needed for trilinear interpolation and * partial derivative computation. It holds a grid location and the node data * on the corners of the grid cube containing that location. */ class ProbeInfo { public: /** Node data on cube corners around given location. * The order of the data elements is as follows: (x0,y0,z0), (x1,y0,z0), * (x0,y1,z0), (x1,y1,z0), (x0,y0,z1), (x1,y0,z1), (x0,y1,z1), (x1,y1,z1). */ Types::DataItem Values[8]; /** Dimensions of the grid cube. */ Types::Coordinate Deltas[3]; /** Relative location of the probed coordinate. * Represented are the relative distances of the probed coordinate from the * surrounding cube's faces. Values range from 0 to 1. The order of the * coefficients is dX0, dY0, dZ0, dX1, dY1, dZ1. Lower x-coordinates for * example are weighted with dX0, others respectively. */ Types::Coordinate Offsets[6]; /** The real location that was probed. */ Vector3D Location; /** Return data value at desired location. * Trlinear interpolation is performed using the pre-calculated coefficients. */ Types::DataItem GetValueTrilinear () const { return static_cast( Offsets[2]*(Offsets[1]*(Offsets[0]*Values[0]+Offsets[3]*Values[1])+ Offsets[4]*(Offsets[0]*Values[2]+Offsets[3]*Values[3]))+ Offsets[5]*(Offsets[1]*(Offsets[0]*Values[4]+Offsets[3]*Values[5])+ Offsets[4]*(Offsets[0]*Values[6]+Offsets[3]*Values[7]) ) ); } /// Return relative weight of given index. Types::Coordinate GetWeight( const int index ) const { switch ( index ) { case 0: return Offsets[2] * Offsets[1] * Offsets[0]; case 1: return Offsets[2] * Offsets[1] * Offsets[3]; case 2: return Offsets[2] * Offsets[4] * Offsets[0]; case 3: return Offsets[2] * Offsets[4] * Offsets[3]; case 4: return Offsets[5] * Offsets[1] * Offsets[0]; case 5: return Offsets[5] * Offsets[1] * Offsets[3]; case 6: return Offsets[5] * Offsets[4] * Offsets[0]; case 7: return Offsets[5] * Offsets[4] * Offsets[3]; } return 0; } }; //@} } // namespace cmtk #endif // #ifndef __cmtkProbeInfo_h_included_ cmtk-3.3.1/libs/Base/cmtkQRDecomposition.h000066400000000000000000000047101276303427400204000ustar00rootroot00000000000000/* // // Copyright 1997-2012 Torsten Rohlfing // // Copyright 2004-2010 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4276 $ // // $LastChangedDate: 2012-04-29 12:52:17 -0700 (Sun, 29 Apr 2012) $ // // $LastChangedBy: torsten_at_home $ // */ #ifndef __cmtkQRDecomposition_h_included_ #define __cmtkQRDecomposition_h_included_ #include #include #include #include "Numerics/ap.h" namespace cmtk { /** \addtogroup Base */ //@{ /** Compute the QRDecomposition of a matrix */ template class QRDecomposition { public: /// This class. typedef QRDecomposition Self; /// Matrix type typedef Matrix2D MatrixType; /// Constructor: compute QR decomposition of given matrix. QRDecomposition( const typename Self::MatrixType& matrix ); /// Constructor: compute QR decomposition of given matrix. template QRDecomposition( const FixedSquareMatrix& matrix ); /// Get the Q factor typename Self::MatrixType& GetQ(); /// Get the R factor typename Self::MatrixType& GetR(); private: /// Number of rows in the input matrix size_t m_Rows; /// Number of columns in the input matrix size_t m_Cols; /// Alglib compact QR representation ap::real_2d_array m_CompactQR; /// Alglib tau array (generated by rmatrixqr, needed to compute Q) ap::real_1d_array m_Tau; /// Q matrix. typename Self::MatrixType::SmartPtr m_Q; /// R matrix. typename Self::MatrixType::SmartPtr m_R; }; //@} } // namespace cmtk #include "cmtkQRDecomposition.txx" #endif // #ifndef __cmtkQRDecomposition_h_included_ cmtk-3.3.1/libs/Base/cmtkQRDecomposition.txx000066400000000000000000000071001276303427400207700ustar00rootroot00000000000000/* // // Copyright 1997-2012 Torsten Rohlfing // // Copyright 2004-2012 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4277 $ // // $LastChangedDate: 2012-04-29 13:28:02 -0700 (Sun, 29 Apr 2012) $ // // $LastChangedBy: torsten_at_home $ // */ #include "Numerics/sevd.h" #include "Numerics/qr.h" namespace cmtk { /** \addtogroup Base */ //@{ template QRDecomposition ::QRDecomposition( const typename Self::MatrixType& matrix ) { this->m_Rows = matrix.NumberOfRows(); this->m_Cols = matrix.NumberOfColumns(); /* Copy matrix into this->m_CompactQR */ this->m_CompactQR.setbounds(0, static_cast( this->m_Rows-1 ), 0, static_cast( this->m_Cols-1 ) ); for ( size_t j = 0; j < this->m_Rows; j++ ) for ( size_t i = 0; i < this->m_Cols; i++ ) this->m_CompactQR(i,j) = static_cast( matrix[i][j] ); /* Run AlgLib QR decomposition */ rmatrixqr( this->m_CompactQR, this->m_Rows, this->m_Cols, this->m_Tau ); } template template QRDecomposition ::QRDecomposition( const FixedSquareMatrix& matrix ) { this->m_Rows = this->m_Cols = NDIM; /* Copy matrix into this->m_CompactQR */ this->m_CompactQR.setbounds(0, static_cast( this->m_Rows-1 ), 0, static_cast( this->m_Cols-1 ) ); for ( size_t j = 0; j < this->m_Rows; j++ ) for ( size_t i = 0; i < this->m_Cols; i++ ) this->m_CompactQR(i,j) = static_cast( matrix[i][j] ); /* Run AlgLib QR decomposition */ rmatrixqr( this->m_CompactQR, this->m_Rows, this->m_Cols, this->m_Tau ); } /// Get the Q factor template Matrix2D& QRDecomposition ::GetQ() { if ( ! this->m_Q ) { this->m_Q = typename Self::MatrixType::SmartPtr ( new typename Self::MatrixType( this->m_Rows, this->m_Cols ) ); /* Extract Q from this->m_CompactQR */ ap::real_2d_array tmp_ap_matrix; rmatrixqrunpackq( this->m_CompactQR, this->m_Rows, this->m_Cols, this->m_Tau, this->m_Cols, tmp_ap_matrix ); for ( int j = 0; j < this->m_Rows; j++ ) for ( int i = 0; i < this->m_Cols; i++ ) (*this->m_Q)[i][j] = tmp_ap_matrix(i,j); } return *(this->m_Q); } /// Get the R factor template Matrix2D& QRDecomposition ::GetR() { if ( ! this->m_R ) { this->m_R = typename Self::MatrixType::SmartPtr ( new typename Self::MatrixType( this->m_Rows, this->m_Cols ) ); /* Extract R from this->m_CompactQR */ ap::real_2d_array tmp_ap_matrix; rmatrixqrunpackr( this->m_CompactQR, this->m_Rows, this->m_Cols, tmp_ap_matrix ); for ( size_t j = 0; j < this->m_Rows; j++ ) for ( size_t i = 0; i < this->m_Cols; i++ ) (*this->m_R)[i][j] = tmp_ap_matrix(i,j); } return *(this->m_R); } } // namespace cmtk cmtk-3.3.1/libs/Base/cmtkRegion.h000066400000000000000000000067421276303427400165530ustar00rootroot00000000000000/* // // Copyright 2010-2012 SRI International // // Copyright 2010 Torsten Rohlfing // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4105 $ // // $LastChangedDate: 2012-03-30 13:47:57 -0700 (Fri, 30 Mar 2012) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkRegion_h_included_ #define __cmtkRegion_h_included_ #include #include #include #include #include namespace cmtk { /// Class for n-dimensional image index. template class Region { public: /// This class. typedef Region Self; /// The region dimension. static const size_t Dimension = NDIM; /// The region index variable type. typedef T ScalarType; /// Index type. typedef FixedVector IndexType; /// Smart pointer to this class. typedef SmartPointer SmartPtr; /// Smart pointer-to-const to this class. typedef SmartConstPointer SmartConstPtr; /// Default constructor. Region() {} /// Constructor from two indexes, from and to. Region( const typename Self::IndexType& fromIndex /*!< Region goes from this index. */, const typename Self::IndexType& toIndex /*!< Region goes to this index. */ ) { this->m_RegionFrom = fromIndex; this->m_RegionTo = toIndex; } /// Get "from". typename Self::IndexType& From() { return this->m_RegionFrom; } /// Get const "from". const typename Self::IndexType& From() const { return this->m_RegionFrom; } /// Get "from". typename Self::IndexType& To() { return this->m_RegionTo; } /// Get const "from". const typename Self::IndexType& To() const { return this->m_RegionTo; } /// Compute region size (e.g., number of pixels for grid regions). T Size() const { T size = std::max( 0, (this->m_RegionTo[0]-this->m_RegionFrom[0]) ); for ( size_t i = 1; i < NDIM; ++i ) size *= std::max( 0, (this->m_RegionTo[i]-this->m_RegionFrom[i]) ); return size; } /// Check if an index is inside region. bool IsInside( const typename Self::IndexType idx ) const { return (this->m_RegionFrom <= idx) && (idx < this->m_RegionTo); } /// Get one face of the region, as a region const Self GetFaceRegion( const int dim /*!< Returned face is orthogonal to this index dimension */, const bool upper = false /*!< If true, upper face is returned, otherwise lower face.*/ ) const; private: /// Lower limit of the region. typename Self::IndexType m_RegionFrom; /// Upper limit of the region. typename Self::IndexType m_RegionTo; }; } // namespace cmtk #include "cmtkRegion.txx" #endif // #ifndef __cmtkRegion_h_included_ cmtk-3.3.1/libs/Base/cmtkRegion.txx000066400000000000000000000035131276303427400171400ustar00rootroot00000000000000/* // // Copyright 2010-2011 SRI International // // Copyright 2010 Torsten Rohlfing // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 3341 $ // // $LastChangedDate: 2011-08-09 11:30:42 -0700 (Tue, 09 Aug 2011) $ // // $LastChangedBy: torstenrohlfing $ // */ namespace cmtk { /// Stream input operator. template std::ofstream& operator<<( std::ofstream& stream, const Region& region ) { return stream << region.From() << region.To(); } /// Stream output operator. template std::ifstream& operator>>( std::ifstream& stream, Region& region ) { return stream >> region.From() >> region.To(); } template const Region Region::GetFaceRegion( const int dim, const bool upper ) const { typename Self::IndexType from = this->m_RegionFrom; typename Self::IndexType to = this->m_RegionTo; if ( upper ) { from[dim] = to[dim]-1; } else { to[dim] = from[dim]+1; } return Self( from, to ); } } // namespace cmtk cmtk-3.3.1/libs/Base/cmtkRegionIndexIterator.h000066400000000000000000000074511276303427400212530ustar00rootroot00000000000000/* // // Copyright 2010-2012 SRI International // // Copyright 2010 Torsten Rohlfing // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4102 $ // // $LastChangedDate: 2012-03-30 11:24:44 -0700 (Fri, 30 Mar 2012) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkRegionIndexIterator_h_included_ #define __cmtkRegionIndexIterator_h_included_ #include #include #include #include #include namespace cmtk { /// Iterator for rectangular region of n-dimensional image, providing grid index of each position. template class RegionIndexIterator { public: /// This class. typedef RegionIndexIterator Self; /// Region type. typedef TRegion RegionType; /// Region dimension. static const size_t Dimension = RegionType::Dimension; /// Region scalar type. typedef typename RegionType::ScalarType ScalarType; /// Index type. typedef FixedVector IndexType; /// Smart pointer to this class. typedef SmartPointer SmartPtr; /// Smart pointer-to-const to this class. typedef SmartConstPointer SmartConstPtr; /// Constructor from two index, from and to. RegionIndexIterator( const typename Self::RegionType& region ) : m_Region( region ), m_Index( region.From() ) { this->m_End = this->m_Region.From(); if ( this->m_Region.From() < this->m_Region.To() ) { // for non-empty regions "End" index is one after last valid element. this->m_End[Self::Dimension-1] = this->m_Region.To()[Self::Dimension-1]; } // (for empty regions, "From" is already the "End" } /// Increment operator. Self& operator++() { for ( size_t idx = 0; idx < Self::Dimension; ++idx) { if ( (++this->m_Index[idx]) >= this->m_Region.To()[idx] ) { if ( idx+1 < Self::Dimension ) this->m_Index[idx] = this->m_Region.From()[idx]; } else break; } return *this; } /// Get index. const typename Self::IndexType& Index() const { return this->m_Index; } /// Region "begin" index. const typename Self::IndexType begin() const { return this->m_Region.From(); } /// Region "end" index. const typename Self::IndexType& end() const { return this->m_End; } /// Assign index. Self& operator=( const typename Self::IndexType& index ) { this->m_Index = index; return *this; } /// Index equality. bool operator==( const typename Self::IndexType& index ) { return (this->m_Index == index); } /// Index inequality. bool operator!=( const typename Self::IndexType& index ) { return !(this->m_Index == index); } private: /// Iterated region. typename Self::RegionType m_Region; /// End index (i.e., first non-valid index). typename Self::IndexType m_End; /// Current index. typename Self::IndexType m_Index; }; } // namespace cmtk #endif // #ifndef __cmtkRegionIndexIterator_h_included_ cmtk-3.3.1/libs/Base/cmtkRegionSphereIterator.h000066400000000000000000000121741276303427400214300ustar00rootroot00000000000000/* // // Copyright 2010-2012 SRI International // // Copyright 2010 Torsten Rohlfing // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4308 $ // // $LastChangedDate: 2012-05-04 10:33:11 -0700 (Fri, 04 May 2012) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkRegionSphereIterator_h_included_ #define __cmtkRegionSphereIterator_h_included_ #include #include #include #include #include #include namespace cmtk { /// Iterator for spherical region of n-dimensional image. template class RegionSphereIterator { public: /// This class. typedef RegionSphereIterator Self; /// Region type. typedef TRegion RegionType; /// Region dimension. static const size_t Dimension = RegionType::Dimension; /// Region scalar type. typedef typename RegionType::ScalarType ScalarType; /// Index type. typedef FixedVector IndexType; /// Smart pointer to this class. typedef SmartPointer SmartPtr; /// Smart pointer-to-const to this class. typedef SmartConstPointer SmartConstPtr; /// Internal index list type. typedef std::list IndexListType; protected: /// Protected default constructor - only to be used by derived class. RegionSphereIterator() {} public: /// Constructor with radius only (center is zero-index). explicit RegionSphereIterator( const typename Self::IndexType radius /*!< Radius of the sphere in each dimension.*/ ) { typename Self::IndexType center( typename Self::IndexType::Init( 0 ) ); this->Populate( radius, center, 0, 1.0 ); } /// Constructor with radius and (not necessarily) non-zero center explicit RegionSphereIterator( const typename Self::IndexType radius /*!< Radius of the sphere in each dimension.*/, const typename Self::IndexType center /*!< Center index of the sphere. */ ) { this->Populate( radius, center, 0, 1.0 ); } /// Increment operator. typename Self::IndexListType::const_iterator& operator++() { return ++this->m_IndexListIterator; } /// Get index. const typename Self::IndexType& Index() const { return *(this->m_IndexListIterator); } /// Iterator assignment. const Self& operator=( const typename Self::IndexListType::const_iterator& it ) { this->m_IndexListIterator = it; return *this; } /// Region "begin" index. const typename Self::IndexListType::const_iterator begin() const { return this->m_IndexList.begin(); } /// Region "end" index. const typename Self::IndexListType::const_iterator end() const { return this->m_IndexList.end(); } /// Equality operator. bool operator==( const typename Self::IndexListType::const_iterator& it ) { return it == this->m_IndexListIterator; } /// Inequality operator. bool operator!=( const typename Self::IndexListType::const_iterator& it ) { return it != this->m_IndexListIterator; } protected: /// Pre-computed list of grid indexes on the sphere. typename Self::IndexListType m_IndexList; /// Current position in index list. typename Self::IndexListType::const_iterator m_IndexListIterator; /// Recursively populate the list of indexes. virtual void Populate( const typename Self::IndexType& radius /*!< Sphere radius in index steps by dimension.*/, const typename Self::IndexType& center /*!< Sphere center. */, const size_t dim /*!< Next dimension. */, const double remainSquare /*!< Remaining proportion of total squared sphere radius. */) { if ( remainSquare >= 0 ) { typename Self::IndexType index = center; const int radiusThisDimension = static_cast( sqrt( remainSquare ) * radius[dim] ); if ( dim < Self::Dimension ) { this->Populate( radius, index, dim+1, remainSquare ); for ( int r = 1; r <= radiusThisDimension; ++r ) { const double newRemainSquare = remainSquare - MathUtil::Square(1.0 * r / radius[dim] ); index[dim] = center[dim]+r; this->Populate( radius, index, dim+1, newRemainSquare ); index[dim] = center[dim]-r; this->Populate( radius, index, dim+1, newRemainSquare ); } } else { this->m_IndexList.push_back( center ); } } } }; } // namespace cmtk #endif // #ifndef __cmtkRegionSphereIterator_h_included_ cmtk-3.3.1/libs/Base/cmtkScalarImage.cxx000066400000000000000000000260701276303427400200470ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2013 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4882 $ // // $LastChangedDate: 2013-09-27 15:16:36 -0700 (Fri, 27 Sep 2013) $ // // $LastChangedBy: torstenrohlfing $ // */ #include "cmtkScalarImage.h" #include #include #include #include #include #include #include namespace cmtk { /** \addtogroup Base */ //@{ ScalarImage::ScalarImage() { this->m_Dims[0] = this->m_Dims[1] = 0; this->m_NumberOfFrames = 1; this->m_ImageSlicePosition = 0; this->m_FrameToFrameSpacing = 0; } ScalarImage::ScalarImage ( const int dimsx, const int dimsy, const int numberOfFrames ) { this->m_Dims[0] = dimsx; this->m_Dims[1] = dimsy; this->m_NumberOfFrames = numberOfFrames; this->m_FrameToFrameSpacing = 0; this->m_PixelSize[0] = this->m_PixelSize[1] = 1; this->m_ImageSlicePosition = 0; } ScalarImage::ScalarImage ( const ScalarImage& source ) { this->SetDims( source.m_Dims ); this->SetPixelSize( source.GetPixelSize() ); this->SetNumberOfFrames( source.GetNumberOfFrames() ); this->SetFrameToFrameSpacing( source.GetFrameToFrameSpacing() ); this->SetImageOrigin( source.GetImageOrigin() ); this->SetImageDirectionX( source.GetImageDirectionX() ); this->SetImageDirectionY( source.GetImageDirectionY() ); this->SetImageSlicePosition( source.GetImageSlicePosition() ); if ( source.GetPixelData() ) this->SetPixelData( TypedArray::SmartPtr( source.GetPixelData()->Clone() ) ); } bool ScalarImage::GetPixelAt ( Types::DataItem& value, const Types::Coordinate i, const Types::Coordinate j ) const { // check if inside valid pixel index range if ( (i < 0) || (i >= this->m_Dims[0]-1) ) return false; if ( (j < 0) || (j >= this->m_Dims[1]-1) ) return false; // compute index const Types::Coordinate I = floor( i ); const Types::Coordinate J = floor( j ); size_t ofs = static_cast( I + this->m_Dims[0] * J ); Types::DataItem v00, v01, v10, v11; const bool success = this->m_PixelData->Get( v00, ofs ) && this->m_PixelData->Get( v10, ofs + 1 ) && this->m_PixelData->Get( v01, ofs + this->m_Dims[0] ) && this->m_PixelData->Get( v11, ofs + this->m_Dims[0] + 1 ); const Types::Coordinate ii = i - I; const Types::Coordinate jj = j - J; // if any of the four lookups hit "Padding Data", return. if ( ! success ) return false; // compute final value by bilinear interpolation value = (1.0 - jj) * ( (1.0 - ii) * v00 + ii * v10 ) + jj * ( (1.0 - ii) * v01 + ii * v11 ); return true; } ScalarImage::SpaceVectorType ScalarImage::GetImageOrigin( const int frame ) const { Self::SpaceVectorType origin; if ( this->m_NumberOfFrames > 1 ) { origin = SurfaceNormal( this->m_ImageDirectionX, this->m_ImageDirectionY ).Get(); origin *= (frame * this->m_FrameToFrameSpacing) / origin.RootSumOfSquares(); origin += this->m_ImageOrigin; } else { origin = this->m_ImageOrigin; } return origin; } void ScalarImage::Mirror( const bool horizontal, const bool vertical ) { if ( vertical ) { for ( int y = 0; y < this->m_Dims[1]/2; ++y ) { this->m_PixelData->BlockSwap( y * this->m_Dims[0], (this->m_Dims[1]-y-1) * this->m_Dims[0], this->m_Dims[0] ); } this->m_ImageOrigin = this->m_ImageOrigin + ((this->m_Dims[1]-1) * this->m_PixelSize[1] / this->m_ImageDirectionY.RootSumOfSquares()) * this->m_ImageDirectionY; this->m_ImageDirectionY *= (-1.0); } if ( horizontal ) { for ( int y = 0; y < this->m_Dims[1]; ++y ) { this->m_PixelData->BlockReverse( y * this->m_Dims[0], this->m_Dims[0] ); } this->m_ImageOrigin = this->m_ImageOrigin + ((this->m_Dims[1]-1) * this->m_PixelSize[0] / this->m_ImageDirectionX.RootSumOfSquares()) * this->m_ImageDirectionX; this->m_ImageDirectionX *= (-1.0); } } void ScalarImage::AdjustToIsotropic ( const Types::Coordinate pixelSize, const bool interpolate ) { if ( pixelSize < this->m_PixelSize[0] ) { // fake pixel size Y, then simply adjust aspect ratio const Types::Coordinate savePixelSizeY = this->m_PixelSize[1]; this->m_PixelSize[1] = pixelSize; this->AdjustAspectX( interpolate ); this->m_PixelSize[1] = savePixelSizeY; } // now we can simply adjust aspect again in the other dimension if ( this->m_PixelSize[0] < this->m_PixelSize[1] ) { this->AdjustAspectY( interpolate ); } } void ScalarImage::AdjustAspect( const bool interpolate ) { if ( this->m_PixelSize[0] < this->m_PixelSize[1] ) this->AdjustAspectY( interpolate ); else if ( this->m_PixelSize[0] > this->m_PixelSize[1] ) this->AdjustAspectX( interpolate ); } void ScalarImage::AdjustAspectY( const bool interpolate ) { if ( this->m_Dims[0] < 2 ) return; const int newDimsY = static_cast( (this->m_Dims[1]-1) * this->m_PixelSize[1]/this->m_PixelSize[0] ) + 1; TypedArray::SmartPtr scaled = TypedArray::Create( this->m_PixelData->GetType(), this->m_Dims[0] * newDimsY ); if ( interpolate ) { // with interpolation std::vector row0( this->m_Dims[0] ), row1( this->m_Dims[0] ); this->m_PixelData->GetSubArray( &(row0[0]), 0, this->m_Dims[0] ); this->m_PixelData->GetSubArray( &(row1[0]), this->m_Dims[0], this->m_Dims[0] ); Types::Coordinate scanLine = 0; int ySource = 0; size_t offset = 0; for ( int y = 0; y < newDimsY; ++y ) { Types::Coordinate factor = scanLine / this->m_PixelSize[1]; for ( int x = 0; x < this->m_Dims[0]; ++x, ++offset ) { scaled->Set( (1.0 - factor ) * row0[x] + factor * row1[x], offset ); } scanLine += this->m_PixelSize[0]; while ( (ySource < this->m_Dims[1]) && (scanLine >= this->m_PixelSize[1]) ) { ++ySource; row0 = row1; this->m_PixelData->GetSubArray( &(row1[0]), (1+ySource) * this->m_Dims[0], this->m_Dims[0] ); scanLine -= this->m_PixelSize[1]; } } } else { // no interpolation; can do with simply block copying char *scaledPtr = static_cast( scaled->GetDataPtr() ); const char *pixelPtr = static_cast( this->m_PixelData->GetDataPtr() ); Types::Coordinate scanLineOffset = this->m_PixelSize[1] / 2; Types::Coordinate scanLine = scanLineOffset; // correct offset for NN int ySource = 0; for ( int y = 0; y < newDimsY; ++y ) { memcpy( scaledPtr, pixelPtr, scaled->GetItemSize() * this->m_Dims[0] ); scanLine += this->m_PixelSize[0]; while ( (ySource < this->m_Dims[1]) && (scanLine >= this->m_PixelSize[1]) ) { ++ySource; pixelPtr += this->m_PixelData->GetItemSize() * this->m_Dims[0]; scanLine -= this->m_PixelSize[1]; } scaledPtr += scaled->GetItemSize() * this->m_Dims[0]; } } this->m_PixelSize[1] = this->m_PixelSize[0]; this->m_Dims[1] = newDimsY; this->SetPixelData( scaled ); } void ScalarImage::AdjustAspectX( const bool interpolate ) { if ( this->m_Dims[1] < 2 ) return; const int newDimsX = static_cast( (this->m_Dims[0]-1) * this->m_PixelSize[0]/this->m_PixelSize[1] ) + 1; TypedArray::SmartPtr scaled = TypedArray::Create( this->m_PixelData->GetType(), newDimsX * this->m_Dims[1] ); if ( interpolate ) { std::vector factor( newDimsX ); std::vector fromPixel( newDimsX ); Types::Coordinate scanLine = 0; int xSource = 0; for ( int x = 0; x < newDimsX; ++x ) { fromPixel[x] = xSource; factor[x] = scanLine / this->m_PixelSize[0]; scanLine += this->m_PixelSize[1]; while ( (xSource < this->m_Dims[0]) && (scanLine >= this->m_PixelSize[0]) ) { ++xSource; scanLine -= this->m_PixelSize[0]; } } std::vector rowFrom( this->m_Dims[0] ); size_t offset = 0; for ( int y = 0; y < this->m_Dims[1]; ++y ) { this->m_PixelData->GetSubArray( &(rowFrom[0]), y * this->m_Dims[0], this->m_Dims[0] ); for ( int x = 0; x < newDimsX; ++x, ++offset ) { scaled->Set( (1.0 - factor[x] ) * rowFrom[fromPixel[x]] + factor[x] * rowFrom[fromPixel[x]+1], offset ); } } } else { Types::Coordinate scanLine = this->m_PixelSize[0] / 2; // correct offset for NN int xSource = 0; std::vector fromPixel( newDimsX ); for ( int x = 0; x < newDimsX; ++x ) { fromPixel[x] = xSource * scaled->GetItemSize(); scanLine += this->m_PixelSize[1]; while ( (xSource < this->m_Dims[0]) && (scanLine >= this->m_PixelSize[0]) ) { ++xSource; scanLine -= this->m_PixelSize[0]; } } // no interpolation; can do with simply block copying char *scaledPtr = static_cast( scaled->GetDataPtr() ); const char *pixelPtr = static_cast( this->m_PixelData->GetDataPtr() ); for ( int y = 0; y < this->m_Dims[1]; ++y ) { for ( int x = 0; x < newDimsX; ++x ) { memcpy( scaledPtr, pixelPtr+fromPixel[x], scaled->GetItemSize() ); scaledPtr += scaled->GetItemSize(); } pixelPtr += scaled->GetItemSize() * this->m_Dims[0]; } } this->m_PixelSize[0] = this->m_PixelSize[1]; this->m_Dims[0] = newDimsX; this->SetPixelData( scaled ); } void ScalarImage::ProjectPixel ( const Self::SpaceVectorType& v, int& i, int& j ) const { Self::SpaceVectorType p(v); p -= this->m_ImageOrigin; i = MathUtil::Round( ( p * this->m_ImageDirectionX ) / ( this->m_ImageDirectionX.SumOfSquares() * this->m_PixelSize[0] ) ); j = MathUtil::Round( ( p * this->m_ImageDirectionY ) / ( this->m_ImageDirectionY.SumOfSquares() * this->m_PixelSize[1] ) ); } void ScalarImage::Print() const { StdErr.printf( "ScalarImage at %p\n", this ); StdErr.indent(); StdErr.printf( "Dimensions: [%d,%d]\n", this->m_Dims[0], this->m_Dims[1] ); StdErr.printf( "Pixel size: [%f,%f]\n", this->m_PixelSize[0], this->m_PixelSize[1] ); StdErr.printf( "Origin: [%f,%f,%f]\n", this->m_ImageOrigin[0], this->m_ImageOrigin[1], this->m_ImageOrigin[2] ); StdErr.printf( "DirectionX: [%f,%f,%f]\n", this->m_ImageDirectionX[0], this->m_ImageDirectionX[1], this->m_ImageDirectionX[2] ); StdErr.printf( "DirectionY: [%f,%f,%f]\n", this->m_ImageDirectionY[0], this->m_ImageDirectionY[1], this->m_ImageDirectionY[2] ); StdErr.unindent(); } } // namespace cmtk cmtk-3.3.1/libs/Base/cmtkScalarImage.h000066400000000000000000000147051276303427400174760ustar00rootroot00000000000000/* // // Copyright 1997-2010 Torsten Rohlfing // // Copyright 2004-2013 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4882 $ // // $LastChangedDate: 2013-09-27 15:16:36 -0700 (Fri, 27 Sep 2013) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkScalarImage_h_included_ #define __cmtkScalarImage_h_included_ #include #include #include #include #include #include #include #include #include #include namespace cmtk { /** \addtogroup Base */ //@{ /** Two-dimensional image with scalar pixel values. */ class ScalarImage { /// Number of image frames for multi-frame images. cmtkGetSetMacro(int,NumberOfFrames); /// Pixel data. cmtkGetSetMacro(TypedArray::SmartPtr,PixelData); /// Pixel spacing. cmtkGetSetMacro2Array(Types::Coordinate,PixelSize); /// Frame-to-frame spacing. cmtkGetSetMacro(Types::Coordinate,FrameToFrameSpacing); public: /// This class. typedef ScalarImage Self; /// Smart pointer to ScalarImage typedef SmartPointer SmartPtr; /// Smart pointer to const ScalarImage typedef SmartConstPointer SmartConstPtr; /// Region type. typedef Region<2,int> RegionType; /// Pixel index type. typedef RegionType::IndexType IndexType; /// Space vector type. typedef FixedVector<3,Types::Coordinate> SpaceVectorType; /// Default constructor creates empty image. ScalarImage(); /// Constructor with separate x and y dimensions. ScalarImage( const int dimsx, const int dimsy, const int numberOfFrames = 1 ); /** Get ROI as sub-image. *\param source The source image to copy an ROI from. *\param roiFrom The (x,y) pixel of the original image that will make up the * top left (0,0) pixel of this image. *\param roiTo The (x,y) pixel of the original image that will make up the * bottom right (dimx-1,dimy-1) pixel of this image. *\note Note that the ROI given by roiFrom and roiTo is an inclusive * boundary, i.e., the indexed row and column will still be part of the * cropped image. The cropped image dimension is therefore * (1+roiTo[0]-roiFrom[0],1+roiTo[0]-roiFrom[0]). */ ScalarImage( const ScalarImage& source ); /// Virtual destructor. virtual ~ScalarImage() {} /// Set dimensions. virtual void SetDims( const Self::IndexType& dims ) { this->m_Dims = dims; } /// Get dimensions. const Self::IndexType GetDims() const { return this->m_Dims; } /// Create pixel data array with given data type. void CreatePixelData( const ScalarDataType dtype ) { this->m_PixelData = TypedArray::SmartPtr( TypedArray::Create( dtype, this->m_Dims[0] * this->m_Dims[1] * this->m_NumberOfFrames ) ); } /** Origin of image in world coordinates. */ Self::SpaceVectorType m_ImageOrigin; /// Set image origin. void SetImageOrigin( const Self::SpaceVectorType& imageOrigin ) { this->m_ImageOrigin = imageOrigin; } /// Get image origin of given frame (default: 0). Self::SpaceVectorType GetImageOrigin( const int frame = 0 ) const; /** Direction of image rows relative to ImageOrigin. */ cmtkGetSetMacro(Self::SpaceVectorType,ImageDirectionX); /** Direction of image columns relative to ImageOrigin. */ cmtkGetSetMacro(Self::SpaceVectorType,ImageDirectionY); /** Image position from coordinate origin along axial direction. * This field is only meaningful if this 2D image is part of a 3D image. */ cmtkGetSetMacro(Types::Coordinate,ImageSlicePosition); /// Get number of pixels. int GetNumberOfPixels() const { return this->m_Dims[0] * this->m_Dims[1]; } /// Get pixel at 2-D index. Types::DataItem GetPixelAt( const int i, const int j ) const { Types::DataItem value; if ( this->m_PixelData->Get( value, i + this->m_Dims[0] * j ) ) return value; return 0; } /** Get pixel at fractional 2-D index by bilinear interpolation. *\return True if value is valid; false if at least one of the four neighbor * pixels was invalid or outside the image. */ bool GetPixelAt( Types::DataItem& value, const Types::Coordinate i, const Types::Coordinate j ) const; /// Set pixel at 2-D index. void SetPixelAt( const int i, const int j, const Types::DataItem data ) { this->m_PixelData->Set( data, i + this->m_Dims[0] * j ); } /// Mirror image horizontally and/or vertically. void Mirror( const bool horizontal, const bool vertical ); /// Adjust aspect ratio. void AdjustAspect( const bool interpolate = false ); /// Adjust aspect ratio. void AdjustToIsotropic( const Types::Coordinate pixelSize, const bool interpolate = false ); /** Project 3D coordinate onto image plane. *\param v Original coordinate. *\param i Index of projected pixel in x direction. *\param j Index of projected pixel in y direction. */ void ProjectPixel( const Self::SpaceVectorType& v, int& i, int& j ) const; /// Print object information. virtual void Print() const; private: /// Image dimensions Self::IndexType m_Dims; /// Adjust aspect ratio by stretching in Y-direction. void AdjustAspectY( const bool interpolate = false ); /// Adjust aspect ratio by stretching in X-direction. void AdjustAspectX( const bool interpolate = false ); }; //@{ /// #define CMTK_SCALARIMAGE_NOCLONEDATA false #define CMTK_SCALARIMAGE_CLONEDATA true #define CMTK_SCALARIMAGE_HORIZONTAL true #define CMTK_SCALARIMAGE_VERTICAL false #define CMTK_SCALARIMAGE_ABSOLUTE true #define CMTK_SCALARIMAGE_SIGNED false //@} //@} } // namespace cmtk #endif // #ifndef __cmtkScalarImage_h_included_ cmtk-3.3.1/libs/Base/cmtkScalarImageGradientField.cxx000066400000000000000000000044431276303427400224710ustar00rootroot00000000000000/* // // Copyright 1997-2010 Torsten Rohlfing // // Copyright 2004-2012 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4416 $ // // $LastChangedDate: 2012-06-04 15:35:09 -0700 (Mon, 04 Jun 2012) $ // // $LastChangedBy: torstenrohlfing $ // */ #include "cmtkScalarImageGradientField.h" #include cmtk::ScalarImageGradientField::ScalarImageGradientField( const UniformVolume& volume ) : m_GradientField( new Self::GradientFieldType( volume.m_Dims, volume.m_Size ) ) { const DataGrid::RegionType wholeImageRegion = volume.GetWholeImageRegion(); size_t ofsPlusMinus = 1; for ( int dim = 0; dim < 3; ++dim ) { size_t ofs = 0; for ( RegionIndexIterator it( wholeImageRegion ); it != it.end(); ++it, ++ofs ) { const DataGrid::IndexType idx = it.Index(); Types::Coordinate div = 0; if ( idx[dim]+1 < wholeImageRegion.To()[dim] ) { (*this->m_GradientField)[ofs][dim] = volume.GetDataAt( ofs + ofsPlusMinus ); div += 1.0; } else (*this->m_GradientField)[ofs][dim] = volume.GetDataAt( ofs ); if ( idx[dim]-1 > wholeImageRegion.From()[dim] ) { (*this->m_GradientField)[ofs][dim] -= volume.GetDataAt( ofs - ofsPlusMinus ); div += 1.0; } else (*this->m_GradientField)[ofs][dim] -= volume.GetDataAt( ofs ); (*this->m_GradientField)[ofs][dim] /= div; } // compute increment for next dimension ofsPlusMinus *= volume.m_Dims[dim]; } } cmtk-3.3.1/libs/Base/cmtkScalarImageGradientField.h000066400000000000000000000037671276303427400221260ustar00rootroot00000000000000/* // // Copyright 1997-2010 Torsten Rohlfing // // Copyright 2004-2012 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4371 $ // // $LastChangedDate: 2012-05-30 10:34:57 -0700 (Wed, 30 May 2012) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkScalarImageGradientField_h_included_ #define __cmtkScalarImageGradientField_h_included_ #include #include #include namespace cmtk { /** \addtogroup Base */ //@{ /// Compute gradient field from scalar image class ScalarImageGradientField { public: /// This class. typedef ScalarImageGradientField Self; /// The gradient field type. typedef ImageTemplate< FixedVector<3,Types::Coordinate> > GradientFieldType; /// Constructor: compute gradient field ScalarImageGradientField( const UniformVolume& volume /*!< Scalar image volume to compute gradients from. */ ); /// Get gradient field. Self::GradientFieldType::SmartPtr Get() { return this->m_GradientField; } private: /// The gradient field. Self::GradientFieldType::SmartPtr m_GradientField; }; //@} } // namespace cmtk #endif // #ifndef __cmtkScalarImageGradientField_h_included_ cmtk-3.3.1/libs/Base/cmtkSegmentationLabel.cxx000066400000000000000000000147531276303427400213010ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2010 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 2152 $ // // $LastChangedDate: 2010-08-04 10:19:33 -0700 (Wed, 04 Aug 2010) $ // // $LastChangedBy: torstenrohlfing $ // */ #include "cmtkSegmentationLabel.h" #include namespace cmtk { /** \addtogroup Base */ //@{ void CreateSystemLabelColorMap( SegmentationLabelMap& map ) { // table of random label RGB color mappings. const byte labelRGB[][3] = { { 000, 000, 000 }, { 255, 128, 255 }, { 177, 18, 255 }, { 45, 255, 47 }, { 106, 209, 255 }, { 122, 255, 158 }, { 255, 87, 186 }, { 183, 150, 54 }, { 216, 255, 159 }, { 181, 169, 80 }, { 255, 152, 0 }, { 42, 255, 255 }, { 116, 255, 77 }, { 6, 253, 234 }, { 218, 108, 255 }, { 24, 255, 108 }, { 59, 255, 91 }, { 255, 127, 14 }, { 255, 196, 142}, { 191, 80, 135 }, { 252, 83, 71 }, { 208, 255, 10 }, { 243, 124, 220 }, { 55, 255, 107 }, { 64, 63, 255 }, { 23, 255, 232 }, { 179, 138, 255 }, { 126, 147, 255 }, { 255, 255, 129 }, { 185, 94, 190 }, { 255, 127, 211 }, { 255, 92, 138 }, { 255, 132, 255 }, { 50, 255, 146 }, { 245, 79, 139 }, { 255, 16, 74 }, { 255, 72, 190 }, { 142, 178, 255 }, { 100, 255, 83 }, { 221, 248, 124 }, { 165, 255, 223 }, { 252, 186, 133 }, { 209, 5,255 }, { 142, 127, 255 }, { 97, 255, 116 }, { 94, 133, 212 }, { 200, 160, 255 }, { 186, 182, 41 }, { 255, 141, 165 }, { 255, 255, 148 }, { 188, 96, 255 }, { 40, 64, 255 }, { 192, 152, 66 }, { 255, 163, 123 }, { 255, 100, 255 }, { 255, 212, 174 }, { 163, 245, 21 }, { 255, 173, 175 }, { 255, 223, 183 }, { 100, 131, 212 }, { 27, 255, 135 }, { 93, 255, 118 }, { 50, 223, 146 }, { 48, 192, 255 }, { 255, 106, 116 }, { 110, 53, 231 }, { 193, 52, 255 }, { 255, 54, 255 }, { 185, 33, 255 }, { 74, 74, 255 }, { 192, 20, 199 }, { 133, 145, 255 }, { 161, 255, 58 }, { 91, 230, 89 }, { 138, 159, 255 }, { 6, 226, 255 }, { 46, 150, 229 }, { 107, 255, 255 }, { 140, 255, 241 }, { 128, 218, 167 }, { 33, 109, 255 }, { 103, 96, 255 }, { 76, 255, 50 }, { 132, 151, 255 }, { 111, 77, 255 }, { 138, 255, 146 }, { 53, 94, 255 }, { 217, 163, 255 }, { 77, 61, 255 }, { 115, 170, 196 }, { 219, 49, 148 }, { 255, 201, 117 }, { 61, 129, 255 }, { 168, 105, 234 }, { 164, 216, 2 }, { 178, 91, 184 }, { 14, 255, 228 }, { 132, 49, 247 }, { 146, 129, 255 }, { 255, 27, 135 }, { 222, 255, 50 }, { 100, 255, 31 }, { 255, 100, 214 }, { 255, 255, 150 }, { 123, 255, 255 }, { 93, 180, 176 }, { 62, 46, 255 }, { 118, 184, 255 }, { 131, 105, 230 }, { 101, 167, 166 }, { 148, 255, 172 }, { 255, 96, 209 }, { 200, 85, 224 }, { 78, 255, 147 }, { 122, 193, 94 }, { 255, 170, 68 }, { 106, 227, 83 }, { 18, 255, 173 }, { 170, 255, 85 }, { 164, 255, 255 }, { 75, 104, 255 }, { 127, 231, 255 }, { 109, 255, 142 }, { 186, 100, 255 }, { 20, 255, 25 }, { 170, 255, 210 }, { 140, 88, 197 }, { 204, 43, 152 }, { 4, 255, 226 }, { 249, 255, 122 }, { 235, 168, 125 }, { 128, 255, 88 }, { 163, 141, 98 }, { 58, 255, 234 }, { 142, 255, 201 }, { 97, 181, 255 }, { 148, 255, 172 }, { 139, 251, 224 }, { 84, 235, 255 }, { 125, 254, 29 }, { 255, 134, 15 }, { 152, 255, 255 }, { 178, 255, 140 }, { 49, 255, 65 }, { 255, 171, 144 }, { 35, 83, 255 }, { 206, 255, 137 }, { 255, 225, 83 }, { 147, 119, 255 }, { 96, 255, 85 }, { 70, 255, 106 }, { 131, 176, 220 }, { 136, 255, 173 }, { 156, 255, 77 }, { 255, 82, 189 }, { 151, 128, 255 }, { 255, 118, 112 }, { 76, 127, 255 }, { 128, 255, 183 }, { 167, 53, 220 }, { 255, 173, 155 }, { 255, 255, 89 }, { 96, 124, 163 }, { 153, 255, 134 }, { 210, 115, 186 }, { 35, 34, 255 }, { 46, 255, 23 }, { 255, 255, 116 }, { 30, 19, 255 }, { 81, 255, 167 }, { 76, 255, 255 }, { 180, 255, 20 }, { 255, 99, 158 }, { 255, 57, 152 }, { 97, 199, 255 }, { 255, 144, 199 }, { 89, 183, 255 }, { 158, 41, 255 }, { 77, 117, 251 }, { 255, 42, 255 }, { 114, 115, 255 }, { 76, 151, 255 }, { 180, 156, 58 }, { 150, 113, 255 }, { 133, 210, 225 }, { 169, 255, 255 }, { 73, 255, 125 }, { 208, 179, 88 }, { 162, 255, 199 }, { 47, 176, 255 }, { 130, 255, 255 }, { 255, 181, 150 }, { 102, 135, 229 }, { 23, 88, 255 }, { 127, 255, 253 }, { 255, 17, 113 }, { 188, 117, 255 }, { 251, 255, 127 }, { 39, 255, 255 }, { 255, 255, 119 }, { 146, 20, 255 }, { 146, 8, 255 }, { 90, 255, 157 }, { 126, 151, 255 }, { 94, 255, 130 }, { 252, 255, 100 }, { 218, 152, 88 }, { 92, 114, 255 }, { 144, 179, 255 }, { 83, 126, 179 }, { 183, 255, 188 }, { 67, 169, 255 }, { 255, 125, 160 }, { 53, 255, 255 }, { 252, 113, 129 }, { 255, 140, 248 }, { 140, 83, 255 }, { 255, 166, 200 }, { 80, 145, 255 }, { 167, 185, 93 }, { 165, 223, 61 }, { 255, 255, 162 }, { 227, 103, 97 }, { 95, 255, 255 }, { 223, 129, 56 }, { 92, 255, 141 }, { 188, 255, 157 }, { 88, 127, 179 }, { 52, 255, 204 }, { 0, 255, 27 }, { 141, 232, 255 }, { 173, 145, 255 }, { 79, 89, 232 }, { 200, 53, 255 }, { 41, 21, 255 }, { 210, 142, 255 }, { 156, 187, 58 }, { 212, 154, 108 }, { 193, 152, 58 }, { 255, 10, 133 }, { 255, 83, 135 }, { 137, 183, 255 }, { 57, 255, 255 }, { 97, 252, 255 }, { 206, 255, 132 }, { 183, 255, 255 }, { 128, 255, 137 }, { 131, 255, 181 }, { 86, 101, 255 }, { 113, 92, 255 }, { 201, 137, 255 }, { 255, 74, 47 }, { 128, 178, 255 }, { 31, 192, 254 }, { 176, 255, 139 }, { 255, 81, 194 } }; for ( size_t i = 0; i < 256; ++i ) { char name[9]; sprintf( name, "label%03d", static_cast( i ) ); map[i].SetName( name ); map[i].SetRGB( labelRGB[i][0], labelRGB[i][1], labelRGB[i][2] ); } } } cmtk-3.3.1/libs/Base/cmtkSegmentationLabel.h000066400000000000000000000037541276303427400207250ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2010 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 2398 $ // // $LastChangedDate: 2010-10-05 14:54:37 -0700 (Tue, 05 Oct 2010) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkSegmentationLabel_h_included_ #define __cmtkSegmentationLabel_h_included_ #include #include #include #include namespace cmtk { /** \addtogroup Base */ //@{ /// A label (class) in a segmentation. class SegmentationLabel { public: /// Init constructor. SegmentationLabel() { this->m_Name = NULL; this->m_RGB[0] = this->m_RGB[1] = this->m_RGB[2] = 0; } /// Destructor. ~SegmentationLabel() { if ( this->m_Name ) free( this->m_Name ); } /// Name of this label. cmtkGetSetMacroString(Name); /// Color as RGB components for visualization. cmtkGetSetMacro3Array(byte,RGB); }; /// Map from numerical IDs to labels. typedef std::map SegmentationLabelMap; /// Create system label map. void CreateSystemLabelColorMap( SegmentationLabelMap& map ); //@} } // namespace cmtk #endif // #ifndef __cmtkSegmentationLabel_h_included_ cmtk-3.3.1/libs/Base/cmtkSincInterpolator.h000066400000000000000000000063601276303427400206230ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2012 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 5240 $ // // $LastChangedDate: 2014-03-18 14:30:29 -0700 (Tue, 18 Mar 2014) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkSincInterpolator_h_included_ #define __cmtkSincInterpolator_h_included_ #include #ifdef HAVE_IEEEFP_H # include #endif namespace cmtk { /** \addtogroup Base */ //@{ namespace Interpolators { /// Sinc interpolator with Hamming window. template class HammingSinc { public: /// This class. typedef HammingSinc Self; /// Size of the interpolation region in grid points to the left and right. static const int RegionSizeLeftRight = NRadius; /// Flag whether this interpolator is suitable for labels. static const bool SuitableForLabels = false; /// Get specific interpolation weight for relative coordinate. static Types::Coordinate GetWeight( const int i, const Types::Coordinate x ) { const Types::Coordinate piDiff = M_PI * (x - i); const Types::Coordinate result = 0.54 + 0.46 * cos( piDiff * Self::InternalFactor ) * sin( piDiff ) / piDiff; return finite( result ) ? result : 1; } private: /// Internal factor. static const Types::Coordinate InternalFactor; }; template const Types::Coordinate HammingSinc::InternalFactor = 1.0 / HammingSinc::RegionSizeLeftRight; /// Sinc interpolator with Cosine window. template class CosineSinc { public: /// This class. typedef CosineSinc Self; /// Flag whether this interpolator is suitable for labels. static const bool SuitableForLabels = false; /// Size of the interpolation region in grid points to the left and right. static const int RegionSizeLeftRight = NRadius; /// Get specific interpolation weight for relative coordinate. static Types::Coordinate GetWeight( const int i, const Types::Coordinate x ) { const Types::Coordinate piDiff = M_PI * (x - i); const Types::Coordinate result = cos( piDiff * Self::InternalFactor ) * sin( piDiff ) / piDiff; return finite( result ) ? result : 1; } private: /// Internal factor. static const Types::Coordinate InternalFactor; }; template const Types::Coordinate CosineSinc::InternalFactor = 1.0 / (2*CosineSinc::RegionSizeLeftRight); } // namespace Interpolators //@} } // namespace cmtk #endif cmtk-3.3.1/libs/Base/cmtkSplineWarpXform.cxx000066400000000000000000000727711276303427400210100ustar00rootroot00000000000000/* // // Copyright 1997-2010 Torsten Rohlfing // // Copyright 2004-2013 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4870 $ // // $LastChangedDate: 2013-09-24 11:01:11 -0700 (Tue, 24 Sep 2013) $ // // $LastChangedBy: torstenrohlfing $ // */ #include "cmtkSplineWarpXform.h" #include #include #include #include namespace cmtk { /** \addtogroup Base */ //@{ SplineWarpXform::SplineWarpXform() { this->Init(); } void SplineWarpXform::Init () { this->m_GlobalScaling = 1.0; memset( this->GridPointOffset, 0, sizeof( this->GridPointOffset ) ); } SplineWarpXform::SplineWarpXform ( const Self::SpaceVectorType& domain, const Types::Coordinate delta, const AffineXform* initialXform, const bool exactDelta ) { this->Init( domain, delta, initialXform, exactDelta ); } SplineWarpXform::SplineWarpXform ( const Self::SpaceVectorType& domain, const Self::ControlPointIndexType& dims, CoordinateVector::SmartPtr& parameters, const AffineXform* initialXform ) { this->Init(); this->m_Domain = domain; this->m_Dims = dims; if ( initialXform ) { this->m_InitialAffineXform = initialXform->Clone(); this->m_GlobalScaling = this->m_InitialAffineXform->GetGlobalScaling(); } else { this->m_InitialAffineXform = AffineXform::SmartPtr( NULL ); this->m_GlobalScaling = 1.0; } this->m_NumberOfControlPoints = this->m_Dims[0] * this->m_Dims[1] * this->m_Dims[2]; this->m_NumberOfParameters = 3 * this->m_NumberOfControlPoints; if ( !parameters ) this->m_ParameterVector = CoordinateVector::SmartPtr( new CoordinateVector( this->m_NumberOfParameters ) ); else this->m_ParameterVector = parameters; this->m_Parameters = this->m_ParameterVector->Elements; this->Update( false /* exactDelta */ ); if ( !parameters ) this->InitControlPoints( this->m_InitialAffineXform ); } void SplineWarpXform ::Init ( const Self::SpaceVectorType& domain, const Types::Coordinate delta, const AffineXform* initialXform, const bool exactDelta ) { this->Init(); this->m_Domain = domain; if ( initialXform ) { this->m_InitialAffineXform = initialXform->Clone(); this->m_GlobalScaling = this->m_InitialAffineXform->GetGlobalScaling(); } else { this->m_InitialAffineXform = AffineXform::SmartPtr( NULL ); this->m_GlobalScaling = 1.0; } if ( exactDelta ) { for ( int dim=0; dim<3; ++dim ) { this->m_Spacing[dim] = delta; this->m_Dims[dim] = static_cast( 4 + (this->m_Domain[dim] / this->m_Spacing[dim]) ); this->m_Domain[dim] = (this->m_Dims[dim] - 3) * this->m_Spacing[dim]; } } else { for ( int dim=0; dim<3; ++dim ) this->m_Dims[dim] = 2 + std::max( 2, 1+static_cast( domain[dim]/delta ) ); } this->m_NumberOfControlPoints = this->m_Dims[0] * this->m_Dims[1] * this->m_Dims[2]; this->AllocateParameterVector( 3 * this->m_NumberOfControlPoints ); this->Update( exactDelta ); this->InitControlPoints( this->m_InitialAffineXform ); } void SplineWarpXform::InitControlPoints( const AffineXform* affineXform ) { Types::Coordinate *ofs = this->m_Parameters; Types::Coordinate pZ = -this->m_Spacing[2]; for ( int z=0; zm_Dims[2]; ++z, pZ+=this->m_Spacing[2] ) { Types::Coordinate pY = -this->m_Spacing[1]; for ( int y=0; ym_Dims[1]; ++y, pY+=this->m_Spacing[1] ) { Types::Coordinate pX = -this->m_Spacing[0]; for ( int x=0; xm_Dims[0]; ++x, pX+=this->m_Spacing[0], ofs+=3 ) { ofs[0] = pX; ofs[1] = pY; ofs[2] = pZ; } } } if ( affineXform ) { ofs = this->m_Parameters; for ( unsigned int idx = 0; idx < this->m_NumberOfControlPoints; ++idx, ofs+=3 ) { const Self::SpaceVectorType p = affineXform->Apply( Self::SpaceVectorType::FromPointer( ofs ) ); ofs[0] = p[0]; ofs[1] = p[1]; ofs[2] = p[2]; } this->m_InverseAffineScaling = affineXform->GetScales(); this->m_GlobalScaling = affineXform->GetGlobalScaling(); } else { this->m_InverseAffineScaling[0] = this->m_InverseAffineScaling[1] = this->m_InverseAffineScaling[2] = this->m_GlobalScaling = 1.0; } } SplineWarpXform::ControlPointRegionType SplineWarpXform::GetInsideControlPointsRegion() const { Self::ControlPointRegionType region = this->GetAllControlPointsRegion(); region.From().AddScalar( 1 ); region.To().AddScalar( -1 ); return region; } void SplineWarpXform::Update ( const bool exactDelta ) { this->WarpXform::Update(); for ( int dim=0; dim<3; ++dim ) { assert( this->m_Dims[dim] > 3 ); if ( exactDelta ) { this->m_InverseSpacing[dim] = 1.0 / this->m_Spacing[dim]; } else { this->m_Spacing[dim] = this->m_Domain[dim] / (this->m_Dims[dim]-3); this->m_InverseSpacing[dim] = 1.0*(this->m_Dims[dim]-3) / this->m_Domain[dim]; } m_Offset[dim] = -this->m_Spacing[dim]; } int dml = 0; for ( int dim = 0; dim<3; ++dim ) for ( int m = 0; m < 4; ++m ) for ( int l = 0; l < 4; ++l, ++dml ) GridPointOffset[dml] = dim + l * nextJ + m * nextK; } SplineWarpXform* SplineWarpXform::CloneVirtual() const { SplineWarpXform *newXform = new SplineWarpXform(); newXform->m_ParameterVector = CoordinateVector::SmartPtr( this->m_ParameterVector->Clone() ); newXform->m_Parameters = newXform->m_ParameterVector->Elements; newXform->m_NumberOfParameters = this->m_NumberOfParameters; newXform->m_NumberOfControlPoints = this->m_NumberOfControlPoints; newXform->m_Dims = this->m_Dims; newXform->m_Domain = this->m_Domain; newXform->m_Spacing = this->m_Spacing; newXform->m_InverseSpacing = this->m_InverseSpacing; newXform->m_Offset = this->m_Offset; if ( this->m_ActiveFlags ) { BitVector::SmartPtr activeFlags( this->m_ActiveFlags->Clone() ); newXform->SetActiveFlags( activeFlags ); } newXform->m_IgnoreEdge = this->m_IgnoreEdge; newXform->m_FastMode = this->m_FastMode; if ( this->m_InitialAffineXform ) { newXform->m_InitialAffineXform = AffineXform::SmartPtr( this->m_InitialAffineXform->Clone() ); } newXform->m_GlobalScaling = this->m_GlobalScaling; newXform->nextI = this->nextI; newXform->nextJ = this->nextJ; newXform->nextK = this->nextK; newXform->nextIJ = this->nextIJ; newXform->nextIK = this->nextIK; newXform->nextJK = this->nextJK; newXform->nextIJK = this->nextIJK; memcpy( newXform->GridPointOffset, this->GridPointOffset, sizeof( this->GridPointOffset ) ); newXform->VolumeDims = this->VolumeDims; newXform->m_GridOffsets = this->m_GridOffsets; newXform->m_GridSpline = this->m_GridSpline; newXform->m_GridDerivSpline = this->m_GridDerivSpline; return newXform; } void SplineWarpXform::Refine() { if ( !this->m_ParameterVector ) return; Self::ControlPointIndexType newDims; for ( int dim=0; dim<3; ++dim ) newDims[dim] = 2 * this->m_Dims[dim] - 3; const int newNumSamples = newDims[0] * newDims[1] * newDims[2]; const int newNumCoefficients = 3 * newNumSamples; CoordinateVector::SmartPtr newParameters( new CoordinateVector( newNumCoefficients ) ); Types::Coordinate* newCoefficients = newParameters->Elements; Types::Coordinate newSpacing[3]; for ( int dim=0; dim<3; ++dim ) { newSpacing[dim] = this->m_Domain[dim] / (newDims[dim]-3); } // no linear refinement here const int newNextI = 3; const int newNextJ = newNextI * newDims[0]; const int newNextK = newNextJ * newDims[1]; const int newNextIJ = newNextJ + newNextI; const int newNextIK = newNextK + newNextI; const int newNextJK = newNextK + newNextJ; const int newNextIJK = newNextJK + newNextI; Types::Coordinate level0[3][3]; memset( level0, 0, sizeof( level0 ) ); Types::Coordinate level1[3]; memset( level1, 0, sizeof( level1 ) ); Types::Coordinate *ncoeff = newCoefficients; for ( int z = 0; zm_NumberOfControlPoints = newNumSamples; this->m_NumberOfParameters = newNumCoefficients; this->m_ParameterVector = newParameters; this->m_Parameters = newCoefficients; this->DeleteParameterActiveFlags(); this->m_Dims = newDims; for ( int dim=0; dim<3; ++dim ) { assert( this->m_Dims[dim] > 1 ); this->m_Spacing[dim] = newSpacing[dim]; this->m_InverseSpacing[dim] = 1.0 / this->m_Spacing[dim]; m_Offset[dim] = -this->m_Spacing[dim]; } // MUST do this AFTER acutal refinement, as precomputed increments are used // for old grid. nextI = newNextI; nextJ = newNextJ; nextK = newNextK; nextIJ = newNextIJ; nextIK = newNextIK; nextJK = newNextJK; nextIJK = newNextIJK; int dml = 0; for ( int dim = 0; dim<3; ++dim ) for ( int m = 0; m < 4; ++m ) for ( int l = 0; l < 4; ++l, ++dml ) GridPointOffset[dml] = dim + l * nextJ + m * nextK; if ( this->m_IgnoreEdge ) this->m_IgnoreEdge = 2 * this->m_IgnoreEdge - 1; this->UnRegisterVolume(); } UniformVolume::CoordinateRegionType SplineWarpXform::GetVolumeOfInfluence( const size_t idx, const UniformVolume::CoordinateRegionType& domain, const bool fastMode ) const { Self::SpaceVectorType regionFrom, regionTo; int relIdx = idx / 3; const int xyz[3] = { ( relIdx % this->m_Dims[0] ), ( (relIdx / this->m_Dims[0]) % this->m_Dims[1] ), ( (relIdx / this->m_Dims[0]) / this->m_Dims[1] ) }; FixedVector<3,Types::Coordinate> xyzLow, xyzUp; if ( fastMode ) { for ( int dim = 0; dim < 3; ++dim ) { xyzLow[dim] = this->m_Spacing[dim] * std::max( 0, xyz[dim]-2 ); xyzUp[dim] = this->m_Spacing[dim] * std::min( this->m_Dims[dim]-3, xyz[dim] ); } } else { for ( int dim = 0; dim < 3; ++dim ) { xyzLow[dim] = this->m_Spacing[dim] * std::max( 0, xyz[dim]-3 ); xyzUp[dim] = this->m_Spacing[dim] * std::min( this->m_Dims[dim]-2, xyz[dim]+1 ); } } for ( int dim = 0; dim < 3; ++dim ) { regionFrom[dim] = std::min( domain.To()[dim], std::max( xyzLow[dim], domain.From()[dim]) ); regionTo[dim] = std::max( domain.From()[dim], std::min( xyzUp[dim], domain.To()[dim]) ); } return UniformVolume::CoordinateRegionType( regionFrom, regionTo ); } void SplineWarpXform::RegisterVolumeAxis ( const DataGrid::IndexType::ValueType dim, const Types::Coordinate delta, const Types::Coordinate origin, const int cpgDim, const size_t ofs, const Types::Coordinate invCpgSpacing, std::vector& gIdx, std::vector& gOfs, std::vector& spline, std::vector& dspline ) { gIdx.resize( dim+1 ); gOfs.resize( dim+1 ); spline.resize( 4*dim ); dspline.resize( 4*dim ); for ( int idx=0; idx( r ), cpgDim-4 ); gOfs[idx] = gIdx[idx] * ofs; const Types::Coordinate f = r - gIdx[idx]; for ( int k = 0; k < 4; ++k ) { spline[4*idx+k] = CubicSpline::ApproxSpline( k, f ); dspline[4*idx+k] = CubicSpline::DerivApproxSpline( k, f ); } } // guard element gIdx[dim] = gOfs[dim] = -1; } void SplineWarpXform::RegisterVolumePoints ( const DataGrid::IndexType& volDims, const Self::SpaceVectorType& delta, const Self::SpaceVectorType& origin ) { const int ijk[3] = { this->nextI, this->nextJ, this->nextK }; for ( int axis = 0; axis < 3; ++axis ) this->RegisterVolumeAxis( volDims[axis], delta[axis], origin[axis], this->m_Dims[axis], ijk[axis], this->m_InverseSpacing[axis], this->m_GridIndexes[axis], this->m_GridOffsets[axis], this->m_GridSpline[axis], this->m_GridDerivSpline[axis] ); this->VolumeDims = volDims; } void SplineWarpXform::UnRegisterVolume() { for ( int axis = 0; axis < 3; ++axis ) { this->m_GridIndexes[axis].resize( 0 ); this->m_GridOffsets[axis].resize( 0 ); this->m_GridSpline[axis].resize( 0 ); this->m_GridDerivSpline[axis].resize( 0 ); } } SplineWarpXform::SpaceVectorType SplineWarpXform::GetDeformedControlPointPosition( const int x, const int y, const int z) const { Self::SpaceVectorType v; // Create a pointer to the front-lower-left corner of the c.p.g. cell. const Types::Coordinate* coeff = m_Parameters + (x-1) * nextI + (y-1) * nextJ + (z-1) * nextK; static const Types::Coordinate spline[3] = { 1.0/6, 4.0/6, 1.0/6 }; for ( int dim = 0; dim<3; ++dim ) { Types::Coordinate mm = 0; const Types::Coordinate *coeff_mm = coeff; for ( int m = 0; m < 3; ++m ) { Types::Coordinate ll = 0; const Types::Coordinate *coeff_ll = coeff_mm; // Loop over 4 c.p.g. planes in y-direction. for ( int l = 0; l < 3; ++l ) { Types::Coordinate kk = 0; const Types::Coordinate *coeff_kk = coeff_ll; // Loop over 4 c.p.g. planes in x-direction. for ( int k = 0; k < 3; ++k, coeff_kk+=3 ) { kk += spline[k] * (*coeff_kk); } ll += spline[l] * kk; coeff_ll += nextJ; } mm += spline[m] * ll; coeff_mm += nextK; } v[dim] = mm; ++coeff; } return v; } SplineWarpXform::SpaceVectorType SplineWarpXform ::GetTransformedGrid ( const int idxX, const int idxY, const int idxZ ) const { Self::SpaceVectorType v; const Types::Coordinate* coeff = this->m_Parameters + this->m_GridOffsets[0][idxX] + this->m_GridOffsets[1][idxY] + this->m_GridOffsets[2][idxZ]; const Types::Coordinate *spX = &this->m_GridSpline[0][idxX<<2], *spY = &this->m_GridSpline[1][idxY<<2], *spZ = &this->m_GridSpline[2][idxZ<<2]; for ( int dim = 0; dim<3; ++dim ) { Types::Coordinate mm = 0; const Types::Coordinate *coeff_mm = coeff; for ( int m = 0; m < 4; ++m ) { Types::Coordinate ll = 0; const Types::Coordinate *coeff_ll = coeff_mm; for ( int l = 0; l < 4; ++l ) { Types::Coordinate kk = 0; const Types::Coordinate *coeff_kk = coeff_ll; for ( int k = 0; k < 4; ++k, coeff_kk+=3 ) { kk += spX[k] * (*coeff_kk); } ll += spY[l] * kk; coeff_ll += nextJ; } mm += spZ[m] * ll; coeff_mm += nextK; } v[ dim ] = mm; ++coeff; } return v; } void SplineWarpXform::GetTransformedGridRow ( const int numPoints, Self::SpaceVectorType *const vIn, const int idxX, const int idxY, const int idxZ ) const { Self::SpaceVectorType *v = vIn; const Types::Coordinate* coeff = m_Parameters + this->m_GridOffsets[0][idxX] + this->m_GridOffsets[1][idxY] + this->m_GridOffsets[2][idxZ]; const Types::Coordinate *spX = &this->m_GridSpline[0][idxX<<2], *spY = &this->m_GridSpline[1][idxY<<2], *spZ = &this->m_GridSpline[2][idxZ<<2]; // precompute the products of B_j(v) and B_k(w) for the 4 x 4 neighborhood // in y- and z-direction. Types::Coordinate sml[16], *psml = sml; for ( int m = 0; m < 4; ++m ) { for ( int l = 0; l < 4; ++l, ++psml ) { *psml = spZ[m] * spY[l]; } } // determine the number of CPG cells on our way along the row const int numberOfCells = (this->m_GridOffsets[0][idxX + numPoints - 1] - this->m_GridOffsets[0][idxX]) / nextI + 4; // pre-compute the contributions of all control points in y- and z-direction // along the way Types::Coordinate phiComp; #ifdef CMTK_COMPILER_VAR_AUTO_ARRAYSIZE Types::Coordinate phiHat[3*numberOfCells]; // GNU compiler can have variable-sized automatic arrays #else std::vector phiHat( 3*numberOfCells ); #endif int phiIdx = 0; for ( int cell = 0; cell < numberOfCells; ++cell, coeff += nextI ) { const int *gpo = &this->GridPointOffset[0]; for ( int dim = 0; dim < 3; ++dim, ++phiIdx ) { phiComp = coeff[ *gpo ] * sml[0]; ++gpo; for ( int ml = 1; ml < 16; ++ml, ++gpo ) { phiComp += coeff[ *gpo ] * sml[ml]; } phiHat[phiIdx] = phiComp; } } // start at the leftmost precomputed CPG cell int cellIdx = 0; // run over all points we're supposed to transform int i = idxX; for ( const int lastPoint = idxX + numPoints; i < lastPoint; ) { // these change only when we enter a new cell const Types::Coordinate* phiPtr = &phiHat[3*cellIdx]; // do everything inside one cell do { Self::SpaceVectorType& vRef = *v; // compute transformed voxel by taking precomputed y- and z-contributions // and adding x. The loops to do this have been unrolled for increased // performance. vRef[0] = spX[0] * phiPtr[0] + spX[1] * phiPtr[3] + spX[2] * phiPtr[6] + spX[3] * phiPtr[9]; vRef[1] = spX[0] * phiPtr[1] + spX[1] * phiPtr[4] + spX[2] * phiPtr[7] + spX[3] * phiPtr[10]; vRef[2] = spX[0] * phiPtr[2] + spX[1] * phiPtr[5] + spX[2] * phiPtr[8] + spX[3] * phiPtr[11]; // go to next voxel ++i; spX += 4; ++v; // repeat this until we leave current CPG cell. } while ( ( this->m_GridOffsets[0][i-1] == this->m_GridOffsets[0][i] ) && ( i < lastPoint ) ); // we've just left a cell -- shift index of precomputed control points // to the next one. ++cellIdx; } } Types::Coordinate SplineWarpXform ::GetGridEnergy() const { double energy = 0; #pragma omp parallel for reduction(+:energy) for ( int z = 1; zm_Dims[2]-1; ++z ) { for ( int y = 1; ym_Dims[1]-1; ++y ) { for ( int x = 1; xm_Dims[0]-1; ++x ) { const Types::Coordinate* coeff = this->m_Parameters + x * nextI + y * nextJ + z * nextK; energy += this->GetGridEnergy( coeff ); } } } return energy / (( this->m_Dims[0] - 2 ) * ( this->m_Dims[1] - 2 ) * ( this->m_Dims[2] - 2 )); } Types::Coordinate SplineWarpXform ::GetGridEnergy( const Self::SpaceVectorType& v ) const { Types::Coordinate r[3], f[3]; int grid[3]; for ( int dim = 0; dim<3; ++dim ) { r[dim] = this->m_InverseSpacing[dim] * v[dim]; grid[dim] = std::min( static_cast( r[dim] ), this->m_Dims[dim]-4 ); f[dim] = std::max( 0, std::min( 1.0, r[dim] - grid[dim] ) ); } const Types::Coordinate* coeff = this->m_Parameters + 3 * ( grid[0] + this->m_Dims[0] * (grid[1] + this->m_Dims[1] * grid[2]) ); // Matrix of one-variable second-order derivatives. double J[3][3]; memset( J, 0, sizeof( J ) ); // Matrix of mixed second-order derivatives. double K[3][3]; memset( K, 0, sizeof( K ) ); for ( int dim = 0; dim<3; ++dim ) { const Types::Coordinate *coeff_mm = coeff; for ( int m = 0; m < 3; ++m ) { Types::Coordinate llJ[3] = { 0, 0, 0 }; Types::Coordinate llK[3] = { 0, 0, 0 }; const Types::Coordinate *coeff_ll = coeff_mm; for ( int l = 0; l < 3; ++l ) { Types::Coordinate kkJ[3] = { 0, 0, 0 }; Types::Coordinate kkK[3] = { 0, 0, 0 }; const Types::Coordinate *coeff_kk = coeff_ll; for ( int k = 0; k < 3; ++k, coeff_kk += nextI ) { const Types::Coordinate tmp = CubicSpline::ApproxSpline( k, f[0] ) * (*coeff_kk); kkJ[0] += CubicSpline::SecondDerivApproxSpline( k, f[0] ) * (*coeff_kk); kkJ[1] += tmp; kkJ[2] += tmp; const Types::Coordinate tmp2 = CubicSpline::DerivApproxSpline( k, f[0] ) * (*coeff_kk); kkK[0] += tmp2; kkK[1] += CubicSpline::ApproxSpline( k, f[0] ) * (*coeff_kk); kkK[2] += tmp2; } const Types::Coordinate tmp = CubicSpline::ApproxSpline( l, f[1] ); llJ[0] += tmp * kkJ[0]; llJ[1] += CubicSpline::SecondDerivApproxSpline( l, f[1] ) * kkJ[1]; llJ[2] += tmp * kkJ[2]; const Types::Coordinate tmp2 = CubicSpline::DerivApproxSpline( l, f[1] ); llK[0] += tmp2 * kkK[0]; llK[1] += CubicSpline::DerivApproxSpline( l, f[1] ) * kkK[1]; llK[2] += tmp2 * kkK[2]; coeff_ll += nextJ; } const Types::Coordinate tmp = CubicSpline::ApproxSpline( m, f[2] ); J[0][dim] += tmp * llJ[0]; J[1][dim] += CubicSpline::ApproxSpline( m, f[2] ) * llJ[1]; J[2][dim] += tmp * llJ[2]; const Types::Coordinate tmp2 = CubicSpline::DerivApproxSpline( m, f[2] ); K[0][dim] += CubicSpline::ApproxSpline( m, f[2] ) * llK[0]; K[1][dim] += tmp2 * llK[1]; K[2][dim] += tmp2 * llK[2]; coeff_mm += nextK; } ++coeff; } const double energy = // single variable second-order derivatives MathUtil::Square( this->m_InverseSpacing[0] ) * ( J[0][0] * J[0][0] + J[0][1] * J[0][1] + J[0][2] * J[0][2] ) + MathUtil::Square( this->m_InverseSpacing[1] ) * ( J[1][0] * J[1][0] + J[1][1] * J[1][1] + J[1][2] * J[1][2] ) + MathUtil::Square( this->m_InverseSpacing[2] ) * ( J[2][0] * J[2][0] + J[2][1] * J[2][1] + J[2][2] * J[2][2] ) + // two-variable mixed derivatives 2 * ( this->m_InverseSpacing[0] * this->m_InverseSpacing[1] * ( K[0][0] * K[0][0] + K[0][1] * K[0][1] + K[0][2] * K[0][2] ) + this->m_InverseSpacing[1] * this->m_InverseSpacing[2] * ( K[1][0] * K[1][0] + K[1][1] * K[1][1] + K[1][2] * K[1][2] ) + this->m_InverseSpacing[2] * this->m_InverseSpacing[0] * ( K[2][0] * K[2][0] + K[2][1] * K[2][1] + K[2][2] * K[2][2] ) ); return energy; } Types::Coordinate SplineWarpXform ::GetGridEnergy( const Types::Coordinate *cp ) const { const double sp[3] = { 1.0/6, 2.0/3, 1.0/6 }; const double dsp[3] = { -1.0/2, 0, 1.0/2 }; const double ddsp[3] = { 1, -2, 1 }; // Matrix of one-variable second-order derivatives. double J[3][3]; memset( J, 0, sizeof( J ) ); // Matrix of mixed second-order derivatives. double K[3][3]; memset( K, 0, sizeof( K ) ); const Types::Coordinate* coeff = cp - nextI - nextJ - nextK; for ( int dim = 0; dim<3; ++dim ) { const Types::Coordinate *coeff_mm = coeff; for ( int m = 0; m < 3; ++m ) { Types::Coordinate llJ[3] = { 0, 0, 0 }; Types::Coordinate llK[3] = { 0, 0, 0 }; const Types::Coordinate *coeff_ll = coeff_mm; for ( int l = 0; l < 3; ++l ) { Types::Coordinate kkJ[3] = { 0, 0, 0 }; Types::Coordinate kkK[3] = { 0, 0, 0 }; const Types::Coordinate *coeff_kk = coeff_ll; for ( int k = 0; k < 3; ++k, coeff_kk += nextI ) { const Types::Coordinate tmp = sp[k] * (*coeff_kk); kkJ[0] += ddsp[k] * (*coeff_kk); kkJ[1] += tmp; kkJ[2] += tmp; const Types::Coordinate tmp2 = dsp[k] * (*coeff_kk); kkK[0] += tmp2; kkK[1] += sp[k] * (*coeff_kk); kkK[2] += tmp2; } llJ[0] += sp[l] * kkJ[0]; llJ[1] += ddsp[l] * kkJ[1]; llJ[2] += sp[l] * kkJ[2]; llK[0] += dsp[l] * kkK[0]; llK[1] += dsp[l] * kkK[1]; llK[2] += sp[l] * kkK[2]; coeff_ll += nextJ; } J[0][dim] += sp[m] * llJ[0]; J[1][dim] += sp[m] * llJ[1]; J[2][dim] += ddsp[m] * llJ[2]; K[0][dim] += sp[m] * llK[0]; K[1][dim] += dsp[m] * llK[1]; K[2][dim] += dsp[m] * llK[2]; coeff_mm += nextK; } ++coeff; } const double energy = // single variable second-order derivatives MathUtil::Square( this->m_InverseSpacing[0] ) * ( J[0][0] * J[0][0] + J[0][1] * J[0][1] + J[0][2] * J[0][2] ) + MathUtil::Square( this->m_InverseSpacing[1] ) * ( J[1][0] * J[1][0] + J[1][1] * J[1][1] + J[1][2] * J[1][2] ) + MathUtil::Square( this->m_InverseSpacing[2] ) * ( J[2][0] * J[2][0] + J[2][1] * J[2][1] + J[2][2] * J[2][2] ) + // two-variable mixed derivatives 2 * ( this->m_InverseSpacing[0] * this->m_InverseSpacing[1] * ( K[0][0] * K[0][0] + K[0][1] * K[0][1] + K[0][2] * K[0][2] ) + this->m_InverseSpacing[1] * this->m_InverseSpacing[2] * ( K[1][0] * K[1][0] + K[1][1] * K[1][1] + K[1][2] * K[1][2] ) + this->m_InverseSpacing[2] * this->m_InverseSpacing[0] * ( K[2][0] * K[2][0] + K[2][1] * K[2][1] + K[2][2] * K[2][2] ) ); return energy; } void SplineWarpXform::GetGridEnergyDerivative ( double& lower, double& upper, const int param, const Types::Coordinate step ) const { const int controlPointIdx = param / nextI; const unsigned short x = ( controlPointIdx % this->m_Dims[0] ); const unsigned short y = ( (controlPointIdx / this->m_Dims[0]) % this->m_Dims[1] ); const unsigned short z = ( (controlPointIdx / this->m_Dims[0]) / this->m_Dims[1] ); const int thisDim = param % nextI; const Types::Coordinate* coeff = m_Parameters + param - thisDim; double ground = 0; const int iFrom = std::max( -1, 1-x ); const int jFrom = std::max( -1, 1-y ); const int kFrom = std::max( -1, 1-z ); const int iTo = std::min( 1, this->m_Dims[0]-2-x ); const int jTo = std::min( 1, this->m_Dims[1]-2-y ); const int kTo = std::min( 1, this->m_Dims[2]-2-z ); for ( int k = kFrom; k < kTo; ++k ) for ( int j = jFrom; j < jTo; ++j ) for ( int i = iFrom; i < iTo; ++i ) { ground += this->GetGridEnergy( coeff + i*nextI + j*nextJ + k*nextK ); } upper = -ground; lower = -ground; const Types::Coordinate oldCoeff = m_Parameters[param]; m_Parameters[param] += step; for ( int k = kFrom; k < kTo; ++k ) for ( int j = jFrom; j < jTo; ++j ) for ( int i = iFrom; i < iTo; ++i ) upper += this->GetGridEnergy( coeff + i*nextI + j*nextJ + k*nextK ); m_Parameters[param] = oldCoeff - step; for ( int k = kFrom; k < kTo; ++k ) for ( int j = jFrom; j < jTo; ++j ) for ( int i = iFrom; i < iTo; ++i ) lower += this->GetGridEnergy( coeff + i*nextI + j*nextJ + k*nextK ); m_Parameters[param] = oldCoeff; upper /= this->m_NumberOfControlPoints; lower /= this->m_NumberOfControlPoints; } Types::Coordinate SplineWarpXform::GetInverseConsistencyError ( const WarpXform* inverse, const UniformVolume* volume, const DataGrid::RegionType* voi ) const { Self::SpaceVectorType v, vv; Types::Coordinate result = 0.0; int count = 0; DataGrid::RegionType myVoi; const DataGrid::RegionType *pVoi = &myVoi; if ( voi ) { pVoi = voi; } else { myVoi = volume->GetWholeImageRegion(); } const int dX = 1 + static_cast( this->m_Spacing[0] / 2 * volume->m_Delta[AXIS_X] ); const int dY = 1 + static_cast( this->m_Spacing[1] / 2 * volume->m_Delta[AXIS_Y] ); const int dZ = 1 + static_cast( this->m_Spacing[2] / 2 * volume->m_Delta[AXIS_Z] ); const int startX = pVoi->From()[0] - (pVoi->From()[0] % dX); const int startY = pVoi->From()[1] - (pVoi->From()[1] % dY); const int startZ = pVoi->From()[2] - (pVoi->From()[2] % dZ); const size_t length = pVoi->To()[0] - startX; #ifdef CMTK_COMPILER_VAR_AUTO_ARRAYSIZE Self::SpaceVectorType vecArray[length]; #else std::vector vecArray( length ); #endif for ( int z = startZ; z < pVoi->To()[2]; z += dZ ) { for ( int y = startY; y < pVoi->To()[1]; y += dY ) { Self::SpaceVectorType* pVec = &vecArray[0]; this->GetTransformedGridRow( length, pVec, startX, y, z ); for ( int x = startX; x < pVoi->To()[0]; x += dX, pVec += dX ) { if ( inverse->InDomain( *pVec ) ) { *pVec = inverse->Apply( *pVec ); v = volume->GetGridLocation( x, y, z ); v -= *pVec; result += v.RootSumOfSquares(); ++count; } } } } return count ? result / count : 0.0; } Types::Coordinate* SplineWarpXform::GetPureDeformation( const bool includeScale ) const { const size_t numberOfParameters = this->m_NumberOfParameters; Types::Coordinate* points = Memory::ArrayC::Allocate( numberOfParameters ); memcpy( points, this->m_Parameters, sizeof( *points ) * numberOfParameters ); AffineXform::SmartPtr xform( this->GetInitialAffineXform()->MakeInverse() ); if ( includeScale ) { xform->SetScales( 1.0, 1.0, 1.0 ); } Types::Coordinate* ptr = points; Self::SpaceVectorType u; for ( size_t pointIdx = 0; pointIdx < numberOfParameters / 3; ++pointIdx, ptr += 3 ) { // undo affine transformation component const Self::SpaceVectorType v = xform->Apply( Self::SpaceVectorType::FromPointer( ptr ) ); // copy the result into ouput array for ( unsigned int dim = 0; dim < 3; ++dim ) ptr[dim] = v[dim]; } return points; } } cmtk-3.3.1/libs/Base/cmtkSplineWarpXform.h000066400000000000000000000434751276303427400204340ustar00rootroot00000000000000/* // // Copyright 1997-2010 Torsten Rohlfing // // Copyright 2004-2014 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 5160 $ // // $LastChangedDate: 2014-01-12 14:33:52 -0800 (Sun, 12 Jan 2014) $ // // $LastChangedBy: torsten_at_home $ // */ #ifndef __cmtkSplineWarpXform_h_included_ #define __cmtkSplineWarpXform_h_included_ #include #include #include #include #include #include #include #include #include #include namespace cmtk { /** \addtogroup Base */ //@{ /** B-spline-based local deformation. */ class SplineWarpXform : /// Inherit generic warp interface and basic functions. public WarpXform { public: /// This class. typedef SplineWarpXform Self; /// Parent class. typedef WarpXform Superclass; /// Smart pointer to SplineWarpXform. typedef SmartPointer SmartPtr; /// Smart pointer to const SplineWarpXform. typedef SmartConstPointer SmartConstPtr; /** Construct empty new warp. * This is mostly useful for the Clone() function that can subsequently * copy all data structures into a newly created empty object. */ SplineWarpXform(); /** Construct new warp from volume size and control grid density. */ SplineWarpXform( const Self::SpaceVectorType& domain, const Types::Coordinate delta, const AffineXform *initialXform = NULL, const bool exactDelta = false ); /** Construct new warp from volume size, grid dimensions and parameters */ SplineWarpXform( const Self::SpaceVectorType& domain, const Self::ControlPointIndexType& dims, CoordinateVector::SmartPtr& parameters = CoordinateVector::SmartPtr::Null(), const AffineXform *initialXform = NULL ); /** Initialize warp from volume size and control grid density. */ void Init( const Self::SpaceVectorType& domain, const Types::Coordinate delta, const AffineXform *initialXform = NULL, const bool exactDelta = false ); /** Get region containing all "inside" control point indexes. * The "inside" control points are those for which the transformation can be evaluated. * Since we are using curbic spline basis functions this region is smaller * than that returned by GetAllControlPointsRegion. */ virtual Self::ControlPointRegionType GetInsideControlPointsRegion() const; /// Clone and return smart pointer. Self::SmartPtr Clone () const { return Self::SmartPtr( this->CloneVirtual() ); } /** Create inverse transformation. * This function returns NULL as there is no explicit inverse of a spline * warp transformation. */ virtual SplineWarpXform* MakeInverse() const { return NULL; } /// Refine control point grid by a factor of two, but maintain transformation exactly. virtual void Refine(); /// Return grid bending energy. virtual Types::Coordinate GetGridEnergy() const; /** Return grid bending energy at one control point. *\param cp The control point where the bending energy is to be evaluated. */ virtual Types::Coordinate GetGridEnergy( const Types::Coordinate *cp ) const; /** Return grid bending energy at arbitrary location. */ virtual Types::Coordinate GetGridEnergy( const Self::SpaceVectorType& v ) const; /// Return derivative of grid energy with respect to one parameter. virtual void GetGridEnergyDerivative( double& lower, double& upper, const int param, const Types::Coordinate step ) const; /// Compute Jacobian determinant at a certain location. virtual Types::Coordinate GetJacobianDeterminant ( const Self::SpaceVectorType& v ) const; /// Compute Jacobian determinant at a certain reference image pixel. virtual Types::Coordinate GetJacobianDeterminant ( const int x, const int y, const int z ) const; /// Compute sequence of Jacobian determinants from given grid location. virtual void GetJacobianDeterminantRow( double *const values, const int x, const int y, const int z, const size_t numberOfPoints = 1 ) const; /** Compute Jacobian determinant at a certain control point. * As we evaluate the spline polynomials and their derivatives at a known * control point, they reduce to constants which allows for a particularly * efficient computation. *\param cp Pointer to the spline control point at which the Jacobian * determinant is to be evaluated. This pointer is assumed to be pointing * to the 0th-component (ie, x) of a non-peripheral control point. Callers * have to make sure that this is true. */ Types::Coordinate JacobianDeterminant ( const Types::Coordinate *cp ) const; /// Return Jacobian constraint of the current transformation grid. virtual Types::Coordinate GetJacobianConstraint() const; /** Relax the deformation to unfold areas with negative Jacobian at the current image sampling. *\note This requires a uniform pixel grid to be registered with this transformation. *\see RegisterVolume */ virtual void RelaxToUnfold(); /// Return rigidity constraint of the current transformation grid. virtual Types::Coordinate GetRigidityConstraint() const; /// Return rigidity constraint of the current transformation grid with local weights. virtual Types::Coordinate GetRigidityConstraint( const DataGrid* weightMap ) const; /// Return derivative of Jacobian constraint with respect to one parameter. virtual void GetJacobianConstraintDerivative( double& lower, double& upper, const int param, const UniformVolume::RegionType&, const Types::Coordinate step ) const; /// Return derivative of Jacobian constraint with respect to one parameter. virtual void GetJacobianConstraintDerivative( double& lower, double& upper, const int param, const Types::Coordinate step ) const; /// Return derivative of rigidity constraint with respect to one parameter. virtual void GetRigidityConstraintDerivative( double& lower, double& upper, const int param, const UniformVolume::RegionType&, const Types::Coordinate step ) const; /// Return derivative of rigidity constraint with respect to one parameter. virtual void GetRigidityConstraintDerivative( double& lower, double& upper, const int param, const UniformVolume::RegionType&, const Types::Coordinate step, const DataGrid* weightMap ) const; /// Return derivative of rigidity constraint with respect to one parameter. virtual void GetRigidityConstraintDerivative( double& lower, double& upper, const int param, const Types::Coordinate step ) const; /** Return inverse consistency. */ virtual Types::Coordinate GetInverseConsistencyError( const WarpXform* inverse, const UniformVolume* volume, const UniformVolume::RegionType* voi = NULL ) const; /** Return origin of warped vector. * Note that since this class of transformation is not closed under inversion * this function computes only a more or less accurate numerical * approximation to the actual origin of a warped vector. Note also that this * computation is everything but computationally efficient. *\return True is the given inverse was succesfully comuted, false if the * given warped vector was outside the target domain of this transformation. */ virtual bool ApplyInverse ( const Self::SpaceVectorType& v, Self::SpaceVectorType& u, const Types::Coordinate accuracy = 0.01 ) const; /// Replace existing vector with transformed location. virtual Self::SpaceVectorType Apply( const Self::SpaceVectorType& v ) const { Self::SpaceVectorType vTransformed; Types::Coordinate f[3]; int grid[3]; // Do some precomputations. for ( int dim = 0; dim<3; ++dim ) { // This is the (real-valued) index of the control point grid cell the // given location is in. const Types::Coordinate r = this->m_InverseSpacing[dim] * v[dim]; // This is the actual cell index. grid[dim] = std::min( static_cast( r ), this->m_Dims[dim]-4 ); // And here's the relative position within the cell. f[dim] = r - grid[dim]; } // Create a pointer to the front-lower-left corner of the c.p.g. cell. const Types::Coordinate* coeff = this->m_Parameters + 3 * ( grid[0] + this->m_Dims[0] * (grid[1] + this->m_Dims[1] * grid[2]) ); for ( int dim = 0; dim<3; ++dim ) { Types::Coordinate mm = 0; const Types::Coordinate *coeff_mm = coeff; // Loop over 4 c.p.g. planes in z-direction. for ( int m = 0; m < 4; ++m ) { Types::Coordinate ll = 0; const Types::Coordinate *coeff_ll = coeff_mm; // Loop over 4 c.p.g. planes in y-direction. for ( int l = 0; l < 4; ++l ) { Types::Coordinate kk = 0; const Types::Coordinate *coeff_kk = coeff_ll; // Loop over 4 c.p.g. planes in x-direction. for ( int k = 0; k < 4; ++k, coeff_kk+=3 ) { kk += CubicSpline::ApproxSpline( k, f[0] ) * (*coeff_kk); } ll += CubicSpline::ApproxSpline( l, f[1] ) * kk; coeff_ll += nextJ; } mm += CubicSpline::ApproxSpline( m, f[2] ) * ll; coeff_mm += nextK; } vTransformed[dim] = mm; ++coeff; } return vTransformed; } /// Precompute spline parameters for one point. void PrecomputeLocationSpline( const Self::SpaceVectorType& v, FixedVector<3,int>& grid, FixedArray< 3, FixedVector<4,Types::Coordinate> >& spline ) const { for ( int dim = 0; dim<3; ++dim ) { // This is the (real-valued) index of the control point grid cell the given location is in. const Types::Coordinate r = this->m_InverseSpacing[dim] * v[dim]; // This is the actual cell index. grid[dim] = std::min( static_cast( r ), this->m_Dims[dim]-4 ); // And here's the relative position within the cell. const Types::Coordinate fractional = r - grid[dim]; for ( int k = 0; k < 4; ++k ) { spline[dim][k] = CubicSpline::ApproxSpline( k, fractional ); } } } /// Get volume influenced by one parameter. virtual UniformVolume::CoordinateRegionType GetVolumeOfInfluence( const size_t idx /*!< Parameter point index */, const UniformVolume::CoordinateRegionType& domain /*!< Underlying image domain.*/ ) const { return this->GetVolumeOfInfluence( idx, domain, this->m_FastMode ); } /// Get volume influenced by one parameter with explicit selection of fast vs. accurate mode. virtual UniformVolume::CoordinateRegionType GetVolumeOfInfluence( const size_t idx /*!< Parameter point index */, const UniformVolume::CoordinateRegionType& domain /*!< Underlying image domain. */, const bool fastMode /*!< Fast mode selector. When 0, "slow", accurate mode is forced. */ ) const; /// Register the grid points of the deformed uniform or non-uniform volume. void RegisterVolume( const UniformVolume& volume ) { this->RegisterVolumePoints( volume.m_Dims, volume.m_Delta, volume.m_Offset ); } /// Register axes points of the volume to be deformed. void RegisterVolumePoints ( const DataGrid::IndexType&, const Self::SpaceVectorType& ); /// Register axes points of the volume to be deformed. void RegisterVolumePoints( const DataGrid::IndexType&, const Self::SpaceVectorType&, const Self::SpaceVectorType& ); /// Unegister axes points, ie free all internal data structures. void UnRegisterVolume(); /// Get a grid point from the deformed grid. Self::SpaceVectorType GetTransformedGrid( const int idxX, const int idxY, const int idxZ ) const; /// Get a sequence of grid points from the deformed grid. void GetTransformedGridRow( const int numPoints, Self::SpaceVectorType *const v, const int idxX, const int idxY, const int idxZ ) const; /// Get parameter stepping. virtual Types::Coordinate GetParamStep( const size_t idx, const Self::SpaceVectorType& volSize, const Types::Coordinate mmStep = 1 ) const { return 4 * WarpXform::GetParamStep( idx, volSize, mmStep ); } /** Get the deformed position of a transformation control point. *\note This function does not return the shifted control point position, * but rather it applies the current transformation to the given control * point. It does so more efficiently than applying the transformation to * the explicit 3D coordinate of the control point, because most spline * coefficients vanish at control points and need not be considered. *\attention The valid range for x,y,z begins at 1, not 0, because we cannot * compute the transformation for control points outside the domain * boundary. */ virtual Self::SpaceVectorType GetDeformedControlPointPosition( const int x, const int y, const int z ) const; /** Return array of pre deformation vectors. * The newly alocated data array contains the control point positions * after deformation without affine components. *\param includeScale If this flag is set (default: off), then the scale * components of the affine transformation remain in the deformation. */ Types::Coordinate* GetPureDeformation( const bool includeScale = false ) const; /// Get local Jacobian. virtual const CoordinateMatrix3x3 GetJacobian( const Self::SpaceVectorType& v ) const; /// Get local Jacobian at control point into existing matrix. virtual const CoordinateMatrix3x3 GetJacobianAtControlPoint( const Types::Coordinate* cp ) const; /// Get sequence of Jacobians for pixel row. virtual void GetJacobianRow( CoordinateMatrix3x3 *const array, const int x, const int y, const int z, const size_t numberOfPoints ) const; private: /// Initialize control point positions, potentially with affine displacement. void InitControlPoints( const AffineXform* affineXform = NULL ); /// Update internal representation. virtual void Update( const bool exactDelta = false ); /// Register a single axis of the uniform volume to be deformed. void RegisterVolumeAxis ( const DataGrid::IndexType::ValueType, const Types::Coordinate delta, const Types::Coordinate origin, const int, const size_t ofs, const Types::Coordinate, std::vector& gIdx, std::vector& gOfs, std::vector& spline, std::vector& dspline ); /// Return rigidity constraint based on given Jacobian matrix. Types::Coordinate GetRigidityConstraint( const CoordinateMatrix3x3& J ) const; protected: /// Clone transformation. virtual SplineWarpXform* CloneVirtual () const; /// Dimensions of the volume image linked to this transformation. DataGrid::IndexType VolumeDims; /**\name Precomputed grid index values. * These arrays hold the precomputed grid indexes of the deformed grid's * voxels with respect to the control point grid of this deformation. */ FixedArray< 3,std::vector > m_GridIndexes; /**\name Precomputed coefficient array offsets. * These arrays hold the precomputed grid offsets of the deformed grid's * voxels with respect to the control point grid of this deformation. These * values are the grid indexes multiplied by the number of elements to skip in * the coefficient array that corresponds to the respective index. */ FixedArray< 3,std::vector > m_GridOffsets; /**\name Precomputed spline coefficients. * These arrays hold the precomputed spline coefficients for deforming the * voxel locations in the associated deformed grid. */ FixedArray< 3,std::vector > m_GridSpline; /**\name Precomputed derivative spline coefficients. * These arrays hold the precomputed derivatives of the spline coefficients. * This allows for rapid evaluation of the Jacobian determinant. */ /// x-axis. FixedArray< 3,std::vector > m_GridDerivSpline; /// Relative offsets of all control points in a 4 x 4 x 4 neighborhood. int GridPointOffset[48]; /** Initialize internal data structures. * This function is called from the various destructors to avoid unnecessary * duplication of code. */ void Init (); /** Thread parameter block for volume resampling. * This structure holds all thread-specific information. A pointer to an * instance of this structure is given to EvaluateGradientThread() for * each thread created. */ typedef struct { /// Pointer to the functional object that created the thread. const SplineWarpXform *thisObject; /// Unique index of this thread instance among all threads. int ThisThreadIndex; /// Total number of threads created. int NumberOfThreads; /// Constraint for subvolume handled by this thread. Types::Coordinate Constraint; } JacobianConstraintThreadInfo; /// Thread function for SMP Jacobian constraint computation. static void GetJacobianConstraintThread( void *const args, const size_t taskIdx, const size_t taskCnt, const size_t, const size_t ); /// Find nearest (after deformation) control point. Self::SpaceVectorType FindClosestControlPoint( const Self::SpaceVectorType& v ) const; /// Friend declaration. friend class SplineWarpXformUniformVolume; /// Fitting class is a friend. friend class FitSplineWarpToDeformationField; /// Fitting class is a friend. friend class FitSplineWarpToXformList; /// Fitting class is a friend. friend class FitSplineWarpToLandmarks; }; } // namespace #endif // #ifndef __cmtkSplineWarpXform_h_included_ cmtk-3.3.1/libs/Base/cmtkSplineWarpXformUniformVolume.cxx000066400000000000000000000150361276303427400235270ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2013 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4870 $ // // $LastChangedDate: 2013-09-24 11:01:11 -0700 (Tue, 24 Sep 2013) $ // // $LastChangedBy: torstenrohlfing $ // */ #include "cmtkSplineWarpXformUniformVolume.h" cmtk::SplineWarpXformUniformVolume::SplineWarpXformUniformVolume( const UniformVolume& volume, const SplineWarpXform::SmartConstPtr& xform ) : m_Xform( xform ) { this->RegisterVolume( volume ); } void cmtk::SplineWarpXformUniformVolume::RegisterVolumeAxis ( const int dim, const Types::Coordinate delta, const Types::Coordinate origin, const int cpgDim, const Types::Coordinate invCpgSpacing, std::vector& g, std::vector& spline, std::vector& dspline ) { g.resize( dim+1 ); spline.resize( 4*dim ); dspline.resize( 4*dim ); for ( int idx=0; idx( r ), cpgDim-4 ); const Types::Coordinate f = r - g[idx]; for ( int k = 0; k < 4; ++k ) { spline[4*idx+k] = CubicSpline::ApproxSpline( k, f ); dspline[4*idx+k] = CubicSpline::DerivApproxSpline( k, f ); } } // guard element g[dim] = -1; } void cmtk::SplineWarpXformUniformVolume::RegisterVolume ( const UniformVolume& volume ) { const SplineWarpXform& xform = *(this->m_Xform); this->RegisterVolumeAxis( volume.m_Dims[0], volume.m_Delta[0], volume.m_Offset[0], xform.m_Dims[0], xform.m_InverseSpacing[0], this->gX, this->splineX, this->dsplineX ); this->RegisterVolumeAxis( volume.m_Dims[1], volume.m_Delta[1], volume.m_Offset[1], xform.m_Dims[1], xform.m_InverseSpacing[1], this->gY, this->splineY, this->dsplineY ); this->RegisterVolumeAxis( volume.m_Dims[2], volume.m_Delta[2], volume.m_Offset[2], xform.m_Dims[2], xform.m_InverseSpacing[2], this->gZ, this->splineZ, this->dsplineZ ); for ( int idx = 0; idx < volume.m_Dims[0]; ++idx ) gX[idx] *= xform.nextI; for ( int idx = 0; idx < volume.m_Dims[1]; ++idx ) gY[idx] *= xform.nextJ; for ( int idx = 0; idx < volume.m_Dims[2]; ++idx ) gZ[idx] *= xform.nextK; } void cmtk::SplineWarpXformUniformVolume ::GetTransformedGrid ( Vector3D& v, const int idxX, const int idxY, const int idxZ ) const { const SplineWarpXform& xform = *(this->m_Xform); const Types::Coordinate* coeff = xform.m_Parameters + gX[idxX] + gY[idxY] + gZ[idxZ]; const Types::Coordinate *spX = &splineX[idxX<<2], *spY = &splineY[idxY<<2], *spZ = &splineZ[idxZ<<2]; for ( int dim = 0; dim<3; ++dim ) { Types::Coordinate mm = 0; const Types::Coordinate *coeff_mm = coeff; for ( int m = 0; m < 4; ++m ) { Types::Coordinate ll = 0; const Types::Coordinate *coeff_ll = coeff_mm; for ( int l = 0; l < 4; ++l ) { Types::Coordinate kk = 0; const Types::Coordinate *coeff_kk = coeff_ll; for ( int k = 0; k < 4; ++k, coeff_kk+=3 ) { kk += spX[k] * (*coeff_kk); } ll += spY[l] * kk; coeff_ll += xform.nextJ; } mm += spZ[m] * ll; coeff_mm += xform.nextK; } v[ dim ] = mm; ++coeff; } } void cmtk::SplineWarpXformUniformVolume::GetTransformedGridRow ( Vector3D *const vIn, const size_t numPoints, const int idxX, const int idxY, const int idxZ ) const { Vector3D *v = vIn; const SplineWarpXform& xform = *(this->m_Xform); const Types::Coordinate* coeff = xform.m_Parameters + gX[idxX] + gY[idxY] + gZ[idxZ]; const Types::Coordinate *spX = &splineX[idxX<<2], *spY = &splineY[idxY<<2], *spZ = &splineZ[idxZ<<2]; // precompute the products of B_j(v) and B_k(w) for the 4 x 4 neighborhood // in y- and z-direction. Types::Coordinate sml[16], *psml = sml; for ( int m = 0; m < 4; ++m ) { for ( int l = 0; l < 4; ++l, ++psml ) { *psml = spZ[m] * spY[l]; } } // determine the number of CPG cells on our way along the row const int numberOfCells = (gX[idxX + numPoints - 1] - gX[idxX]) / xform.nextI + 4; // pre-compute the contributions of all control points in y- and z-direction // along the way Types::Coordinate phiComp; std::vector phiHat( 3*numberOfCells ); int phiIdx = 0; for ( int cell = 0; cell < numberOfCells; ++cell, coeff += xform.nextI ) { const int *gpo = &this->GridPointOffset[0]; for ( int dim = 0; dim < 3; ++dim, ++phiIdx ) { phiComp = coeff[ *gpo ] * sml[0]; ++gpo; for ( int ml = 1; ml < 16; ++ml, ++gpo ) { phiComp += coeff[ *gpo ] * sml[ml]; } phiHat[phiIdx] = phiComp; } } // start at the leftmost precomputed CPG cell int cellIdx = 0; // run over all points we're supposed to transform int i = idxX; for ( const int lastPoint = idxX + numPoints; i < lastPoint; ) { // these change only when we enter a new cell const Types::Coordinate* phiPtr = &phiHat[3*cellIdx]; // do everything inside one cell do { Vector3D& vRef = *v; // compute transformed voxel by taking precomputed y- and z-contributions // and adding x. The loops to do this have been unrolled for increased // performance. vRef[0] = spX[0] * phiPtr[0] + spX[1] * phiPtr[3] + spX[2] * phiPtr[6] + spX[3] * phiPtr[9]; vRef[1] = spX[0] * phiPtr[1] + spX[1] * phiPtr[4] + spX[2] * phiPtr[7] + spX[3] * phiPtr[10]; vRef[2] = spX[0] * phiPtr[2] + spX[1] * phiPtr[5] + spX[2] * phiPtr[8] + spX[3] * phiPtr[11]; // go to next voxel ++i; spX += 4; ++v; // repeat this until we leave current CPG cell. } while ( ( gX[i-1] == gX[i] ) && ( i < lastPoint ) ); // we've just left a cell -- shift index of precomputed control points // to the next one. ++cellIdx; } } cmtk-3.3.1/libs/Base/cmtkSplineWarpXformUniformVolume.h000066400000000000000000000102461276303427400231520ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2010 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 2752 $ // // $LastChangedDate: 2011-01-17 11:33:31 -0800 (Mon, 17 Jan 2011) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkSplineWarpXformUniformVolume_h_included_ #define __cmtkSplineWarpXformUniformVolume_h_included_ #include #include #include #include namespace cmtk { /** \addtogroup Base */ //@{ /** Pre-compute transformation for grid locations in a uniform volume. */ class SplineWarpXformUniformVolume : /// Inherit from class to prevent copying. public XformUniformVolume { public: /// This class. typedef SplineWarpXformUniformVolume Self; /// Parent class. typedef XformUniformVolume Superclass; /// Smart pointer to this class. typedef SmartPointer SmartPtr; /// Smart pointer-to-const to this class. typedef SmartConstPointer SmartConstPtr; /// Constructor. SplineWarpXformUniformVolume( const UniformVolume& volume, const SplineWarpXform::SmartConstPtr& xform ); /// Virtual destructor. virtual ~SplineWarpXformUniformVolume() {} /** Get transformed location of linked grid pixel. */ virtual void GetTransformedGrid( Vector3D& v, const int idxX, const int idxY, const int idxZ ) const; /** Get transformed locations of a series (scanline) of linked grid pixels. */ virtual void GetTransformedGridRow( Vector3D *const v, const size_t numPoints, const int idxX, const int idxY, const int idxZ ) const; private: /// The linked transformation. const SplineWarpXform::SmartConstPtr m_Xform; /// Register axes points of the volume to be deformed. void RegisterVolume( const UniformVolume& volume ); /// Register a single axis of the uniform volume to be deformed. void RegisterVolumeAxis ( const int, const Types::Coordinate delta, const Types::Coordinate origin, const int, const Types::Coordinate, std::vector& g, std::vector& spline, std::vector& dspline ); /**\name Precomputed grid indices. * These arrays hold the precomputed grid indices of the deformed grid's * voxels with respect to the control point grid of this deformation. */ //@{ /// x-axis. std::vector gX; /// y-axis. std::vector gY; /// z-axis. std::vector gZ; //@} /**\name Precomputed spline coefficients. * These arrays hold the precomputed spline coefficients for deforming the * voxel locations in the associated deformed grid. */ //@{ /// x-axis. std::vector splineX; /// y-axis. std::vector splineY; /// z-axis. std::vector splineZ; //@} /**\name Precomputed derivative spline coefficients. * These arrays hold the precomputed derivatives of the spline coefficients. * This allows for rapid evaluation of the Jacobian determinant. */ //@{ /// x-axis. std::vector dsplineX; /// y-axis. std::vector dsplineY; /// z-axis. std::vector dsplineZ; //@} /// Relative offsets of all control points in a 4 x 4 x 4 neighborhood. int GridPointOffset[48]; }; //@} } // namespace cmtk #endif // #ifdef __cmtkSplineWarpXformUniformVolume_h_included_ cmtk-3.3.1/libs/Base/cmtkSplineWarpXform_Inverse.cxx000066400000000000000000000056351276303427400224760ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2012 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 5160 $ // // $LastChangedDate: 2014-01-12 14:33:52 -0800 (Sun, 12 Jan 2014) $ // // $LastChangedBy: torsten_at_home $ // */ #include "cmtkSplineWarpXform.h" #include namespace cmtk { /** \addtogroup Base */ //@{ bool SplineWarpXform::ApplyInverse ( const Self::SpaceVectorType& v, Self::SpaceVectorType& u, const Types::Coordinate accuracy ) const { return this->ApplyInverseWithInitial( v, u, this->FindClosestControlPoint( v ), accuracy ); } SplineWarpXform::SpaceVectorType SplineWarpXform::FindClosestControlPoint ( const Self::SpaceVectorType& v ) const { // find closest control point -- we'll go from there. Types::Coordinate closestDistance = FLT_MAX; Types::Coordinate idx[3]; for ( int dim = 0; dim < 3; ++dim ) idx[dim] = 0.5 * this->m_Dims[dim]; for ( Types::Coordinate step = 0.25 * MathUtil::Min( 3, idx ); step > 0.01; step *= 0.5 ) { bool improved = true; while ( improved ) { improved = false; int closestDim = 0, closestDir = 0; for ( int dim = 0; dim < 3; ++dim ) { for ( int dir = -1; dir < 2; dir +=2 ) { const Types::Coordinate oldIdx = idx[dim]; idx[dim] += dir * step; if ( (idx[dim] > 0) && (idx[dim] <= this->m_Dims[dim]-2) ) { Self::SpaceVectorType cp = this->Apply( this->GetOriginalControlPointPosition( idx[0], idx[1], idx[2] ) ); cp -= v; const Types::Coordinate distance = cp.RootSumOfSquares(); if ( distance < closestDistance ) { closestDistance = distance; closestDim = dim; closestDir = dir; improved = true; } } idx[dim] = oldIdx; } } if ( improved ) { idx[closestDim] += closestDir * step; } } } assert( (idx[0] <= this->m_Dims[0]-1) && (idx[1] <= this->m_Dims[1]-1 ) && (idx[2] <= this->m_Dims[2]-1) ); assert( idx[0] >= 0 && idx[1] >= 0 && idx[2] >= 0 ); return this->GetOriginalControlPointPosition( idx[0], idx[1], idx[2] ); } } // namespace cmtk cmtk-3.3.1/libs/Base/cmtkSplineWarpXform_Jacobian.cxx000066400000000000000000000674371276303427400226010ustar00rootroot00000000000000/* // // Copyright 1997-2010 Torsten Rohlfing // // Copyright 2004-2014 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 5160 $ // // $LastChangedDate: 2014-01-12 14:33:52 -0800 (Sun, 12 Jan 2014) $ // // $LastChangedBy: torsten_at_home $ // */ #include "cmtkSplineWarpXform.h" #include #include #include #include namespace cmtk { /** \addtogroup Base */ //@{ void SplineWarpXform::GetJacobianRow ( CoordinateMatrix3x3 *const array, const int x, const int y, const int z, const size_t numberOfPoints ) const { const Types::Coordinate *spX = &this->m_GridSpline[0][x<<2], *spY = &this->m_GridSpline[1][y<<2], *spZ = &this->m_GridSpline[2][z<<2]; const Types::Coordinate *dspX = &this->m_GridDerivSpline[0][x<<2], *dspY = &this->m_GridDerivSpline[1][y<<2], *dspZ = &this->m_GridDerivSpline[2][z<<2]; const Types::Coordinate *coeff = this->m_Parameters + this->m_GridOffsets[0][x] + this->m_GridOffsets[1][y] + this->m_GridOffsets[2][z]; // precompute the products of B_j(v) and B_k(w) for the 4 x 4 neighborhood // in y- and z-direction. Types::Coordinate smlX[16], smlY[16], smlZ[16]; for ( int i = 0, m = 0; m < 4; ++m ) { for ( int l = 0; l < 4; ++l, ++i ) { smlX[i] = spZ[m] * spY[l]; smlY[i] = spZ[m] * dspY[l]; smlZ[i] = dspZ[m] * spY[l]; } } // determine the number of CPG cells on our way along the row const int numberOfCells = (this->m_GridOffsets[0][x + numberOfPoints - 1] - this->m_GridOffsets[0][x]) / nextI + 4; // pre-compute the contributions of all control points in y- and z-direction // along the way Types::Coordinate phiCompX, phiCompY, phiCompZ; #ifdef CMTK_COMPILER_VAR_AUTO_ARRAYSIZE Types::Coordinate phiHatX[3*numberOfCells]; Types::Coordinate phiHatY[3*numberOfCells]; Types::Coordinate phiHatZ[3*numberOfCells]; #else std::vector phiHatX(3*numberOfCells); std::vector phiHatY(3*numberOfCells); std::vector phiHatZ(3*numberOfCells); #endif int phiIdx = 0; for ( int cell = 0; cell < numberOfCells; ++cell, coeff += nextI ) { const int *gpo = &GridPointOffset[0]; for ( int dim = 0; dim < 3; ++dim, ++phiIdx ) { phiCompX = phiCompY = phiCompZ = 0; for ( int ml = 0; ml < 16; ++ml, ++gpo ) { phiCompX += coeff[ *gpo ] * smlX[ml]; phiCompY += coeff[ *gpo ] * smlY[ml]; phiCompZ += coeff[ *gpo ] * smlZ[ml]; } phiHatX[phiIdx] = phiCompX; phiHatY[phiIdx] = phiCompY; phiHatZ[phiIdx] = phiCompZ; } } // start at the leftmost precomputed CPG cell int cellIdx = 0; CoordinateMatrix3x3 J; // run over all points we're supposed to transform int i = x; for ( const int lastPoint = x + numberOfPoints; i < lastPoint; ) { // these change only when we enter a new cell #ifdef CMTK_COMPILER_VAR_AUTO_ARRAYSIZE const Types::Coordinate* phiPtrX = phiHatX + 3*cellIdx; const Types::Coordinate* phiPtrY = phiHatY + 3*cellIdx; const Types::Coordinate* phiPtrZ = phiHatZ + 3*cellIdx; #else const Types::Coordinate* phiPtrX = &phiHatX[3*cellIdx]; const Types::Coordinate* phiPtrY = &phiHatY[3*cellIdx]; const Types::Coordinate* phiPtrZ = &phiHatZ[3*cellIdx]; #endif // do everything inside one cell do { // compute transformed voxel by taking precomputed y- and z-contributions // and adding x. The loops to do this have been unrolled for increased // performance. J[0][0] = this->m_InverseSpacing[0] * ( dspX[0] * phiPtrX[0] + dspX[1] * phiPtrX[3] + dspX[2] * phiPtrX[6] + dspX[3] * phiPtrX[9] ); J[0][1] = this->m_InverseSpacing[0] * ( dspX[0] * phiPtrX[1] + dspX[1] * phiPtrX[4] + dspX[2] * phiPtrX[7] + dspX[3] * phiPtrX[10] ); J[0][2] = this->m_InverseSpacing[0] * ( dspX[0] * phiPtrX[2] + dspX[1] * phiPtrX[5] + dspX[2] * phiPtrX[8] + dspX[3] * phiPtrX[11] ); J[1][0] = this->m_InverseSpacing[1] * ( spX[0] * phiPtrY[0] + spX[1] * phiPtrY[3] + spX[2] * phiPtrY[6] + spX[3] * phiPtrY[9] ); J[1][1] = this->m_InverseSpacing[1] * ( spX[0] * phiPtrY[1] + spX[1] * phiPtrY[4] + spX[2] * phiPtrY[7] + spX[3] * phiPtrY[10] ); J[1][2] = this->m_InverseSpacing[1] * ( spX[0] * phiPtrY[2] + spX[1] * phiPtrY[5] + spX[2] * phiPtrY[8] + spX[3] * phiPtrY[11] ); J[2][0] = this->m_InverseSpacing[2] * ( spX[0] * phiPtrZ[0] + spX[1] * phiPtrZ[3] + spX[2] * phiPtrZ[6] + spX[3] * phiPtrZ[9] ); J[2][1] = this->m_InverseSpacing[2] * ( spX[0] * phiPtrZ[1] + spX[1] * phiPtrZ[4] + spX[2] * phiPtrZ[7] + spX[3] * phiPtrZ[10] ); J[2][2] = this->m_InverseSpacing[2] * ( spX[0] * phiPtrZ[2] + spX[1] * phiPtrZ[5] + spX[2] * phiPtrZ[8] + spX[3] * phiPtrZ[11] ); array[i-x].Set( &J[0][0] ); // go to next voxel ++i; spX += 4; dspX += 4; // repeat this until we leave current CPG cell. } while ( ( this->m_GridOffsets[0][i-1] == this->m_GridOffsets[0][i] ) && ( i < lastPoint ) ); // we've just left a cell -- shift index of precomputed control points // to the next one. ++cellIdx; } } const CoordinateMatrix3x3 SplineWarpXform::GetJacobianAtControlPoint ( const Types::Coordinate* cp ) const { CoordinateMatrix3x3 J = CoordinateMatrix3x3::Zero(); const double sp[3] = { 1.0/6, 2.0/3, 1.0/6 }; const double dsp[3] = { -1.0/2, 0, 1.0/2 }; const Types::Coordinate* coeff = cp - nextI - nextJ - nextK; for ( int dim = 0; dim<3; ++dim ) { const Types::Coordinate *coeff_mm = coeff; for ( int m = 0; m < 3; ++m ) { Types::Coordinate ll[3] = { 0, 0, 0 }; const Types::Coordinate *coeff_ll = coeff_mm; for ( int l = 0; l < 3; ++l ) { Types::Coordinate kk[3] = { 0, 0, 0 }; const Types::Coordinate *coeff_kk = coeff_ll; for ( int k = 0; k < 3; ++k, coeff_kk += nextI ) { kk[0] += dsp[k] * (*coeff_kk); kk[1] += sp[k] * (*coeff_kk); kk[2] += sp[k] * (*coeff_kk); } ll[0] += sp[l] * kk[0]; ll[1] += dsp[l] * kk[1]; ll[2] += sp[l] * kk[2]; coeff_ll += nextJ; } J[0][dim] += sp[m] * ll[0]; J[1][dim] += sp[m] * ll[1]; J[2][dim] += dsp[m] * ll[2]; coeff_mm += nextK; } ++coeff; } for ( int i = 0; i<3; ++i ) { for ( int j = 0; j<3; ++j ) J[i][j] *= this->m_InverseSpacing[i]; } return J; } const CoordinateMatrix3x3 SplineWarpXform::GetJacobian ( const Self::SpaceVectorType& v ) const { Types::Coordinate r[3], f[3]; int grid[3]; for ( int dim = 0; dim<3; ++dim ) { r[dim] = this->m_InverseSpacing[dim] * v[dim]; grid[dim] = std::min( static_cast( r[dim] ), this->m_Dims[dim]-4 ); f[dim] = std::max( 0, std::min( 1.0, r[dim] - grid[dim] ) ); } CoordinateMatrix3x3 J = CoordinateMatrix3x3::Zero(); const Types::Coordinate* coeff = this->m_Parameters + 3 * ( grid[0] + this->m_Dims[0] * (grid[1] + this->m_Dims[1] * grid[2]) ); // loop over the three components of the coordinate transformation function, // x, y, z. for ( int dim = 0; dim<3; ++dim, ++coeff ) { const Types::Coordinate *coeff_mm = coeff; for ( int m = 0; m < 4; ++m, coeff_mm += nextK ) { Types::Coordinate ll[3] = { 0, 0, 0 }; const Types::Coordinate *coeff_ll = coeff_mm; for ( int l = 0; l < 4; ++l, coeff_ll += nextJ ) { Types::Coordinate kk[3] = { 0, 0, 0 }; const Types::Coordinate *coeff_kk = coeff_ll; for ( int k = 0; k < 4; ++k, coeff_kk+=3 ) { // dT / dx kk[0] += CubicSpline::DerivApproxSpline( k, f[0] ) * (*coeff_kk); // dT / dy const Types::Coordinate tmp = CubicSpline::ApproxSpline( k, f[0] ) * (*coeff_kk); kk[1] += tmp; // dT / dz kk[2] += tmp; } // dT / dx const Types::Coordinate tmp = CubicSpline::ApproxSpline( l, f[1] ); ll[0] += tmp * kk[0]; // dT / dy ll[1] += CubicSpline::DerivApproxSpline( l, f[1] ) * kk[1]; // dT / dz ll[2] += tmp * kk[2]; } // dT / dx const Types::Coordinate tmp = CubicSpline::ApproxSpline( m, f[2] ); J[dim][0] += tmp * ll[0]; // dT / dy J[dim][1] += tmp * ll[1]; // dT / dz J[dim][2] += CubicSpline::DerivApproxSpline( m, f[2] ) * ll[2]; } } // scale with grid spacing to normalize Jacobian (chain rule of derivation) for ( int i = 0; i<3; ++i ) { for ( int j = 0; j<3; ++j ) J[i][j] *= this->m_InverseSpacing[i]; } return J; } Types::Coordinate SplineWarpXform::GetJacobianDeterminant ( const int x, const int y, const int z ) const { const Types::Coordinate *spX = &this->m_GridSpline[0][x<<2], *spY = &this->m_GridSpline[1][y<<2], *spZ = &this->m_GridSpline[2][z<<2]; const Types::Coordinate *dspX = &this->m_GridDerivSpline[0][x<<2], *dspY = &this->m_GridDerivSpline[1][y<<2], *dspZ = &this->m_GridDerivSpline[2][z<<2]; const Types::Coordinate *coeff = this->m_Parameters + this->m_GridOffsets[0][x] + this->m_GridOffsets[1][y] + this->m_GridOffsets[2][z]; double J[3][3]; memset( J, 0, sizeof( J ) ); for ( int dim = 0; dim<3; ++dim ) { const Types::Coordinate *coeff_mm = coeff; for ( int m = 0; m < 4; ++m ) { Types::Coordinate ll[3] = { 0, 0, 0 }; const Types::Coordinate *coeff_ll = coeff_mm; for ( int l = 0; l < 4; ++l ) { Types::Coordinate kk[3] = { 0, 0, 0 }; const Types::Coordinate *coeff_kk = coeff_ll; for ( int k = 0; k < 4; ++k, coeff_kk+=3 ) { kk[0] += dspX[k] * (*coeff_kk); kk[1] += spX[k]; kk[2] += spX[k]; } ll[0] += spY[l] * kk[0]; ll[1] += dspY[l] * kk[1]; ll[2] += spY[l] * kk[2]; coeff_ll += nextJ; } J[0][dim] += spZ[m] * ll[0]; J[1][dim] += spZ[m] * ll[1]; J[2][dim] += dspZ[m] * ll[2]; coeff_mm += nextK; } ++coeff; } return this->m_InverseSpacing[0] * this->m_InverseSpacing[1] * this->m_InverseSpacing[2] * ( J[0][0] * (J[1][1]*J[2][2] - J[1][2]*J[2][1]) + J[0][1] * (J[1][2]*J[2][0] - J[1][0]*J[2][2]) + J[0][2] * (J[1][0]*J[2][1] - J[1][1]*J[2][0]) ); } Types::Coordinate SplineWarpXform::GetJacobianDeterminant ( const Self::SpaceVectorType& v ) const { Types::Coordinate r[3], f[3]; int grid[3]; double J[3][3]; memset( J, 0, sizeof( J ) ); for ( int dim = 0; dim<3; ++dim ) { r[dim] = this->m_InverseSpacing[dim] * v[dim]; grid[dim] = std::min( static_cast( r[dim] ), this->m_Dims[dim]-4 ); f[dim] = std::max( 0, std::min( 1.0, r[dim] - grid[dim] ) ); } const Types::Coordinate* coeff = this->m_Parameters + 3 * ( grid[0] + this->m_Dims[0] * (grid[1] + this->m_Dims[1] * grid[2]) ); for ( int dim = 0; dim<3; ++dim ) { const Types::Coordinate *coeff_mm = coeff; for ( int m = 0; m < 4; ++m ) { Types::Coordinate ll[3] = { 0, 0, 0 }; const Types::Coordinate *coeff_ll = coeff_mm; for ( int l = 0; l < 4; ++l ) { Types::Coordinate kk[3] = { 0, 0, 0 }; const Types::Coordinate *coeff_kk = coeff_ll; for ( int k = 0; k < 4; ++k, coeff_kk+=3 ) { kk[0] += CubicSpline::DerivApproxSpline( k, f[0] ) * (*coeff_kk); const Types::Coordinate tmp = CubicSpline::ApproxSpline( k, f[0] ) * (*coeff_kk); kk[1] += tmp; kk[2] += tmp; } const Types::Coordinate tmp = CubicSpline::ApproxSpline( l, f[1] ); ll[0] += tmp * kk[0]; ll[1] += CubicSpline::DerivApproxSpline( l, f[1] ) * kk[1]; ll[2] += tmp * kk[2]; coeff_ll += nextJ; } const Types::Coordinate tmp = CubicSpline::ApproxSpline( m, f[2] ); J[0][dim] += tmp * ll[0]; J[1][dim] += tmp * ll[1]; J[2][dim] += CubicSpline::DerivApproxSpline( m, f[2] ) * ll[2]; coeff_mm += nextK; } ++coeff; } return this->m_InverseSpacing[0] * this->m_InverseSpacing[1] * this->m_InverseSpacing[2] * ( J[0][0] * (J[1][1]*J[2][2] - J[1][2]*J[2][1]) + J[0][1] * (J[1][2]*J[2][0] - J[1][0]*J[2][2]) + J[0][2] * (J[1][0]*J[2][1] - J[1][1]*J[2][0]) ); } void SplineWarpXform::GetJacobianDeterminantRow ( double *const values, const int x, const int y, const int z, const size_t numberOfPoints ) const { const Types::Coordinate *spX = &this->m_GridSpline[0][x<<2], *spY = &this->m_GridSpline[1][y<<2], *spZ = &this->m_GridSpline[2][z<<2]; const Types::Coordinate *dspX = &this->m_GridDerivSpline[0][x<<2], *dspY = &this->m_GridDerivSpline[1][y<<2], *dspZ = &this->m_GridDerivSpline[2][z<<2]; const Types::Coordinate *coeff = this->m_Parameters + this->m_GridOffsets[0][x] + this->m_GridOffsets[1][y] + this->m_GridOffsets[2][z]; const Types::Coordinate globalInverseSpacing = this->m_InverseSpacing[0] * this->m_InverseSpacing[1] * this->m_InverseSpacing[2]; // precompute the products of B_j(v) and B_k(w) for the 4 x 4 neighborhood // in y- and z-direction. Types::Coordinate smlX[16], smlY[16], smlZ[16]; for ( int i = 0, m = 0; m < 4; ++m ) { for ( int l = 0; l < 4; ++l, ++i ) { smlX[i] = spZ[m] * spY[l]; smlY[i] = spZ[m] * dspY[l]; smlZ[i] = dspZ[m] * spY[l]; } } // determine the number of CPG cells on our way along the row const int numberOfCells = (this->m_GridOffsets[0][x + numberOfPoints - 1] - this->m_GridOffsets[0][x]) / nextI + 4; // pre-compute the contributions of all control points in y- and z-direction // along the way Types::Coordinate phiCompX, phiCompY, phiCompZ; #ifdef CMTK_COMPILER_VAR_AUTO_ARRAYSIZE Types::Coordinate phiHatX[3*numberOfCells]; Types::Coordinate phiHatY[3*numberOfCells]; Types::Coordinate phiHatZ[3*numberOfCells]; #else std::vector phiHatX(3*numberOfCells); std::vector phiHatY(3*numberOfCells); std::vector phiHatZ(3*numberOfCells); #endif int phiIdx = 0; for ( int cell = 0; cell < numberOfCells; ++cell, coeff += nextI ) { const int *gpo = &GridPointOffset[0]; for ( int dim = 0; dim < 3; ++dim, ++phiIdx ) { phiCompX = phiCompY = phiCompZ = 0; for ( int ml = 0; ml < 16; ++ml, ++gpo ) { phiCompX += coeff[ *gpo ] * smlX[ml]; phiCompY += coeff[ *gpo ] * smlY[ml]; phiCompZ += coeff[ *gpo ] * smlZ[ml]; } phiHatX[phiIdx] = phiCompX; phiHatY[phiIdx] = phiCompY; phiHatZ[phiIdx] = phiCompZ; } } // start at the leftmost precomputed CPG cell int cellIdx = 0; Types::Coordinate JXX, JXY, JXZ, JYX, JYY, JYZ, JZX, JZY, JZZ; // run over all points we're supposed to transform int i = x; for ( const int lastPoint = x + numberOfPoints; i < lastPoint; ) { // these change only when we enter a new cell #ifdef CMTK_COMPILER_VAR_AUTO_ARRAYSIZE const Types::Coordinate* phiPtrX = phiHatX + 3*cellIdx; const Types::Coordinate* phiPtrY = phiHatY + 3*cellIdx; const Types::Coordinate* phiPtrZ = phiHatZ + 3*cellIdx; #else const Types::Coordinate* phiPtrX = &phiHatX[3*cellIdx]; const Types::Coordinate* phiPtrY = &phiHatY[3*cellIdx]; const Types::Coordinate* phiPtrZ = &phiHatZ[3*cellIdx]; #endif // do everything inside one cell do { // compute transformed voxel by taking precomputed y- and z-contributions // and adding x. The loops to do this have been unrolled for increased // performance. JXX = dspX[0] * phiPtrX[0] + dspX[1] * phiPtrX[3] + dspX[2] * phiPtrX[6] + dspX[3] * phiPtrX[9]; JXY = dspX[0] * phiPtrX[1] + dspX[1] * phiPtrX[4] + dspX[2] * phiPtrX[7] + dspX[3] * phiPtrX[10]; JXZ = dspX[0] * phiPtrX[2] + dspX[1] * phiPtrX[5] + dspX[2] * phiPtrX[8] + dspX[3] * phiPtrX[11]; JYX = spX[0] * phiPtrY[0] + spX[1] * phiPtrY[3] + spX[2] * phiPtrY[6] + spX[3] * phiPtrY[9]; JYY = spX[0] * phiPtrY[1] + spX[1] * phiPtrY[4] + spX[2] * phiPtrY[7] + spX[3] * phiPtrY[10]; JYZ = spX[0] * phiPtrY[2] + spX[1] * phiPtrY[5] + spX[2] * phiPtrY[8] + spX[3] * phiPtrY[11]; JZX = spX[0] * phiPtrZ[0] + spX[1] * phiPtrZ[3] + spX[2] * phiPtrZ[6] + spX[3] * phiPtrZ[9]; JZY = spX[0] * phiPtrZ[1] + spX[1] * phiPtrZ[4] + spX[2] * phiPtrZ[7] + spX[3] * phiPtrZ[10]; JZZ = spX[0] * phiPtrZ[2] + spX[1] * phiPtrZ[5] + spX[2] * phiPtrZ[8] + spX[3] * phiPtrZ[11]; values[i-x] = globalInverseSpacing * ( JXX * (JYY*JZZ - JYZ*JZY) + JXY * (JYZ*JZX - JYX*JZZ) + JXZ * (JYX*JZY - JYY*JZX) ); // go to next voxel ++i; spX += 4; dspX += 4; // repeat this until we leave current CPG cell. } while ( ( this->m_GridOffsets[0][i-1] == this->m_GridOffsets[0][i] ) && ( i < lastPoint ) ); // we've just left a cell -- shift index of precomputed control points // to the next one. ++cellIdx; } } Types::Coordinate SplineWarpXform::JacobianDeterminant ( const Types::Coordinate *cp ) const { const CoordinateMatrix3x3 J = this->GetJacobianAtControlPoint( cp ); return this->m_InverseSpacing[0] * this->m_InverseSpacing[1] * this->m_InverseSpacing[2] * ( J[0][0] * (J[1][1]*J[2][2] - J[1][2]*J[2][1]) + J[0][1] * (J[1][2]*J[2][0] - J[1][0]*J[2][2]) + J[0][2] * (J[1][0]*J[2][1] - J[1][1]*J[2][0]) ); } void SplineWarpXform ::GetJacobianConstraintThread( void *const args, const size_t taskIdx, const size_t taskCnt, const size_t, const size_t ) { Self::JacobianConstraintThreadInfo *info = static_cast( args ); const SplineWarpXform *me = info->thisObject; const int pixelsPerRow = me->VolumeDims[0]; std::vector valuesJ( pixelsPerRow ); const int rowCount = ( me->VolumeDims[1] * me->VolumeDims[2] ); const int rowFrom = ( rowCount / taskCnt ) * taskIdx; const int rowTo = ( taskIdx == (taskCnt-1) ) ? rowCount : ( rowCount/taskCnt ) * (taskIdx+1); int rowsToDo = rowTo - rowFrom; int yFrom = rowFrom % me->VolumeDims[1]; int zFrom = rowFrom / me->VolumeDims[2]; double constraint = 0; for ( int z = zFrom; (z < me->VolumeDims[2]) && rowsToDo; ++z ) { for ( int y = yFrom; (y < me->VolumeDims[1]) && rowsToDo; yFrom = 0, ++y, --rowsToDo ) { me->GetJacobianDeterminantRow( &(valuesJ[0]), 0, y, z, pixelsPerRow ); for ( int x = 0; x < pixelsPerRow; ++x ) { constraint += fabs( log ( valuesJ[x] / me->m_GlobalScaling ) ); } } } // Divide by number of control points to normalize with respect to the // number of local Jacobians in the computation. info->Constraint = constraint; } Types::Coordinate SplineWarpXform::GetJacobianConstraint () const { ThreadPool& threadPool = ThreadPool::GetGlobalThreadPool(); const size_t numberOfThreads = threadPool.GetNumberOfThreads(); const size_t numberOfTasks = std::min( 4 * numberOfThreads - 3, this->m_Dims[2] ); // Info blocks for parallel tasks that evaulate the constraint. std::vector constraintTaskInfo( numberOfTasks ); for ( size_t taskIdx = 0; taskIdx < numberOfTasks; ++taskIdx ) { constraintTaskInfo[taskIdx].thisObject = this; } threadPool.Run( Self::GetJacobianConstraintThread, constraintTaskInfo ); double constraint = 0; for ( size_t taskIdx = 0; taskIdx < numberOfTasks; ++taskIdx ) { constraint += constraintTaskInfo[taskIdx].Constraint; } // Divide by number of control points to normalize with respect to the // number of local Jacobians in the computation. return constraint / ( VolumeDims[0] * VolumeDims[1] * VolumeDims[2] ); } void SplineWarpXform::GetJacobianConstraintDerivative ( double& lower, double& upper, const int param, const DataGrid::RegionType& voi, const Types::Coordinate step ) const { const int pixelsPerRow = voi.To()[0] - voi.From()[0]; std::vector valuesJ( pixelsPerRow ); double ground = 0; for ( int k = voi.From()[2]; k < voi.To()[2]; ++k ) for ( int j = voi.From()[1]; j < voi.To()[1]; ++j ) { this->GetJacobianDeterminantRow( &(valuesJ[0]), voi.From()[0], j, k, pixelsPerRow ); for ( int i = 0; i < pixelsPerRow; ++i ) ground += fabs( log( valuesJ[i] / this->m_GlobalScaling ) ); } upper = -ground; lower = -ground; const Types::Coordinate oldCoeff = this->m_Parameters[param]; this->m_Parameters[param] += step; for ( int k = voi.From()[2]; k < voi.To()[2]; ++k ) for ( int j = voi.From()[1]; j < voi.To()[1]; ++j ) { this->GetJacobianDeterminantRow( &(valuesJ[0]), voi.From()[0], j, k, pixelsPerRow ); for ( int i = 0; i < pixelsPerRow; ++i ) { upper += fabs( log( valuesJ[i] / this->m_GlobalScaling ) ); } } this->m_Parameters[param] = oldCoeff - step; for ( int k = voi.From()[2]; k < voi.To()[2]; ++k ) for ( int j = voi.From()[1]; j < voi.To()[1]; ++j ) { this->GetJacobianDeterminantRow( &(valuesJ[0]), voi.From()[0], j, k, pixelsPerRow ); for ( int i = 0; i < pixelsPerRow; ++i ) { lower += fabs( log( valuesJ[i] / this->m_GlobalScaling ) ); } } this->m_Parameters[param] = oldCoeff; const double invVolume = 1.0 / voi.Size(); upper *= invVolume; lower *= invVolume; } void SplineWarpXform::GetJacobianConstraintDerivative ( double& lower, double& upper, const int param, const Types::Coordinate step ) const { const int controlPointIdx = param / nextI; const unsigned short x = ( controlPointIdx % this->m_Dims[0] ); const unsigned short y = ( (controlPointIdx / this->m_Dims[0]) % this->m_Dims[1] ); const unsigned short z = ( (controlPointIdx / this->m_Dims[0]) / this->m_Dims[1] ); const int thisDim = param % nextI; const Types::Coordinate* coeff = this->m_Parameters + param - thisDim; double ground = 0; const int iFrom = std::max( -1, 1-x ); const int jFrom = std::max( -1, 1-y ); const int kFrom = std::max( -1, 1-z ); const int iTo = std::min( 1, this->m_Dims[0]-2-x ); const int jTo = std::min( 1, this->m_Dims[1]-2-y ); const int kTo = std::min( 1, this->m_Dims[2]-2-z ); for ( int k = kFrom; k < kTo; ++k ) for ( int j = jFrom; j < jTo; ++j ) for ( int i = iFrom; i < iTo; ++i ) ground += fabs( log( this->GetJacobianDeterminant( Self::SpaceVectorType::FromPointer( coeff + i*nextI + j*nextJ + k*nextK ) ) / this->m_GlobalScaling ) ); upper = -ground; lower = -ground; const Types::Coordinate oldCoeff = this->m_Parameters[param]; this->m_Parameters[param] += step; for ( int k = kFrom; k < kTo; ++k ) for ( int j = jFrom; j < jTo; ++j ) for ( int i = iFrom; i < iTo; ++i ) upper += fabs( log( this->GetJacobianDeterminant( Self::SpaceVectorType::FromPointer( coeff + i*nextI + j*nextJ + k*nextK ) ) / this->m_GlobalScaling ) ); this->m_Parameters[param] = oldCoeff - step; for ( int k = kFrom; k < kTo; ++k ) for ( int j = jFrom; j < jTo; ++j ) for ( int i = iFrom; i < iTo; ++i ) lower += fabs( log( this->GetJacobianDeterminant( Self::SpaceVectorType::FromPointer( coeff + i*nextI + j*nextJ + k*nextK ) ) / this->m_GlobalScaling ) ); this->m_Parameters[param] = oldCoeff; upper /= this->m_NumberOfControlPoints; lower /= this->m_NumberOfControlPoints; } void SplineWarpXform::RelaxToUnfold() { std::vector cpList( this->m_NumberOfControlPoints ); std::vector jacobiansRow( this->VolumeDims[0] ); bool isFolded = true; while ( isFolded ) { // check all Jacobian determinant values to see if grid is folded, and what control points are affected isFolded = false; std::fill( cpList.begin(), cpList.end(), 0 ); for ( int k = 0; k < this->VolumeDims[2]; ++k ) { for ( int j = 0; j < this->VolumeDims[1]; ++j ) { this->GetJacobianDeterminantRow( &(jacobiansRow[0]), 0, j, k, this->VolumeDims[0] ); for ( int i = 0; i < this->VolumeDims[0]; ++i ) { if ( jacobiansRow[i] <= 0 ) { isFolded = true; cpList[ (this->m_GridOffsets[0][i] + this->m_GridOffsets[1][j] + this->m_GridOffsets[2][k])/3 ] = 1; } } } } if ( isFolded ) { // Expand affected control points to their 4x4x4 neighbourhoods on all sides. for ( int k = 0; k < this->m_Dims[2]; ++k ) { for ( int j = 0; j < this->m_Dims[1]; ++j ) { for ( int i = 0; i < this->m_Dims[0]; ++i ) { if ( cpList[ (i * this->nextI + j * this->nextJ + k * this->nextK)/3 ] == 1 ) { for ( int kk = std::max( 0, k-3 ); kk < std::min( k+4, this->m_Dims[2] ); ++kk ) { for ( int jj = std::max( 0, j-3 ); jj < std::min( j+4, this->m_Dims[1] ); ++jj ) { for ( int ii = std::max( 0, i-3 ); ii < std::min( i+4, this->m_Dims[0] ); ++ii ) { const size_t cpIdx = (ii * this->nextI + jj * this->nextJ + kk * this->nextK)/3; if ( cpList[ cpIdx ] != 1 ) { cpList[ cpIdx ] = 2; } } } } } } } } // Get pure deformation at each control point. std::vector pureDeformation( 3 * this->m_NumberOfControlPoints ); size_t param = 0; for ( int k = 0; k < this->m_Dims[2]; ++k ) { for ( int j = 0; j < this->m_Dims[1]; ++j ) { for ( int i = 0; i < this->m_Dims[0]; ++i, param+=3 ) { const Self::SpaceVectorType cp = this->m_InitialAffineXform->GetInverse()->Apply( Self::SpaceVectorType::FromPointer( this->m_Parameters+param ) ) - this->GetOriginalControlPointPosition( i, j, k ); for ( int dim = 0; dim < 3; ++dim ) pureDeformation[param+dim] = cp[dim]; } } } // regularize the affected control points std::vector smoothed( this->m_NumberOfControlPoints ); for ( int dim = 0; dim < 3; ++dim ) { // get all control point positions for the "dim" component size_t cp = 0; for ( int k = 0; k < this->m_Dims[2]; ++k ) { for ( int j = 0; j < this->m_Dims[1]; ++j ) { for ( int i = 0; i < this->m_Dims[0]; ++i, ++cp ) { if ( cpList[cp] ) { // if this is a control point that needs regularizing, do it Types::Coordinate weight = 0; FixedVector<3,Types::Coordinate> delta; for ( int kk = std::max( 0, k-3 ); kk < std::min( k+4, this->m_Dims[2] ); ++kk ) { delta[2] = k-kk; for ( int jj = std::max( 0, j-3 ); jj < std::min( j+4, this->m_Dims[1] ); ++jj ) { delta[1] = j-jj; for ( int ii = std::max( 0, i-3 ); ii < std::min( i+4, this->m_Dims[0] ); ++ii ) { delta[0] = i-ii; const Types::Coordinate w = exp( -delta.SumOfSquares() ); smoothed[cp] = w * pureDeformation[ dim + ii*this->nextI + jj*this->nextJ + kk*this->nextK ]; weight += w; } } } if ( weight > 0 ) smoothed[cp] /= weight; } else { // if this control point does not need regularizing, keep it as is smoothed[cp] = pureDeformation[dim+cp*3]; } } } } // copy modified control point position component back for ( size_t cp2 = 0; cp2 < this->m_NumberOfControlPoints; ++cp2 ) { pureDeformation[dim+cp2*3] = smoothed[cp2]; } } // put offsets and affine component back param = 0; for ( int k = 0; k < this->m_Dims[2]; ++k ) { for ( int j = 0; j < this->m_Dims[1]; ++j ) { for ( int i = 0; i < this->m_Dims[0]; ++i, param+=3 ) { Self::SpaceVectorType cp = Self::SpaceVectorType::FromPointer( &(pureDeformation[0])+param ); cp += this->GetOriginalControlPointPosition( i, j, k ); cp = this->m_InitialAffineXform->Apply( cp ); for ( int dim = 0; dim < 3; ++dim ) this->m_Parameters[param+dim] = cp[dim]; } } } } } } } // namespace cmtk cmtk-3.3.1/libs/Base/cmtkSplineWarpXform_Rigidity.cxx000066400000000000000000000201611276303427400226360ustar00rootroot00000000000000/* // // Copyright 1997-2012 Torsten Rohlfing // // Copyright 2004-2012 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4275 $ // // $LastChangedDate: 2012-04-29 12:39:40 -0700 (Sun, 29 Apr 2012) $ // // $LastChangedBy: torsten_at_home $ // */ #include "cmtkSplineWarpXform.h" #include #include namespace cmtk { /** \addtogroup Base */ //@{ void SplineWarpXform::GetRigidityConstraintDerivative ( double& lower, double& upper, const int param, const DataGrid::RegionType& voi, const Types::Coordinate step ) const { const int pixelsPerRow = voi.To()[0] - voi.From()[0]; std::vector arrayJ( pixelsPerRow ); double ground = 0; for ( int k = voi.From()[2]; k < voi.To()[2]; ++k ) for ( int j = voi.From()[1]; j < voi.To()[1]; ++j ) { this->GetJacobianRow( &(arrayJ[0]), voi.From()[0], j, k, pixelsPerRow ); for ( int i = 0; i < pixelsPerRow; ++i ) { ground += this->GetRigidityConstraint( arrayJ[i] ); } } upper = -ground; lower = -ground; const Types::Coordinate oldCoeff = this->m_Parameters[param]; this->m_Parameters[param] += step; for ( int k = voi.From()[2]; k < voi.To()[2]; ++k ) for ( int j = voi.From()[1]; j < voi.To()[1]; ++j ) { this->GetJacobianRow( &(arrayJ[0]), voi.From()[0], j, k, pixelsPerRow ); for ( int i = 0; i < pixelsPerRow; ++i ) { upper += this->GetRigidityConstraint( arrayJ[i] ); } } this->m_Parameters[param] = oldCoeff - step; for ( int k = voi.From()[2]; k < voi.To()[2]; ++k ) for ( int j = voi.From()[1]; j < voi.To()[1]; ++j ) { this->GetJacobianRow( &(arrayJ[0]), voi.From()[0], j, k, pixelsPerRow ); for ( int i = 0; i < pixelsPerRow; ++i ) { lower += this->GetRigidityConstraint( arrayJ[i] ); } } this->m_Parameters[param] = oldCoeff; const double invVolume = 1.0 / ((voi.To()[0]-voi.From()[0])*(voi.To()[1]-voi.From()[1])*(voi.To()[2]-voi.From()[2])); upper *= invVolume; lower *= invVolume; } void SplineWarpXform::GetRigidityConstraintDerivative ( double& lower, double& upper, const int param, const DataGrid::RegionType& voi, const Types::Coordinate step, const DataGrid* weightMap ) const { const int pixelsPerRow = voi.To()[0] - voi.From()[0]; std::vector arrayJ( pixelsPerRow ); double ground = 0; for ( int k = voi.From()[2]; k < voi.To()[2]; ++k ) for ( int j = voi.From()[1]; j < voi.To()[1]; ++j ) { this->GetJacobianRow( &(arrayJ[0]), voi.From()[0], j, k, pixelsPerRow ); for ( int i = 0; i < pixelsPerRow; ++i ) { ground += weightMap->GetDataAt( voi.From()[0] + i, j, k ) * this->GetRigidityConstraint( arrayJ[i] ); } } upper = -ground; lower = -ground; const Types::Coordinate oldCoeff = this->m_Parameters[param]; this->m_Parameters[param] += step; for ( int k = voi.From()[2]; k < voi.To()[2]; ++k ) for ( int j = voi.From()[1]; j < voi.To()[1]; ++j ) { this->GetJacobianRow( &(arrayJ[0]), voi.From()[0], j, k, pixelsPerRow ); for ( int i = 0; i < pixelsPerRow; ++i ) { upper += weightMap->GetDataAt( voi.From()[0] + i, j, k ) * this->GetRigidityConstraint( arrayJ[i] ); } } this->m_Parameters[param] = oldCoeff - step; for ( int k = voi.From()[2]; k < voi.To()[2]; ++k ) for ( int j = voi.From()[1]; j < voi.To()[1]; ++j ) { this->GetJacobianRow( &(arrayJ[0]), voi.From()[0], j, k, pixelsPerRow ); for ( int i = 0; i < pixelsPerRow; ++i ) { lower += weightMap->GetDataAt( voi.From()[0] + i, j, k ) * this->GetRigidityConstraint( arrayJ[i] ); } } this->m_Parameters[param] = oldCoeff; const double invVolume = 1.0 / voi.Size(); upper *= invVolume; lower *= invVolume; } void SplineWarpXform::GetRigidityConstraintDerivative ( double& lower, double& upper, const int param, const Types::Coordinate step ) const { const int controlPointIdx = param / nextI; const unsigned short x = ( controlPointIdx % this->m_Dims[0] ); const unsigned short y = ( (controlPointIdx / this->m_Dims[0]) % this->m_Dims[1] ); const unsigned short z = ( (controlPointIdx / this->m_Dims[0]) / this->m_Dims[1] ); const int thisDim = param % nextI; const Types::Coordinate* coeff = this->m_Parameters + param - thisDim; double ground = 0; const int iFrom = std::max( -1, 1-x ); const int jFrom = std::max( -1, 1-y ); const int kFrom = std::max( -1, 1-z ); const int iTo = std::min( 1, this->m_Dims[0]-2-x ); const int jTo = std::min( 1, this->m_Dims[1]-2-y ); const int kTo = std::min( 1, this->m_Dims[2]-2-z ); for ( int k = kFrom; k < kTo; ++k ) for ( int j = jFrom; j < jTo; ++j ) for ( int i = iFrom; i < iTo; ++i ) { ground += this->GetRigidityConstraint( this->GetJacobianAtControlPoint( coeff + i*nextI + j*nextJ + k*nextK ) ); } upper = -ground; lower = -ground; const Types::Coordinate oldCoeff = this->m_Parameters[param]; this->m_Parameters[param] += step; for ( int k = kFrom; k < kTo; ++k ) for ( int j = jFrom; j < jTo; ++j ) for ( int i = iFrom; i < iTo; ++i ) { upper += this->GetRigidityConstraint( this->GetJacobianAtControlPoint( coeff + i*nextI + j*nextJ + k*nextK ) ); } this->m_Parameters[param] = oldCoeff - step; for ( int k = kFrom; k < kTo; ++k ) for ( int j = jFrom; j < jTo; ++j ) for ( int i = iFrom; i < iTo; ++i ) { lower += this->GetRigidityConstraint( this->GetJacobianAtControlPoint( coeff + i*nextI + j*nextJ + k*nextK ) ); } this->m_Parameters[param] = oldCoeff; upper /= this->m_NumberOfControlPoints; lower /= this->m_NumberOfControlPoints; } Types::Coordinate SplineWarpXform::GetRigidityConstraint () const { const int pixelsPerRow = VolumeDims[0]; std::vector arrayJ( pixelsPerRow ); double constraint = 0; for ( int z = 0; z < VolumeDims[2]; ++z ) for ( int y = 0; y < VolumeDims[1]; ++y ) { this->GetJacobianRow( &(arrayJ[0]), 0, y, z, pixelsPerRow ); for ( int x = 0; x < pixelsPerRow; ++x ) { constraint += this->GetRigidityConstraint( arrayJ[x] ); } } // Divide by number of control points to normalize with respect to the // number of local Jacobians in the computation. return constraint / ( VolumeDims[0] * VolumeDims[1] * VolumeDims[2] ); } Types::Coordinate SplineWarpXform::GetRigidityConstraint( const DataGrid* weightMap ) const { const int pixelsPerRow = VolumeDims[0]; std::vector arrayJ( pixelsPerRow ); double constraint = 0; for ( int z = 0; z < VolumeDims[2]; ++z ) for ( int y = 0; y < VolumeDims[1]; ++y ) { this->GetJacobianRow( &(arrayJ[0]), 0, y, z, pixelsPerRow ); for ( int x = 0; x < pixelsPerRow; ++x ) { constraint += weightMap->GetDataAt( x, y, z ) * this->GetRigidityConstraint( arrayJ[x] ); } } // Divide by number of control points to normalize with respect to the // number of local Jacobians in the computation. return constraint / ( VolumeDims[0] * VolumeDims[1] * VolumeDims[2] ); } Types::Coordinate SplineWarpXform::GetRigidityConstraint( const CoordinateMatrix3x3& J ) const { const Matrix2D R = QRDecomposition( J ).GetR(); return MathUtil::Square( R[0][1] / R[0][0] ) + MathUtil::Square( R[0][2] / R[0][0] ) + MathUtil::Square( R[1][2] / R[1][1] ); } } // namespace cmtk cmtk-3.3.1/libs/Base/cmtkSurfaceNormal.h000066400000000000000000000037451276303427400200710ustar00rootroot00000000000000/* // // Copyright 2010, 2012 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4415 $ // // $LastChangedDate: 2012-06-04 14:57:12 -0700 (Mon, 04 Jun 2012) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkSurfaceNormal_h_included_ #define __cmtkSurfaceNormal_h_included_ #include #include namespace cmtk { /** \addtogroup Base */ //@{ /// Class that computes the surface normal. class SurfaceNormal { public: /// This class. typedef SurfaceNormal Self; /// Space vector type. typedef FixedVector<3,Types::Coordinate> SpaceVectorType; /// Constructor: takes two non-collinear vectors that span the surface. SurfaceNormal( const SpaceVectorType& s1, const SpaceVectorType& s2 ) { this->m_Normal = FixedVectorStaticInitializer<3,Types::Coordinate>::Init( s1[1] * s2[2] - s1[2] * s2[1], s1[2] * s2[0] - s1[0] * s2[2], s1[0] * s2[1] - s1[1] * s2[0] ); } /// Get the normal vector. const SpaceVectorType& Get() const { return this->m_Normal; } private: /// The surface normal vector. SpaceVectorType m_Normal; }; //@} } // namespace cmtk #endif // #ifndef __cmtkSurfaceNormal_h_included_ cmtk-3.3.1/libs/Base/cmtkSymmetricMatrix.h000066400000000000000000000061711276303427400204650ustar00rootroot00000000000000/* // // Copyright 2011 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 3146 $ // // $LastChangedDate: 2011-04-15 11:14:10 -0700 (Fri, 15 Apr 2011) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkSymmetricMatrix_h_included_ #define __cmtkSymmetricMatrix_h_included_ #include #include namespace cmtk { /** \addtogroup Base */ //@{ /// Class template for variable-size symmetric matrix. template class SymmetricMatrix { public: /// This class. typedef SymmetricMatrix Self; /// Constructor. SymmetricMatrix( const size_t dim = 0 ) : m_Dim( dim ), m_MatrixElements( Self::NumberOfElements( dim ) ) {} /// Reference to matrix element. TElement& operator()( const size_t i, const size_t j ) { if ( i > j ) return this->m_MatrixElements[j + i*(i+1)/2]; else return this->m_MatrixElements[i + j*(j+1)/2]; } /// Reference to const matrix element. const TElement& operator()( const size_t i, const size_t j ) const { if ( i > j ) return this->m_MatrixElements[j + i*(i+1)/2]; else return this->m_MatrixElements[i + j*(j+1)/2]; } /// Get matrix dimension. size_t Dim() const { return this->m_Dim; } /// Resize matrix. void Resize( const size_t newDim ) { this->m_Dim = newDim; this->m_MatrixElements.resize( Self::NumberOfElements( newDim ) ); } /// Resize matrix with explicit initializer for newly allocated elements. void Resize( const size_t newDim, const TElement initValue ) { this->m_Dim = newDim; this->m_MatrixElements.resize( Self::NumberOfElements( newDim ), initValue ); } /// Equality operator. bool operator==( const Self& other ) const { if ( this->m_Dim != other.m_Dim ) return false; return (this->m_MatrixElements == other.m_MatrixElements); } /// Inequality operator. bool operator!=( const Self& other ) const { return ! (*this == other); } private: /// Static member: compute number of elements from dimension. static size_t NumberOfElements( const size_t dim ) { return dim*(dim+1) / 2; } /// Matrix dimension. size_t m_Dim; /// Vector that stores matrix elements. std::vector m_MatrixElements; }; //@} } // namespace cmtk #endif // #ifndef __cmtkSymmetricMatrix_h_included_ cmtk-3.3.1/libs/Base/cmtkTemplateArray.h000066400000000000000000000557741276303427400201130ustar00rootroot00000000000000/* // // Copyright 1997-2011 Torsten Rohlfing // // Copyright 2004-2013 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4882 $ // // $LastChangedDate: 2013-09-27 15:16:36 -0700 (Fri, 27 Sep 2013) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkTemplateArray_h_included_ #define __cmtkTemplateArray_h_included_ #include #include #include #include #include // for float abs() #include // for int abs() namespace cmtk { /** \addtogroup Base */ //@{ /** Template for Variable-Typed Data Arrays. * From this object, various children are derived for the concrete data types * to be handled. * @author Torsten Rohlfing */ template class TemplateArray : /** Inherit class interface from virtual base class. */ public TypedArray { public: /// This type. typedef TemplateArray Self; /// Base class. typedef TypedArray Superclass; /// Smart pointer. typedef SmartPointer SmartPtr; /** Data type traits. */ typedef DataTypeTraits TypeTraits; /// Create array of this type. static typename Self::SmartPtr Create( const size_t size ) { return typename Self::SmartPtr( new Self( size ) ); } /// Return const pointer to actual data. const T* GetDataPtrConcrete() const { return this->Data; } /// Return pointer to actual data. T* GetDataPtrConcrete() { return this->Data; } /// Set the value to mark non-existent data using template data type. void SetPaddingValueTemplate ( const T paddingData ) { this->Padding = paddingData; this->PaddingFlag = true; } /** Constructor. * A typed array is built from an existing array. *\param data Pointer to the array of values to be stored. *\param datasize Number of elements in the data array. *\param paddingflag Flag that indicates whether there are missing elements in * the existing data array. *\param paddingData Value that marks missing elements in the data array if "paddingFlag" is true. *\param deallocator Pointer to deallocator object, or NULL if deallocation of data block is handled by another object. */ TemplateArray ( void *const data, const size_t datasize, const bool paddingflag, const void* paddingData, const Memory::DeallocatorFunctionPointer deallocator = NULL ) { this->m_Deallocator = deallocator; m_DataType = TypeTraits::DataTypeID; Data = static_cast( data ); DataSize = datasize; PaddingFlag = paddingflag; if ( paddingData ) Padding = *((T*)paddingData); else Padding = (T) 0; } /** Constructor. * Allocate an array of a given size. */ TemplateArray ( const size_t datasize = 0 ) : Padding( 0 ) { m_DataType = TypeTraits::DataTypeID; Data = NULL; this->Alloc( datasize ); } /** Destructor. * Free memory by a call to FreeData(). */ virtual ~TemplateArray () { this->FreeData(); } /// Set the value to mark non-existent data using interface data type. virtual void SetPaddingValue ( const Types::DataItem paddingData ) { this->SetPaddingValueTemplate( TypeTraits::Convert( paddingData ) ); } /// Set the value to mark non-existent data using interface data type. virtual void SetPaddingPtr ( const void* paddingData ) { this->SetPaddingValueTemplate( *((T*) paddingData) ); } /// Replace Padding data (padding) with given value. virtual void ReplacePaddingData ( const Types::DataItem value = 0 ) { if ( PaddingFlag ) { T v = TypeTraits::Convert( value ); for ( size_t i = 0; i < DataSize; ++i ) if ( Data[i] == Padding ) { Data[i] = v; } } } /** Check for NULL data at given index. * If this array does not have PaddingFlag set, the result is always false. */ virtual bool IsPaddingAt( const size_t index ) const { return PaddingFlag && (Data[index]==Padding); } /** Check for PADDING data or zero value at given index. */ virtual bool IsPaddingOrZeroAt( const size_t index ) const { return (PaddingFlag && (Data[index]==Padding)) || Data[index] == (T)0; } virtual void SetPaddingAt ( const size_t index = 0 ) { if ( !PaddingFlag ) { Padding = TypeTraits::ChoosePaddingValue(); PaddingFlag = true; } Data[index] = Padding; } /// Return id of primitive data type handled by this object. virtual ScalarDataType GetType () const { return TypeTraits::DataTypeID; } /// Return size in bytes of the primitive data type handled by this object. virtual size_t GetItemSize () const { return sizeof(T); } /// Return pointer to an element in this objects data array. virtual void* GetDataPtr( const size_t offset = 0 ) { return Data + offset; } /// Return const pointer to an element in this objects data array. virtual const void* GetDataPtr( const size_t offset = 0 ) const { return Data + offset; } /// Return pointer to an element in this objects data array. virtual T* GetDataPtrTemplate( const size_t offset = 0 ) { return Data + offset; } /// Return const pointer to an element in this objects data array. virtual const T* GetDataPtrTemplate( const size_t offset = 0 ) const { return Data + offset; } /** Convert and copy continuous sub-array to a given destination. *\param toPtr A pointer to the location where the data shall be stored. *\param fromIdx The index of the first copied data item in the array, * beginning with 0. *\param len Length, ie. number of values, to copy. The calling function * must take care that there is enough memory pointed to by toPtr to store * this many values of the transfer data type. *\param substPadding Where there is padding data in the copied range, this value * is put to the output. *\return The pointer given to this function to store the desired data to. */ virtual Types::DataItem* GetSubArray( Types::DataItem *const toPtr, const size_t fromIdx , const size_t len, const Types::DataItem substPadding = 0 ) const { int index = fromIdx; if ( PaddingFlag ) { for ( size_t i = 0; i( value ); } } else { for ( size_t i = 0; i( Data[index] ); } return toPtr; } /** Return a sub array of the current instance. * The data is returned in a newly allocated primitive array of the Types::DataItem * data exchange type. *\param fromIdx Copying starts at this index in the array. The range for * this parameter is [0..Size-1]. *\param len Number of data elements to be copied. *\param substPadding If this flag is set (default is NO), then during copying * all elements marked as "non-existent" are replaced by zero. *\return Pointer to a newly allocated Types::DataItem array. Allocation is done * using the allocator given as template parameter "M" to this class. */ virtual Types::DataItem* GetSubArray( const size_t fromIdx, const size_t len, const Types::DataItem substPadding = 0 ) const { Types::DataItem* buffer = Memory::ArrayC::Allocate( len ); return this->GetSubArray( buffer, fromIdx, len, substPadding ); } /** Convert Types::DataItem to template type. * This function takes an Types::DataItem value and converts it into a value of the * type stored in this array. *\param value The item in Types::DataItem representation. *\return The value of type T corresponding to the "value" parameter's value. * Conversion is done using the type converter class given as template * parameter "C" to this class. Therefore, rounding will occur if necessary. */ virtual T ConvertItem ( const Types::DataItem value ) { return TypeTraits::Convert( value, PaddingFlag, Padding ); } /** Convert to typed array of any given template type. */ virtual TypedArray::SmartPtr Convert( const ScalarDataType dtype ) const; /** Convert a sub-array to any given primitive data type. *\todo It would probably be a good idea to preserve PaddingData as much as * possible during the conversion. */ virtual void* ConvertSubArray( const ScalarDataType dtype, const size_t fromIdx, const size_t len ) const; /** Convert a sub-array to given primitive data type into existing array. *\return Pointer provided as "destination". */ virtual void* ConvertSubArray( void *const destination, const ScalarDataType dtype, const size_t fromIdx, const size_t len ) const; /** Change endianness of data. */ virtual void ChangeEndianness(); /** Scale values in the array. * A call to this member function will perform an in-place rescaling of the * values in the array. *\param scale The original data value is multiplied by the parameter first. *\param offset This value is added to the original data value after * multiplying it by the scale parameter. */ virtual void Rescale( const Types::DataItem scale = 1, const Types::DataItem offset = 0 ) { #pragma omp parallel for for ( int i = 0; i < static_cast( this->DataSize ); ++i ) if ( ! PaddingFlag || (Data[i] != Padding ) ) Data[i] = TypeTraits::Convert( (scale * Data[i]) + offset ); } /** Scale and shift values in the array. * Data values are scaled first, then the offsewt is added, and finally the (left) bit shift is applied. * In fact, to make sure we're not messing up floats, the bit shift is applied as a multiplication. */ virtual void RescaleAndShift( const Types::DataItem scale = 1, const Types::DataItem offset = 0, const size_t shiftBits = 0 ) { const long int shiftMultiplier = (1<( this->DataSize ); ++i ) if ( ! PaddingFlag || (Data[i] != Padding ) ) Data[i] = TypeTraits::Convert( ((scale * Data[i]) + offset) * shiftMultiplier ); } /** Scale values in the array with truncation boundaries. * A call to this member function will perform an in-place rescaling of the * values in the array with value range truncation. Truncation takes place * after the scaling itself, i.e., the truncation boundaries refer to the * already scaled values. *\param scale The original data value is multiplied by the parameter first. *\param offset This value is added to the original data value after * multiplying it by the scale parameter. *\param truncLo Lower truncation boundary. Scaled items below this * threshold will be set to equal its value. *\param truncHi Upper truncation boundary. Scaled items above this * threshold will be set to equal its value. */ virtual void Rescale( const Types::DataItem scale, const Types::DataItem offset, const Types::DataItem truncLo, const Types::DataItem truncHi = CMTK_ITEM_MAX ) { #pragma omp parallel for for ( int i = 0; i < static_cast( this->DataSize ); ++i ) if ( ! PaddingFlag || (Data[i] != Padding ) ) { Data[i] = TypeTraits::Convert( (scale * Data[i]) + offset ); if ( Data[i] < truncLo ) Data[i] = TypeTraits::Convert( truncLo ); else if ( Data[i] > truncHi ) Data[i] = TypeTraits::Convert( truncHi ); } } /** Apply gamma correction. *\param gamma The gamma correction coefficient. */ virtual void GammaCorrection( const Types::DataItem gamma ); /** Apply real function to data. */ virtual void ApplyFunctionFloat( typename Self::FunctionTypeFloat f ); /** Apply real function to data. */ virtual void ApplyFunctionDouble( typename Self::FunctionTypeDouble f ); /** Threshold data. * All values above upper threshold are set to upper thrershold. All values * below lower threshold are set to lower threshold. */ virtual void Threshold( const Types::DataItemRange& range ) { const T lo = TypeTraits::Convert( range.m_LowerBound ); const T hi = TypeTraits::Convert( range.m_UpperBound ); #pragma omp parallel for for ( int i = 0; i < static_cast( this->DataSize ); ++i ) if ( ! PaddingFlag || (Data[i] != Padding ) ) { if ( Data[i] < lo ) Data[i] = lo; else if ( Data[i] > hi ) Data[i] = hi; } } /** Threshold data. * All values outside the threshold range are set to the Padding (padding) * value. */ virtual void ThresholdToPadding( const Types::DataItemRange& range ) { const T lo = TypeTraits::Convert( range.m_LowerBound ); const T hi = TypeTraits::Convert( range.m_UpperBound ); #pragma omp parallel for for ( int i = 0; i < static_cast( this->DataSize ); ++i ) if ( ! PaddingFlag || (Data[i] != Padding ) ) { if ( (Data[i] < lo) || (Data[i] > hi) ) Data[i] = this->Padding; } } /** Binarize array values with given threshold. * All values greater than threshold (default: zero) are set to one, all * values smaller or equal are set to zero. */ virtual void Binarize( const Types::DataItem threshold = 0 ) { T thresh = TypeTraits::Convert( threshold ); T one = TypeTraits::Convert( 1.0 ), zero = TypeTraits::Convert( 0.0 ); #pragma omp parallel for for ( int i = 0; i < static_cast( this->DataSize ); ++i ) if ( ! PaddingFlag || (Data[i] != Padding ) ) { if ( Data[i] > thresh ) Data[i] = one; else Data[i] = zero; } } /// Convert all values to absolute values. virtual void MakeAbsolute() { #pragma omp parallel for for ( int i = 0; i < static_cast( this->DataSize ); ++i ) if ( ! PaddingFlag || (Data[i] != Padding ) ) Data[i] = std::abs( Data[i] ); } /** Get an item from the array. *\param value The variable referenced by this parameter is set to the * data item stored at the given location in the array. If this item is * marked is "padding data", ie. non-existent, "value" is set to zero. *\param index The index of the item to retrieve. Valid values are in the * range [0..Datasize()-1]. *\return A non-zero value is returned if and only if the value stored in * the array at the given location is marked as valid data. */ virtual bool Get ( Types::DataItem& value, const size_t index ) const { CheckBounds( index, DataSize ); if (PaddingFlag && (Padding==Data[index])) { value = 0; return false; } value = static_cast( Data[index] ); return true; } /// Return data or a given default value if no data exists there. virtual Types::DataItem ValueAt ( const size_t idx, const Types::DataItem defaultValue = 0.0 ) const { CheckBounds( idx, this->DataSize ); if (this->PaddingFlag && (this->Padding==this->Data[idx])) { return defaultValue; } else { return static_cast( Data[idx] ); } } /** Get a sequence of items from the array. *\param values This must point to an allocated array of at least as many * Types::DataItem objects as given in the "length" parameter. *\param index The index of the item to retrieve. Valid values are in the * range [0..Datasize()-1]. *\param length Number of consecutive values to get. */ virtual void GetSequence ( Types::DataItem *const values, const size_t index, const size_t length ) const { CheckBounds( index+length-1, DataSize ); for ( size_t i = 0; i < index+length; ++i ) if (PaddingFlag && (Padding==Data[index])) values[i] = 0; else values[i] = static_cast( Data[index] ); } /** Set an item in the array. *\param value The new value for the specified item. *\param index The index of the item to set. Valid values are in the * range [0..Datasize()-1]. *\see Convert */ virtual void Set ( const Types::DataItem value, const size_t index ) { CheckBounds( index, DataSize ); Data[index] = this->ConvertItem( value ); } /** Return padding data value as pointer to native representation. * This function returns a pointer to the value used by this object for * marking non-existent data. As the type of this value depends on the * template parameters of this class, only a void pointer can be returned. * The caller has to interpret the value stored at that location itself. * * If this array does NOT have a padding data value, the data pointed to by the * result of this function is undefined. *\return Pointer to the padding value of this object. */ virtual void* GetPaddingPtr () const { return (void*)&Padding; } /** Return padding data value as pointer to native representation. */ virtual Types::DataItem GetPaddingValue() const { return static_cast( Padding ); } /// Test if there is PADDING data at a particular location. virtual bool PaddingDataAt ( const size_t index ) const { return PaddingFlag && (Data[index] == Padding); } /** Get the whole array data as an exchange type array. *\return Pointer to a memory region allocated by Memory::ArrayC::Allocate(). This region is * filled with all values in the present array as Types::DataItem values. The created * array is not maintained by this object. The caller has to make sure free() * is called for it. */ virtual Types::DataItem* GetData () const { Types::DataItem* Result = Memory::ArrayC::Allocate( DataSize ); if ( Result ) { for ( size_t idx = 0; idx < DataSize; ++idx ) Result[idx] = (Types::DataItem) Data[idx]; } return Result; } /** Set all data from an Types::DataItem array. * This function sets all values stored in the present array from a memory * region with Types::DataItem values. *\param data Pointer to an array of Types::DataItem values. * Control over the source array is not taken by this object. If it is on the heap, * then the calling routine remains responsible for de-allocating the array afterwards. */ virtual void SetData ( Types::DataItem *const data ) { #pragma omp parallel for for ( int idx = 0; idx < static_cast( this->DataSize ); ++idx ) Data[idx] = this->ConvertItem( data[idx] ); } /** Clear entire array. *\param usePaddingData If this flag is set, then the array will be filled with * the PaddingData value, if one exists. Otherwise, the array will be filled * with the respective data type's zero value. */ virtual void ClearArray ( const bool usePaddingData = false ) { if ( usePaddingData && PaddingFlag ) { for ( size_t idx = 0; idx < DataSize; ++idx ) Data[idx] = Padding; } else { memset( Data, 0, sizeof( *Data ) * this->GetDataSize() ); } } /// Calculate minimum and maximum data value. virtual const Types::DataItemRange GetRange() const; /// Calculate minimum and maximum data value. virtual const Types::Range GetRangeTemplate() const; /// Calculate entropy of distribution of values in this array. virtual double GetEntropy( const bool fractional = CMTK_HISTOGRAM_DISCRETE, const int numberOfBins = 128 ) const; virtual double GetEntropy( Histogram& histogram ) const; virtual double GetEntropy( Histogram& histogram, const bool fractional = CMTK_HISTOGRAM_DISCRETE ) const; virtual double GetEntropy( Histogram& histogram, const double* kernel, const size_t kernelRadius ) const; /** Calculate statistics. * Results will be both zero if there is not data in the array. *\return The number of valid (i.e., non-padding) values that constitute the * given results. */ virtual size_t GetStatistics ( Types::DataItem& mean, Types::DataItem& variance ) const; /** Set data block to constant value. */ virtual void BlockSet( const Types::DataItem value, const size_t fromOffset, const size_t toOffset ); /** Get data histogram. *\return A histogram object filled with the relative frequencies of values * in this array. */ virtual Histogram::SmartPtr GetHistogram( const unsigned int numberOfBins /*!< Number of histogram bins */, const bool centeredBins = false /*!< Flag for bins centered around the samples*/ ) const; virtual void ApplyFunctionObject( const TypedArrayFunction& f ); protected: /// Clone this object. virtual Self* CloneVirtual() const { Self* clone = new Self( this->DataSize ); memcpy( clone->Data, this->Data, this->DataSize * sizeof( T ) ); clone->Padding = this->Padding; clone->PaddingFlag = this->PaddingFlag; clone->m_DataClass = this->m_DataClass; return clone; } private: /// The acutal data array. T *Data; /// Value used for missing data. T Padding; /** Allocate data array. *\param datasize Number of data items to allocate memory for. */ virtual void Alloc ( const size_t datasize ) { DataSize = datasize; if ( DataSize ) { if ( Data && this->m_Deallocator ) { this->m_Deallocator( Data ); } Data = Memory::ArrayC::Allocate( DataSize ); this->m_Deallocator = &Memory::ArrayC::DeleteWrapper; if ( Data == NULL ) { this->DataSize = 0; } } else { Data = NULL; this->m_Deallocator = NULL; } } /** De-allocate data array. * The array is freed using the same memory handler that allocated it. * In any case, the current Data pointer is set to NULL. */ virtual void FreeData() { if ( Data && this->m_Deallocator ) { this->m_Deallocator( Data ); } Data = NULL; } }; /**\name Shortcut class typedefs for typed arrays. */ //@{ /// Array of (unsigned) byte values. typedef TemplateArray ByteArray; /// Array of (signed) char values. typedef TemplateArray CharArray; /// Array of signed short values. typedef TemplateArray ShortArray; /// Array of unsigned short values. typedef TemplateArray UShortArray; /// Array of (signed) integer values. typedef TemplateArray IntArray; /// Array of (unsigned) integer values. typedef TemplateArray UIntArray; /// Array of single-precision float values. typedef TemplateArray FloatArray; /// Array of double-precision float values. typedef TemplateArray DoubleArray; //@} //@} } // namespace cmtk #include "cmtkTemplateArray.txx" #include "cmtkTemplateArray_Statistics.txx" #endif // #ifndef __cmtkTemplateArray_h_included_ cmtk-3.3.1/libs/Base/cmtkTemplateArray.txx000066400000000000000000000246731276303427400205010ustar00rootroot00000000000000/* // // Copyright 1997-2010 Torsten Rohlfing // // Copyright 2004-2011 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 3521 $ // // $LastChangedDate: 2011-10-27 14:24:12 -0700 (Thu, 27 Oct 2011) $ // // $LastChangedBy: torstenrohlfing $ // */ #include #ifdef HAVE_IEEEFP_H # include #endif #include namespace cmtk { /** \addtogroup Base */ //@{ template double TemplateArray ::GetEntropy( const bool fractional, const int numberOfBins ) const { double entropy = 0; if ( fractional ) { Histogram histogram( numberOfBins ); histogram.SetRange( this->GetRange() ); for ( size_t idx = 0; idx < this->DataSize; ++idx ) if ( !this->PaddingFlag || (this->Data[idx] != this->Padding) ) histogram.IncrementFractional( histogram.ValueToBinFractional( this->Data[idx] ) ); entropy = histogram.GetEntropy(); } else { Histogram histogram( numberOfBins ); histogram.SetRange( this->GetRange() ); for ( size_t idx = 0; idx < this->DataSize; ++idx ) if ( !this->PaddingFlag || (this->Data[idx] != this->Padding) ) histogram.Increment( histogram.ValueToBin( this->Data[idx] ) ); entropy = histogram.GetEntropy(); } return entropy; } template double TemplateArray ::GetEntropy( Histogram& histogram ) const { histogram.Reset(); for ( size_t idx = 0; idx < this->DataSize; ++idx ) if ( !this->PaddingFlag || (this->Data[idx] != this->Padding) ) histogram.Increment( histogram.ValueToBin( this->Data[idx] ) ); return histogram.GetEntropy(); } template double TemplateArray ::GetEntropy( Histogram& histogram, const bool fractional ) const { histogram.Reset(); if ( fractional ) { for ( size_t idx = 0; idx < this->DataSize; ++idx ) if ( !this->PaddingFlag || (this->Data[idx] != this->Padding) ) histogram.IncrementFractional( histogram.ValueToBinFractional( this->Data[idx] ) ); } else { for ( size_t idx = 0; idx < this->DataSize; ++idx ) if ( !this->PaddingFlag || (this->Data[idx] != this->Padding) ) histogram.Increment( histogram.ValueToBin( this->Data[idx] ) ); } return histogram.GetEntropy(); } template double TemplateArray ::GetEntropy( Histogram& histogram, const double* kernel, const size_t kernelRadius ) const { histogram.Reset(); for ( size_t idx = 0; idx < this->DataSize; ++idx ) if ( !this->PaddingFlag || (this->Data[idx] != this->Padding) ) histogram.AddWeightedSymmetricKernelFractional( histogram.ValueToBinFractional( this->Data[idx] ), kernelRadius, kernel ); return histogram.GetEntropy(); } template const Types::DataItemRange TemplateArray ::GetRange() const { return Types::DataItemRange( this->GetRangeTemplate() ); } template const Types::Range TemplateArray ::GetRangeTemplate() const { Types::Range range( 0, 0 ); // find first finite and non-Padding element size_t idx = 0; if ( this->PaddingFlag ) { while ( (idx < this->DataSize) && ((this->Data[idx] == this->Padding) || !finite(this->Data[idx])) ) ++idx; } else { while ( (idx < this->DataSize) && !finite(this->Data[idx]) ) ++idx; } // didn't find any? return with error flag. if ( idx < this->DataSize) { // otherwise: search for min/max from here range.m_LowerBound = range.m_UpperBound = this->Data[idx]; if ( this->PaddingFlag ) { for ( ; idx < this->DataSize; ++idx ) { if ( (this->Data[idx] != this->Padding) && finite(this->Data[idx]) ) { if (this->Data[idx] > range.m_UpperBound) range.m_UpperBound = this->Data[idx]; if (this->Data[idx] < range.m_LowerBound) range.m_LowerBound = this->Data[idx]; } } } else { for ( ; idx < this->DataSize; ++idx ) { if ( finite(this->Data[idx]) ) { if (this->Data[idx] > range.m_UpperBound) range.m_UpperBound = this->Data[idx]; if (this->Data[idx] < range.m_LowerBound) range.m_LowerBound = this->Data[idx]; } } } } return range; } /**\todo This should somehow preserve PaddingFlag and Padding of the original * array. */ template TypedArray::SmartPtr TemplateArray ::Convert( const ScalarDataType dtype ) const { TypedArray::SmartPtr result = TypedArray::Create( dtype, this->ConvertArray( dtype ), this->DataSize, false /* paddingFlag */, NULL /*paddingValue*/, Memory::ArrayC::Delete ); if ( this->PaddingFlag ) result->SetPaddingValue( this->Padding ); return result; } template void* TemplateArray ::ConvertSubArray ( const ScalarDataType dtype, const size_t fromIdx, const size_t len ) const { return this->ConvertSubArray( Memory::ArrayC::Allocate( len * TypeItemSize( dtype ) ), dtype, fromIdx, len ); } template void TemplateArray ::GammaCorrection( const Types::DataItem gamma ) { if ( gamma > 0 ) { Types::Range range = this->GetRangeTemplate(); const T diff = range.m_UpperBound - range.m_LowerBound; const double scale = 1.0 / diff; #pragma omp parallel for if (this->DataSize>1e5) for ( int i = 0; i < static_cast( this->DataSize ); ++i ) if ( ! this->PaddingFlag || (this->Data[i] != this->Padding ) ) { if ( this->Data[i] > range.m_LowerBound ) { this->Data[i] = range.m_LowerBound + TypeTraits::Convert( diff * exp( log( scale * (this->Data[i]-range.m_LowerBound) ) / gamma ) ); } } } } template void TemplateArray ::ApplyFunctionFloat( typename Self::FunctionTypeFloat f ) { #pragma omp parallel for if (this->DataSize>1e5) for ( int i = 0; i < static_cast( this->DataSize ); ++i ) if ( ! this->PaddingFlag || (this->Data[i] != this->Padding ) ) { this->Data[i] = TypeTraits::Convert( f( (float)this->Data[i] ) ); } } template void TemplateArray ::ApplyFunctionDouble( typename Self::FunctionTypeDouble f ) { #pragma omp parallel for if (this->DataSize>1e5) for ( int i = 0; i < static_cast( this->DataSize ); ++i ) if ( ! this->PaddingFlag || (this->Data[i] != this->Padding ) ) { this->Data[i] = TypeTraits::Convert( f( (double)this->Data[i] ) ); } } template void* TemplateArray ::ConvertSubArray ( void *const destination, const ScalarDataType dtype, const size_t fromIdx, const size_t len ) const { if ( dtype == this->GetType() ) memcpy( destination, this->Data + fromIdx, len * this->GetItemSize() ); else { switch ( dtype ) { case TYPE_BYTE: #pragma omp parallel for if (len>1e5) for ( int idx = 0; idx < static_cast( len ); ++idx ) ((byte*)destination)[idx] = DataTypeTraits::Convert( this->Data[ idx + fromIdx ] ); break; case TYPE_CHAR: #pragma omp parallel for if (len>1e5) for ( int idx = 0; idx < static_cast( len ); ++idx ) ((char*)destination)[idx] = DataTypeTraits::Convert( this->Data[ idx + fromIdx ] ); break; case TYPE_USHORT: #pragma omp parallel for if (len>1e5) for ( int idx = 0; idx < static_cast( len ); ++idx ) ((unsigned short*)destination)[idx] = DataTypeTraits::Convert( this->Data[ idx + fromIdx ] ); break; case TYPE_SHORT: #pragma omp parallel for if (len>1e5) for ( int idx = 0; idx < static_cast( len ); ++idx ) ((short*)destination)[idx] = DataTypeTraits::Convert( this->Data[ idx + fromIdx ] ); break; case TYPE_INT: #pragma omp parallel for if (len>1e5) for ( int idx = 0; idx < static_cast( len ); ++idx ) ((int*)destination)[idx] = DataTypeTraits::Convert( this->Data[ idx + fromIdx ] ); break; case TYPE_UINT: #pragma omp parallel for if (len>1e5) for ( int idx = 0; idx < static_cast( len ); ++idx ) ((unsigned int*)destination)[idx] = DataTypeTraits::Convert( this->Data[ idx + fromIdx ] ); break; case TYPE_FLOAT: #pragma omp parallel for if (len>1e5) for ( int idx = 0; idx < static_cast( len ); ++idx ) ((float*)destination)[idx] = DataTypeTraits::Convert( this->Data[ idx + fromIdx ] ); break; case TYPE_DOUBLE: #pragma omp parallel for if (len>1e5) for ( int idx = 0; idx < static_cast( len ); ++idx ) ((double*)destination)[idx] = DataTypeTraits::Convert( this->Data[ idx + fromIdx ] ); break; default: // unsupported / unknown data type. do nothing. break; } } return destination; } template void TemplateArray ::ChangeEndianness() { size_t itemSize = this->GetItemSize(); if ( itemSize < 2 ) return; size_t dataBytes = this->DataSize * itemSize; // f is the index of the FIRST byte of the current data item, l is the // index of that items LAST byte. size_t f, l; for ( f=0, l=itemSize-1; fData)[l-j]; ((char*)this->Data)[l-j] = ((char*)this->Data)[f+j]; ((char*)this->Data)[f+j] = d; } } template void TemplateArray ::ApplyFunctionObject( const TypedArrayFunction& f ) { #pragma omp parallel for for ( int i = 0; i < static_cast( this->DataSize ); ++i ) { if ( !this->PaddingFlag || (this->Data[i] != this->Padding) ) this->Data[i] = TypeTraits::Convert( f( this->Data[i] ) ); } } template void TemplateArray ::BlockSet ( const Types::DataItem value, const size_t fromOffset, const size_t toOffset ) { T valueT = TypeTraits::Convert( value ); #pragma omp parallel for for ( int i = fromOffset; i < static_cast( toOffset ); ++i ) this->Data[i] = valueT; } } // namespace cmtk cmtk-3.3.1/libs/Base/cmtkTemplateArray_Statistics.txx000066400000000000000000000046131276303427400227030ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2010 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 2553 $ // // $LastChangedDate: 2010-11-06 15:14:57 -0700 (Sat, 06 Nov 2010) $ // // $LastChangedBy: torsten_at_home $ // */ namespace cmtk { /** \addtogroup Base */ //@{ template size_t TemplateArray::GetStatistics ( Types::DataItem& mean, Types::DataItem& variance ) const { size_t Count = 0; Types::DataItem Sum = 0, SumOfSquares = 0; for ( size_t i = 0; i < DataSize; ++i ) { if ( !this->PaddingFlag || (this->Data[i] != this->Padding) ) { ++Count; Sum += this->Data[i]; SumOfSquares += MathUtil::Square( this->Data[i] ); } } if ( Count ) { mean = Sum / Count; variance = (SumOfSquares - 2*mean*Sum)/Count + MathUtil::Square(mean); } else { variance = mean = 0; } return Count; } template Histogram::SmartPtr TemplateArray::GetHistogram( const unsigned int numberOfBins, const bool centeredBins ) const { Histogram::SmartPtr histogram( new Histogram( numberOfBins ) ); if ( centeredBins ) histogram->SetRangeCentered( Types::DataItemRange( this->GetRangeTemplate() ) ); else histogram->SetRange( Types::DataItemRange( this->GetRangeTemplate() ) ); for ( size_t idx = 0; idx < DataSize; ++idx ) if ( !this->PaddingFlag || (this->Data[idx] != this->Padding) ) histogram->Increment( histogram->ValueToBin( this->Data[idx] ) ); return histogram; } } // namespace cmtk cmtk-3.3.1/libs/Base/cmtkTransformChangeFromSpaceAffine.cxx000066400000000000000000000046661276303427400237000ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2011 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 3498 $ // // $LastChangedDate: 2011-10-21 11:58:46 -0700 (Fri, 21 Oct 2011) $ // // $LastChangedBy: torsten_at_home $ // */ #include "cmtkTransformChangeFromSpaceAffine.h" cmtk::TransformChangeFromSpaceAffine ::TransformChangeFromSpaceAffine ( const AffineXform& xform, const UniformVolume& reference, const UniformVolume& floating, const char* forceSpace ) { // adapt transformation to Slicer's image coordinate systems as defined in the Nrrd files we probably read UniformVolume::SmartPtr refVolumeOriginalSpace( reference.CloneGrid() ); UniformVolume::SmartPtr fltVolumeOriginalSpace( floating.CloneGrid() ); // first bring volumes back into their native coordinate space, or forced space if one was provided. if ( forceSpace ) { refVolumeOriginalSpace->ChangeCoordinateSpace( forceSpace ); fltVolumeOriginalSpace->ChangeCoordinateSpace( forceSpace ); } else { refVolumeOriginalSpace->ChangeCoordinateSpace( reference.GetMetaInfo( META_SPACE_ORIGINAL ) ); fltVolumeOriginalSpace->ChangeCoordinateSpace( floating.GetMetaInfo( META_SPACE_ORIGINAL ) ); } // now determine image-to-physical transformations and concatenate these. AffineXform::MatrixType concatMatrix = refVolumeOriginalSpace->GetImageToPhysicalMatrix (); AffineXform::MatrixType fltMatrix = fltVolumeOriginalSpace->GetImageToPhysicalMatrix (); (concatMatrix *= xform.Matrix) *= fltMatrix.GetInverse(); // create output transformation and write this->m_NewXform.SetMatrix( concatMatrix ); } cmtk-3.3.1/libs/Base/cmtkTransformChangeFromSpaceAffine.h000066400000000000000000000044231276303427400233140ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2012 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4085 $ // // $LastChangedDate: 2012-03-26 11:04:52 -0700 (Mon, 26 Mar 2012) $ // // $LastChangedBy: torsten_at_home $ // */ #ifndef __cmtkTransformChangeFromSpaceAffine_h_included_ #define __cmtkTransformChangeFromSpaceAffine_h_included_ #include #include #include #include namespace cmtk { /** \addtogroup Base */ //@{ /// Compute affine coordinate transformation in standard space from transformation in native reference and floating image coordinate spaces. class TransformChangeFromSpaceAffine { public: /// Simplified constructor: compute transformation between images in new, common space. TransformChangeFromSpaceAffine( const AffineXform& xform /*!< Transformation from reference to floating in their current spaces.*/, const UniformVolume& reference /*!< Reference (fixed) image.*/, const UniformVolume& floating /*! Floating (moving) image.*/, const char* forceSpace = NULL /*!< Force transformation to be in this coordinate space.*/ ); /// Return transformation in native spaces. const AffineXform& GetTransformation() const { return this->m_NewXform; } private: /// Transformation between native spaces. AffineXform m_NewXform; }; //@} } // namespace cmtk #endif // #ifndef __cmtkTransformChangeFromSpaceAffine_h_included_ cmtk-3.3.1/libs/Base/cmtkTransformChangeToSpaceAffine.cxx000066400000000000000000000053531276303427400233510ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2011, 2013 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 5129 $ // // $LastChangedDate: 2014-01-09 13:48:15 -0800 (Thu, 09 Jan 2014) $ // // $LastChangedBy: torstenrohlfing $ // */ #include "cmtkTransformChangeToSpaceAffine.h" #include cmtk::TransformChangeToSpaceAffine ::TransformChangeToSpaceAffine ( const AffineXform& xform, const UniformVolume& reference, const UniformVolume& floating, const char* forceSpace ) { // adapt transformation to Slicer's image coordinate systems as defined in the Nrrd files we probably read UniformVolume::SmartPtr refVolumeOriginalSpace( reference.CloneGrid() ); UniformVolume::SmartPtr fltVolumeOriginalSpace( floating.CloneGrid() ); // first bring volumes back into their native coordinate space, or into forced space if one is provided. if ( forceSpace ) { refVolumeOriginalSpace->ChangeCoordinateSpace( forceSpace ); fltVolumeOriginalSpace->ChangeCoordinateSpace( forceSpace ); } else { refVolumeOriginalSpace->ChangeCoordinateSpace( reference.GetMetaInfo( META_SPACE_ORIGINAL ) ); fltVolumeOriginalSpace->ChangeCoordinateSpace( floating.GetMetaInfo( META_SPACE_ORIGINAL ) ); } // now determine image-to-physical transformations and concatenate these. const AffineXform::MatrixType refMatrix = refVolumeOriginalSpace->GetImageToPhysicalMatrix(); const AffineXform::MatrixType fltMatrix = fltVolumeOriginalSpace->GetImageToPhysicalMatrix(); try { const AffineXform::MatrixType concatMatrix = (refMatrix.GetInverse() * xform.Matrix) * fltMatrix; // create output transformation and write this->m_NewXform.SetMatrix( concatMatrix ); } catch ( const AffineXform::MatrixType::SingularMatrixException& ) { StdErr << "ERROR: singular matrix in TransformChangeToSpaceAffine constructor.\n"; throw ExitException( 1 ); } } cmtk-3.3.1/libs/Base/cmtkTransformChangeToSpaceAffine.h000066400000000000000000000043331276303427400227730ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2011 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 2994 $ // // $LastChangedDate: 2011-03-14 11:27:30 -0700 (Mon, 14 Mar 2011) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkTransformChangeToSpaceAffine_h_included_ #define __cmtkTransformChangeToSpaceAffine_h_included_ #include #include #include #include namespace cmtk { /** \addtogroup Base */ //@{ /// Compute affine coordinate transformation between native spaces of reference and floating images. class TransformChangeToSpaceAffine { public: /// Simplified constructor: compute transformation between images in new, common space. TransformChangeToSpaceAffine( const AffineXform& xform /*!< Transformation from reference to floating in their current spaces.*/, const UniformVolume& reference /*!< Reference (fixed) image.*/, const UniformVolume& floating /*! Floating (moving) image.*/, const char* forceSpace = NULL /*!< Force transformation to be in this coordinate space.*/ ); /// Return transformation in native spaces. const AffineXform& GetTransformation() const { return this->m_NewXform; } private: /// Transformation between native spaces. AffineXform m_NewXform; }; //@} } // namespace cmtk #endif // #ifndef __cmtkTransformChangeToSpaceAffine_h_included_ cmtk-3.3.1/libs/Base/cmtkTransformedVolumeAxes.cxx000066400000000000000000000110551276303427400221710ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2012 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4538 $ // // $LastChangedDate: 2012-10-02 15:16:13 -0700 (Tue, 02 Oct 2012) $ // // $LastChangedBy: torstenrohlfing $ // */ #include "cmtkTransformedVolumeAxes.h" #include namespace cmtk { /** \addtogroup Base */ //@{ TransformedVolumeAxes::TransformedVolumeAxes ( const UniformVolume& volume, const AffineXform* xform, const Types::Coordinate* deltas, const Types::Coordinate* otherOrigin ) { // define volume corners UniformVolume::CoordinateVectorType dX = FixedVectorStaticInitializer<3,Types::Coordinate>::Init(1,0,0); UniformVolume::CoordinateVectorType dY = FixedVectorStaticInitializer<3,Types::Coordinate>::Init(0,1,0); UniformVolume::CoordinateVectorType dZ = FixedVectorStaticInitializer<3,Types::Coordinate>::Init(0,0,1); UniformVolume::CoordinateVectorType V( volume.m_Offset ); dX += volume.m_Offset; dY += volume.m_Offset; dZ += volume.m_Offset; if ( xform ) { V = xform->Apply(V); dX = xform->Apply(dX); dY = xform->Apply(dY); dZ = xform->Apply(dZ); } dX -= V; dY -= V; dZ -= V; if ( otherOrigin ) { V -= FixedVector<3,Types::Coordinate>::FromPointer( otherOrigin ); } // Apply post-transformation scaling if ( deltas ) { const UniformVolume::CoordinateVectorType deltasV = UniformVolume::CoordinateVectorType::FromPointer( deltas ); dX /= deltasV; dY /= deltasV; dZ /= deltasV; V /= deltasV; } this->MakeHash( volume, V, dX, dY, dZ ); } TransformedVolumeAxes::TransformedVolumeAxes ( const UniformVolume& volume, const ParametricPlane& mirrorPlane, const Types::Coordinate* deltas ) { // define volume corners UniformVolume::CoordinateVectorType dX = FixedVectorStaticInitializer<3,Types::Coordinate>::Init(1,0,0); UniformVolume::CoordinateVectorType dY = FixedVectorStaticInitializer<3,Types::Coordinate>::Init(0,1,0); UniformVolume::CoordinateVectorType dZ = FixedVectorStaticInitializer<3,Types::Coordinate>::Init(0,0,1); UniformVolume::CoordinateVectorType V( volume.m_Offset ); // apply mirror transformation mirrorPlane.MirrorInPlace(V); mirrorPlane.MirrorInPlace(dX); dX -= V; mirrorPlane.MirrorInPlace(dY); dY -= V; mirrorPlane.MirrorInPlace(dZ); dZ -= V; // Apply post-transformation scaling if ( deltas ) { const UniformVolume::CoordinateVectorType deltasV = UniformVolume::CoordinateVectorType::FromPointer( deltas ); dX /= deltasV; dY /= deltasV; dZ /= deltasV; V /= deltasV; } this->MakeHash( volume, V, dX, dY, dZ ); } void TransformedVolumeAxes::MakeHash ( const UniformVolume& volume, const UniformVolume::SpaceVectorType& offset, const UniformVolume::SpaceVectorType& dX, const UniformVolume::SpaceVectorType& dY, const UniformVolume::SpaceVectorType& dZ ) { this->m_Dims = volume.m_Dims; for ( int dim = 0; dim<3; ++dim ) { this->m_Hash[dim] = Memory::ArrayC::Allocate( this->m_Dims[dim] ); assert( this->m_Hash[dim] != NULL ); } const Types::Coordinate deltaX = volume.m_Delta[0]; const Types::Coordinate deltaY = volume.m_Delta[1]; const Types::Coordinate deltaZ = volume.m_Delta[2]; int idx; for ( idx=0; idx < this->m_Dims[0]; ++idx ) this->m_Hash[0][idx] = deltaX*idx*dX; for ( idx=0; idx < this->m_Dims[1]; ++idx ) this->m_Hash[1][idx] = deltaY*idx*dY; for ( idx=0; idx < this->m_Dims[2]; ++idx ) (this->m_Hash[2][idx] = deltaZ*idx*dZ) += offset; } TransformedVolumeAxes::~TransformedVolumeAxes() { for ( int dim = 0; dim<3; ++dim ) { assert( this->m_Hash[dim] != NULL ); Memory::ArrayC::Delete( this->m_Hash[dim] ); } } } // namespace cmtk cmtk-3.3.1/libs/Base/cmtkTransformedVolumeAxes.h000066400000000000000000000117551276303427400216250ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2011 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4415 $ // // $LastChangedDate: 2012-06-04 14:57:12 -0700 (Mon, 04 Jun 2012) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkTransformedVolumeAxes_h_included_ #define __cmtkTransformedVolumeAxes_h_included_ #include #include #include #include #include namespace cmtk { /** \addtogroup Base */ //@{ /** Class that represents pre-transformed axes samples for 3D volumes. * This class generates an array of 3D coordinate vectors for each of the * three coordinate axes X, Y, and Z. These arrays contain direction vectors * that directly point to the coordinate of every sample on the respective * axis. These vectors are computed with respect to the object's coordinate * transformation as well as a second Volume's transformation and coordinate * offset. The Z-axis array contains this Volume's coordinate offset as * defined by the transformation translation component as well. * The vectors from the arrays can therefore directly be used for probing * the other Volume using the ProbeNoXform member function. */ class TransformedVolumeAxes { public: /// This class. typedef TransformedVolumeAxes Self; /// Smart pointer to this class. typedef SmartPointer SmartPtr; /** Constructor using affine transformation. *\param volume The volume for which we are constructing an axes hash. *\param xform Coordinate transformation. If this pointer is NULL, identity is assumed. *\param deltas If this parameter is given, it is used as a pointer to a * three-element coordinate array defining the voxel size in the other volume * to obtain fractional voxel indices rather than actual coordinates. * * Alternatively, this parameter can also be used to provide the volume size (Volume::Size), which creates * normalized coordinates (0..1) for each volume axis. * *\param otherOrigin If this parameter is given, it is used as a pointer to a * three-element coordinate array defining an m_Origin vector for the transformed * coordinates. *\return Pointer to an array of three pointers. Each of these points to an * array of Vector3D objects that contain the vector hashes for the X-, Y-, * and Z-axis. */ TransformedVolumeAxes( const UniformVolume& volume, const AffineXform* xform = NULL, const Types::Coordinate* deltas = NULL, const Types::Coordinate* otherOrigin = NULL ); /** Constructor using mirror plane. *\param volume The volume whose axes we are transforming. *\param mirrorPlane Mirror plane with respect to which the coordinates of * this volume and thus all hash values are mirrored. *\param deltas If this parameter is given, it is used as a pointer to a * 3 element coordinate array defining the voxel size in the other volume * to obtain fractional voxel indices rather than actual coordinates. * * Alternatively, this parameter can also be used to provide the volume size (Volume::Size), which creates * normalized coordinates (0..1) for each volume axis. */ TransformedVolumeAxes( const UniformVolume& volume, const ParametricPlane& mirrorPlane, const Types::Coordinate* deltas = NULL ); /// Free all storage. ~TransformedVolumeAxes(); /// Access operator. const Vector3D* operator[]( const size_t index ) const { return this->m_Hash[index]; } /// Get dimensions. const FixedVector<3,int>& Dims() const { return this->m_Dims; } private: /// Array of pointers to transformed axes points. FixedArray<3,UniformVolume::SpaceVectorType*> m_Hash; /// Dimensions of the transformed grid: numbers of samples per axis. FixedVector<3,int> m_Dims; /// Create the actual hash: allocate and fill according to given offset and delta vectors. void MakeHash( const UniformVolume& volume, const UniformVolume::SpaceVectorType& offset, const UniformVolume::SpaceVectorType& dX, const UniformVolume::SpaceVectorType& dY, const UniformVolume::SpaceVectorType& dZ ); }; //@} } // namespace cmtk #endif // #ifndef __cmtkTransformedVolumeAxes_h_included_ cmtk-3.3.1/libs/Base/cmtkTypedArray.cxx000066400000000000000000000151221276303427400177570ustar00rootroot00000000000000/* // // Copyright 1997-2011 Torsten Rohlfing // // Copyright 2004-2011 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 3518 $ // // $LastChangedDate: 2011-10-27 13:38:23 -0700 (Thu, 27 Oct 2011) $ // // $LastChangedBy: torstenrohlfing $ // */ #include #include #include namespace cmtk { /** \addtogroup Base */ //@{ void TypedArray ::RescaleToRange( const Types::DataItemRange& toRange ) { const Types::DataItemRange fromRange = this->GetRange(); const Types::DataItem scale = toRange.Width() / fromRange.Width(); const Types::DataItem offset = toRange.m_LowerBound - (fromRange.m_LowerBound * scale); this->Rescale( scale, offset ); } void TypedArray ::BlockSwap ( const size_t fromOffset, const size_t toOffset, const size_t blockLength ) { char buffer[2048]; size_t itemSize = this->GetItemSize(); char *dataPtr = static_cast( this->GetDataPtr() ); size_t bytesToGo = itemSize * blockLength; char *fromPtr = dataPtr + itemSize * fromOffset; char *toPtr = dataPtr + itemSize * toOffset; while ( bytesToGo > sizeof( buffer ) ) { memcpy( buffer, toPtr, sizeof( buffer ) ); memcpy( toPtr, fromPtr, sizeof( buffer ) ); memcpy( fromPtr, buffer, sizeof( buffer ) ); fromPtr += sizeof( buffer ); toPtr += sizeof( buffer ); bytesToGo -= sizeof( buffer ); } if ( bytesToGo ) { memcpy( buffer, toPtr, bytesToGo ); memcpy( toPtr, fromPtr, bytesToGo ); memcpy( fromPtr, buffer, bytesToGo ); } } void TypedArray ::BlockReverse ( const size_t fromOffset, const size_t blockLength ) { // we get into trouble here for item sizes > 128 bits. char buffer[16]; size_t itemSize = this->GetItemSize(); char *dataPtr = static_cast( this->GetDataPtr() ); char *fromPtr = dataPtr + fromOffset * itemSize; char *toPtr = fromPtr + (blockLength-1) * itemSize; for ( size_t itemsToGo = blockLength / 2; itemsToGo; --itemsToGo ) { memcpy( buffer, toPtr, itemSize ); memcpy( toPtr, fromPtr, itemSize ); memcpy( fromPtr, buffer, itemSize ); fromPtr += itemSize; toPtr -= itemSize; } } TypedArray::SmartPtr TypedArray ::Create ( const ScalarDataType dtype, void *const data, const size_t size, const bool paddingFlag, const void* paddingData, const Memory::DeallocatorFunctionPointer deallocator ) { switch (dtype) { case TYPE_BYTE: return Self::SmartPtr( new ByteArray( data, size, paddingFlag, paddingData, deallocator ) ); break; case TYPE_CHAR: return Self::SmartPtr( new CharArray( data, size, paddingFlag, paddingData, deallocator ) ); break; case TYPE_SHORT: return Self::SmartPtr( new ShortArray( data, size, paddingFlag, paddingData, deallocator ) ); break; case TYPE_USHORT: return Self::SmartPtr( new UShortArray( data, size, paddingFlag, paddingData, deallocator ) ); break; case TYPE_INT: return Self::SmartPtr( new IntArray( data, size, paddingFlag, paddingData, deallocator ) ); break; case TYPE_UINT: return Self::SmartPtr( new UIntArray( data, size, paddingFlag, paddingData, deallocator ) ); break; case TYPE_FLOAT: return Self::SmartPtr( new FloatArray( data, size, paddingFlag, paddingData, deallocator ) ); break; case TYPE_DOUBLE: return Self::SmartPtr( new DoubleArray( data, size, paddingFlag, paddingData, deallocator ) ); break; default: break; } fprintf(stderr,"TypedArray::Create - Data type %d unknown.",dtype); return Self::SmartPtr(); } TypedArray::SmartPtr TypedArray ::Create( const ScalarDataType dtype, const size_t size ) { switch ( dtype ) { case TYPE_BYTE: return Self::SmartPtr( new ByteArray( size ) ); break; case TYPE_CHAR: return Self::SmartPtr( new CharArray( size ) ); break; case TYPE_SHORT: return Self::SmartPtr( new ShortArray( size ) ); break; case TYPE_USHORT: return Self::SmartPtr( new UShortArray( size ) ); break; case TYPE_INT: return Self::SmartPtr( new IntArray( size ) ); break; case TYPE_UINT: return Self::SmartPtr( new UIntArray( size ) ); break; case TYPE_FLOAT: return Self::SmartPtr( new FloatArray( size ) ); break; case TYPE_DOUBLE: return Self::SmartPtr( new DoubleArray( size ) ); break; default: break; } fprintf( stderr, "TypedArray::Create - Data type %d unknown.", dtype ); return Self::SmartPtr(); } void TypedArray ::PruneHistogram ( const bool pruneHi, const bool pruneLo, const size_t numberOfBinsTarget, const size_t numberOfBinsInternal ) { Histogram::SmartPtr originalHistogram( this->GetHistogram( numberOfBinsInternal ) ); const size_t oneBinFraction = this->GetDataSize() / numberOfBinsTarget; const Types::DataItemRange range = this->GetRange(); Types::DataItem min = range.m_LowerBound; Types::DataItem max = range.m_UpperBound; if ( pruneHi ) { size_t accumulatedNumberOfSamples = 0; for ( size_t binIdx = numberOfBinsInternal-1; binIdx > 0; --binIdx ) { accumulatedNumberOfSamples += (*originalHistogram)[binIdx]; if ( accumulatedNumberOfSamples > oneBinFraction ) { max = range.m_LowerBound + range.Width()/numberOfBinsInternal*binIdx; break; } } } if ( pruneLo ) { size_t accumulatedNumberOfSamples = 0; for ( size_t binIdx = 0; binIdx < numberOfBinsInternal; ++binIdx ) { accumulatedNumberOfSamples += (*originalHistogram)[binIdx]; if ( accumulatedNumberOfSamples > oneBinFraction ) { min = range.m_LowerBound + range.Width()/numberOfBinsInternal*binIdx; break; } } } this->Threshold( Types::DataItemRange( min, max ) ); } } // namespace cmtk cmtk-3.3.1/libs/Base/cmtkTypedArray.h000066400000000000000000000474461276303427400174220ustar00rootroot00000000000000/* // // Copyright 1997-2010 Torsten Rohlfing // // Copyright 2004-2013 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4998 $ // // $LastChangedDate: 2013-10-21 12:09:23 -0700 (Mon, 21 Oct 2013) $ // // $LastChangedBy: torsten_at_home $ // */ #ifndef __cmtkTypedArray_h_included_ #define __cmtkTypedArray_h_included_ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef DEBUG #define CheckBounds(index,bound) \ if (!(index SmartPtr; /// Smart pointer to const. typedef SmartConstPointer SmartConstPtr; /** Create typed data array from existing array of values. *\param dtype Type specifier. *\param data Pointer to the existing data array. *\param size Number of data items in the array. * array's memory if and only if this flag is true. *\param paddingFlag If this flag is not zero, padding data exists in the array. *\param paddingData Value used for padding data. *\param deallocator Pointer to a deallocator object that should be used to free the given data block (or NULL if memory is handled by another object) *\return A pointer to a new typed array object, or NULL if an error * occurred. */ static Self::SmartPtr Create ( const ScalarDataType dtype, void *const data, const size_t size, const bool paddingFlag = false, const void* paddingData = NULL, const Memory::DeallocatorFunctionPointer deallocator = NULL ); /** Create typed data array, allocating new memory for the items array. *\param dtype Type specifier. *\param size Number of items in the array to be allocated. Memory will be * freed when the created object is destroyed. The values in the array are * not initialized. *\return A pointer to a new typed array object, or NULL if an error * occurred. */ static Self::SmartPtr Create( const ScalarDataType dtype, const size_t size ); /** Get an item from the specified index in the data array. * If there is no valid data present at the given index, zero is returned * and the resulting item is zero, too. This can only happen if PaddingFlag * is not zero. */ virtual bool Get ( Types::DataItem&, const size_t ) const = 0; /// Return data or a given default value if no data exists there. virtual Types::DataItem ValueAt ( const size_t idx, const Types::DataItem defaultValue = 0.0 ) const = 0; /** Get a sequence of items from the array. *\param values This must point to an allocated array of at least as many * Types::DataItem objects as given in the "length" parameter. *\param index The index of the item to retrieve. Valid values are in the * range [0..Datasize()-1]. *\param length Number of consecutive values to get. */ virtual void GetSequence ( Types::DataItem *const values, const size_t index, const size_t length ) const = 0; /// Sets the specified array element. virtual void Set ( const Types::DataItem, const size_t ) = 0; /// Return type of stored data. virtual ScalarDataType GetType () const = 0; /// Return the number of bytes per stored item. virtual size_t GetItemSize () const = 0; /** Get the adress of the real value used for no data present. */ virtual void* GetPaddingPtr () const = 0; /// Get the transfer representation of the value used for no data present. virtual Types::DataItem GetPaddingValue () const = 0; /// Test if there is NULL data at a particular location. virtual bool PaddingDataAt ( const size_t index ) const = 0; /** Get complete aray of data values. * The calling routine is responsible for de-allocating the returned array * by calling 'free' after use. */ virtual Types::DataItem* GetData () const = 0; /** Set all data from an Types::DataItem array. * This function sets all values stored in the present array from a memory * region with Types::DataItem values. *\param data Pointer to an array of Types::DataItem values. * Control over the source array is not taken by this object. If it is on the heap, * then the calling routine remains responsible for de-allocating the array afterwards. */ virtual void SetData( Types::DataItem *const data ) = 0; /** Convert to typed array of any given template type. */ virtual Self::SmartPtr Convert( const ScalarDataType dtype ) const = 0; /** Convert a sub-array to any given primitive data type. *\return Newly allocated memory of given type. Caller is responsible for freeing this memory with a call to * Memory::ArrayC::Delete(). */ virtual void* ConvertSubArray( const ScalarDataType dtype, const size_t fromIdx, const size_t len ) const = 0; /** Convert a sub-array to given primitive data type into existing array. *\return Pointer "destination". */ virtual void* ConvertSubArray( void *const destination, const ScalarDataType dtype, const size_t fromIdx, const size_t len ) const = 0; /** Convert the array to any given data type. * This function uses ConvertSubArray to convert the complete array. *\return Newly allocated memory of given type. Caller is responsible for freeing this memory with a call to * Memory::ArrayC::Delete(). *\see ConvertSubArray */ virtual void* ConvertArray ( const ScalarDataType dtype ) const { return this->ConvertSubArray( dtype, 0, DataSize ); } /** Clear entire array. * This method is implemented by derived template classes for better * efficiency. *\param usePaddingData If this flag is set, then the array will be filled with * the PaddingData value, if one exists. Otherwise, the array will be filled * with the respective data type's zero value. */ virtual void ClearArray ( const bool usePaddingData = false ) = 0; /** Change endianness of data. */ virtual void ChangeEndianness() = 0; /** Scale values in the array. * A call to this member function will perform an in-place rescaling of the * values in the array. *\param scale The original data value is multiplied by the parameter first. *\param offset This value is added to the original data value after * multiplying it by the scale parameter. */ virtual void Rescale( const Types::DataItem scale = 1, const Types::DataItem offset = 0 ) = 0; /** Scale and shift values in the array. */ virtual void RescaleAndShift( const Types::DataItem scale = 1, const Types::DataItem offset = 0, const size_t shiftBits = 0 ) = 0; /** Scale values in the array to a given target range. */ virtual void RescaleToRange( const Types::DataItemRange& toRange ); /** Apply gamma correction. *\param gamma The gamma correction coefficient. */ virtual void GammaCorrection( const Types::DataItem gamma ) = 0; /// Function pointer type: double to double. typedef double (*FunctionTypeDouble)(const double); /// Function pointer type: float to float. typedef float (*FunctionTypeFloat)(const float); /** Apply real function to data. */ virtual void ApplyFunction( Self::FunctionTypeFloat f ) { this->ApplyFunctionFloat( f ); } /** Apply real function to data. */ virtual void ApplyFunction( Self::FunctionTypeDouble f ) { this->ApplyFunctionDouble( f ); } /** Apply real function to data. */ virtual void ApplyFunctionFloat( Self::FunctionTypeFloat f ) = 0; /** Apply real function to data. */ virtual void ApplyFunctionDouble( Self::FunctionTypeDouble f ) = 0; /// Convert all values to absolute values. virtual void MakeAbsolute() = 0; /** Scale values in the array with truncation boundaries. * A call to this member function will perform an in-place rescaling of the * values in the array with value range truncation. Truncation takes place * after the scaling itself, i.e., the truncation boundaries refer to the * already scaled values. *\param scale The original data value is multiplied by the parameter first. *\param offset This value is added to the original data value after * multiplying it by the scale parameter. *\param truncLo Lower truncation boundary. Scaled items below this * threshold will be set to equal its value. *\param truncHi Upper truncation boundary. Scaled items above this * threshold will be set to equal its value. */ virtual void Rescale( const Types::DataItem scale, const Types::DataItem offset, const Types::DataItem truncLo, const Types::DataItem truncHi = CMTK_ITEM_MAX ) = 0; /** Threshold data. * All values above upper threshold are set to upper thrershold. All values * below lower threshold are set to lower threshold. */ virtual void Threshold( const Types::DataItemRange& range ) = 0; /** Threshold data. * All values outside the threshold range are set to the Padding (padding) * value. */ virtual void ThresholdToPadding( const Types::DataItemRange& range ) = 0; /** Prune histogram to trim noise. * This function trims values on the upper and lower end of the value range by * thresholding in such a way that the trimmed number of samples on either side * amounts to the relative fraction of samples based on a target histogram bin * count. */ virtual void PruneHistogram( const bool pruneHi, const bool pruneLo, const size_t numberOfBinsTarget, const size_t numberOfBinsInternal = 1024 ); /** Binarize array values with given threshold. * All values greater than threshold (default: zero) are set to one, all * values smaller or equal are set to zero. */ virtual void Binarize( const Types::DataItem threshold = 0 ) = 0; /** Clone an existing TypedArray. * Memory is allocated and the source array copied item by item. Data is * read by the source object's Get method and stored by the destination * object's Set method. */ Self::SmartPtr Clone() const { return Self::SmartPtr( this->CloneVirtual() ); } /// Default constructor. TypedArray () : m_DataClass( DATACLASS_GREY ), m_DataType( TYPE_NONE ), m_Deallocator( NULL ), DataSize( 0 ), PaddingFlag( false ) {} /** Destructor. * Just has to be declared virtual. */ virtual ~TypedArray () {} /** Free data pointer. * Derived classes may have to overload this method to take care of other * memory management libraries. */ virtual void FreeData() = 0; /** Release pointer to data array. * The pointer must remain valid until object is destructed itself. */ void ReleaseDataPointer () { this->m_Deallocator = NULL; } /** Return the number of array elements. *\return The number of array elements */ size_t GetDataSize () const { return DataSize; } /** Return the array size in bytes. */ size_t GetDataSizeBytes () const { return this->GetItemSize() * DataSize; } /** Return address of the data array. *\return A pointer to the real data array. */ virtual void* GetDataPtr( const size_t offset = 0) = 0; /** Return address of the data array. *\return A pointer to the real data array. */ virtual const void* GetDataPtr( const size_t offset = 0) const = 0; /** Get part of the stored array. *\return A pointer to the buffer given is returned. */ virtual Types::DataItem* GetSubArray( Types::DataItem *const, const size_t, const size_t, const Types::DataItem = 0 ) const = 0; /** Allocate memory and get part of the stored array. *\return A pointer to the newly allocated buffer is returned. */ virtual Types::DataItem* GetSubArray( const size_t, const size_t, const Types::DataItem = 0 ) const = 0; /** Return the flag for padding data. *\return If return value is zero, all data stored in the array is valid. * A non-zero value indicates, that there are locations with no data present. */ bool GetPaddingFlag () const { return PaddingFlag; } /// Clear padding flag. This effectively turns padded pixels into actual data. void ClearPaddingFlag() { this->PaddingFlag = false; } /// Set the specified array element to no data present. virtual void SetPaddingAt ( const size_t ) = 0; /// Select the value to mark non-existent data. virtual void SetPaddingValue ( const Types::DataItem paddingData ) = 0; /// Select the value to mark non-existent data. virtual void SetPaddingPtr ( const void* paddingData ) = 0; /// Replace Padding data (padding) with given value. virtual void ReplacePaddingData ( const Types::DataItem value = 0 ) = 0; /// Check for padding data at given location. virtual bool IsPaddingAt( const size_t index ) const = 0; /// Check for padding data or zero at given location. virtual bool IsPaddingOrZeroAt( const size_t index ) const = 0; /// Calculate and return minimum and maximum data value. virtual const Types::DataItemRange GetRange() const = 0; /// Calculate entropy of distribution of values in this array. virtual double GetEntropy( const bool fractional = CMTK_HISTOGRAM_DISCRETE, const int numberOfBins = 128 ) const = 0; /** Calculate entropy of values in this array using existing histogram. * By using an already existing histogram, this function ensures that the * same numbers and sizes of bins are used for repeated calls of this * function, even when the actual data in this array has changed. On the * other hand, there is of course a risk that the data may have changed in * such a way that the old histogram arrangement does not fit it anymore. * * In the process of entropy computation, the histogram is also filled with * the data from this array. It is therefore available for subsequent * operations after returning from this function. */ virtual double GetEntropy( Histogram& histogram ) const = 0; /** Calculate entropy of values in this array using existing histogram. *\see TypedArray::GetEntropy */ virtual double GetEntropy( Histogram& histogram, const bool fractional = CMTK_HISTOGRAM_DISCRETE ) const = 0; /** Calculate entropy of values in this array using existing histogram and kernel for Parzen windowing. *\see TypedArray::GetEntropy */ virtual double GetEntropy( Histogram& histogram, const double* kernel, const size_t kernelRadius ) const = 0; /** Calculate statistics. * Results will be both zero if there is not data in the array. *\return The number of valid (i.e., non-padding) values that constitute the * given results. */ virtual size_t GetStatistics ( Types::DataItem& mean, Types::DataItem& variance ) const = 0; /** Compute approximate percentile value from histogram. */ virtual Types::DataItem GetPercentile( const Types::DataItem percentile /*!< The percentile to be computed. Value must be between 0 and 1.*/, const size_t nBins = 256 /*!< Number of histogram bins for percentile estimation.*/ ) const; /** Compute list of approximate percentile values from histogram. * This function calls GetPercentile for each value in the given input vector and puts * all resulting values into the output vector in the same order. The main advantage of * using this function is that it is more efficient as a single histogram is created to * compute all percentiles. */ virtual std::vector GetPercentileList( const std::vector& percentileList /*!< The list of percentiles to be computed. Each value must be between 0 and 1.*/, const size_t nBins = 256 /*!< Number of histogram bins for percentile estimation.*/ ) const; /** Get data histogram. *\return A histogram object filled with the relative frequencies of values * in this array. */ virtual Histogram::SmartPtr GetHistogram( const unsigned int numberOfBins /*!< Number of histogram bins */, const bool centeredBins = false /*!< Flag for bins centered around the samples*/ ) const = 0; /** Set data block to constant value. */ virtual void BlockSet( const Types::DataItem value, const size_t fromOffset, const size_t toOffset ) = 0; /** Fill entire array with one value. */ virtual void Fill( const Types::DataItem value ) { this->BlockSet( value, 0, this->GetDataSize() ); } /** Copy data block to other array. * This is really just a convenience wrapper for ConvertSubArray(). */ virtual void BlockCopy( Self& target, const size_t toOffset, const size_t fromOffset, const size_t blockLength ) const { this->ConvertSubArray( target.GetDataPtr( toOffset ), target.GetType(), fromOffset, blockLength ); } /// Exception class for array size mismatched. class SizeMismatchException : public Exception {}; /** Copy other array. * This is a convenience wrapper for ConvertSubArray(). */ virtual void Copy( const Self& other ) { if ( this->GetDataSize() != other.GetDataSize() ) { throw SizeMismatchException(); } other.ConvertSubArray( this->GetDataPtr(), this->GetType(), 0, this->GetDataSize() ); } /** Exchange two data blocks. * Internally, the data is copied in chunks of up to 2KB (currently), using * an 'automatic' intermediate buffer of that size. */ virtual void BlockSwap( const size_t fromOffset, const size_t toOffset, const size_t blockLength ); /** Revert order of items in a given range. *\attention This implementation only works for item sizes up to 16 bytes * (128 bits); above that, the internal buffer size needs to be increased. */ virtual void BlockReverse( const size_t fromOffset, const size_t blockLength ); /** Apply function class to the values of this array. */ virtual void ApplyFunctionObject( const TypedArrayFunction& f ) = 0; protected: /** Scalar data type ID. * This field is not yet actually used anywhere. It merely serves a debugging * purpose since it allows easy identification of the type of this array from * inspecting its data. */ ScalarDataType m_DataType; /// Deallocator function: if not NULL, this is a pointer to the function called to free the data array. Memory::DeallocatorFunctionPointer m_Deallocator; /// The size of the data array, i.e. the number of items allocated. size_t DataSize; /** If true, PaddingFlag indicates there are items with no data present. */ bool PaddingFlag; /// Allocate array for the given number of elements. virtual void Alloc ( const size_t datasize ) = 0; /// Virtual clone function: only to be calle by Clone(). virtual Self* CloneVirtual() const = 0; }; //@} } // namespace cmtk #endif // #ifndef __cmtkTypedArray_h_included_ cmtk-3.3.1/libs/Base/cmtkTypedArrayFunction.h000066400000000000000000000033311276303427400211110ustar00rootroot00000000000000/* // // Copyright 2004-2011 SRI International // // Copyright 1997-2009 Torsten Rohlfing // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 3454 $ // // $LastChangedDate: 2011-10-10 13:31:40 -0700 (Mon, 10 Oct 2011) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkTypedArrayFunction_h_included_ #define __cmtkTypedArrayFunction_h_included_ #include #include namespace cmtk { /** \addtogroup Base */ //@{ /** Base class for lookup classes. * The purpose of a lookup class is to provide a value translation table for the * cmtk::TypedArray class and its derivatives. */ class TypedArrayFunction { public: /// Map a single value to its new value. virtual Types::DataItem operator()( const Types::DataItem valueIn ) const = 0; /// Virtual destructor. virtual ~TypedArrayFunction() {} }; //@} } // namespace cmtk #endif // #ifndef __cmtkTypedArrayFunction_h_included_ cmtk-3.3.1/libs/Base/cmtkTypedArrayFunctionHistogramEqualization.cxx000066400000000000000000000037551276303427400257420ustar00rootroot00000000000000/* // // Copyright 2004-2010 SRI International // // Copyright 1997-2009 Torsten Rohlfing // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 2022 $ // // $LastChangedDate: 2010-07-21 15:26:03 -0700 (Wed, 21 Jul 2010) $ // // $LastChangedBy: torstenrohlfing $ // */ #include "cmtkTypedArrayFunctionHistogramEqualization.h" #include #include cmtk::TypedArrayFunctionHistogramEqualization ::TypedArrayFunctionHistogramEqualization ( const TypedArray& variableArray, const size_t numberOfHistogramBins ) { this->m_Histogram = Histogram::SmartPtr( variableArray.GetHistogram( numberOfHistogramBins ) ); (*this->m_Histogram)[0] = 0; // this effectively stretches the distribution this->m_Histogram->ConvertToCumulative(); const Types::DataItemRange range = variableArray.GetRange(); this->m_MinValue = range.m_LowerBound; this->m_ScaleFactor = 1.0 * range.Width() / (*this->m_Histogram)[numberOfHistogramBins-1]; } cmtk::Types::DataItem cmtk::TypedArrayFunctionHistogramEqualization ::operator()( const cmtk::Types::DataItem valueIn ) const { return this->m_MinValue + this->m_ScaleFactor * (*this->m_Histogram)[ this->m_Histogram->ValueToBin( valueIn ) ]; } cmtk-3.3.1/libs/Base/cmtkTypedArrayFunctionHistogramEqualization.h000066400000000000000000000045471276303427400253670ustar00rootroot00000000000000/* // // Copyright 2004-2010 SRI International // // Copyright 1997-2009 Torsten Rohlfing // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 2398 $ // // $LastChangedDate: 2010-10-05 14:54:37 -0700 (Tue, 05 Oct 2010) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkTypedArrayFunctionHistogramEqualization_h_included_ #define __cmtkTypedArrayFunctionHistogramEqualization_h_included_ #include #include #include #include namespace cmtk { /** \addtogroup Base */ //@{ /** Lookup class for histogram equalization. */ class TypedArrayFunctionHistogramEqualization /// Inherit from base class. : public TypedArrayFunction { public: /// This class. typedef TypedArrayFunctionHistogramEqualization Self; /// Default number of histogram bins. static const size_t DefaultNumberOfHistogramBins = 1024; /// Constructor: build lookup. TypedArrayFunctionHistogramEqualization( const TypedArray& variableArray, const size_t numberOfHistogramBins = Self::DefaultNumberOfHistogramBins ); /// Map a single value from the variable array to its new value. virtual Types::DataItem operator()( const Types::DataItem valueIn ) const; private: /// Data histogram. Histogram::SmartPtr m_Histogram; /// Scale factor from cumulative distribution to histogram. Types::DataItem m_ScaleFactor; /// Minimum data value. Types::DataItem m_MinValue; }; //@} } // namespace cmtk #endif // #ifndef __cmtkTypedArrayFunctionHistogramEqualization_h_included_ cmtk-3.3.1/libs/Base/cmtkTypedArrayFunctionHistogramMatching.cxx000066400000000000000000000067541276303427400250310ustar00rootroot00000000000000/* // // Copyright 2004-2012 SRI International // // Copyright 1997-2009 Torsten Rohlfing // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4156 $ // // $LastChangedDate: 2012-04-10 10:13:18 -0700 (Tue, 10 Apr 2012) $ // // $LastChangedBy: torstenrohlfing $ // */ #include "cmtkTypedArrayFunctionHistogramMatching.h" #include cmtk::TypedArrayFunctionHistogramMatching ::TypedArrayFunctionHistogramMatching ( const TypedArray& variableArray, const TypedArray& fixedArray, const size_t numberOfHistogramBins ) : m_Lookup( numberOfHistogramBins ) { this->m_FixedArrayHistogram = fixedArray.GetHistogram( numberOfHistogramBins, true /*centeredBins*/ ); this->m_FixedArrayHistogram->ConvertToCumulative(); this->m_VariableArrayHistogram = variableArray.GetHistogram( numberOfHistogramBins, true /*centeredBins*/ ); this->m_VariableArrayHistogram->ConvertToCumulative(); this->CreateLookup(); } cmtk::TypedArrayFunctionHistogramMatching ::TypedArrayFunctionHistogramMatching( const Self::HistogramType& variableHistogram, const Self::HistogramType& fixedHistogram ) : m_Lookup( variableHistogram.GetNumberOfBins() ) { this->m_FixedArrayHistogram = Self::HistogramType::SmartPtr( fixedHistogram.Clone() ); this->m_FixedArrayHistogram->ConvertToCumulative(); this->m_VariableArrayHistogram = Self::HistogramType::SmartPtr( variableHistogram.Clone() ); this->m_VariableArrayHistogram->ConvertToCumulative(); this->CreateLookup(); } void cmtk::TypedArrayFunctionHistogramMatching ::CreateLookup() { const size_t variableNumBins = this->m_VariableArrayHistogram->GetNumberOfBins(); std::vector normalizedVariableHistogram( variableNumBins ); for ( size_t l = 0; l < variableNumBins; ++l ) { normalizedVariableHistogram[l] = 1.0 * (*(this->m_VariableArrayHistogram))[l] / (*(this->m_VariableArrayHistogram))[variableNumBins-1]; } const size_t fixedNumBins = this->m_FixedArrayHistogram->GetNumberOfBins(); std::vector normalizedFixedHistogram( fixedNumBins ); for ( size_t l = 0; l < fixedNumBins; ++l ) { normalizedFixedHistogram[l] = 1.0 * (*(this->m_FixedArrayHistogram))[l] / (*(this->m_FixedArrayHistogram))[fixedNumBins-1]; } this->m_Lookup[0] = 0; size_t j = 0; for ( size_t i = 1; i < variableNumBins; ++i ) { while ((j < fixedNumBins) && (normalizedFixedHistogram[j] < normalizedVariableHistogram[i])) { ++j; } this->m_Lookup[i] = j; } } cmtk::Types::DataItem cmtk::TypedArrayFunctionHistogramMatching ::operator()( const cmtk::Types::DataItem valueIn ) const { return this->m_FixedArrayHistogram->BinToValue( this->m_Lookup[ this->m_VariableArrayHistogram->ValueToBin( valueIn ) ] ); } cmtk-3.3.1/libs/Base/cmtkTypedArrayFunctionHistogramMatching.h000066400000000000000000000067671276303427400244620ustar00rootroot00000000000000/* // // Copyright 2004-2010 SRI International // // Copyright 1997-2009 Torsten Rohlfing // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 2398 $ // // $LastChangedDate: 2010-10-05 14:54:37 -0700 (Tue, 05 Oct 2010) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkTypedArrayFunctionHistogramMatching_h_included_ #define __cmtkTypedArrayFunctionHistogramMatching_h_included_ #include #include #include #include namespace cmtk { /** \addtogroup Base */ //@{ /** Lookup class for histogram intensity matching. * This class provides a lookup table that is computed from the histograms of two * cmtk::TypedArray objects. The lookup can then be applied to the "variable" array * so that its distribution afterwards matches, as closely as possible, the distribution * of the "fixed" array. * * To apply histogram matching to "variableArray" based on the distribution of "fixedArray", * use the following: *\code * variableArray->ApplyFunction( cmtk::TypedArrayFunctionHistogramMatching( variableArray, fixedArray ) ); *\endcode * The variable array for setting up the matching function need not be the same as the array * that the function is applied to: *\code * variableArray->ApplyFunction( cmtk::TypedArrayFunctionHistogramMatching( testArray, fixedArray ) ); *\endcode */ class TypedArrayFunctionHistogramMatching /// Inherit from base class. : public TypedArrayFunction { public: /// This class. typedef TypedArrayFunctionHistogramMatching Self; /// Default number of histogram bins. static const size_t DefaultNumberOfHistogramBins = 1024; /// Histogram type. typedef Histogram HistogramType; /// Constructor: build lookup from two data arrays. TypedArrayFunctionHistogramMatching( const TypedArray& variableArray, const TypedArray& fixedArray, const size_t numberOfHistogramBins = Self::DefaultNumberOfHistogramBins ); /// Constructor: build lookup from two histograms. TypedArrayFunctionHistogramMatching( const Self::HistogramType& variableHistogram, const Self::HistogramType& fixedHistogram ); /// Map a single value from the variable array to its new value. virtual Types::DataItem operator()( const Types::DataItem valueIn ) const; private: /// Fixed array histogram. HistogramType::SmartPtr m_FixedArrayHistogram; /// Variable array histogram. HistogramType::SmartPtr m_VariableArrayHistogram; /// Lookup table that translates between the two histograms. std::vector m_Lookup; /// Create lookup from histograms. void CreateLookup(); }; //@} } // namespace cmtk #endif // #ifndef __cmtkTypedArrayFunctionHistogramMatching_h_included_ cmtk-3.3.1/libs/Base/cmtkTypedArrayNoiseEstimatorNaiveGaussian.cxx000066400000000000000000000044531276303427400253300ustar00rootroot00000000000000/* // // Copyright 2008-2011 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 3385 $ // // $LastChangedDate: 2011-08-18 14:16:15 -0700 (Thu, 18 Aug 2011) $ // // $LastChangedBy: torstenrohlfing $ // */ #include "cmtkTypedArrayNoiseEstimatorNaiveGaussian.h" namespace cmtk { TypedArrayNoiseEstimatorNaiveGaussian::TypedArrayNoiseEstimatorNaiveGaussian ( const TypedArray& data, const size_t histogramBins ) { Histogram::SmartPtr histogram( data.GetHistogram( histogramBins ) ); // find first maximum size_t idx = 0; while ( (idx < (histogramBins-1)) && ( (*histogram)[idx] <= (*histogram)[idx+1] ) ) { ++idx; } const Types::DataItem noiseMean = histogram->BinToValue( idx ); // now find following minimum while ( (idx < (histogramBins-1)) && ( (*histogram)[idx] > (*histogram)[idx+1] ) ) { ++idx; } // then, compute standard deviation of all values below that threshold from // first maximum. this->m_Threshold = histogram->BinToValue( idx ); Types::DataItem sdev = 0; size_t count = 0; for ( size_t i = 0; i < data.GetDataSize(); ++i ) { Types::DataItem value; if ( data.Get( value, i ) ) { if ( value <= this->m_Threshold ) { sdev += static_cast( MathUtil::Square( value - noiseMean ) ); ++count; } } } if ( count ) this->m_NoiseLevelSigma = static_cast( sqrt( sdev/count ) ); else this->m_NoiseLevelSigma = 0.0; } } // namespace cmtk cmtk-3.3.1/libs/Base/cmtkTypedArrayNoiseEstimatorNaiveGaussian.h000066400000000000000000000041201276303427400247440ustar00rootroot00000000000000/* // // Copyright 2008-2011 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 3385 $ // // $LastChangedDate: 2011-08-18 14:16:15 -0700 (Thu, 18 Aug 2011) $ // // $LastChangedBy: torstenrohlfing $ // */ #include #include #include namespace cmtk { /** Estimate noise level in data stored in a TypedArray. * Estimate Gaussian noise variance using naive peak finding method. *\author Torsten Rohlfing */ class TypedArrayNoiseEstimatorNaiveGaussian { public: /// This class. typedef TypedArrayNoiseEstimatorNaiveGaussian Self; /// Constructor. TypedArrayNoiseEstimatorNaiveGaussian( const TypedArray& data, const size_t histogramBins = 255 ); /// Get noise level. Types::DataItem GetNoiseLevelSigma() const { return this->m_NoiseLevelSigma; } /// Get noise threshold. Types::DataItem GetNoiseThreshold() const { return this->m_Threshold; } protected: /// Default constructor; should not be invoked by user code. TypedArrayNoiseEstimatorNaiveGaussian() { this->m_Threshold = 0; this->m_NoiseLevelSigma = 0; } /// The estimated noise threshold. Types::DataItem m_Threshold; /// The estimate noise sigma. Types::DataItem m_NoiseLevelSigma; }; } // namespace cmtk cmtk-3.3.1/libs/Base/cmtkTypedArray_Statistics.cxx000066400000000000000000000035061276303427400221740ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2010 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 2425 $ // // $LastChangedDate: 2010-10-07 16:14:23 -0700 (Thu, 07 Oct 2010) $ // // $LastChangedBy: torstenrohlfing $ // */ #include "cmtkTypedArray.h" namespace cmtk { /** \addtogroup Base */ //@{ Types::DataItem TypedArray ::GetPercentile ( const Types::DataItem percentile, const size_t nBins ) const { const Histogram::SmartPtr histogram( this->GetHistogram( nBins ) ); return histogram->GetPercentile( percentile ); } std::vector TypedArray ::GetPercentileList ( const std::vector& percentileList, const size_t nBins ) const { const Histogram::SmartPtr histogram( this->GetHistogram( nBins ) ); std::vector results( percentileList.size() ); for ( size_t i = 0; i < percentileList.size(); ++i ) results[i] = histogram->GetPercentile( percentileList[i] ); return results; } } // namespace cmtk cmtk-3.3.1/libs/Base/cmtkTypes.cxx000066400000000000000000000064731276303427400170100ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2012 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4408 $ // // $LastChangedDate: 2012-06-01 14:54:11 -0700 (Fri, 01 Jun 2012) $ // // $LastChangedBy: torstenrohlfing $ // */ #include #include namespace cmtk { /** \addtogroup Base */ //@{ const char *DataClassString[] = { "grey", "label", "unknown", NULL }; const char* DataTypeName[] = { "byte (8bit unsigned)", "char (8bit signed)", "short (16bit signed)", "ushort (16bit unsigned)", "int (32bit signed)", "uint (32bit unsigned)", "float (32bit)", "double (64bit)" }; DataClass StringToDataClass( const char *dataClassStr ) { if ( dataClassStr ) { for ( int idx=0; DataClassString[idx]; ++idx ) { if ( !strcmp( dataClassStr, DataClassString[idx] ) ) return (DataClass) idx; } } return DATACLASS_UNKNOWN; } const char* DataClassToString( const DataClass dataClass ) { return DataClassString[ static_cast( dataClass ) ]; } size_t TypeItemSize ( const ScalarDataType dtype ) { switch (dtype) { case TYPE_BYTE: return sizeof(byte); case TYPE_CHAR: return sizeof(char); case TYPE_SHORT: return sizeof(short); case TYPE_USHORT: return sizeof(unsigned short); case TYPE_INT: return sizeof(int); case TYPE_UINT: return sizeof(int); case TYPE_FLOAT: return sizeof(float); case TYPE_DOUBLE: return sizeof(double); default : return 0; } } ScalarDataType SelectDataTypeInteger( const byte itemSize, const bool signBit ) { if ( signBit ) { switch ( itemSize ) { case 1 : return TYPE_CHAR; case 2 : return TYPE_SHORT; case 4 : return TYPE_INT; default: return TYPE_NONE; } } else { switch ( itemSize ) { case 1 : return TYPE_BYTE; case 2 : return TYPE_USHORT; case 4 : return TYPE_INT; default: return TYPE_NONE; } } } ScalarDataType GetSignedDataType( const ScalarDataType dtype ) { switch ( dtype ) { case TYPE_BYTE: return TYPE_CHAR; case TYPE_USHORT: return TYPE_SHORT; case TYPE_UINT: return TYPE_INT; default: return dtype; } } ScalarDataType GetUnsignedDataType( const ScalarDataType dtype ) { switch ( dtype ) { case TYPE_CHAR: return TYPE_BYTE; case TYPE_SHORT: return TYPE_USHORT; case TYPE_INT: return TYPE_UINT; default: return dtype; } } } // namespace cmtk cmtk-3.3.1/libs/Base/cmtkTypes.h000066400000000000000000000145201276303427400164250ustar00rootroot00000000000000/* // // Copyright 2004-2012 SRI International // // Copyright 1997-2011, 2013 Torsten Rohlfing // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 5396 $ // // $LastChangedDate: 2016-01-13 21:24:32 -0800 (Wed, 13 Jan 2016) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkTypes_h_included_ #define __cmtkTypes_h_included_ #include #include #include #include #include #include #ifdef HAVE_VALUES_H # include #endif #ifdef _MSC_VER typedef unsigned short ushort; #endif #ifndef NULL #define NULL 0 #endif #ifndef _HAVE_BYTE_ typedef unsigned char byte; #define _HAVE_BYTE_ #endif // #ifndef _HAVE_BYTE_ namespace cmtk { /** \addtogroup Base */ //@{ /** Identifiers for coordinate axes. *\note It is very important that the enumeration constants remain defined * equal to the integer constants 0 through 2, since quite a bit of code * depends on this for array indexing. */ enum { /// x-axis. AXIS_X = 0, /// y-axis. AXIS_Y = 1, /// z-axis. AXIS_Z = 2 }; /// Class of image data. typedef enum { /// Grey-level data. DATACLASS_GREY, /// (Segmented) label data. DATACLASS_LABEL, /// Data type unknown. DATACLASS_UNKNOWN } DataClass; /// Convert string to data class identifier. DataClass StringToDataClass( const char *dataClassStr ); /// Convert data class identifier to string. const char* DataClassToString( const DataClass dataClass ); /// Scalar data type identifiers. typedef enum { /// Unsigned byte data (8 bit, range 0-255). TYPE_BYTE = 0, /// Signed byte data (8 bit, range -128-127). TYPE_CHAR = 1, /// Signed byte data (16 bit, range -32768-32767). TYPE_SHORT = 2, /// Unsigned byte data (16 bit, range 0-65535). TYPE_USHORT = 3, /// Signed integer data (32 bit). TYPE_INT = 4, /// Unsigned integer data (32 bits). TYPE_UINT = 5, /// Single precision float data (32 bits). TYPE_FLOAT = 6, /// Double precision float data (64 bits). TYPE_DOUBLE = 7, /// No data type defined. TYPE_NONE = -1 } ScalarDataType; /// Names of scalar data types. extern const char* DataTypeName[]; #ifdef CMTK_DATA_FLOAT namespace Types { /** @memo Definition of the data exchange item type * All data retrievals, stores and conversions are done using this type. */ typedef float DataItem; } const ScalarDataType TYPE_ITEM = TYPE_FLOAT; #define CMTK_ITEM_MAX FLT_MAX #define CMTK_ITEM_MIN FLT_MIN #define CMTK_ITEM_NAN CMTK_FLOAT_NAN #else namespace Types { typedef double DataItem; } const ScalarDataType TYPE_ITEM = TYPE_DOUBLE; #define CMTK_ITEM_MAX DBL_MAX #define CMTK_ITEM_MIN DBL_MIN #define CMTK_ITEM_NAN CMTK_DOUBLE_NAN #endif // #ifdef CMTK_DATA_FLOAT namespace Types { /// Range of DataItem values specified as lower and upper bound. template class Range { public: /// Constructor. Range( const T& lowerBound, const T& upperBound ) : m_LowerBound( lowerBound ), m_UpperBound( upperBound ) {} /// Conversion constructor. template explicit Range( const Range& range ) : m_LowerBound( range.m_LowerBound ), m_UpperBound( range.m_UpperBound ) {} /// Compute "width" of range, i.e., upper minus lower bound. T Width() const { return this->m_UpperBound - this->m_LowerBound; } /// Test whether a given value is within the range. bool InRange( const T& value ) const { return (value >= this->m_LowerBound) && (value <= this->m_UpperBound); } /// Lower bound. T m_LowerBound; /// Upper bound. T m_UpperBound; }; /// Convenience declaration: range of DataItem values. typedef Range DataItemRange; } namespace Types { // Type for grid indexes in images. typedef long long int GridIndexType; } #ifdef CMTK_COORDINATES_FLOAT /** @memo Definition of the coordinate data type * All spatial locations, distances, etc. are stored using this type. */ namespace Types { typedef float Coordinate; } const ScalarDataType TYPE_COORDINATE = TYPE_FLOAT; #else /// Define float type used for coordinates. namespace Types { typedef double Coordinate; } const ScalarDataType TYPE_COORDINATE = TYPE_DOUBLE; #endif /// Return item size for given scalar data type. size_t TypeItemSize ( const ScalarDataType dtype ); /// Select integer data type based on item size and sign bit. ScalarDataType SelectDataTypeInteger( const byte itemSize,const bool signBit ); /// Return signed datatype ID corresponding to given datatype. ScalarDataType GetSignedDataType( const ScalarDataType dtype ); /// Return unsigned datatype ID corresponding to given datatype. ScalarDataType GetUnsignedDataType( const ScalarDataType dtype ); namespace Types { /// Template for traits class to combine two different real (floating point) data types. template class Combined { }; /// Combination of two single-precision floating point values. template<> class Combined { public: /// Single-precision floating point. typedef float Type; }; /// Combination of single- and double-precision floating point values. template<> class Combined { public: /// Double-precision floating point. typedef double Type; }; /// Combination of double- and single-precision floating point values. template<> class Combined { public: /// Double-precision floating point. typedef double Type; }; /// Combination of two double-precision floating point values. template<> class Combined { public: /// Double-precision floating point. typedef double Type; }; } // namespace Types //@} } // namespace cmtk #endif // #ifndef __cmtkTypes_h_included_ cmtk-3.3.1/libs/Base/cmtkUniformDistanceMap.cxx000066400000000000000000000321441276303427400214260ustar00rootroot00000000000000/* // // Copyright 2003 Calvin R. Maurer, Jr. // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2013 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 5016 $ // // $LastChangedDate: 2013-11-22 14:00:55 -0800 (Fri, 22 Nov 2013) $ // // $LastChangedBy: torstenrohlfing $ // */ #include "cmtkUniformDistanceMap.h" #include #include namespace cmtk { /** \addtogroup Base */ //@{ template UniformDistanceMap ::UniformDistanceMap ( const UniformVolume& volume, const byte flags, const Types::DataItem value, const Types::DataItem window ) { this->BuildDistanceMap( volume, flags, value, window ); if ( flags & Self::SIGNED ) { UniformVolume::SmartConstPtr outsideDistanceMap = this->m_DistanceMap; const UniformVolume& oMap = *outsideDistanceMap; // compute complementary distance map. this->BuildDistanceMap( volume, flags ^ Self::INSIDE, value, window ); UniformVolume& iMap = *this->m_DistanceMap; const size_t nPixels = volume.GetNumberOfPixels(); #pragma omp parallel for for ( int i = 0; i < static_cast( nPixels ); ++i ) { iMap.SetDataAt( oMap.GetDataAt( i ) - iMap.GetDataAt( i ), i ); } } this->m_DistanceMap->m_IndexToPhysicalMatrix = volume.m_IndexToPhysicalMatrix; this->m_DistanceMap->m_AlternativeIndexToPhysicalMatrices = volume.m_AlternativeIndexToPhysicalMatrices; this->m_DistanceMap->SetOffset( volume.m_Offset ); this->m_DistanceMap->CopyMetaInfo( volume ); } template void UniformDistanceMap ::BuildDistanceMap ( const UniformVolume& volume, const byte flags, const Types::DataItem value, const Types::DataItem window ) { this->m_DistanceMap = UniformVolume::SmartPtr( new UniformVolume( volume.m_Dims, volume.m_Size ) ); TypedArray::SmartPtr distanceArray = TypedArray::SmartPtr( TypedArray::Create( DataTypeTraits::DataTypeID, volume.GetNumberOfPixels() ) ); DistanceDataType *Distance = static_cast( distanceArray->GetDataPtr() ); const byte inside = ( flags & Self::INSIDE ) ? 0 : 1; const byte outside = 1 - inside; const TypedArray& feature = *(volume.GetData()); Types::DataItem c; DistanceDataType *p = Distance; if ( flags & Self::VALUE_EXACT ) { for ( size_t i = 0; i < volume.GetNumberOfPixels(); i++, p++ ) { if ( feature.Get( c, i ) ) { *p = (c == value) ? inside : outside; } else { *p = outside; } } } else if ( flags & Self::VALUE_THRESHOLD ) { for ( size_t i = 0; i < volume.GetNumberOfPixels(); i++, p++ ) { if ( feature.Get( c, i ) ) { *p = (c >= value) ? inside : outside; } else { *p = outside; } } } else if ( flags & Self::VALUE_WINDOW ) { for ( size_t i = 0; i < volume.GetNumberOfPixels(); i++, p++ ) { if ( feature.Get( c, i ) ) { *p = (fabs(c - value)<=window) ? inside : outside; } else { *p = outside; } } } else { for ( size_t i = 0; i < volume.GetNumberOfPixels(); i++, p++ ) { if ( feature.Get( c, i ) ) { *p = (c) ? inside : outside; } else { *p = outside; } } } this->ComputeEDT( Distance ); if ( !(flags & Self::SQUARED) ) { p = Distance; for ( size_t i = 0; i < volume.GetNumberOfPixels(); ++i, ++p ) { #if defined(_MSC_VER) || defined(__SUNPRO_CC) *p = static_cast( sqrt( (double)*p ) ); #else *p = static_cast( sqrt( *p ) ); #endif } } this->m_DistanceMap->SetData( distanceArray ); } template void UniformDistanceMap ::ComputeEDT( DistanceDataType *const distance ) { ThreadPool& threadPool = ThreadPool::GetGlobalThreadPool(); const size_t numberOfThreads = threadPool.GetNumberOfThreads(); const size_t numberOfTasks = 4 * numberOfThreads - 3; this->m_G.resize( numberOfThreads ); this->m_H.resize( numberOfThreads ); std::vector params( numberOfTasks ); for ( size_t idx = 0; idx < numberOfTasks; ++idx ) { params[idx].thisObject = this; params[idx].m_Distance = distance; } threadPool.Run( ComputeEDTThreadPhase1, params ); threadPool.Run( ComputeEDTThreadPhase2, params ); } template void UniformDistanceMap ::ComputeEDTThreadPhase1 ( void *const args, const size_t taskIdx, const size_t taskCnt, const size_t threadIdx, const size_t ) { ThreadParametersEDT* params = static_cast( args ); Self* This = params->thisObject; const Self* ThisConst = This; /* nXY is number of voxels in each plane (xy) */ /* nXYZ is number of voxels in 3D image */ const size_t nXY = ThisConst->m_DistanceMap->m_Dims[0] * ThisConst->m_DistanceMap->m_Dims[1]; /* compute D_2 */ /* call edtComputeEDT_2D for each plane */ DistanceDataType *p = params->m_Distance + nXY * taskIdx; for ( int k = taskIdx; k < ThisConst->m_DistanceMap->m_Dims[2]; k += taskCnt, p += nXY * taskCnt ) { This->ComputeEDT2D( p, This->m_G[threadIdx], This->m_H[threadIdx] ); } } template void UniformDistanceMap ::ComputeEDTThreadPhase2 ( void *const args, const size_t taskIdx, const size_t taskCnt, const size_t threadIdx, const size_t ) { ThreadParametersEDT* params = static_cast( args ); Self* This = params->thisObject; const Self* ThisConst = This; const size_t nXY = ThisConst->m_DistanceMap->m_Dims[0] * ThisConst->m_DistanceMap->m_Dims[1]; /* compute D_3 */ /* solve 1D problem for each column (z direction) */ std::vector f( This->m_DistanceMap->m_Dims[2] ); for ( size_t i = taskIdx; i < nXY; i += taskCnt ) { /* fill array f with D_2 distances in column */ /* this is essentially line 4 in Procedure VoronoiEDT() in tPAMI paper */ DistanceDataType *p = params->m_Distance + i; DistanceDataType *q = &f[0]; for ( int k = 0; k < ThisConst->m_DistanceMap->m_Dims[2]; k++, p += nXY, q++) { *q = *p; } /* call edtVoronoiEDT */ if ( This->VoronoiEDT( &f[0], ThisConst->m_DistanceMap->m_Dims[2], static_cast( ThisConst->m_DistanceMap->m_Delta[2] ), This->m_G[threadIdx], This->m_H[threadIdx] ) ) { p = params->m_Distance + i; q = &f[0]; for ( int k = 0; k < ThisConst->m_DistanceMap->m_Dims[2]; k++, p += nXY, q++ ) { *p = *q; } } } } template void UniformDistanceMap ::ComputeEDT2D ( DistanceDataType *const plane, std::vector& gTemp, std::vector& hTemp ) /* * This procedure computes the squared EDT of a 2D binary image with * anisotropic voxels. See notes for edtComputeEDT_2D. The difference * relative to edtComputeEDT_2D is that the edt is a float array instead of * a long array, and there are additional parameters for the image voxel * dimensions wX and wY. */ { /* compute D_1 as simple forward-and-reverse distance propagation */ /* (instead of calling edtVoronoiEDT) */ /* D_1 is distance to closest feature voxel in row (x direction) */ /* it is possible to use a simple distance propagation for D_1 because */ /* L_1 and L_2 norms are equivalent for 1D case */ DistanceDataType *p; for ( int j = 0; j < this->m_DistanceMap->m_Dims[1]; j++ ) { /* forward pass */ p = plane + j * this->m_DistanceMap->m_Dims[0]; DistanceDataType d = static_cast( Self::EDT_MAX_DISTANCE_SQUARED ); for ( int i = 0; i < this->m_DistanceMap->m_Dims[0]; i++, p++ ) { /* set d = 0 when we encounter a feature voxel */ if ( *p ) { *p = d = 0; } /* increment distance ... */ else if ( d != Self::EDT_MAX_DISTANCE_SQUARED ) { *p = ++d; } /* ... unless we haven't encountered a feature voxel yet */ else { *p = static_cast( Self::EDT_MAX_DISTANCE_SQUARED ); } } /* reverse pass */ if ( *(--p) != Self::EDT_MAX_DISTANCE_SQUARED ) { d = static_cast( Self::EDT_MAX_DISTANCE_SQUARED ); for ( int i = this->m_DistanceMap->m_Dims[0] - 1; i >= 0; i--, p-- ) { /* set d = 0 when we encounter a feature voxel */ if ( *p == 0 ) { d = 0; } /* increment distance after encountering a feature voxel */ else if ( d != Self::EDT_MAX_DISTANCE_SQUARED ) { /* compare forward and reverse distances */ if ( ++d < *p ) { *p = d; } } /* square distance */ /* (we use squared distance in rest of algorithm) */ *p = static_cast( *p * this->m_DistanceMap->m_Delta[0] ); *p *= *p; } } } /* compute D_2 = squared EDT */ /* solve 1D problem for each column (y direction) */ std::vector f( this->m_DistanceMap->m_Dims[1] ); for ( int i = 0; i < this->m_DistanceMap->m_Dims[0]; i++ ) { /* fill array f with D_1 distances in column */ /* this is essentially line 4 in Procedure VoronoiEDT() in tPAMI paper */ p = plane + i; DistanceDataType *q = &f[0]; for ( int j = 0; j < this->m_DistanceMap->m_Dims[1]; j++, p += this->m_DistanceMap->m_Dims[0], q++) { *q = *p; } /* call edtVoronoiEDT */ if ( this->VoronoiEDT( &f[0], this->m_DistanceMap->m_Dims[1], static_cast( this->m_DistanceMap->m_Delta[1] ), gTemp, hTemp ) ) { p = plane + i; q = &f[0]; for ( int j = 0; j < this->m_DistanceMap->m_Dims[1]; j++, p += this->m_DistanceMap->m_Dims[0], q++ ) { *p = *q; } } } } /* edtComputeEDT_2D_anisotropic */ template bool UniformDistanceMap ::VoronoiEDT ( DistanceDataType *const distanceSoFar, const int nSize, const DistanceDataType delta, std::vector& gTemp, std::vector& hTemp ) { long i, l, n_S; DistanceDataType a, b, c, v, lhs, rhs; /* alloc arrays if this is first call to procedure, or if arrays */ /* are too small and need to be reallocated */ gTemp.resize( nSize ); hTemp.resize( nSize ); DistanceDataType* g = &(gTemp[0]); DistanceDataType* h = &(hTemp[0]); /* construct partial Voronoi diagram */ /* this loop is lines 1-14 in Procedure edtVoronoiEDT() in tPAMI paper */ /* note we use 0 indexing in this program whereas paper uses 1 indexing */ DistanceDataType deltai = 0; for (i = 0, l = -1; i < nSize; i++, deltai += delta) { /* line 4 */ if ( distanceSoFar[i] != Self::EDT_MAX_DISTANCE_SQUARED ) { /* line 5 */ if ( l < 1 ) { /* line 6 */ g[++l] = distanceSoFar[i]; h[l] = deltai; } /* line 7 */ else { /* line 8 */ while (l >= 1) { /* compute removeEDT() in line 8 */ v = h[l]; a = v - h[l-1]; b = deltai - v; c = a + b; /* compute Eq. 2 */ if ((c*g[l] - b*g[l-1] - a*distanceSoFar[i] - a*b*c) > 0) { /* line 9 */ l--; } else { break; } } /* line 11 */ g[++l] = distanceSoFar[i]; h[l] = deltai; } } } /* query partial Voronoi diagram */ /* this is lines 15-25 in Procedure edtVoronoiEDT() in tPAMI paper */ /* lines 15-17 */ if ((n_S = l + 1) == 0) { return false; } /* lines 18-19 */ deltai = 0; for (i = 0, l = 0; i < nSize; i++, deltai += delta) { /* line 20 */ /* we reduce number of arithmetic operations by taking advantage of */ /* similarities in successive computations instead of treating them as */ /* independent ones */ a = h[l] - deltai; lhs = g[l] + a * a; while (l < n_S - 1) { a = h[l+1] - deltai; rhs = g[l+1] + a * a; if (lhs > rhs) { /* line 21 */ l++; lhs = rhs; } else { break; } } /* line 23 */ /* we put distance into the 1D array that was passed; */ /* must copy into EDT in calling procedure */ distanceSoFar[i] = lhs; } /* line 25 */ /* return 1 if we queried diagram, 0 if we returned because n_S = 0 */ return true; } //@} } // namespace cmtk template class cmtk::UniformDistanceMap; template class cmtk::UniformDistanceMap; template class cmtk::UniformDistanceMap; cmtk-3.3.1/libs/Base/cmtkUniformDistanceMap.h000066400000000000000000000100741276303427400210510ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2012 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 3945 $ // // $LastChangedDate: 2012-02-29 11:33:37 -0800 (Wed, 29 Feb 2012) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkUniformDistanceMap_h_included_ #define __cmtkUniformDistanceMap_h_included_ #include #include #include #include #include #include namespace cmtk { /** \addtogroup Base */ //@{ /** Distance map on a uniform grid. *\author This class is based on code originally written by Calvin R. Maurer, Jr. */ template class UniformDistanceMap : public DistanceMap { public: /** This class. */ typedef UniformDistanceMap Self; /** Superclass. */ typedef DistanceMap Superclass; /// Smart pointer to distance map. typedef SmartPointer SmartPtr; /// Smart pointer to distance map. typedef SmartConstPointer SmartConstPtr; /** Distance data type. */ typedef TDistanceDataType DistanceDataType; /** Construct map from given volume. *\param volume 3-D feature image. *\param flags Computation flags. *\param value Feature value *\param window Window radius around feature value. */ UniformDistanceMap( const UniformVolume& volume, const byte flags = Self::DEFAULT, const Types::DataItem value = 0, const Types::DataItem window = 0 ); // Get the computed distance map. UniformVolume::SmartPtr Get() { return this->m_DistanceMap; } private: /// Compute distance map. void BuildDistanceMap( const UniformVolume& volume, const byte flags, const Types::DataItem value=0, const Types::DataItem window = 0 ); /// Compute 3-D Euclidean Distance Transformation. void ComputeEDT( DistanceDataType *const distance ); /// Compute 2-D Euclidean Distance Transformation for one image plane. void ComputeEDT2D( DistanceDataType *const plane, std::vector& gTemp, std::vector& hTemp ); /// Compute 1-D Voronoi Euclidean Distance Transform. bool VoronoiEDT( DistanceDataType *const lpY, const int nSize, const DistanceDataType delta, std::vector& gTemp, std::vector& hTemp ); /// Internal: pointer to row storage. std::vector< std::vector > m_G; /// Internal: pointer to row storage. std::vector< std::vector > m_H; /** Thread parameters. */ class ThreadParametersEDT : /** Inherit from standard parameters. */ public ThreadParameters { public: /** Distance map pointer. */ DistanceDataType* m_Distance; }; /** Thread function for first phase (xy) of EDT computation. */ static void ComputeEDTThreadPhase1( void *const args, const size_t taskIdx, const size_t taskCnt, const size_t threadIdx, const size_t ); /** Thread function for second phase (z) of EDT computation. */ static void ComputeEDTThreadPhase2( void *const args, const size_t taskIdx, const size_t taskCnt, const size_t threadIdx, const size_t ); /// The computed distance map. UniformVolume::SmartPtr m_DistanceMap; }; //@} } // namespace cmtk #endif // #ifndef __cmtkDistanceMap_h_included_ cmtk-3.3.1/libs/Base/cmtkUniformVolume.cxx000066400000000000000000000464311276303427400205110ustar00rootroot00000000000000/* // // Copyright 2016 Google, Inc. // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2014 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 5397 $ // // $LastChangedDate: 2016-01-13 22:39:01 -0800 (Wed, 13 Jan 2016) $ // // $LastChangedBy: torstenrohlfing $ // */ #include "cmtkUniformVolume.h" #include #include namespace cmtk { UniformVolume::UniformVolume ( const UniformVolume& other ) : Volume( other ), m_Delta( other.m_Delta ), m_IndexToPhysicalMatrix( other.m_IndexToPhysicalMatrix ), m_AlternativeIndexToPhysicalMatrices( other.m_AlternativeIndexToPhysicalMatrices ) {} UniformVolume::UniformVolume ( const DataGrid::IndexType& dims, const Self::CoordinateVectorType& size, TypedArray::SmartPtr& data ) : Volume( dims, size, data ) { for ( int i=0; i<3; ++i ) { this->m_Delta[i] = ( (this->m_Dims[i] > 1) && (this->m_Size[i] > 0) ) ? this->m_Size[i] / (this->m_Dims[i] - 1) : 1.0; } this->CropRegion() = this->GetWholeImageRegion(); this->CreateDefaultIndexToPhysicalMatrix(); } UniformVolume::UniformVolume ( const DataGrid::IndexType& dims, const Types::Coordinate deltaX, const Types::Coordinate deltaY, const Types::Coordinate deltaZ, TypedArray::SmartPtr& data ) : Volume( dims, dims, data ) { this->m_Delta[0] = deltaX; this->m_Delta[1] = deltaY; this->m_Delta[2] = deltaZ; for ( int i=0; i<3; ++i ) this->m_Size[i] = this->m_Delta[i] * (this->m_Dims[i]-1); this->CropRegion() = this->GetWholeImageRegion(); this->CreateDefaultIndexToPhysicalMatrix(); } UniformVolume* UniformVolume::CloneVirtual( const bool copyData ) { if ( copyData ) { return this->CloneVirtual(); } else { UniformVolume *result = this->CloneGridVirtual(); result->SetData( this->GetData() ); return result; } } UniformVolume* UniformVolume::CloneVirtual() const { UniformVolume *result = this->CloneGridVirtual(); if ( this->GetData() ) { TypedArray::SmartPtr clonedData( this->GetData()->Clone() ); result->SetData( clonedData ); } else { result->SetData( TypedArray::SmartPtr::Null() ); } return result; } UniformVolume* UniformVolume::CloneGridVirtual() const { UniformVolume* clone = new UniformVolume( this->m_Dims, this->m_Size ); clone->m_Delta = this->m_Delta; // for single-slice images clone->SetOffset( this->m_Offset ); clone->CopyMetaInfo( *this ); clone->m_IndexToPhysicalMatrix = this->m_IndexToPhysicalMatrix; clone->m_AlternativeIndexToPhysicalMatrices = this->m_AlternativeIndexToPhysicalMatrices; return clone; } UniformVolume* UniformVolume::GetDownsampledAndAveraged( const Types::GridIndexType downsample, const bool approxIsotropic ) const { if ( approxIsotropic ) { const Types::Coordinate minDelta = std::min( this->m_Delta[0], std::min( this->m_Delta[1], this->m_Delta[2] ) ); const Types::GridIndexType downsampleByAxis[3] = { std::max( 1, downsample / std::max( 1, static_cast(this->m_Delta[0] / minDelta) ) ), std::max( 1, downsample / std::max( 1, static_cast(this->m_Delta[1] / minDelta) ) ), std::max( 1, downsample / std::max( 1, static_cast(this->m_Delta[2] / minDelta) ) ) }; return this->GetDownsampledAndAveraged( downsampleByAxis ); } else { const Types::GridIndexType downsampleByAxis[3] = { downsample, downsample, downsample }; return this->GetDownsampledAndAveraged( downsampleByAxis ); } } UniformVolume* UniformVolume::GetDownsampledAndAveraged( const Types::GridIndexType (&downsample)[3] ) const { DataGrid::SmartPtr newDataGrid( this->DataGrid::GetDownsampledAndAveraged( downsample ) ); TypedArray::SmartPtr newData = newDataGrid->GetData(); // create downsample grid UniformVolume* dsVolume = new UniformVolume( newDataGrid->GetDims(), downsample[0] * this->m_Delta[0], downsample[1] * this->m_Delta[1], downsample[2] * this->m_Delta[2], newData ); // compute shift of volume origin const Types::Coordinate shift[3] = { (downsample[0]-1)*this->m_Delta[0]/2, (downsample[1]-1)*this->m_Delta[1]/2, (downsample[2]-1)*this->m_Delta[2]/2 }; // apply shift to origin Self::CoordinateVectorType offset( this->m_Offset ); offset += Self::CoordinateVectorType::FromPointer( shift ); dsVolume->SetOffset( offset ); // set crop region while considering new image offset dsVolume->SetHighResCropRegion( this->GetHighResCropRegion() ); dsVolume->CopyMetaInfo( *this ); dsVolume->m_IndexToPhysicalMatrix = this->m_IndexToPhysicalMatrix; // apply offset shift to index-to-physical matrix for ( int axis = 0; axis < 3; ++axis ) { for ( int i = 0; i < 3; ++i ) { dsVolume->m_IndexToPhysicalMatrix[3][i] += (downsample[i]-1) * dsVolume->m_IndexToPhysicalMatrix[axis][i] / 2; // also update voxel size dsVolume->m_IndexToPhysicalMatrix[axis][i] *= downsample[i]; } } // do the same for any alternative matrices dsVolume->m_AlternativeIndexToPhysicalMatrices = this->m_AlternativeIndexToPhysicalMatrices; for ( std::map::iterator it = dsVolume->m_AlternativeIndexToPhysicalMatrices.begin(); it != dsVolume->m_AlternativeIndexToPhysicalMatrices.end(); ++it ) { // apply offset shift to index-to-physical matrix for ( int axis = 0; axis < 3; ++axis ) { for ( int i = 0; i < 3; ++i ) { (it->second)[3][i] += (downsample[i]-1) * (it->second)[axis][i] / 2; // also update voxel size (it->second)[axis][i] *= downsample[i]; } } } return dsVolume; } UniformVolume* UniformVolume::GetDownsampled( const Types::GridIndexType downsample, const bool approxIsotropic ) const { if ( approxIsotropic ) { const Types::Coordinate minDelta = std::min( this->m_Delta[0], std::min( this->m_Delta[1], this->m_Delta[2] ) ); const Types::GridIndexType downsampleByAxis[3] = { std::max( 1, downsample / std::max( 1, static_cast(this->m_Delta[0] / minDelta) ) ), std::max( 1, downsample / std::max( 1, static_cast(this->m_Delta[1] / minDelta) ) ), std::max( 1, downsample / std::max( 1, static_cast(this->m_Delta[2] / minDelta) ) ) }; return this->GetDownsampled( downsampleByAxis ); } else { const Types::GridIndexType downsampleByAxis[3] = { downsample, downsample, downsample }; return this->GetDownsampled( downsampleByAxis ); } } UniformVolume* UniformVolume::GetDownsampled( const Types::GridIndexType (&downsample)[3] ) const { DataGrid::SmartPtr newDataGrid( this->DataGrid::GetDownsampled( downsample ) ); TypedArray::SmartPtr newData = newDataGrid->GetData(); // create downsample grid UniformVolume* dsVolume = new UniformVolume( newDataGrid->GetDims(), downsample[0] * this->m_Delta[0], downsample[1] * this->m_Delta[1], downsample[2] * this->m_Delta[2], newData ); dsVolume->SetOffset( this->m_Offset ); dsVolume->SetHighResCropRegion( this->GetHighResCropRegion() ); dsVolume->CopyMetaInfo( *this ); dsVolume->m_IndexToPhysicalMatrix = this->m_IndexToPhysicalMatrix; // apply offset shift to index-to-physical matrix for ( int axis = 0; axis < 3; ++axis ) for ( int i = 0; i < 3; ++i ) { dsVolume->m_IndexToPhysicalMatrix[3][i] += (downsample[i]-1) * dsVolume->m_IndexToPhysicalMatrix[axis][i] / 2; // also update voxel size dsVolume->m_IndexToPhysicalMatrix[axis][i] *= downsample[i]; } // do the same for any alternative matrices dsVolume->m_AlternativeIndexToPhysicalMatrices = this->m_AlternativeIndexToPhysicalMatrices; for ( std::map::iterator it = dsVolume->m_AlternativeIndexToPhysicalMatrices.begin(); it != dsVolume->m_AlternativeIndexToPhysicalMatrices.end(); ++it ) { // apply offset shift to index-to-physical matrix for ( int axis = 0; axis < 3; ++axis ) { for ( int i = 0; i < 3; ++i ) { (it->second)[3][i] += (downsample[i]-1) * (it->second)[axis][i] / 2; // also update voxel size (it->second)[axis][i] *= downsample[i]; } } } return dsVolume; } UniformVolume* UniformVolume::GetInterleavedSubVolume ( const int axis, const Types::GridIndexType factor, const Types::GridIndexType idx ) const { Self::IndexType dims; Self::CoordinateVectorType delta; for ( int dim = 0; dim < 3; ++dim ) { dims[dim] = this->m_Dims[dim]; delta[dim] = this->m_Delta[dim]; } dims[axis] = this->m_Dims[axis] / factor; if ( this->m_Dims[axis] % factor > idx ) ++dims[axis]; delta[axis] = factor * this->m_Delta[axis]; Self::CoordinateVectorType offset( 0.0 ); offset[axis] = idx * this->m_Delta[axis]; UniformVolume* volume = new UniformVolume( dims, delta[0], delta[1], delta[2] ); volume->SetOffset( offset ); for ( Types::GridIndexType i = 0; i < dims[axis]; ++i ) { ScalarImage::SmartPtr slice( this->GetOrthoSlice( axis, idx + i * factor ) ); volume->SetOrthoSlice( axis, i, slice ); } volume->CopyMetaInfo( *this ); volume->m_IndexToPhysicalMatrix = this->m_IndexToPhysicalMatrix; // update coordinate offset according to sub-volume index for ( int i = 0; i < 3; ++i ) volume->m_IndexToPhysicalMatrix[3][i] += idx * volume->m_IndexToPhysicalMatrix[axis][i]; // scale direction vector along axis for ( int i = 0; i < 3; ++i ) volume->m_IndexToPhysicalMatrix[axis][i] *= factor; // do the same for any alternative matrices volume->m_AlternativeIndexToPhysicalMatrices = this->m_AlternativeIndexToPhysicalMatrices; for ( std::map::iterator it = volume->m_AlternativeIndexToPhysicalMatrices.begin(); it != volume->m_AlternativeIndexToPhysicalMatrices.end(); ++it ) { // update coordinate offset according to sub-volume index for ( int i = 0; i < 3; ++i ) (it->second)[3][i] += idx * (it->second)[axis][i]; // scale direction vector along axis for ( int i = 0; i < 3; ++i ) (it->second)[axis][i] *= factor; } if ( this->GetData()->GetPaddingFlag() ) { volume->GetData()->SetPaddingValue( this->GetData()->GetPaddingValue() ); } return volume; } UniformVolume* UniformVolume::GetInterleavedPaddedSubVolume ( const int axis, const Types::GridIndexType factor, const Types::GridIndexType idx ) const { Types::GridIndexType sDims = this->m_Dims[axis] / factor; if ( this->m_Dims[axis] % factor > idx ) ++sDims; UniformVolume* volume = new UniformVolume( this->m_Dims, this->m_Size ); (volume->CreateDataArray( this->GetData()->GetType() ))->Fill( 0.0 ); volume->SetOffset( this->m_Offset ); for ( Types::GridIndexType i = 0; i < sDims; ++i ) { const Types::GridIndexType sliceIdx = idx + i * factor; ScalarImage::SmartPtr slice( this->GetOrthoSlice( axis, sliceIdx ) ); volume->SetOrthoSlice( axis, sliceIdx, slice ); } volume->CopyMetaInfo( *this ); volume->m_IndexToPhysicalMatrix = this->m_IndexToPhysicalMatrix; volume->m_AlternativeIndexToPhysicalMatrices = this->m_AlternativeIndexToPhysicalMatrices; for ( std::map::iterator it = volume->m_AlternativeIndexToPhysicalMatrices.begin(); it != volume->m_AlternativeIndexToPhysicalMatrices.end(); ++it ) { // update coordinate offset according to sub-volume index for ( int i = 0; i < 3; ++i ) (it->second)[3][i] += idx * (it->second)[axis][i]; // scale direction vector along axis for ( int i = 0; i < 3; ++i ) (it->second)[axis][i] *= factor; } return volume; } const UniformVolume::RegionType UniformVolume::GetGridRange ( const Self::CoordinateRegionType& region ) const { Self::IndexType from, to; for ( size_t i = 0; i < 3; ++i ) { from[i] = std::max( 0, static_cast( (region.From()[i]-this->m_Offset[i]) / this->m_Delta[i] ) ); to[i] = 1+std::min( this->m_Dims[i]-1, 1+static_cast( (region.To()[i]-this->m_Offset[i]) / this->m_Delta[i] ) ); } return UniformVolume::RegionType( from, to ); } void UniformVolume::Mirror ( const int axis ) { this->DataGrid::ApplyMirrorPlane( axis ); this->CropRegion().From()[ axis ] = this->m_Dims[ axis ] - 1 - this->CropRegion().From()[ axis ]; this->CropRegion().To()[ axis ] = this->m_Dims[ axis ] - 1 - this->CropRegion().To()[ axis ]; } ScalarImage::SmartPtr UniformVolume::GetOrthoSlice ( const int axis, const Types::GridIndexType plane ) const { ScalarImage::SmartPtr sliceImage = DataGrid::GetOrthoSlice( axis, plane ); sliceImage->SetImageSlicePosition( this->GetPlaneCoord( axis, plane ) ); Self::CoordinateVectorType imageOffset( 0.0 ); Self::CoordinateVectorType directionX( 0.0 ); Self::CoordinateVectorType directionY( 0.0 ); switch ( axis ) { case AXIS_X: sliceImage->SetPixelSize( this->GetDelta( AXIS_Y ), this->GetDelta( AXIS_Z ) ); imageOffset[0] = this->GetPlaneCoord( AXIS_X, plane ); directionX[1] = 1; directionY[2] = 1; break; case AXIS_Y: sliceImage->SetPixelSize( this->GetDelta( AXIS_X ), this->GetDelta( AXIS_Z ) ); imageOffset[1] = this->GetPlaneCoord( AXIS_X, plane ); directionX[0] = 1; directionY[2] = 1; break; case AXIS_Z: sliceImage->SetPixelSize( this->GetDelta( AXIS_X ), this->GetDelta( AXIS_Y ) ); imageOffset[2] = this->GetPlaneCoord( AXIS_X, plane ); directionX[0] = 1; directionY[1] = 1; break; } sliceImage->SetImageDirectionX( directionX ); sliceImage->SetImageDirectionY( directionY ); sliceImage->SetImageOrigin( imageOffset ); return sliceImage; } UniformVolume::SmartPtr UniformVolume::ExtractSlice ( const int axis, const Types::GridIndexType plane ) const { DataGrid::SmartPtr sliceGrid( this->DataGrid::ExtractSlice( axis, plane ) ); Self::SmartPtr sliceVolume( new Self( sliceGrid->m_Dims, this->m_Delta[0], this->m_Delta[1], this->m_Delta[2], sliceGrid->m_Data ) ); sliceVolume->m_Offset = this->m_Offset; sliceVolume->m_Offset[axis] += plane * this->m_Delta[axis]; return sliceVolume; } ScalarImage::SmartPtr UniformVolume::GetNearestOrthoSlice ( const int axis, const Types::Coordinate location ) const { return this->GetOrthoSlice( axis, this->GetCoordIndex( axis, location ) ); } ScalarImage::SmartPtr UniformVolume::GetOrthoSliceInterp ( const int axis, const Types::Coordinate location ) const { Types::GridIndexType baseSliceIndex = this->GetCoordIndex( axis, location ); const Types::Coordinate baseSliceLocation = this->GetPlaneCoord( axis, baseSliceIndex ); const Types::Coordinate nextSliceLocation = this->GetPlaneCoord( axis, baseSliceIndex+1 ); // only bother to interpolate if we're more than 1% towards the next slice. if ( ((location - baseSliceLocation) / (nextSliceLocation-baseSliceLocation )) < 0.01 ) return this->GetOrthoSlice( axis, baseSliceIndex ); // only bother to interpolate if we're more than 1% towards the next slice. if ( ((nextSliceLocation - location) / (nextSliceLocation-baseSliceLocation )) < 0.01 ) return this->GetOrthoSlice( axis, baseSliceIndex+1 ); ScalarImage::SmartPtr image0 = this->GetOrthoSlice( axis, baseSliceIndex ); ScalarImage::SmartPtr image1 = this->GetOrthoSlice( axis, baseSliceIndex+1 ); TypedArray::SmartPtr data0 = image0->GetPixelData(); TypedArray::SmartPtr data1 = image1->GetPixelData(); Types::Coordinate weight0 = (nextSliceLocation - location) / (nextSliceLocation - baseSliceLocation ); Types::DataItem value0, value1; for ( Types::GridIndexType idx = 0; idx < data0->GetDataSize(); ++idx ) { if ( data0->Get( value0, idx ) && data1->Get( value1, idx ) ) { data0->Set( weight0 * value0 + (1-weight0) * value1, idx ); } else { data0->SetPaddingAt( idx ); } } image0->SetImageSlicePosition( location ); image0->SetImageOrigin( weight0 * image0->GetImageOrigin() + (1-weight0) * image1->GetImageOrigin() ); return image0; } void UniformVolume ::GetPrincipalAxes( Matrix3x3& directions, Self::CoordinateVectorType& centerOfMass ) const { centerOfMass = this->GetCenterOfMass(); const Types::Coordinate xg = centerOfMass[0]; const Types::Coordinate yg = centerOfMass[1]; const Types::Coordinate zg = centerOfMass[2]; Types::DataItem ixx = 0, iyy = 0, izz = 0, ixy = 0, iyz = 0, izx = 0; for ( Types::GridIndexType k = 0; k < this->m_Dims[2]; ++k ) { const Types::Coordinate Dz = this->GetPlaneCoord( AXIS_Z, k ) - zg; const Types::Coordinate Dz2 = Dz * Dz; for ( Types::GridIndexType j = 0; j < this->m_Dims[1]; ++j ) { const Types::Coordinate Dy = this->GetPlaneCoord( AXIS_Y, j ) - yg; const Types::Coordinate Dy2 = Dy * Dy; for ( Types::GridIndexType i = 0; i < this->m_Dims[0]; ++i ) { const Types::Coordinate Dx = this->GetPlaneCoord( AXIS_X, i ) - xg; const Types::Coordinate Dx2 = Dx * Dx; Types::DataItem v; if ( this->GetDataAt( v, i, j, k ) ) { ixx += v * ( Dy2 + Dz2 ); iyy += v * ( Dz2 + Dx2 ); izz += v * ( Dx2 + Dy2 ); ixy += v * Dx * Dy; iyz += v * Dy * Dz; izx += v * Dz * Dx; } } } } Matrix3x3 inertiaMatrix; inertiaMatrix[0][0] = ixx; inertiaMatrix[0][1] = -ixy; inertiaMatrix[0][2] = -izx; inertiaMatrix[1][0] = -ixy; inertiaMatrix[1][1] = iyy; inertiaMatrix[1][2] = -iyz; inertiaMatrix[2][0] = -izx; inertiaMatrix[2][1] = -iyz; inertiaMatrix[2][2] = izz; const EigenSystemSymmetricMatrix3x3 eigensystem( inertiaMatrix ); for ( int n = 0; n < 3; ++n ) { const Self::CoordinateVectorType v = eigensystem.GetNthEigenvector( n ); for ( int i = 0; i < 3; ++i ) { directions[n][i] = v[i]; } } // correct for negative determinant const Types::Coordinate det = directions.Determinant(); for ( int i = 0; i < 3; i++ ) { directions[2][i] *= det; } for ( int i = 0; i < 3; i++ ) { const Types::Coordinate norm = sqrt( directions[i][0]*directions[i][0] + directions[i][1]*directions[i][1] + directions[i][2]*directions[i][2] ); for ( int j = 0; j < 3; j++ ) { directions[i][j] /= norm; } } } } // namespace cmtk cmtk-3.3.1/libs/Base/cmtkUniformVolume.h000066400000000000000000000650211276303427400201320ustar00rootroot00000000000000/* // // Copyright 2016 Google, Inc. // // Copyright 1997-2010 Torsten Rohlfing // // Copyright 2004-2013 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 5397 $ // // $LastChangedDate: 2016-01-13 22:39:01 -0800 (Wed, 13 Jan 2016) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkUniformVolume_h_included_ #define __cmtkUniformVolume_h_included_ #include #include #include #include #include namespace cmtk { /** \addtogroup Base */ //@{ /// Forward declaration. class VolumeGridToGridLookup; /** Uniform volume. * This class handles 3D isotropic data of any scalar type. */ class UniformVolume : /// Inherit from generic Volume class. public Volume { public: /// This class. typedef UniformVolume Self; /// Superclass. typedef Volume Superclass; /// Smart pointer to UniformVolume. typedef SmartPointer SmartPtr; /// Smart pointer to const UniformVolume. typedef SmartConstPointer SmartConstPtr; /// Region type. typedef Superclass:: CoordinateRegionType CoordinateRegionType; /// Index type. typedef Superclass::CoordinateVectorType CoordinateVectorType; /// Grid spacing for three dimensions CoordinateVectorType m_Delta; /// Get pixel size vector. const CoordinateVectorType& Deltas() const { return this->m_Delta; } /// Get pixel size vector. CoordinateVectorType& Deltas() { return this->m_Delta; } /// Destructor. virtual ~UniformVolume() {} /** Copy constructor. *\param other Original volume that will be copied. */ UniformVolume( const UniformVolume& other ); /** Create uniform volume "from scratch". *\param dims Number of grid elements for the three spatial dimensions. *\param size Size of the volume in real-world coordinates. *\param data An existing TypedArray containing the scalar voxel data. */ UniformVolume( const DataGrid::IndexType& dims, const Self::CoordinateVectorType& size, TypedArray::SmartPtr& data = TypedArray::SmartPtr::Null() ); /** Create uniform volume "from scratch". *\param dims Number of grid elements for the three spatial dimensions. *\param deltaX Pixel size in x direction. *\param deltaY Pixel size in y direction. *\param deltaZ Pixel size in z direction. *\param data An existing TypedArray containing the scalar voxel data. */ UniformVolume( const DataGrid::IndexType& dims, const Types::Coordinate deltaX, const Types::Coordinate deltaY, const Types::Coordinate deltaZ, TypedArray::SmartPtr& data = TypedArray::SmartPtr::Null() ); /// Test whether this grid matches another one, i.e., has the same pixel sizes. bool GridMatches( const Self& other ) const { return Superclass::GridMatches( other ) && ((this->m_Delta-other.m_Delta).MaxAbsValue() < 1e-5) && ((this->m_Offset-other.m_Offset).MaxAbsValue() < 1e-5); } /** Resample volume to given resolution. *\param resolution Resolution of the newly created volume in world units. *\param allowUpsampling If true, then local upsampling is allowed in regions * where the original image resolution (non-uniform) was coarser than the * given resolution of the resampled volume. *\return Newly created, resampled volume. */ virtual UniformVolume* GetResampled( const Types::Coordinate resolution, const bool allowUpsampling = false ) const; /** Resample volume to an exact given resolution. * Unlike GetResampled(), this function forces the pixel size of the resampled image to be exactly the given "resolution." Grid dimensions and volume size are adjusted accordingly. *\param resolution Resolution of the newly created volume in world units. *\return Newly created, resampled volume. */ virtual UniformVolume* GetResampledExact( const Types::Coordinate resolution ) const; /** Coordinate transformation from index to physical position. * This incorporates image axis directions and first pixel offset. *\note Strictly, this is not a transformation from the pixel index * to physical coordinates, but from pixel index times pixel size to * physical coordinates. This way, the transformation maps from physical * space back into physical space. */ AffineXform::MatrixType m_IndexToPhysicalMatrix; /** Some images (notably those read from NIFTI files) may have several alternative transformations to different spaces (e.g., physical and atlas spaces). * The map key is an integer that identifies the type of transformation by some coding system (e.g., we use positive values for NIFTI qforms, negative ones for sforms). */ std::map m_AlternativeIndexToPhysicalMatrices; /** Change volume coordinate space. * Re-arrange volume's direction vectors to refer to a different coordinate space. *\invariant This should keep the result of GetOrientationFromDirections invariant. */ virtual void ChangeCoordinateSpace( const std::string& newSpace ); /** Get anatomical orientation from space directions. */ std::string GetOrientationFromDirections() const; /** Create a default index-to-physical transformation matrix. * The new matrix is the identity matrix with diagonal elements scaled according to pixel size. */ virtual void CreateDefaultIndexToPhysicalMatrix(); /** Get matrix that maps from current image coordinates to physical space. * The returned matrix is computed by removing pixel size from the one stored * in m_IndexToPhysicalMatrix. */ virtual AffineXform::MatrixType GetImageToPhysicalMatrix() const; /** Set matrix that maps form image coordinates to physical space. * The given matrix is converted to internal grid-to-physical representation, * i.e., its columns are multiplied with the pixel sizes. */ virtual void SetImageToPhysicalMatrix( const AffineXform::MatrixType& matrix /*!< Existing image-to-physical matrix */ ); /** Create a physical copy of this object. *\param copyData If true, the associated data array is also copied. */ Self::SmartPtr Clone( const bool copyData ) { return Self::SmartPtr( this->CloneVirtual( copyData ) ); } Self::SmartPtr Clone() const { return Self::SmartPtr( this->CloneVirtual() ); } /// Create igsUniformObject with identical geometry but no data. Self::SmartPtr CloneGrid() const { return Self::SmartPtr( this->CloneGridVirtual() ); } /// Get delta (pixel size) in one dimension. virtual Types::Coordinate GetDelta( const int axis ) const { return this->m_Delta[axis]; } /** Get minimum grid spacing for all dimensions. * For each dimension, the minimum delta is retrieved by a call to the * derived class's corresponding method. */ virtual Types::Coordinate GetMinDelta () const { return this->m_Delta.MinValue(); } /** Get maximum grid spacing for all dimensions. * For each dimension, the maximum delta is retrieved by a call to the * derived class's corresponding method. */ virtual Types::Coordinate GetMaxDelta () const { return this->m_Delta.MaxValue(); } /** Resample other volume. * Resampling is done by a sliding window algorithm combining both * interpolation and averaging, even within one volume. *\return Pointer to a newly created TypedArray object holding the * resampled volume data. NULL is returned if sufficient memory is not * available. */ virtual TypedArray::SmartPtr Resample ( const UniformVolume& ) const; /// Get volume reoriented to a different anatomical axis alignment. const UniformVolume::SmartPtr GetReoriented ( const char* newOrientation = AnatomicalOrientation::ORIENTATION_STANDARD ) const; /** Downsampling and pixel averaging constructor function. *\param downsample Downsampling factor. *\param approxIsotropic If this is set (default: off), then the downsample * factors per dimension are adjusted so that the resulting output volume * is as close to isotropic as possible without interpolation. */ virtual UniformVolume* GetDownsampledAndAveraged( const Types::GridIndexType downsample, const bool approxIsotropic = false ) const; /** Downsampling and pixel averaging constructor function. *\param downsample Array of per-dimension downsampling factors. */ virtual UniformVolume* GetDownsampledAndAveraged( const Types::GridIndexType (&downsample)[3] ) const; /** Downsampling constructor function. *\param downsample Downsampling factor. *\param approxIsotropic If this is set (default: off), then the downsample * factors per dimension are adjusted so that the resulting output volume * is as close to isotropic as possible without interpolation. */ virtual UniformVolume* GetDownsampled( const Types::GridIndexType downsample, const bool approxIsotropic = false ) const; /** Downsampling constructor function. *\param downsample Array of per-dimension downsampling factors. */ virtual UniformVolume* GetDownsampled( const Types::GridIndexType (&downsample)[3] ) const; /** Get interleaved sub-volume along given axis and with given interleave offset. *\param axis Coordinate axis along which the image is interleaved. *\param factor Interleave factor, i.e., the number of interleaved sub-volumes. *\param idx Index of interleaved sub-volume to extract. */ UniformVolume* GetInterleavedSubVolume( const int axis, const Types::GridIndexType factor, const Types::GridIndexType idx ) const; /** Get interleaved sub-volume along given axis and with given interleave offset, padded with empty image planes. *\param axis Coordinate axis along which the image is interleaved. *\param factor Interleave factor, i.e., the number of interleaved sub-volumes. *\param idx Index of interleaved sub-volume to extract. */ UniformVolume* GetInterleavedPaddedSubVolume( const int axis, const Types::GridIndexType factor, const Types::GridIndexType idx ) const; /// Mirror volume and associated data. virtual void Mirror ( const int axis = AXIS_X /*!< Mirror with respect to this coordinate axis.*/); /** Return orthogonal slice. * This function calls its equivalent in DataGrid and adds calibration * info (i.e., correct pixel sizes) to the resulting image. *\note The pixel size if taken from the size of the first grid element along * each axis -- non-uniform spacings will lead to incorrect results. */ virtual ScalarImage::SmartPtr GetOrthoSlice( const int axis, const Types::GridIndexType plane ) const; /** Extract orthogonal slice as a new volume. */ Self::SmartPtr ExtractSlice( const int axis, const Types::GridIndexType plane ) const; /** Return interpolated orthogonal slice. * This function calls its non-interpolating counterpart twice and performs * a 1-D interpolation on the results to form the interpolated slice. *\note The pixel size if taken from the size of the first grid element along * each axis -- non-uniform spacings will lead to incorrect results. */ virtual ScalarImage::SmartPtr GetOrthoSliceInterp( const int axis, const Types::Coordinate location ) const; /** Return orthogonal slice by location. * This function looks up a given orthogonal slice location and returns the * nearest slice from this volume. *\note The pixel size if taken from the size of the first grid element along * each axis -- non-uniform spacings will lead to incorrect results. */ virtual ScalarImage::SmartPtr GetNearestOrthoSlice( const int axis, const Types::Coordinate location ) const; /** Get date gradient vector at pixel using central differences. * This function cannot be called for pixels on the volume boundaries, i.e., * we require that 0 < i,j,k < [Dims[0]-1,Dims[1]-1,Dims[2]-1]. * *\warning No parameter range checking is currently performed! */ virtual const Self::CoordinateVectorType GetGradientAt( const Types::GridIndexType i, const Types::GridIndexType j, const Types::GridIndexType k ); /** Get data Hessian matrix at pixel using central differences. * This function cannot be called for pixels within a two-pixel distance from the * volume boundary, i.e., we require that 1 < i,j,k < [Dims[0]-2,Dims[1]-2,Dims[2]-2]. * *\warning No parameter range checking is currently performed! */ virtual Matrix3x3 GetHessianAt( const Types::GridIndexType i, const Types::GridIndexType j, const Types::GridIndexType k ); /// Get data value at specified coordinate. template inline bool ProbeData( TData& result, const TData* dataPtr, const Self::CoordinateVectorType& location ) const; /// Return linearly interpolated voxel without applying a transformation. inline bool ProbeNoXform ( ProbeInfo&, const Self::CoordinateVectorType& ) const; /** Find a voxel in the volume. *\param location Real-world coordinates of the location that is to be * found. *\param idx This array is used to store the index components of the model * voxel containing the given location. Values range from 0 to * ModelDims[dim-1]. *\param from Real-world coordinate of the voxel's lower left corner. *\param to Real-world coordinate of the voxel's upper right corner. *\return A non-zero value is returned if and only if the given location * lies within the model volume. If zero is returned, the location is outside * and all other output values (see parameters) are invalid. */ inline bool FindVoxel( const Self::CoordinateVectorType& location, Types::GridIndexType *const idx, Types::Coordinate *const from, Types::Coordinate *const to ) const; /** Find a voxel in the volume. *\param location Real-world coordinates of the location that is to be * found. *\param idx This array is used to store the index components of the model * voxel containing the given location. Values range from 0 to * ModelDims[dim-1]. *\return A non-zero value is returned if and only if the given location * lies within the model volume. If zero is returned, the location is outside * and all other output values (see parameters) are invalid. */ inline bool FindVoxel( const Self::CoordinateVectorType& location, Types::GridIndexType *const idx ) const; /** Find a grid index inside or outside the volume. *\param location Real-world coordinates of the location that is to be * found. *\param idx This array is used to store the index components of the model * voxel containing the given location. Values range from 0 to * ModelDims[dim-1]. */ inline void GetVoxelIndexNoBounds( const Self::CoordinateVectorType& location, Types::GridIndexType *const idx ) const; /** Find a voxel in the volume by fractional index. *\param fracIndex Fractional 3D voxel index. *\param idx Output: integer 3D voxel index. *\param frac Output: fractional within-voxel location. *\return True value is returned if and only if the given location lies * within the model volume. If false is returned, the location is outside * and all other output values (see parameters) are invalid. */ inline bool FindVoxelByIndex( const Self::CoordinateVectorType& fracIndex, Types::GridIndexType *const idx, Types::Coordinate *const frac ) const; /// Get 3D grid region from continuous lower and upper corner. const Self::RegionType GetGridRange( const Self::CoordinateRegionType& region /*!< The coordinate region*/ ) const; /// Get plane coordinate. virtual Types::Coordinate GetPlaneCoord( const int axis, const Types::GridIndexType plane ) const { return this->m_Offset[axis] + plane * this->m_Delta[axis]; } /** Get grid index of slice with highest coordinate smaller than given. * @param axis The coordinate axis that the location parameters refers to. * @param location The location along the selected coordinate axis in the range from 0 to Size[axis]. */ virtual Types::GridIndexType GetCoordIndex( const int axis, const Types::Coordinate location ) const { return std::max< Types::GridIndexType>( 0, std::min< Types::GridIndexType>( (Types::GridIndexType) ((location-this->m_Offset[axis]) / this->m_Delta[axis]), this->m_Dims[axis]-1 ) ); } /** Get grid index corresponding (as close as possible) to coordinate. * @param axis The coordinate axis that the location parameters refers to. * @param location The location along the selected coordinate axis in the range from 0 to Size[axis]. */ virtual Types::GridIndexType GetClosestCoordIndex( const int axis, const Types::Coordinate location ) const { const Types::GridIndexType idx = (Types::GridIndexType)MathUtil::Round((location-this->m_Offset[axis]) / this->m_Delta[axis]); return std::max( 0, std::min( idx, this->m_Dims[axis]-1 ) ); } /** Get grid index corresponding (as close as possible) to coordinate. *\return True if given point is inside image, false if outside. */ virtual bool GetClosestGridPointIndex( const Self::CoordinateVectorType v, Self::IndexType& idx ) const { for ( int dim = 0; dim < 3; ++dim ) { idx[dim] = static_cast( MathUtil::Round((v[dim]-this->m_Offset[dim]) / this->m_Delta[dim]) ); if ( (idx[dim] < 0) || ( idx[dim] > this->m_Dims[dim]-1) ) return false; } return true; } /** Get grid index corresponding to coordinate by truncation, not rounding. * @param axis The coordinate dimension that the location parameters refers to. * @param location The location in the range from 0 to Size[axis]. */ virtual Types::GridIndexType GetTruncCoordIndex( const int axis, const Types::Coordinate location ) const { const Types::GridIndexType idx = static_cast((location-this->m_Offset[axis]) / this->m_Delta[axis]); return std::max( 0, std::min( idx, this->m_Dims[axis]-1 ) ); } /** Get grid index corresponding to coordinate by truncation, not rounding. *\return True if given point is inside image, false if outside. */ virtual bool GetTruncGridPointIndex( const Self::CoordinateVectorType v /*!< Location to find in the grid. */, Self::IndexType& idx /*!< Truncated grid point index (i.e., nearest grid point between location and grid coordinate origin. */ ) const { for ( int dim = 0; dim < 3; ++dim ) { idx[dim] = static_cast((v[dim]-this->m_Offset[dim]) / this->m_Delta[dim]); if ( (idx[dim] < 0) || (idx[dim] > this->m_Dims[dim]-1) ) return false; } return true; } /** Get a grid location in image coordinates. *\param x,y,z The indices of the intended grid element with respect to the * three coordinate axes. Valid range is from 0 to Dims[...]-1. *\return The location in image coordinates of the given grid element as a Self::CoordinateVectorType. */ virtual const Self::CoordinateVectorType GetGridLocation( const Types::GridIndexType x, const Types::GridIndexType y, const Types::GridIndexType z ) const { const Types::Coordinate loc[3] = { this->m_Offset[0] + x * this->m_Delta[0], this->m_Offset[1] + y * this->m_Delta[1], this->m_Offset[2] + z * this->m_Delta[2] }; return Self::CoordinateVectorType::FromPointer( loc ); } /** Get a grid location in image coordinates. *\return The location in image coordinates of the given grid element as a Self::CoordinateVectorType. */ virtual const Self::CoordinateVectorType GetGridLocation( const Self::CoordinateVectorType& xyz /*!< Grid index (possibly fractional) */ ) const { return this->m_Offset + ComponentMultiply( xyz, this->m_Delta ); } /** Get a grid location in physical coordinates. *\param idxV The index of the intended grid element with respect to the * three coordinate axes. Valid range is from 0 to Dims[...]-1. Fractional coordinates are permitted. *\return The location in image coordinates of the given grid element as a Self::CoordinateVectorType. */ virtual const Self::CoordinateVectorType IndexToPhysical( const Self::CoordinateVectorType& idxV ) const { return idxV * this->m_IndexToPhysicalMatrix; } /** Get a grid index (fractional) corresponding to given physical coordinates. *\return The grid index location coresponding to physical location. */ virtual const Self::CoordinateVectorType PhysicalToIndex( const Self::CoordinateVectorType& physical ) const { return physical * this->m_IndexToPhysicalMatrix.GetInverse(); } /** Get a grid coordinate by continuous pixel index. * This function directly calculates the grid location from the volume's * grid deltas. *\param idx The index of the intended grid element. * Valid range is from 0 to (Dims[0]*Dims[1]*Dims[2])-1. *\return The location of the given grid element as a Self::CoordinateVectorType. */ virtual const Self::CoordinateVectorType GetGridLocation( const Types::GridIndexType idx ) const { const Types::Coordinate loc[3] = { this->m_Offset[0] + (idx % this->nextJ) * this->m_Delta[0], this->m_Offset[1] + (idx % this->nextK) / this->nextJ * this->m_Delta[1], this->m_Offset[2] + (idx / this->nextK) * this->m_Delta[2] }; return Self::CoordinateVectorType::FromPointer( loc ); } //@} /** Set cropping region in real-world coordinates. */ void SetHighResCropRegion( const Self::CoordinateRegionType& crop ); /** Get cropped volume in real-world coordinates. */ const Self::CoordinateRegionType GetHighResCropRegion() const; /// Catch calls to inherited SetCropRegion() and reset high-res crop region. virtual void SetCropRegion( const Self::RegionType& region ) { this->m_HighResCropRegion = Self::CoordinateRegionType::SmartPtr( NULL ); Superclass::SetCropRegion( region ); } /** Calculate volume center. *\return Returned is the center of the bounding box. */ Self::CoordinateVectorType GetCenterCropRegion() const { const Self::CoordinateRegionType region = this->GetHighResCropRegion(); return 0.5 * ( region.From() + region.To() ); } /** Return cropped uniform volume with currently set crop region. */ Self::SmartPtr GetCroppedVolume() const; /** Return cropped uniform volume with explicit crop region. */ Self::SmartPtr GetCroppedVolume( const Self::RegionType& region ) const; /// Get center of mass of pixel data. virtual Self::CoordinateVectorType GetCenterOfMass() const { Self::CoordinateVectorType com = this->Superclass::GetCenterOfMassGrid(); for ( int dim = 0; dim < 3; ++dim ) (com[dim] *= this->m_Delta[dim]) += this->m_Offset[dim]; return com; } /// Get center of mass of pixel data. virtual Self::CoordinateVectorType GetCenterOfMass( Self::CoordinateVectorType& firstOrderMoment ) const { Self::CoordinateVectorType com = this->Superclass::GetCenterOfMassGrid( firstOrderMoment ); for ( int dim = 0; dim < 3; ++dim ) { (com[dim] *= this->m_Delta[dim]) += this->m_Offset[dim]; firstOrderMoment[dim] *= this->m_Delta[dim]; } return com; } /** Get principal axes. *\return This function returns two types of information: the principal directions (axes) of the * image data, and the relative scales. The principal axes are returned as a 3x3 array, such that * directions[0] is the 3D array of the major principal direction, i.e. the one with the largest * norm. Then, directions[0][1] is the y-component of that vector, etc. Accordingly, directions[1] * and directions[2] are the two remaining principal directions, with the norm of directions[1] * larger than, or equal to, the norm of directions[2]. */ void GetPrincipalAxes( Matrix3x3& directions, Self::CoordinateVectorType& centerOfMass ) const; protected: /** Create a physical copy of this object. *\param copyData If true, the associated data array is also copied. */ virtual Self* CloneVirtual( const bool copyData ); virtual Self* CloneVirtual() const; /// Virtual grid cloning constructor. virtual Self* CloneGridVirtual() const; private: /** Optional high-resolution crop region. * If this is unset (i.e., a NULL pointer), then calls to GetHighResCropRegion() will simply convert the grid-based crop region. * *\note The crop region includes the volume offset, m_Offset. Its upper limit can, therefore, be larger * than m_Size, which does NOT include m_Offset. */ Self::CoordinateRegionType::SmartPtr m_HighResCropRegion; /** Friend declaration of WarpXform class. * This allows direct access to Dims and Delta fields in RegisterVolumePoints * member function of WarpXform. */ friend class WarpXform; /// Make axes hash a friend. friend class VolumeAxesHash; /// Grid grid-to-grid lookup friend. friend class VolumeGridToGridLookup; class ResampleThreadInfo; /// Give thread parameter class access to local types. friend class ResampleThreadInfo; /** Thread parameter block for volume resampling. * This structure holds all thread-specific information. A pointer to an * instance of this structure is given to EvaluateGradientThread() for * each thread created. */ class ResampleTaskInfo : public ThreadParameters { public: /// Array that takes up resamples data. Types::DataItem *ResampledData; /// Lookup for grid-to-grid cell translation. const VolumeGridToGridLookup *GridLookup; /// The other volume that has the original data. const Self* OtherVolume; /// The other volume that has the original data. const TypedArray* FromData; }; /// Multi-threaded resampling for grey data. static void ResampleThreadPoolExecuteGrey( void *const arg, const size_t taskIdx, const size_t taskCnt, const size_t, const size_t ); /// Multi-threaded resampling for label data (using partial volume averaging). static void ResampleThreadPoolExecuteLabels( void *const arg, const size_t taskIdx, const size_t taskCnt, const size_t, const size_t ); }; //@} } // namespace cmtk #include "cmtkUniformVolume.txx" #endif // #ifndef __cmtkUniformVolume_h_included_ cmtk-3.3.1/libs/Base/cmtkUniformVolume.txx000066400000000000000000000120171276303427400205230ustar00rootroot00000000000000/* // // Copyright 2016 Google, Inc. // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2012 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 5397 $ // // $LastChangedDate: 2016-01-13 22:39:01 -0800 (Wed, 13 Jan 2016) $ // // $LastChangedBy: torstenrohlfing $ // */ #include namespace cmtk { /** \addtogroup Base */ //@{ template inline bool UniformVolume::ProbeData ( TData& result, const TData* dataPtr, const Self::CoordinateVectorType& location ) const { result=0; Self::CoordinateVectorType l( location ); l -= this->m_Offset; if ( (l[0] < 0) || (l[1] < 0) || (l[2] < 0) ) return false; const Types::GridIndexType idxX=(Types::GridIndexType) (l[0]/this->m_Delta[0]); if ( idxX>=this->m_Dims[0]-1 ) return false; const Types::GridIndexType idxY=(Types::GridIndexType) (l[1]/this->m_Delta[1]); if ( idxY>=this->m_Dims[1]-1 ) return false; const Types::GridIndexType idxZ=(Types::GridIndexType) (l[2]/this->m_Delta[2]); if ( idxZ>=this->m_Dims[2]-1 ) return false; const Types::Coordinate from[3] = { idxX*this->m_Delta[0], idxY*this->m_Delta[1], idxZ*this->m_Delta[2] }; const Types::Coordinate to[3] = { from[0]+this->m_Delta[0], from[1]+this->m_Delta[1], from[2]+this->m_Delta[2] }; result = this->TrilinearInterpolation( dataPtr, idxX, idxY, idxZ, l, from, to ); return true; } inline bool UniformVolume::ProbeNoXform ( ProbeInfo& probeInfo, const Self::CoordinateVectorType& location ) const { Self::CoordinateVectorType l( location ); l -= this->m_Offset; if ( (l[0] < 0) || (l[1] < 0) || (l[2] < 0) ) return false; const Types::GridIndexType idxX=(Types::GridIndexType) (l[0]/this->m_Delta[0]); if ( idxX>=this->m_Dims[0]-1 ) return false; const Types::GridIndexType idxY=(Types::GridIndexType) (l[1]/this->m_Delta[1]); if ( idxY>=this->m_Dims[1]-1 ) return false; const Types::GridIndexType idxZ=(Types::GridIndexType) (l[2]/this->m_Delta[2]); if ( idxZ>=this->m_Dims[2]-1 ) return false; const Types::Coordinate from[3] = { idxX*this->m_Delta[0], idxY*this->m_Delta[1], idxZ*this->m_Delta[2] }; const Types::Coordinate to[3] = { from[0]+this->m_Delta[0], from[1]+this->m_Delta[1], from[2]+this->m_Delta[2] }; return this->GetTrilinear( probeInfo, idxX, idxY, idxZ, l, from, to ); } inline bool UniformVolume::FindVoxel ( const Self::CoordinateVectorType& location, Types::GridIndexType *const idx, Types::Coordinate *const from, Types::Coordinate *const to ) const { Self::CoordinateVectorType l( location ); l -= this->m_Offset; if ( (l[0] < 0) || (l[1] < 0) || (l[2] < 0) ) return false; for ( int dim = 0; dim < 3; ++dim ) { idx[dim] = static_cast( l[dim] / this->m_Delta[dim] ); if ( idx[dim]>=(this->m_Dims[dim]-1) ) return false; (to[dim] = (from[dim] = this->m_Offset[dim] + (idx[dim] * this->m_Delta[dim]))) += this->m_Delta[dim]; } return true; } inline bool UniformVolume::FindVoxel ( const Self::CoordinateVectorType& location, Types::GridIndexType *const idx ) const { Self::CoordinateVectorType l( location ); l -= this->m_Offset; if ( (l[0] < 0) || (l[1] < 0) || (l[2] < 0) ) return false; for ( int dim = 0; dim < 3; ++dim ) { idx[dim] = static_cast( l[dim] / this->m_Delta[dim] ); if ( idx[dim]>=(this->m_Dims[dim]-1) ) return false; } return true; } inline void UniformVolume::GetVoxelIndexNoBounds ( const Self::CoordinateVectorType& location, Types::GridIndexType *const idx ) const { for ( int dim = 0; dim < 3; ++dim ) { idx[dim] = static_cast( floor( (location[dim]-this->m_Offset[dim]) / this->m_Delta[dim]) ); } } inline bool UniformVolume::FindVoxelByIndex ( const Self::CoordinateVectorType& fracIndex, Types::GridIndexType *const idx, Types::Coordinate *const frac ) const { if ( (fracIndex[0]<0) || (fracIndex[1]<0) || (fracIndex[2]<0) ) return false; for ( int dim = 0; dim < 3; ++dim ) { idx[dim] = static_cast( fracIndex[dim] ); if ( (idx[dim] >= (this->m_Dims[dim]-1)) ) return false; frac[dim] = fracIndex[dim] - idx[dim]; } return true; } } // namespace cmtk cmtk-3.3.1/libs/Base/cmtkUniformVolumeGaussianFilter.cxx000066400000000000000000000051151276303427400233440ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2010 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4111 $ // // $LastChangedDate: 2012-03-30 14:30:21 -0700 (Fri, 30 Mar 2012) $ // // $LastChangedBy: torstenrohlfing $ // */ #include "cmtkUniformVolumeGaussianFilter.h" #include #include #include namespace cmtk { /** \addtogroup Base */ //@{ TypedArray::SmartPtr UniformVolumeGaussianFilter::GetFiltered3D( const Units::GaussianSigma& sigma, const Types::Coordinate maxError ) const { return DataGridFilter::GetDataKernelFiltered( GaussianKernel::GetHalfKernel( sigma / this->m_UniformVolume->Deltas()[0], maxError ), GaussianKernel::GetHalfKernel( sigma / this->m_UniformVolume->Deltas()[1], maxError ), GaussianKernel::GetHalfKernel( sigma / this->m_UniformVolume->Deltas()[2], maxError ) ); } TypedArray::SmartPtr UniformVolumeGaussianFilter::GetFiltered1D( const int direction, const Units::GaussianSigma& sigma, const Types::Coordinate maxError ) const { const std::vector empty( 1, 1.0 ); switch ( direction ) { default: case 0: return DataGridFilter::GetDataKernelFiltered( GaussianKernel::GetHalfKernel( sigma / this->m_UniformVolume->Deltas()[0], maxError ), empty, empty ); case 1: return DataGridFilter::GetDataKernelFiltered( empty, GaussianKernel::GetHalfKernel( sigma / this->m_UniformVolume->Deltas()[1], maxError ), empty ); case 2: return DataGridFilter::GetDataKernelFiltered( empty, empty, GaussianKernel::GetHalfKernel( sigma / this->m_UniformVolume->Deltas()[2], maxError ) ); } } } // namespace cmtk cmtk-3.3.1/libs/Base/cmtkUniformVolumeGaussianFilter.h000066400000000000000000000052271276303427400227750ustar00rootroot00000000000000/* // // Copyright 2004-2012 SRI International // // Copyright 1997-2009 Torsten Rohlfing // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4111 $ // // $LastChangedDate: 2012-03-30 14:30:21 -0700 (Fri, 30 Mar 2012) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkUniformVolumeGaussianFilter_h_included_ #define __cmtkUniformVolumeGaussianFilter_h_included_ #include #include #include #include namespace cmtk { /** \addtogroup Base */ //@{ /** Filter operations for 3D uniform image data. */ class UniformVolumeGaussianFilter : /// Prevent copying by inheritance. private DataGridFilter { public: /// This class. typedef UniformVolumeGaussianFilter Self; /// Constructor: link to UniformVolume object. explicit UniformVolumeGaussianFilter( UniformVolume::SmartConstPtr& volume ) : DataGridFilter( volume ), m_UniformVolume( volume ) {} /// Gaussian filter (using faster, separable filtering). TypedArray::SmartPtr GetFiltered3D( const Units::GaussianSigma& sigma, /*!< Kernel parameter "sigma" (standard deviation) */ const Types::Coordinate maxError = 0.01 /*!< Maximum approximation error: the kernel is truncated when it falls below this threshold */ ) const; /// Gaussian 1D filter. TypedArray::SmartPtr GetFiltered1D( const int direction /*!< Coordinate direction: 0 = x, 1 = y, 2 = z */, const Units::GaussianSigma& sigma, /*!< Kernel parameter "sigma" (standard deviation) */ const Types::Coordinate maxError = 0.01 /*!< Maximum approximation error: the kernel is truncated when it falls below this threshold */ ) const; private: /// The UniformVolume object we're working on. UniformVolume::SmartConstPtr m_UniformVolume; }; } // namespace cmtk #endif // #ifndef __cmtkUniformVolumeGaussianFilter_h_included_ cmtk-3.3.1/libs/Base/cmtkUniformVolumeHoughTransformSphere.cxx000066400000000000000000000063471276303427400245510ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2012 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4427 $ // // $LastChangedDate: 2012-06-12 11:23:22 -0700 (Tue, 12 Jun 2012) $ // // $LastChangedBy: torstenrohlfing $ // */ #include "cmtkUniformVolumeHoughTransformSphere.h" #include #include namespace cmtk { TypedArray::SmartPtr UniformVolumeHoughTransformSphere::Get( const Types::Coordinate radius ) const { const UniformVolume& volume = *(this->m_UniformVolume); TypedArray::SmartPtr result( TypedArray::Create( volume.GetData()->GetType(), volume.GetNumberOfPixels() ) ); const int dRadius[3] = { MathUtil::Round( radius / volume.m_Delta[0] ), MathUtil::Round( radius / volume.m_Delta[1] ), MathUtil::Round( radius / volume.m_Delta[2] ) }; RegionSphereSurfaceIterator sphereIterator( (DataGrid::IndexType::FromPointer( dRadius )) ); const DataGrid::RegionType wholeImageRegion = volume.GetWholeImageRegion(); const DataGrid::IndexType center = 0.5 * (wholeImageRegion.To() - wholeImageRegion.From()); for ( sphereIterator = sphereIterator.begin(); sphereIterator != sphereIterator.end(); ++sphereIterator ) { const DataGrid::IndexType pt = center+sphereIterator.Index(); result->Set( 1, volume.GetOffsetFromIndex( pt ) ); } return result; #ifndef _OPENMP const DataGrid::RegionType region = wholeImageRegion; #else // _OPENMP const int sliceFrom = wholeImageRegion.From()[2]; const int sliceTo = wholeImageRegion.To()[2]; #pragma omp parallel for for ( int slice = sliceFrom; slice < sliceTo; ++slice ) { DataGrid::RegionType region = wholeImageRegion; region.From()[2] = slice; region.To()[2] = slice+1; #endif // #ifdef _OPENMP for ( RegionIndexIterator it( region ); it != it.end(); ++it ) { const DataGrid::IndexType center = it.Index(); const size_t toOffset = volume.GetOffsetFromIndex( center ); for ( sphereIterator = sphereIterator.begin(); sphereIterator != sphereIterator.end(); ++sphereIterator ) { const DataGrid::IndexType pt = center+sphereIterator.Index(); if ( region.IsInside( pt ) ) { result->Set( result->ValueAt( toOffset ) + volume.GetDataAt( volume.GetOffsetFromIndex( pt ) ), toOffset ); } } } #ifdef _OPENMP } #endif // #ifdef _OPENMP return result; } } // namespace cmtk cmtk-3.3.1/libs/Base/cmtkUniformVolumeHoughTransformSphere.h000066400000000000000000000040241276303427400241640ustar00rootroot00000000000000/* // // Copyright 2004-2012 SRI International // // Copyright 1997-2009 Torsten Rohlfing // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4178 $ // // $LastChangedDate: 2012-04-12 15:04:59 -0700 (Thu, 12 Apr 2012) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkUniformVolumeHoughTransformSphere_h_included_ #define __cmtkUniformVolumeHoughTransformSphere_h_included_ #include #include #include namespace cmtk { /** \addtogroup Base */ //@{ /** Spherical Hough transform for 3D uniform image data. */ class UniformVolumeHoughTransformSphere { public: /// This class. typedef UniformVolumeHoughTransformSphere Self; /// Constructor: link to UniformVolume object. explicit UniformVolumeHoughTransformSphere( UniformVolume::SmartConstPtr& volume ) : m_UniformVolume( volume ) {} /// Get Hough-transformed data for a specific radius in world units. TypedArray::SmartPtr Get( const Types::Coordinate radius /*!< Radius of detected spheres */ ) const; private: /// The UniformVolume object we're working on. UniformVolume::SmartConstPtr m_UniformVolume; }; } // namespace cmtk #endif // #ifndef __cmtkUniformVolumeHoughTransformSphere_h_included_ cmtk-3.3.1/libs/Base/cmtkUniformVolumeInterpolator.h000066400000000000000000000063071276303427400225370ustar00rootroot00000000000000/* // // Copyright 2016 Google, Inc. // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2010 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 5399 $ // // $LastChangedDate: 2016-01-14 19:00:40 -0800 (Thu, 14 Jan 2016) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkUniformVolumeInterpolator_h_included_ #define __cmtkUniformVolumeInterpolator_h_included_ #include #include #include #include #include #include namespace cmtk { /** \addtogroup Base */ //@{ /** Class template for kernel-based volume interpolators. * * This class is templated over the interpolation function, e.g., linear, cubic, or sinc. * *\see LinearInterpolator *\see CubicInterpolator *\see SincInterpolator *\see NearestNeighborInterpolator */ template class UniformVolumeInterpolator : /// Inherit interface from base class. public UniformVolumeInterpolatorBase { public: /// This class type. typedef UniformVolumeInterpolator Self; /// Superclass type. typedef UniformVolumeInterpolatorBase Superclass; /// Smart pointer to this class. typedef SmartPointer SmartPtr; /// Constructor. UniformVolumeInterpolator( const UniformVolume& volume ) : UniformVolumeInterpolatorBase( volume ) { if ( !TInterpolationFunction::SuitableForLabels && (volume.GetData()->GetDataClass() == DATACLASS_LABEL ) ) { StdErr << "WARNING: using an unsuitable interpolator on label data\n"; } } /** Get data at location. * * This function performs interpolation of one value from m_Volume at location * v using the interpolation function given as the class template parameter. * * This function should return true if a value can be interpolated from * m_Volume at v, and it should return false if v is outside the range * where a value can be interpolated (i.e., outside the volume boundaries). */ virtual bool GetDataAt( const Vector3D& v, Types::DataItem& value ) const; virtual Types::DataItem GetDataDirect( const Types::GridIndexType* imageGridPoint, const Types::Coordinate* insidePixel ) const; }; //@} } // namespace cmtk #include "cmtkUniformVolumeInterpolator.txx" #endif // #ifndef __cmtkUniformVolumeInterpolator_h_included_ cmtk-3.3.1/libs/Base/cmtkUniformVolumeInterpolator.txx000066400000000000000000000150251276303427400231300ustar00rootroot00000000000000/* // // Copyright 2016 Google, Inc. // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2012 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 5399 $ // // $LastChangedDate: 2016-01-14 19:00:40 -0800 (Thu, 14 Jan 2016) $ // // $LastChangedBy: torstenrohlfing $ // */ namespace cmtk { /** \addtogroup Base */ //@{ template bool UniformVolumeInterpolator ::GetDataAt(const Vector3D& v, Types::DataItem& value) const { Types::Coordinate lScaled[3]; Types::GridIndexType imageGridPoint[3]; for ( int n = 0; n < 3; ++n ) { lScaled[n] = (v[n] - this->m_VolumeOffset[n]) / this->m_VolumeDeltas[n]; imageGridPoint[n] = (int) floor( lScaled[n] ); if ( ( imageGridPoint[n] < 0 ) || ( imageGridPoint[n] >= this->m_VolumeDims[n]-1 ) ) return false; } const Types::GridIndexType xx = imageGridPoint[0] + 1 - TInterpolationFunction::RegionSizeLeftRight; const Types::GridIndexType yy = imageGridPoint[1] + 1 - TInterpolationFunction::RegionSizeLeftRight; const Types::GridIndexType zz = imageGridPoint[2] + 1 - TInterpolationFunction::RegionSizeLeftRight; Types::Coordinate interpolationWeights[3][2 * TInterpolationFunction::RegionSizeLeftRight]; for ( int n = 0; n < 3; ++n ) { const Types::Coordinate relative = lScaled[n] - imageGridPoint[n]; for ( Types::GridIndexType m = 1-TInterpolationFunction::RegionSizeLeftRight; m <= TInterpolationFunction::RegionSizeLeftRight; ++m ) { interpolationWeights[n][m+TInterpolationFunction::RegionSizeLeftRight-1] = TInterpolationFunction::GetWeight( m, relative ); } } const Types::GridIndexType iMin = std::max( 0, -xx ); const Types::GridIndexType iMax = std::min( 2 * TInterpolationFunction::RegionSizeLeftRight, this->m_VolumeDims[0] - xx ); const Types::GridIndexType jMin = std::max( 0, -yy ); const Types::GridIndexType jMax = std::min( 2 * TInterpolationFunction::RegionSizeLeftRight, this->m_VolumeDims[1] - yy ); const Types::GridIndexType kMin = std::max( 0, -zz ); const Types::GridIndexType kMax = std::min( 2 * TInterpolationFunction::RegionSizeLeftRight, this->m_VolumeDims[2] - zz ); Types::DataItem interpolatedData = 0; Types::Coordinate totalWeight = 0; for ( Types::GridIndexType k = kMin; k < kMax; ++k ) { for ( Types::GridIndexType j = jMin; j < jMax; ++j ) { const Types::Coordinate weightJK = interpolationWeights[1][j] * interpolationWeights[2][k]; size_t offset = this->GetOffsetFromIndex( xx + iMin, yy + j, zz + k ); for ( Types::GridIndexType i = iMin; i < iMax; ++i, ++offset ) { const Types::DataItem data = this->m_VolumeDataArray[offset]; if ( finite( data ) ) { const Types::Coordinate weightIJK = interpolationWeights[0][i] * weightJK; interpolatedData += static_cast( data * weightIJK ); totalWeight += weightIJK; } } } } if ( totalWeight == 0 ) return false; else value = static_cast( interpolatedData / totalWeight ); return true; } template Types::DataItem UniformVolumeInterpolator ::GetDataDirect( const Types::GridIndexType* imageGridPoint, const Types::Coordinate* insidePixel ) const { Types::Coordinate interpolationWeights[3][2 * TInterpolationFunction::RegionSizeLeftRight]; for ( int n = 0; n < 3; ++n ) { for ( Types::GridIndexType m = 1-TInterpolationFunction::RegionSizeLeftRight; m <= TInterpolationFunction::RegionSizeLeftRight; ++m ) { interpolationWeights[n][m+TInterpolationFunction::RegionSizeLeftRight-1] = TInterpolationFunction::GetWeight( m, insidePixel[n] ); } } const Types::GridIndexType xx = imageGridPoint[0] + 1 - TInterpolationFunction::RegionSizeLeftRight; const Types::GridIndexType yy = imageGridPoint[1] + 1 - TInterpolationFunction::RegionSizeLeftRight; const Types::GridIndexType zz = imageGridPoint[2] + 1 - TInterpolationFunction::RegionSizeLeftRight; const Types::GridIndexType iMin = std::max( 0, -xx ); const Types::GridIndexType iMax = std::min( 2 * TInterpolationFunction::RegionSizeLeftRight, this->m_VolumeDims[0] - xx ); const Types::GridIndexType jMin = std::max( 0, -yy ); const Types::GridIndexType jMax = std::min( 2 * TInterpolationFunction::RegionSizeLeftRight, this->m_VolumeDims[1] - yy ); const Types::GridIndexType kMin = std::max( 0, -zz ); const Types::GridIndexType kMax = std::min( 2 * TInterpolationFunction::RegionSizeLeftRight, this->m_VolumeDims[2] - zz ); Types::DataItem interpolatedData = 0; Types::Coordinate totalWeight = 0; for ( Types::GridIndexType k = kMin; k < kMax; ++k ) { for ( Types::GridIndexType j = jMin; j < jMax; ++j ) { const Types::Coordinate weightJK = interpolationWeights[1][j] * interpolationWeights[2][k]; size_t offset = (xx + iMin) + (yy + j) * this->m_NextJ + (zz + k) * this->m_NextK; for ( Types::GridIndexType i = iMin; i < iMax; ++i, ++offset ) { const Types::DataItem data = this->m_VolumeDataArray[offset]; if ( finite( data ) ) { const Types::Coordinate weightIJK = interpolationWeights[0][i] * weightJK; interpolatedData += static_cast( data * weightIJK ); totalWeight += weightIJK; } } } } if ( totalWeight == 0 ) return 0; else return static_cast( interpolatedData / totalWeight ); } } // namespace cmtk cmtk-3.3.1/libs/Base/cmtkUniformVolumeInterpolatorBase.cxx000066400000000000000000000034711276303427400237040ustar00rootroot00000000000000/* // // Copyright 2016 Google, Inc. // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2010 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 5398 $ // // $LastChangedDate: 2016-01-14 18:29:18 -0800 (Thu, 14 Jan 2016) $ // // $LastChangedBy: torstenrohlfing $ // */ #include "cmtkUniformVolumeInterpolatorBase.h" #include void cmtk::UniformVolumeInterpolatorBase ::SetVolume( const UniformVolume& volume ) { const TypedArray& data = *(volume.GetData()); const size_t nPixels = data.GetDataSize(); this->m_VolumeDataArray.resize( nPixels ); for ( Types::GridIndexType n = 0; n < nPixels; ++n ) { if ( !data.Get( this->m_VolumeDataArray[n], n ) ) this->m_VolumeDataArray[n] = std::numeric_limits::infinity(); } this->m_VolumeDims = volume.GetDims(); this->m_VolumeDeltas = volume.Deltas(); this->m_VolumeOffset = volume.m_Offset; this->m_NextJ = this->m_VolumeDims[0]; this->m_NextK = this->m_NextJ * this->m_VolumeDims[1]; } cmtk-3.3.1/libs/Base/cmtkUniformVolumeInterpolatorBase.h000066400000000000000000000105431276303427400233270ustar00rootroot00000000000000/* // // Copyright 2016 Google, Inc. // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2012 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 5398 $ // // $LastChangedDate: 2016-01-14 18:29:18 -0800 (Thu, 14 Jan 2016) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkUniformVolumeInterpolatorBase_h_included_ #define __cmtkUniformVolumeInterpolatorBase_h_included_ #include #include #include #include #include #include #include namespace cmtk { /** \addtogroup Base */ //@{ /** Base class for kernel-based uniform volume. */ class UniformVolumeInterpolatorBase { public: /// This class type. typedef UniformVolumeInterpolatorBase Self; /// Smart pointer to this class. typedef SmartPointer SmartPtr; /// Smart pointer-to-const to this class. typedef SmartConstPointer SmartConstPtr; /// Constructor. UniformVolumeInterpolatorBase( const UniformVolume& volume ) { this->SetVolume( volume ); } /// Virtual dummy destructor. virtual ~UniformVolumeInterpolatorBase() {}; /** Set volume. * This function sets a smart pointer to the volume the class will interpolate * from. It may also perform some pre-computations to speed up interpolation, * such as indexing etc. It does not perform any interpolation itself. */ virtual void SetVolume( const UniformVolume& volume /*!< Source volume for interpolation */ ); /** Get data at location. * * This function performs interpolation of one value from m_Volume at location * v using the interpolation function given as the class template parameter. * * This function should return true if a value can be interpolated from * m_Volume at v, and it should return false if v is outside the range * where a value can be interpolated (i.e., outside the volume boundaries). * *\return True is the interpolation was successful and a valid value is returned in "value". */ virtual bool GetDataAt( const Vector3D& v /*!< Location for interpolation.*/, Types::DataItem& value /*!< The interpolated value is stored via this reference*/ ) const = 0; /** Get data at a pre-computed relative pixel index. This is faster if we already know the pixel index and fractional coordinate of a location. *\return Interpolated value. */ virtual Types::DataItem GetDataDirect( const Types::GridIndexType* imageGridPoint /*!< Grid index in image */, const Types::Coordinate* insidePixel /*!< Relative position inside indexed pixel */ ) const = 0; protected: /// Pointer to volume data array. std::vector m_VolumeDataArray; /// Image dimensions. DataGrid::IndexType m_VolumeDims; /// Image pixel size. UniformVolume::CoordinateVectorType m_VolumeDeltas; /// Image offset vector. UniformVolume::CoordinateVectorType m_VolumeOffset; /// Index increment when increasing "j" pixel index (i.e., moving to next image row). Types::GridIndexType m_NextJ; /// Index increment when increasing "k" pixel index (i.e., moving to next image plane). Types::GridIndexType m_NextK; /// Get offset from pixel index. size_t GetOffsetFromIndex( const Types::GridIndexType i /*!< Grid index #0 */, const Types::GridIndexType j /*!< Grid index #1 */, const Types::GridIndexType k /*!< Grid index #2 */ ) const { return i + j * this->m_NextJ + k * this->m_NextK; } }; //@} } // namespace cmtk #endif // #ifndef __cmtkUniformVolumeInterpolatorBase_h_included_ cmtk-3.3.1/libs/Base/cmtkUniformVolumeInterpolatorPartialVolume.cxx000066400000000000000000000122711276303427400256140ustar00rootroot00000000000000/* // // Copyright 2016 Google, Inc. // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2011 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 5399 $ // // $LastChangedDate: 2016-01-14 19:00:40 -0800 (Thu, 14 Jan 2016) $ // // $LastChangedBy: torstenrohlfing $ // */ #include "cmtkUniformVolumeInterpolatorPartialVolume.h" #include #ifdef HAVE_IEEEFP_H # include #endif namespace cmtk { /** \addtogroup Base */ //@{ bool UniformVolumeInterpolatorPartialVolume ::GetDataAt(const Vector3D& v, Types::DataItem& value) const { value=0; Types::Coordinate lScaled[3]; Types::GridIndexType imageGridPoint[3]; for ( int n = 0; n < 3; ++n ) { lScaled[n] = (v[n]-this->m_VolumeOffset[n]) / this->m_VolumeDeltas[n]; imageGridPoint[n] = (Types::GridIndexType) floor( lScaled[n] ); if ( ( imageGridPoint[n] < 0 ) || ( imageGridPoint[n] >= this->m_VolumeDims[n]-1 ) ) return false; } const size_t offset = this->GetOffsetFromIndex( imageGridPoint[0], imageGridPoint[1], imageGridPoint[2] ); Types::DataItem corners[8]; bool dataPresent = false; size_t idx = 0; for ( Types::GridIndexType k = 0; k < 2; ++k ) { for ( Types::GridIndexType j = 0; j < 2; ++j ) { for ( Types::GridIndexType i = 0; i < 2; ++i, ++idx ) { corners[idx] = this->m_VolumeDataArray[offset + this->GetOffsetFromIndex( i, j, k )]; const bool dataHere = (finite( corners[idx] ) != 0); dataPresent |= dataHere; } } } if (dataPresent) { const Types::Coordinate revX = lScaled[0]-imageGridPoint[0]; const Types::Coordinate revY = lScaled[1]-imageGridPoint[1]; const Types::Coordinate revZ = lScaled[2]-imageGridPoint[2]; const Types::Coordinate offsX = 1-revX; const Types::Coordinate offsY = 1-revY; const Types::Coordinate offsZ = 1-revZ; const Types::Coordinate weights[8] = { offsX * offsY * offsZ, revX * offsY * offsZ, offsX * revY * offsZ, revX * revY * offsZ, offsX * offsY * revZ, revX * offsY * revZ, offsX * revY * revZ, revX * revY * revZ }; bool done[8]; memset( done, 0, sizeof( done ) ); Types::Coordinate maxWeight = 0; for ( Types::GridIndexType j = 0; j < 8; ++j ) { if ( done[j] ) continue; Types::Coordinate weight = weights[j]; for ( Types::GridIndexType i = j+1; i < 8; ++i ) { if ( done[i] ) continue; if ( corners[i] == corners[j] ) { weight += weights[i]; done[i] = true; } } if ( weight > maxWeight ) { value = corners[j]; maxWeight = weight; } } return true; } return false; } Types::DataItem UniformVolumeInterpolatorPartialVolume ::GetDataDirect( const Types::GridIndexType* imageGridPoint, const Types::Coordinate* insidePixel ) const { Types::DataItem value = 0; const size_t offset = this->GetOffsetFromIndex( imageGridPoint[0], imageGridPoint[1], imageGridPoint[2] ); bool done[8]; Types::DataItem corners[8]; bool dataPresent = false; size_t idx = 0; for ( Types::GridIndexType k = 0; k < 2; ++k ) { for ( Types::GridIndexType j = 0; j < 2; ++j ) { for ( Types::GridIndexType i = 0; i < 2; ++i, ++idx ) { corners[idx] = this->m_VolumeDataArray[offset + this->GetOffsetFromIndex( i, j, k )]; const bool dataHere = (finite( corners[idx] ) != 0); done[idx] = !dataHere; dataPresent |= dataHere; } } } if (dataPresent) { const Types::Coordinate offsX = 1-insidePixel[0]; const Types::Coordinate offsY = 1-insidePixel[1]; const Types::Coordinate offsZ = 1-insidePixel[2]; const Types::Coordinate weights[8] = { offsX * offsY * offsZ, insidePixel[0] * offsY * offsZ, offsX * insidePixel[1] * offsZ, insidePixel[0] * insidePixel[1] * offsZ, offsX * offsY * insidePixel[2], insidePixel[0] * offsY * insidePixel[2], offsX * insidePixel[1] * insidePixel[2], insidePixel[0] * insidePixel[1] * insidePixel[2] }; Types::Coordinate maxWeight = 0; for ( Types::GridIndexType j = 0; j < 8; ++j ) { if ( done[j] ) continue; Types::Coordinate weight = weights[j]; for ( Types::GridIndexType i = j+1; i < 8; ++i ) { if ( done[i] ) continue; if ( corners[i] == corners[j] ) { weight += weights[i]; done[i] = true; } } if ( weight > maxWeight ) { value = corners[j]; maxWeight = weight; } } } return value; }; } // namespace cmtk cmtk-3.3.1/libs/Base/cmtkUniformVolumeInterpolatorPartialVolume.h000066400000000000000000000054461276303427400252470ustar00rootroot00000000000000/* // // Copyright 2016 Google, Inc. // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2010 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 5399 $ // // $LastChangedDate: 2016-01-14 19:00:40 -0800 (Thu, 14 Jan 2016) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkUniformVolumeInterpolatorPartialVolume_h_included_ #define __cmtkUniformVolumeInterpolatorPartialVolume_h_included_ #include #include #include #include #include namespace cmtk { /** \addtogroup Base */ //@{ /** Partial-volume interpolation class. * */ class UniformVolumeInterpolatorPartialVolume : /// Inherit interface from base class. public UniformVolumeInterpolatorBase { public: /// This class type. typedef UniformVolumeInterpolatorPartialVolume Self; /// Smart pointer to this class. typedef SmartPointer SmartPtr; /// Constructor. UniformVolumeInterpolatorPartialVolume( const UniformVolume& volume ) : UniformVolumeInterpolatorBase( volume ) { } /** Get data at location. * * This function performs interpolation of one value from m_Volume at location * v using the interpolation function given as the class template parameter. * * This function should return true if a value can be interpolated from * m_Volume at v, and it should return false if v is outside the range * where a value can be interpolated (i.e., outside the volume boundaries). */ virtual bool GetDataAt( const Vector3D& v, Types::DataItem& value ) const; /// Get data at a pre-computed relative pixel index. This is faster if we already know the pixel index and fractional coordinate of a location. virtual Types::DataItem GetDataDirect( const Types::GridIndexType* imageGridPoint, const Types::Coordinate* insidePixel ) const; }; //@} } // namespace cmtk #endif // #ifndef __cmtkUniformVolumeInterpolatorPartialVolume_h_included_ cmtk-3.3.1/libs/Base/cmtkUniformVolumeLaplaceFilter.cxx000066400000000000000000000031651276303427400231360ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2012 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4113 $ // // $LastChangedDate: 2012-03-30 16:15:18 -0700 (Fri, 30 Mar 2012) $ // // $LastChangedBy: torstenrohlfing $ // */ #include "cmtkUniformVolumeLaplaceFilter.h" #include #include #include namespace cmtk { /** \addtogroup Base */ //@{ TypedArray::SmartPtr UniformVolumeLaplaceFilter::Get() const { // Apply Laplacian symmetric kernel (-1, 2, -1) to each dimension separately. std::vector kernel( 2 ); kernel[0] = 2; kernel[1] = -1; return DataGridFilter::GetDataKernelFiltered( kernel, kernel, kernel, false /*do not normalize - sum of kernel elements is zero*/ ); } } // namespace cmtk cmtk-3.3.1/libs/Base/cmtkUniformVolumeLaplaceFilter.h000066400000000000000000000040241276303427400225560ustar00rootroot00000000000000/* // // Copyright 2004-2012 SRI International // // Copyright 1997-2009 Torsten Rohlfing // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4110 $ // // $LastChangedDate: 2012-03-30 14:23:39 -0700 (Fri, 30 Mar 2012) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkUniformVolumeLaplaceFilter_h_included_ #define __cmtkUniformVolumeLaplaceFilter_h_included_ #include #include #include #include namespace cmtk { /** \addtogroup Base */ //@{ /** Laplacian edge enhancement filter for uniform 3D volume images. */ class UniformVolumeLaplaceFilter : /// Prevent copying by inheritance. private DataGridFilter { public: /// This class. typedef UniformVolumeLaplaceFilter Self; /// Constructor: link to UniformVolume object. explicit UniformVolumeLaplaceFilter( UniformVolume::SmartConstPtr& volume ) : DataGridFilter( volume ), m_UniformVolume( volume ) {} /// Laplacian (edge enhancing) filter. TypedArray::SmartPtr Get() const; private: /// The UniformVolume object we're working on. UniformVolume::SmartConstPtr m_UniformVolume; }; } // namespace cmtk #endif // #ifndef __cmtkUniformVolumeLaplaceFilter_h_included_ cmtk-3.3.1/libs/Base/cmtkUniformVolumeMorphologicalOperators.cxx000066400000000000000000000104141276303427400251200ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2014 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 5265 $ // // $LastChangedDate: 2014-03-28 11:10:05 -0700 (Fri, 28 Mar 2014) $ // // $LastChangedBy: torstenrohlfing $ // */ #include "cmtkUniformVolumeMorphologicalOperators.h" #include #include #include #include #include namespace cmtk { UniformVolumeMorphologicalOperators ::UniformVolumeMorphologicalOperators( const UniformVolume::SmartConstPtr& uniformVolume ) : m_UniformVolume( uniformVolume ) {} TypedArray::SmartPtr UniformVolumeMorphologicalOperators::GetErodedByDistance( const Types::Coordinate erodeBy ) const { if ( !this->m_UniformVolume->GetData() ) return TypedArray::SmartPtr( NULL ); TypedArray::SmartPtr erodedData = UniformDistanceMap( *(this->m_UniformVolume), DistanceMap::INSIDE ).Get()->GetData(); erodedData->Binarize( erodeBy + 0.5 ); erodedData->SetDataClass( DATACLASS_LABEL ); return erodedData->Convert( TYPE_BYTE ); } TypedArray::SmartPtr UniformVolumeMorphologicalOperators::GetErodedByDistanceMultiLabels( const Types::Coordinate erodeBy ) const { if ( !this->m_UniformVolume->GetData() ) return TypedArray::SmartPtr( NULL ); const UniformVolume& volume = *(this->m_UniformVolume); const size_t nPixels = volume.GetNumberOfPixels(); // Make a set of label values that occur in the volume. Treat everything as unsigned int at most. unsigned int maxLabel = 0; std::set existingLabels; for ( size_t idx = 0; idx < nPixels; ++idx ) { const unsigned int value = static_cast( volume.GetDataAt( idx ) ); if ( value != 0 ) existingLabels.insert( value ); if ( value > maxLabel ) { maxLabel = value; } } // Sort out how many labels there are and allocate smallest type that can accomodate them TypedArray::SmartPtr resultData; if ( maxLabel < 256 ) resultData = TypedArray::Create( TYPE_BYTE, nPixels ); else if ( maxLabel < 65536 ) resultData = TypedArray::Create( TYPE_USHORT, nPixels ); else resultData = TypedArray::Create( TYPE_UINT, nPixels ); resultData->SetDataClass( DATACLASS_LABEL ); resultData->ClearArray(); // Run over all labels and compound the per-label eroded label maps for ( std::set::const_iterator it = existingLabels.begin(); it != existingLabels.end(); ++it ) { // Use EDT to erode TypedArray::SmartPtr erodedData = UniformDistanceMap( volume, DistanceMap::INSIDE | DistanceMap::VALUE_EXACT, *it ).Get()->GetData(); erodedData->Binarize( erodeBy + 0.5 ); // Compound into final map for ( size_t idx = 0; idx < nPixels; ++idx ) { if ( erodedData->ValueAt( idx ) > 0 ) resultData->Set( *it, idx ); } } return resultData; } TypedArray::SmartPtr UniformVolumeMorphologicalOperators::GetDilatedByDistance( const Types::Coordinate dilateBy ) const { if ( !this->m_UniformVolume->GetData() ) return TypedArray::SmartPtr( NULL ); TypedArray::SmartPtr dilatedData = UniformDistanceMap( *(this->m_UniformVolume) ).Get()->GetData(); dilatedData->Binarize( dilateBy + 0.5 ); dilatedData->Rescale( -1 /*scale*/, +1 /*offset*/ ); // this is binary inversion, 0->1, 1->0 dilatedData->SetDataClass( DATACLASS_LABEL ); return dilatedData->Convert( TYPE_BYTE ); } } // namespace cmtk cmtk-3.3.1/libs/Base/cmtkUniformVolumeMorphologicalOperators.h000066400000000000000000000054551276303427400245560ustar00rootroot00000000000000/* // // Copyright 2004-2014 SRI International // // Copyright 1997-2010 Torsten Rohlfing // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 5259 $ // // $LastChangedDate: 2014-03-27 16:28:47 -0700 (Thu, 27 Mar 2014) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkUniformVolumeMorphologicalOperators_h_included_ #define __cmtkUniformVolumeMorphologicalOperators_h_included_ #include #include #include namespace cmtk { /** \addtogroup Base */ //@{ /** Morphological operators acting on a 3D volume with known grid spacings. * This class provides implementations of morphological operators that take into account * the spacing of grid points (i.e., pixel size). These operators may be preferable to the * ones in cmtk::DataGridMorphologicalOperators in cases where the data grid is anisotropic. */ class UniformVolumeMorphologicalOperators : /// Prevent copying by inheritance. private CannotBeCopied { public: /// This class. typedef UniformVolumeMorphologicalOperators Self; /// Constructor: link to UniformVolume object. UniformVolumeMorphologicalOperators( const UniformVolume::SmartConstPtr& uniformVolume ); /// Get binary data after erosion operator using the Euclidean distance transform. TypedArray::SmartPtr GetErodedByDistance( const Types::Coordinate erodeBy /*!< Erosion distance. */ ) const; /// Get multi-label data after erosion operator using the Euclidean distance transform. TypedArray::SmartPtr GetErodedByDistanceMultiLabels( const Types::Coordinate erodeBy /*!< Erosion distance. */ ) const; /// Get binary data after dilation operator using the Euclidean distance transform. TypedArray::SmartPtr GetDilatedByDistance( const Types::Coordinate dilateBy /*!< Dilation distance. */ ) const; private: /// The UniformVolume object we're working on. UniformVolume::SmartConstPtr m_UniformVolume; }; } // namespace cmtk #endif // #ifndef __cmtkUniformVolumeMorphologicalOperators_h_included_ cmtk-3.3.1/libs/Base/cmtkUniformVolumePainter.cxx000066400000000000000000000125141276303427400220270ustar00rootroot00000000000000/* // // Copyright 2016 Google, Inc. // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2012 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 5398 $ // // $LastChangedDate: 2016-01-14 18:29:18 -0800 (Thu, 14 Jan 2016) $ // // $LastChangedBy: torstenrohlfing $ // */ #include "cmtkUniformVolumePainter.h" void cmtk::UniformVolumePainter::DrawSphere ( const UniformVolume::CoordinateVectorType& center, const Types::Coordinate radius, const Types::DataItem value ) { UniformVolume& volume = *(this->m_Volume); UniformVolume::CoordinateVectorType centerAbsolute( center ); Types::Coordinate radiusAbsolute[3] = { radius, radius, radius }; switch ( this->m_CoordinateMode ) { default: case Self::COORDINATES_ABSOLUTE: // nothing to do - already absolute break; case Self::COORDINATES_RELATIVE: for ( int dim = 0; dim < 3; ++dim ) { (centerAbsolute[dim] *= volume.m_Size[dim]) += volume.m_Offset[dim]; radiusAbsolute[dim] *= volume.m_Size[dim]; } break; case Self::COORDINATES_INDEXED: for ( int dim = 0; dim < 3; ++dim ) { (centerAbsolute[dim] *= volume.m_Delta[dim]) += volume.m_Offset[dim]; radiusAbsolute[dim] *= volume.m_Delta[dim]; } break; } DataGrid::RegionType region = volume.GetWholeImageRegion(); for ( int dim = 0; dim < 3; ++dim ) { region.From()[dim] = std::max( region.From()[dim], static_cast( (centerAbsolute[dim] - radiusAbsolute[dim] - volume.m_Offset[dim]) / volume.m_Delta[dim] ) - 1 ); region.To()[dim] = std::min( region.To()[dim], static_cast( (centerAbsolute[dim] + radiusAbsolute[dim] - volume.m_Offset[dim]) / volume.m_Delta[dim] ) + 1 ); } for ( Types::GridIndexType k = region.From()[2]; k < region.To()[2]; ++k ) { const Types::Coordinate Z = volume.GetPlaneCoord( 2, k ); for ( Types::GridIndexType j = region.From()[1]; j < region.To()[1]; ++j ) { const Types::Coordinate Y = volume.GetPlaneCoord( 1, j ); size_t offset = region.From()[0] + volume.m_Dims[0] * ( j + volume.m_Dims[1] * k ); for ( Types::GridIndexType i = region.From()[0]; i < region.To()[0]; ++i, ++offset ) { const Types::Coordinate X = volume.GetPlaneCoord( 0, i ); UniformVolume::CoordinateVectorType v = FixedVectorStaticInitializer<3,Types::Coordinate>::Init( X, Y, Z ); v -= centerAbsolute; for ( int dim = 0; dim < 3; ++dim ) { v[dim] /= radiusAbsolute[dim]; } if ( v.RootSumOfSquares() <= 1.0 ) volume.SetDataAt( value, offset ); } } } } void cmtk::UniformVolumePainter::DrawBox ( const UniformVolume::CoordinateVectorType& boxFrom, const UniformVolume::CoordinateVectorType& boxTo, const Types::DataItem value ) { UniformVolume& volume = *(this->m_Volume); Types::GridIndexType indexFrom[3], indexTo[3]; switch ( this->m_CoordinateMode ) { default: case Self::COORDINATES_ABSOLUTE: for ( int dim = 0; dim < 3; ++dim ) { indexFrom[dim] = static_cast( MathUtil::Round( boxFrom[dim] / volume.m_Delta[dim] ) ); indexTo[dim] = static_cast( MathUtil::Round( boxTo[dim] / volume.m_Delta[dim] ) ); } break; case Self::COORDINATES_RELATIVE: for ( int dim = 0; dim < 3; ++dim ) { indexFrom[dim] = static_cast( MathUtil::Round( boxFrom[dim] * volume.m_Size[dim] / volume.m_Delta[dim] ) ); indexTo[dim] = static_cast( MathUtil::Round( boxTo[dim] * volume.m_Size[dim] / volume.m_Delta[dim] ) ); } break; case Self::COORDINATES_INDEXED: // nothing to do - already indexed for ( int dim = 0; dim < 3; ++dim ) { indexFrom[dim] = static_cast( boxFrom[dim] + 0.5 ); indexTo[dim] = static_cast( boxTo[dim] + 0.5 ); } break; } // make sure boundaries are in correct order and in valid range for ( int dim = 0; dim < 3; ++dim ) { if ( indexFrom[dim] > indexTo[dim] ) std::swap( indexFrom[dim], indexTo[dim] ); indexFrom[dim] = std::max( 0, std::min( volume.m_Dims[dim]-1, indexFrom[dim] ) ); indexTo[dim] = std::max( 0, std::min( volume.m_Dims[dim]-1, indexTo[dim] ) ); } for ( Types::GridIndexType k = indexFrom[2]; k <= indexTo[2]; ++k ) { for ( Types::GridIndexType j = indexFrom[1]; j <= indexTo[1]; ++j ) { for ( Types::GridIndexType i = indexFrom[0]; i <= indexTo[0]; ++i ) { volume.SetDataAt( value, volume.GetOffsetFromIndex( i, j, k ) ); } } } } cmtk-3.3.1/libs/Base/cmtkUniformVolumePainter.h000066400000000000000000000055601276303427400214570ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2010 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 2398 $ // // $LastChangedDate: 2010-10-05 14:54:37 -0700 (Tue, 05 Oct 2010) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkUniformVolumePainter_h_included_ #define __cmtkUniformVolumePainter_h_included_ #include #include namespace cmtk { /** \addtogroup Base */ //@{ /** Class for painting in uniform volume objects. * This class provides operations to draw simple geometric objects into UniformVolume objects. * This is useful, for example, to create electronic phantom images. */ class UniformVolumePainter { public: /// This class. typedef UniformVolumePainter Self; /// Smart pointer. typedef SmartPointer SmartPtr; /// Enum for coordinate mode. typedef enum { /// Absolute coordinates: all coordinates are absoluate values within the volume field of view. COORDINATES_ABSOLUTE = 0, /// Relative coordinates: all coordinates are in range [0,1] which is mapped to volume field of view for each dimension. COORDINATES_RELATIVE = 1, /// Indexed grid coordinates: all coordinates are grid indexes. COORDINATES_INDEXED = 2 } CoordinateModeEnum; /// Constructor: link to target volume. UniformVolumePainter( UniformVolume::SmartPtr& volume, const CoordinateModeEnum coordinateMode = Self::COORDINATES_INDEXED ) : m_Volume( volume ), m_CoordinateMode( coordinateMode ) {} /// Draw a sphere. void DrawSphere( const UniformVolume::CoordinateVectorType& center, const Types::Coordinate radius, const Types::DataItem value ); /// Draw a box. void DrawBox( const UniformVolume::CoordinateVectorType& boxFrom, const UniformVolume::CoordinateVectorType& boxTo, const Types::DataItem value ); private: /// Pointer to target volume. UniformVolume::SmartPtr m_Volume; /// Coordinate mode for all operations. Self::CoordinateModeEnum m_CoordinateMode; }; //@} } // namespace cmtk #endif // #ifndef __cmtkUniformVolumePainter_h_included_ cmtk-3.3.1/libs/Base/cmtkUniformVolume_Crop.cxx000066400000000000000000000105061276303427400214660ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2013 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 5021 $ // // $LastChangedDate: 2013-11-23 09:17:13 -0800 (Sat, 23 Nov 2013) $ // // $LastChangedBy: torstenrohlfing $ // */ #include "cmtkUniformVolume.h" namespace cmtk { /** \addtogroup Base */ //@{ void UniformVolume::SetHighResCropRegion ( const Self::CoordinateRegionType& crop ) { if ( !this->m_HighResCropRegion ) this->m_HighResCropRegion = Self::CoordinateRegionType::SmartPtr( new CoordinateRegionType ); *this->m_HighResCropRegion = crop; for ( int dim = 0; dim<3; ++dim ) { this->CropRegion().From()[dim] = std::max( static_cast( (crop.From()[dim] - this->m_Offset[dim]) / this->m_Delta[dim] ), 0 ); this->CropRegion().To()[dim] = 1 + std::min( static_cast( (crop.To()[dim] - this->m_Offset[dim]) / this->m_Delta[dim] ), this->m_Dims[dim]-1 ); } } const UniformVolume::CoordinateRegionType UniformVolume::GetHighResCropRegion () const { if ( this->m_HighResCropRegion ) { return *this->m_HighResCropRegion; } else { UniformVolume::CoordinateRegionType region; for ( int dim = 0; dim<3; ++dim ) { region.From()[dim] = this->m_Offset[dim] + this->m_Delta[dim] * (this->CropRegion().From()[dim]); // take a half pixel off to move between pixels region.To()[dim] = this->m_Offset[dim] + this->m_Delta[dim] * (this->CropRegion().To()[dim]-1); // add a hald pixel to move between pixels } return region; } } UniformVolume::SmartPtr UniformVolume::GetCroppedVolume() const { return this->GetCroppedVolume( this->CropRegion() ); } UniformVolume::SmartPtr UniformVolume::GetCroppedVolume( const Self::RegionType& region ) const { const Self::IndexType cropDims = region.To() - region.From(); Self::CoordinateVectorType cropSize( cropDims ); for ( size_t i = 0; i < 3; ++i ) (cropSize[i] -= 1) *= this->m_Delta[i]; Self::SmartPtr volume( new UniformVolume( cropDims, cropSize ) ); // get cropped data. TypedArray::SmartPtr croppedData( this->GetRegionData( region ) ); volume->SetData( croppedData ); // prepare new index-to-physical transformation. volume->m_IndexToPhysicalMatrix = this->m_IndexToPhysicalMatrix; for ( int i = 0; i < 3; ++i ) { for ( int j = 0; j < 3; ++j ) { volume->m_IndexToPhysicalMatrix[3][i] += region.From()[j] * volume->m_IndexToPhysicalMatrix[j][i]; } } volume->m_AlternativeIndexToPhysicalMatrices = this->m_AlternativeIndexToPhysicalMatrices; for ( std::map::iterator it = volume->m_AlternativeIndexToPhysicalMatrices.begin(); it != volume->m_AlternativeIndexToPhysicalMatrices.end(); ++it ) { for ( int i = 0; i < 3; ++i ) { for ( int j = 0; j < 3; ++j ) { (it->second)[3][i] += region.From()[j] * (it->second)[j][i]; } } } // use m_Offset to keep track of new volume origin Self::CoordinateVectorType volumeOffset = this->m_Offset; for ( int i = 0; i < 3; ++i ) volumeOffset[i] += (region.From()[i] * this->m_Delta[i]); volume->SetOffset( volumeOffset ); if ( this->m_HighResCropRegion ) volume->SetHighResCropRegion( *this->m_HighResCropRegion ); volume->CopyMetaInfo( *this, META_IMAGE_ORIENTATION ); volume->CopyMetaInfo( *this, META_IMAGE_ORIENTATION_ORIGINAL ); volume->CopyMetaInfo( *this, META_SPACE ); return volume; } } // namespace cmtk cmtk-3.3.1/libs/Base/cmtkUniformVolume_Differential.cxx000066400000000000000000000062661276303427400231670ustar00rootroot00000000000000/* // // Copyright 2016 Google, Inc. // // Copyright 2010 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 5398 $ // // $LastChangedDate: 2016-01-14 18:29:18 -0800 (Thu, 14 Jan 2016) $ // // $LastChangedBy: torstenrohlfing $ // */ #include "cmtkUniformVolume.h" const cmtk::UniformVolume::CoordinateVectorType cmtk::UniformVolume ::GetGradientAt( const Types::GridIndexType i, const Types::GridIndexType j, const Types::GridIndexType k ) { Self::CoordinateVectorType g; g[0] = (this->GetDataAt( i+1, j, k ) - this->GetDataAt( i-1, j, k )) / (2*this->m_Delta[0]); g[1] = (this->GetDataAt( i, j+1, k ) - this->GetDataAt( i, j-1, k )) / (2*this->m_Delta[1]); g[2] = (this->GetDataAt( i, j, k+1 ) - this->GetDataAt( i, j, k-1 )) / (2*this->m_Delta[2]); return g; } cmtk::Matrix3x3 cmtk::UniformVolume ::GetHessianAt( const Types::GridIndexType i, const Types::GridIndexType j, const Types::GridIndexType k ) { cmtk::Matrix3x3 H; // implementation following central differences formulas from http://www.technion.ac.il/docs/sas/ormp/chap5/sect28.htm const Types::DataItem central = 30 * this->GetDataAt( i, j, k ); H[0][0] = (-this->GetDataAt( i+2, j, k ) + 16 * this->GetDataAt( i+1, j, k ) - central + 16 * this->GetDataAt( i-1, j, k ) - this->GetDataAt( i-2, j, k )) / ( 12 * this->m_Delta[0] * this->m_Delta[0] ); H[1][1] = (-this->GetDataAt( i, j+2, k ) + 16 * this->GetDataAt( i, j+1, k ) - central + 16 * this->GetDataAt( i, j-1, k ) - this->GetDataAt( i, j-2, k )) / ( 12 * this->m_Delta[1] * this->m_Delta[1] ); H[2][2] = (-this->GetDataAt( i, j, k+2 ) + 16 * this->GetDataAt( i, j, k+1 ) - central + 16 * this->GetDataAt( i, j, k-1 ) - this->GetDataAt( i, j, k-2 )) / ( 12 * this->m_Delta[2] * this->m_Delta[2] ); H[0][1] = H[1][0] = (this->GetDataAt( i+1, j+1, k ) - this->GetDataAt( i+1, j-1, k ) - this->GetDataAt( i-1, j+1, k ) + this->GetDataAt( i-1, j-1, k )) / ( 4 * this->m_Delta[0] * this->m_Delta[1] ); H[0][2] = H[2][0] = (this->GetDataAt( i+1, j, k+1 ) - this->GetDataAt( i+1, j, k-1 ) - this->GetDataAt( i-1, j, k+1 ) + this->GetDataAt( i-1, j, k-1 )) / ( 4 * this->m_Delta[0] * this->m_Delta[2] ); H[1][2] = H[2][1] = (this->GetDataAt( i, j+1, k+1 ) - this->GetDataAt( i, j+1, k-1 ) - this->GetDataAt( i, j-1, k+1 ) + this->GetDataAt( i, j-1, k-1 )) / ( 4 * this->m_Delta[1] * this->m_Delta[2] ); return H; } cmtk-3.3.1/libs/Base/cmtkUniformVolume_Resample.cxx000066400000000000000000000212031276303427400223270ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2012, 2014 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 5245 $ // // $LastChangedDate: 2014-03-18 15:43:47 -0700 (Tue, 18 Mar 2014) $ // // $LastChangedBy: torstenrohlfing $ // */ #include "cmtkUniformVolume.h" #include #include #include namespace cmtk { /** \addtogroup Base */ //@{ UniformVolume* UniformVolume::GetResampled ( const Types::Coordinate resolution, const bool allowUpsampling ) const { Self::IndexType newDims; Self::SpaceVectorType newSize; Self::SpaceVectorType newDelta; for ( int dim=0; dim<3; ++dim ) { newSize[dim] = this->m_Size[dim]; const int new_dims=(int) (newSize[dim]/resolution)+1; if ( allowUpsampling || (new_dims<=this->m_Dims[dim]) ) { newDims[dim]=new_dims; newDelta[dim] = newSize[dim]/(new_dims-1); } else { if ( this->m_Dims[dim] == 1 ) { newDelta[dim] = newSize[dim]; newDims[dim] = 1; } else { newDelta[dim] = this->m_Delta[dim]; newDims[dim] = ((int)(newSize[dim]/newDelta[dim])) + 1; newSize[dim] = (newDims[dim]-1) * newDelta[dim]; } } } UniformVolume* newVolume = new UniformVolume( newDims, newSize ); newVolume->SetData( TypedArray::SmartPtr( newVolume->Resample( *this ) ) ); newVolume->SetImageToPhysicalMatrix( this->GetImageToPhysicalMatrix() ); newVolume->SetHighResCropRegion( this->GetHighResCropRegion() ); newVolume->SetOffset( this->m_Offset ); newVolume->CopyMetaInfo( *this ); return newVolume; } UniformVolume* UniformVolume::GetResampledExact ( const Types::Coordinate resolution ) const { Self::IndexType newDims; Self::SpaceVectorType newSize; for ( int dim=0; dim<3; ++dim ) { newDims[dim] = static_cast( this->m_Size[dim] / resolution ) + 1; newSize[dim] = (newDims[dim]-1) * resolution; } UniformVolume* newVolume = new UniformVolume( newDims, newSize ); newVolume->SetData( TypedArray::SmartPtr( newVolume->Resample( *this ) ) ); newVolume->SetImageToPhysicalMatrix( this->GetImageToPhysicalMatrix() ); newVolume->SetHighResCropRegion( this->GetHighResCropRegion() ); newVolume->SetOffset( this->m_Offset ); newVolume->CopyMetaInfo( *this ); return newVolume; } TypedArray::SmartPtr UniformVolume::Resample( const UniformVolume& other ) const { const TypedArray* fromData = other.GetData(); const VolumeGridToGridLookup gridLookup( other, *this ); // compute number of tasks: we go by image plane and use twice as many tasks as threads, so we hopefully get decent load balancing. ThreadPool& threadPool = ThreadPool::GetGlobalThreadPool(); const size_t numberOfTasks = std::min( 4 * threadPool.GetNumberOfThreads() - 3, this->m_Dims[2] ); // Info blocks for parallel tasks that do the resampling. std::vector taskInfoVector( numberOfTasks ); Types::DataItem *resampledData = Memory::ArrayC::Allocate( this->GetNumberOfPixels() ); for ( size_t taskIdx = 0; taskIdx < numberOfTasks; ++taskIdx ) { taskInfoVector[taskIdx].thisObject = this; taskInfoVector[taskIdx].GridLookup = &gridLookup; taskInfoVector[taskIdx].OtherVolume = &other; taskInfoVector[taskIdx].FromData = fromData; taskInfoVector[taskIdx].ResampledData = resampledData; } switch ( fromData->GetDataClass() ) { case DATACLASS_GREY: default: { threadPool.Run( UniformVolume::ResampleThreadPoolExecuteGrey, taskInfoVector ); } break; case DATACLASS_LABEL: { threadPool.Run( UniformVolume::ResampleThreadPoolExecuteLabels, taskInfoVector ); break; } } TypedArray::SmartPtr result = TypedArray::Create( fromData->GetType(), this->GetNumberOfPixels() ); result->SetData( resampledData ); result->SetDataClass( fromData->GetDataClass() ); if ( fromData->GetPaddingFlag() ) { result->SetPaddingValue( fromData->GetPaddingValue() ); } Memory::ArrayC::Delete( resampledData ); return result; } void UniformVolume::ResampleThreadPoolExecuteLabels( void *const arg, const size_t taskIdx, const size_t taskCnt, const size_t, const size_t ) { UniformVolume::ResampleTaskInfo *info = static_cast( arg ); const UniformVolume *me = info->thisObject; const UniformVolume *other = info->OtherVolume; Types::DataItem *dest = info->ResampledData; const VolumeGridToGridLookup *gridLookup = info->GridLookup; Types::Coordinate labelWeights[256]; int x, y; int pX, pY, pZ; Types::DataItem value; for ( int z = taskIdx; z < me->m_Dims[2]; z += taskCnt ) { int offset = z * me->m_Dims[0] * me->m_Dims[1]; for ( y = 0; y < me->m_Dims[1]; ++y ) { for ( x = 0; x < me->m_Dims[0]; ++x, ++offset ) { memset( labelWeights, 0, sizeof( labelWeights ) ); for ( pZ=0; pZGetSourceCount(2,z); ++pZ ) { const Types::Coordinate weightZ=gridLookup->GetWeight(2,z,pZ); for ( pY=0; pYGetSourceCount(1,y); ++pY ) { const Types::Coordinate weightYZ=weightZ*gridLookup->GetWeight(1,y,pY); for ( pX=0; pXGetSourceCount(0,x); ++pX ) { const Types::Coordinate weight=weightYZ*gridLookup->GetWeight(0,x,pX); if ( other->GetDataAt( value, pX + gridLookup->GetFromIndex(0,x), pY + gridLookup->GetFromIndex(1,y), pZ + gridLookup->GetFromIndex(2,z)) ) { labelWeights[static_cast( value )] += weight; } } } } Types::Coordinate maxLabelWeight = 0; byte maxLabelIndex = 0; for ( int l=0; l<256; ++l ) { if ( labelWeights[l] > maxLabelWeight ) { maxLabelWeight = labelWeights[l]; maxLabelIndex = l; } } if ( maxLabelWeight > 0 ) dest[offset] = maxLabelIndex; else dest[offset] = std::numeric_limits::signaling_NaN(); } } } } void UniformVolume::ResampleThreadPoolExecuteGrey( void *const arg, const size_t taskIdx, const size_t taskCnt, const size_t, const size_t ) { UniformVolume::ResampleTaskInfo *info = static_cast( arg ); const UniformVolume *me = info->thisObject; Types::DataItem *dest = info->ResampledData; const UniformVolume *other = info->OtherVolume; const VolumeGridToGridLookup *gridLookup = info->GridLookup; Types::DataItem tempValue, value; int x, y; int pX, pY, pZ; bool FoundNullData; for ( int z = taskIdx; z < me->m_Dims[2]; z += taskCnt ) { int offset = z * me->m_Dims[0] * me->m_Dims[1]; const Types::Coordinate volumeZ = gridLookup->GetLength(2,z); for ( y=0; y < me->m_Dims[1]; ++y ) { const Types::Coordinate volumeYZ = volumeZ * gridLookup->GetLength(1,y); for ( x=0; x < me->m_Dims[0]; ++x, ++offset ) { tempValue = 0; FoundNullData = false; for ( pZ=0; pZGetSourceCount(2,z); ++pZ ) { const Types::Coordinate weightZ=gridLookup->GetWeight(2,z,pZ); for ( pY=0; pYGetSourceCount(1,y); ++pY ) { const Types::Coordinate weightYZ=weightZ*gridLookup->GetWeight(1,y,pY); for ( pX=0; pXGetSourceCount(0,x); ++pX ) { const Types::Coordinate weight=weightYZ*gridLookup->GetWeight(0,x,pX); if ( other->GetDataAt(value,pX+gridLookup->GetFromIndex(0,x), pY+gridLookup->GetFromIndex(1,y), pZ+gridLookup->GetFromIndex(2,z) ) ) { tempValue+=static_cast( weight*value ); } else { FoundNullData = true; } } } } if ( ! FoundNullData ) { const Types::Coordinate volume = volumeYZ*gridLookup->GetLength(0,x); dest[offset] = static_cast( tempValue / volume ); } else { dest[offset] = std::numeric_limits::signaling_NaN(); } } } } } } // namespace cmtk cmtk-3.3.1/libs/Base/cmtkUniformVolume_Space.cxx000066400000000000000000000123521276303427400216170ustar00rootroot00000000000000/* // // Copyright 2016 Google, Inc. // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2013 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 5398 $ // // $LastChangedDate: 2016-01-14 18:29:18 -0800 (Thu, 14 Jan 2016) $ // // $LastChangedBy: torstenrohlfing $ // */ #include "cmtkUniformVolume.h" #include #include namespace cmtk { void UniformVolume::CreateDefaultIndexToPhysicalMatrix() { this->m_IndexToPhysicalMatrix = AffineXform::MatrixType::Identity(); for ( int axis = 0; axis < 3; ++axis ) for ( int i = 0; i < 3; ++i ) this->m_IndexToPhysicalMatrix[axis][i] *= this->m_Delta[axis]; } const UniformVolume::SmartPtr UniformVolume::GetReoriented( const char* newOrientation ) const { const std::string curOrientation = this->GetMetaInfo( META_IMAGE_ORIENTATION ); DataGrid::SmartPtr temp( DataGrid::GetReoriented( newOrientation ) ); AnatomicalOrientation::PermutationMatrix pmatrix( this->m_Dims, curOrientation, newOrientation ); FixedVector<3,Types::Coordinate> newSize = pmatrix.GetPermutedArray( this->m_Size ); UniformVolume::SmartPtr result( new UniformVolume( temp->GetDims(), newSize, temp->GetData() ) ); result->m_Offset = pmatrix.GetPermutedArray( this->m_Offset ); result->m_IndexToPhysicalMatrix = pmatrix.GetPermutedMatrix( this->m_IndexToPhysicalMatrix ); for ( std::map::const_iterator it = this->m_AlternativeIndexToPhysicalMatrices.begin(); it != this->m_AlternativeIndexToPhysicalMatrices.end(); ++it ) { result->m_AlternativeIndexToPhysicalMatrices[it->first] = pmatrix.GetPermutedMatrix( it->second ); } result->CopyMetaInfo( *temp ); return result; } void UniformVolume ::ChangeCoordinateSpace( const std::string& newSpace ) { const std::string currentSpace = this->GetMetaInfo( META_SPACE ); if ( currentSpace == "" ) { StdErr << "WARNING: trying to change image coordinate space, but no current space is defined. Coordinate system of the resulting image is very likely incorrect.\n"; return; } if ( currentSpace == newSpace ) return; // nothing to do. Types::GridIndexType axesPermutation[3][3]; AnatomicalOrientation::GetImageToSpaceAxesPermutation( axesPermutation, newSpace.c_str(), currentSpace.c_str() ); AffineXform::MatrixType newMatrix = AffineXform::MatrixType::Identity(); for ( int j = 0; j < 3; ++j ) { for ( int j2 = 0; j2 < 3; ++j2 ) { if ( axesPermutation[j][j2] != 0 ) { for ( int i = 0; i < 4; ++i ) { newMatrix[i][j] = axesPermutation[j][j2] * this->m_IndexToPhysicalMatrix[i][j2]; } } } } this->SetMetaInfo( META_SPACE, newSpace ); this->m_IndexToPhysicalMatrix = newMatrix; for ( std::map::iterator it = this->m_AlternativeIndexToPhysicalMatrices.begin(); it != this->m_AlternativeIndexToPhysicalMatrices.end(); ++it ) { newMatrix = AffineXform::MatrixType::Identity(); for ( int j = 0; j < 3; ++j ) { for ( int j2 = 0; j2 < 3; ++j2 ) { if ( axesPermutation[j][j2] != 0 ) { for ( int i = 0; i < 4; ++i ) { newMatrix[i][j] = axesPermutation[j][j2] * (it->second)[i][j2]; } } } } it->second = newMatrix; } } std::string UniformVolume ::GetOrientationFromDirections() const { const AffineXform::MatrixType& matrix = this->m_IndexToPhysicalMatrix; char orientationString[4] = { 0,0,0,0 }; AnatomicalOrientation::GetOrientationFromDirections( orientationString, matrix, this->GetMetaInfo( META_SPACE ).c_str() ); return std::string( orientationString ); } AffineXform::MatrixType UniformVolume::GetImageToPhysicalMatrix() const { AffineXform::MatrixType matrix = this->m_IndexToPhysicalMatrix; // mDelta[3] is implicitly == 1 (homogeneous coordinates), so 4th matrix row (translation/coordinate origin) stays untouched for ( int i = 0; i < 3; ++i ) { if ( this->m_Delta[i] > 0 ) { for ( int j = 0; j < 3; ++j ) matrix[i][j] /= this->m_Delta[i]; } } return matrix; } void UniformVolume::SetImageToPhysicalMatrix( const AffineXform::MatrixType& matrix ) { this->m_IndexToPhysicalMatrix = matrix; // mDelta[3] is implicitly == 1 (homogeneous coordinates), so 4th matrix row (translation/coordinate origin) stays untouched for ( int i = 0; i < 3; ++i ) for ( int j = 0; j < 3; ++j ) this->m_IndexToPhysicalMatrix[i][j] *= this->m_Delta[i]; } } // namespace cmtk cmtk-3.3.1/libs/Base/cmtkUnionFind.h000066400000000000000000000047071276303427400172200ustar00rootroot00000000000000/* // // Copyright 1997-2010 Torsten Rohlfing // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 2199 $ // // $LastChangedDate: 2010-08-12 14:20:39 -0700 (Thu, 12 Aug 2010) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkUnionFind_h_included_ #define __cmtkUnionFind_h_included_ #include #include #include namespace cmtk { /** \addtogroup Base */ //@{ /// Class template for (relatively) efficient union-find algorithm. template class UnionFind { public: /// Internal set type. typedef std::set SetType; /// Internal list type. typedef std::list ListType; /// Opqaue result of "find" operation. typedef typename ListType::iterator FindResultType; /// Find operation. FindResultType Find( const T& key ) { for ( FindResultType it = this->m_List.begin(); it != this->m_List.end(); ++it ) { if ( it->find( key ) != it->end() ) return it; } return this->End(); } /// Find representative key. const T FindKey( const T& key ) { return *(this->Find( key )->begin()); } /// End-of-list iterator. FindResultType End() { return this->m_List.end(); } /// Union operation. void Union( const FindResultType& s1, const FindResultType& s2 ) { if ( s1 != s2 ) { s1->insert( s2->begin(), s2->end() ); this->m_List.erase( s2 ); } } /// Insert a new key by itself. void Insert( const T& key ) { SetType newSet; newSet.insert( key ); this->m_List.push_back( newSet ); } private: /// The list of sets. ListType m_List; }; //@} } // namespace cmtk #endif // #ifndef __cmtkUnionFind_h_included_ cmtk-3.3.1/libs/Base/cmtkUnits.h000066400000000000000000000106171276303427400164260ustar00rootroot00000000000000/* // // Copyright 2010-2012 SRI International // // Copyright 2010 Torsten Rohlfing // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4043 $ // // $LastChangedDate: 2012-03-20 14:29:11 -0700 (Tue, 20 Mar 2012) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkUnits_h_included_ #define __cmtkUnits_h_included_ #include namespace cmtk { /** \addtogroup Base */ //@{ /// Classes for type-safe values with physical units, and automatic conversions between them. namespace Units { /// Base class for a value with a physical unit. class UnitBase { public: /// Constructor. explicit UnitBase( const double value ) : m_Value( value ) {}; /// Get value. double Value() const { return this->m_Value; } /// Constant: pi. static double Pi() { return 3.14159265358979323846; } private: /// Actual value. double m_Value; }; /// Template for arithmetic on units (treated as a vector space) template class Arithmetic { public: /// Left-hand scalar multiplication. friend const T operator*( const double lhs, const T& rhs ) { return T( lhs * rhs.Value() ); } /// Right-hand scalar multiplication. friend const T operator*( const T& lhs, const double rhs ) { return T( lhs.Value() * rhs ); } /// Right-hand scalar division. friend const T operator/( const T& lhs, const double rhs ) { return T( lhs.Value() / rhs ); } /// Addition. friend const T operator+( const T& lhs, const T& rhs ) { return T( lhs.Value() + rhs.Value() ); } /// Subtraction. friend const T operator-( const T& lhs, const T& rhs ) { return T( lhs.Value() - rhs.Value() ); } /// Negation. friend const T operator-( const T& rhs ) { return T( -rhs.Value() ); } /// Less-than operator friend bool operator<( const T& lhs, const T& rhs ) { return lhs.Value() < rhs.Value(); } }; /// Forward declaration. class Radians; /// Angle of rotation in degrees. class Degrees : public UnitBase, public Arithmetic { public: /// Constructor. explicit Degrees( const double value = 0 ) : UnitBase( value ) {}; /// Conversion constructor. inline Degrees( const Radians& radians ); }; /// Angle of rotation in radians. class Radians : public UnitBase, public Arithmetic { public: /// Constructor. explicit Radians( const double value = 0 ) : UnitBase( value ) {}; /// Conversion constructor. inline Radians( const Degrees& degrees ); }; inline Degrees::Degrees( const Radians& radians ) : UnitBase( radians.Value() / (UnitBase::Pi() / 180) ) {} inline Radians::Radians( const Degrees& degrees ) : UnitBase( degrees.Value() * (UnitBase::Pi() / 180) ) {} /// Forward declaration. class GaussianFWHM; /// Parameter "\sigma" of Gaussian kernel class GaussianSigma : public UnitBase, public Arithmetic { public: /// Constructor. explicit GaussianSigma( const double value = 0 ) : UnitBase( value ) {}; /// Conversion constructor. inline GaussianSigma( const GaussianFWHM& radians ); }; /// Full width at half maximum of Gaussian kernel. class GaussianFWHM : public UnitBase, public Arithmetic { public: /// Constructor. explicit GaussianFWHM( const double value = 0 ) : UnitBase( value ) {}; /// Conversion constructor. inline GaussianFWHM( const GaussianSigma& degrees ); }; inline GaussianSigma::GaussianSigma( const GaussianFWHM& fwhm ) : UnitBase( fwhm.Value() / 2.354820045 ) {} inline GaussianFWHM::GaussianFWHM( const GaussianSigma& sigma ) : UnitBase( sigma.Value() * 2.354820045 ) {} } //@} } // namespace cmtk #endif // #define __cmtkUnits_h_included_ cmtk-3.3.1/libs/Base/cmtkValueSequence.h000066400000000000000000000111571276303427400200710ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2010 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 2152 $ // // $LastChangedDate: 2010-08-04 10:19:33 -0700 (Wed, 04 Aug 2010) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkValueSequence_h_included_ #define __cmtkValueSequence_h_included_ #include #include namespace cmtk { /** \addtogroup Base */ //@{ /** Class for computing characteristic values of number sequences. * Instances of this class take a sequence of (real) numbers via calls to the * Proceed() member function. After last last number in the sequence, the * object can be queried for minimum and maximum values (both absolute and * standard), variance, and average value. Further calls to Proceed() are * allowed, thus enabling incremental computations. *\note This class performs a lot of potentially unnecessary computations if, * for example, only the sum of values is to be computed. If you need efficient * evaluation of more elementary expressions, you are encouraged to implement * these directly; this present class is merely a convenience tool. */ template class ValueSequence { public: /// This class. typedef ValueSequence Self; /// Default constructor. ValueSequence() { this->Reset(); } /// Reset all computations. void Reset() { NValues = 0; Sum = 0; SumAbs = 0; SumOfSquares = 0; Minimum = Maximum = MinimumAbs = MaximumAbs = 0; } /// Proceed with the next number in the sequence. void Proceed( const T v ) { if ( ! NValues ) { Minimum = Maximum = v; MinimumAbs = MaximumAbs = fabs( v ); } else { if ( v < Minimum ) Minimum = v; if ( v > Maximum ) Maximum = v; if ( fabs( v ) < MinimumAbs ) MinimumAbs = fabs( v ); if ( fabs( v ) > MaximumAbs ) MaximumAbs = fabs( v ); } ++NValues; Sum += v; SumAbs += fabs( v ); SumOfSquares += v*v; } /// Return minimum of all values. double GetMinimum() const { return Minimum; } /// Return maximum of all values. double GetMaximum() const { return Maximum; } /// Return minimum of all absolute values. double GetMinimumAbs() const { return MinimumAbs; } /// Return maximum of all absolute values. double GetMaximumAbs() const { return MaximumAbs; } /// Return total number of values. int GetNValues() const { return NValues; } /// Return variance of all values. double GetVariance( const bool unbiased = true ) const { const double mu = this->GetAverage(); return ( NValues * mu * mu - 2 * mu * Sum + SumOfSquares ) / ( unbiased ? (NValues-1) : NValues ); } /// Return sum of all values. double GetSum() const { return static_cast( Sum ); } /// Return sum of squres of all values. double GetSumOfSquares() const { return static_cast( SumOfSquares ); } /// Return average value. double GetAverage() const { return static_cast( Sum / NValues ); } /// Return average value. double GetAverageAbs() const { return static_cast( SumAbs / NValues ); } /// Assignment operator. ValueSequence& operator=( const ValueSequence& other ); private: /// Sum of all values. T Sum; /// Sum of all absolute values. T SumAbs; /// Sum of the squares of all values. T SumOfSquares; /// Number of values. int NValues; /// Minimum value so far. T Minimum; /// Maximum value so far. T Maximum; /// Minimum absolute value so far. T MinimumAbs; /// Maximum absolute value so far. T MaximumAbs; /// Allow addition operator direct access. template friend ValueSequence operator+( const ValueSequence& a, const ValueSequence& b ); }; //@} } // namespace cmtk #include "cmtkValueSequence.txx" #endif // #ifndef __cmtkValueSequence_h_included_ cmtk-3.3.1/libs/Base/cmtkValueSequence.txx000066400000000000000000000040711276303427400204620ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // Copyright 2004-2009 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 11 $ // // $LastChangedDate: 2009-05-30 11:30:08 -0700 (Sat, 30 May 2009) $ // // $LastChangedBy: torstenrohlfing $ // */ namespace cmtk { /** \addtogroup Base */ //@{ template ValueSequence& ValueSequence::operator=( const ValueSequence& other ) { this->NValues = other.NValues; this->Sum = other.Sum; this->SumAbs = other.SumAbs; this->SumOfSquares = other.SumOfSquares; this->Minimum = other.Minimum; this->Maximum = other.Maximum; this->MinimumAbs = other.MinimumAbs; this->MaximumAbs = other.MaximumAbs; return *this; } template ValueSequence operator+( const ValueSequence& a, const ValueSequence& b ) { ValueSequence result; result.NValues = a.NValues + b.NValues; result.Sum = a.Sum + b.Sum; result.SumAbs = a.SumAbs + b.SumAbs; result.SumOfSquares = a.SumOfSquares + b.SumOfSquares; result.Minimum = std::min( a.Minimum, b.Minimum ); result.Maximum = std::max( a.Maximum, b.Maximum ); result.MinimumAbs = std::min( a.MinimumAbs, b.MinimumAbs ); result.MaximumAbs = std::max( a.MaximumAbs, b.MaximumAbs ); return result; } } // namespace cmtk cmtk-3.3.1/libs/Base/cmtkVector.cxx000066400000000000000000000022341276303427400171350ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // Copyright 2004-2009 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 2398 $ // // $LastChangedDate: 2010-10-05 14:54:37 -0700 (Tue, 05 Oct 2010) $ // // $LastChangedBy: torstenrohlfing $ // */ #include const char* DimensionMismatchError = "Vector dimension mismatch."; cmtk-3.3.1/libs/Base/cmtkVector.h000066400000000000000000000254211276303427400165650ustar00rootroot00000000000000/* // // Copyright 1997-2010 Torsten Rohlfing // // Copyright 2004-2011, 2013 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 5045 $ // // $LastChangedDate: 2013-11-27 14:58:43 -0800 (Wed, 27 Nov 2013) $ // // $LastChangedBy: torsten_at_home $ // */ #ifndef __cmtkVector_h_included_ #define __cmtkVector_h_included_ #include #include #include #include #include #include #include #include #include #include namespace cmtk { /** \addtogroup Base */ //@{ /** Numerical vector class. *\author Torsten Rohlfing */ template class Vector { public: /// Vector dimension. size_t Dim; /// Vector elements. T *Elements; /// This class. typedef Vector Self; /// Smart pointer to igsFloatVector. typedef SmartPointer SmartPtr; /**\name Constructors */ //@{ /// Create constant (zero-)vector. Vector ( const size_t dim = 0, const T value = 0 ) { Dim = dim; if ( Dim ) { Elements = Memory::ArrayC::Allocate( Dim ); FreeElements = true; if ( value==0 ) memset( Elements, 0, Dim * sizeof(T) ); else for ( size_t i=0; i( Dim ); FreeElements = true; memcpy( Elements, other.Elements + from, Dim * sizeof(T) ); } //@} /// Clone (sub)vector. Vector* Clone( const size_t len = 0, const size_t from = 0 ) const { return new Vector( *this, len, from ); } /// Destructor. ~Vector () { if ( Elements && FreeElements ) { Memory::ArrayC::Delete( this->Elements ); } } /** Set vector dimension. * If the current vector dimension is not equal to the requested dimension, * the elements array is deleted and a new one is allocated. In any case, * there is no guarantee that the data stored in the vector before this call * remains unchanged. This is even true for initial elements. *\param dim The number of elements to be stored in this vector after * returning from this function. *\param zero If this parameter is true, all vector elements are set to * the zero value in their respective data type. *\return A reference to this object after changing the dimension. */ Vector& SetDim ( const size_t dim, const bool zero = true ) { if ( Dim != dim ) { if ( Elements ) { Memory::ArrayC::Delete( this->Elements ); } Dim = dim; if ( Dim ) { Elements = Memory::ArrayC::Allocate( Dim ); } else Elements = NULL; } if ( zero && Dim ) { memset( Elements, 0, Dim * sizeof(T) ); } return *this; } /** Adjust vector dimension. * Unlike SetDim(), this function preserves the values of elements in the * vector if they are still in the valid index range after size adjustment. *\param dim The number of elements to be stored in this vector after * returning from this function. *\param zero If this parameter is true, all new vector elements are set to * the zero value in their respective data type. *\return A reference to this object after changing the dimension. */ Vector& AdjustDimension( const size_t dim, const bool zero = true ) { // If old and new size are the same, there is nothing to do. if ( Dim != dim ) { T* newElements = Memory::ArrayC::Allocate( dim ); // copy common elements memcpy( newElements, this->Elements, sizeof(T) * std::min( dim, Dim ) ); // reset new elements if so desired if ( zero && (dim > Dim) ) { memset( newElements + Dim, 0, sizeof(T) * (dim-Dim) ); } // new set new array. this->Dim = dim; if ( this->FreeElements ) { Memory::ArrayC::Delete( this->Elements ); } this->Elements = newElements; this->FreeElements = true; } return *this; } /// Vector assignment. Vector& operator = ( const Vector& other ) { if ( Dim != other.Dim ) { if (Elements) { Memory::ArrayC::Delete( this->Elements ); Elements = NULL; } Dim = other.Dim; } if ( Elements == NULL ) { Elements = Memory::ArrayC::Allocate( Dim ); } memcpy( Elements, other.Elements, Dim * sizeof(T) ); return *this; } /** Copy another vector to given offset. *\param other Vector from which the specified elements are copied. *\param offs Destination offset. Copying starts at this position in this * instance. *\param len Number of elements to be copied. If zero, all elements are * copied until the end of one of the vectors is reached. */ void CopyToOffset( const Vector& other, const size_t offs = 0, size_t len = 0 ) { if ( ! len ) len = std::min( this->Dim - offs, other.Dim ); for ( size_t idx=0; idx1e4) reduction(+:Result) #endif for ( int i=0; i( this->Dim ); ++i ) { const T e = Elements[i]; Result+=e*e; } return sqrt(Result); } /// Calculate maximum vector norm. T MaxNorm () const { T Result = 0; for ( size_t i=0; i( Result, fabs( Elements[i] ) ); } return Result; } /// Set all vector elements to zero. void Clear() { memset( Elements, 0, Dim * sizeof( *Elements ) ); } /// Set vector from C-style array of arbitrary type (that can be converted to vector's element type). template void SetFromArray( const T2* ptr, const size_t dim = 0 ) { const size_t nCopy = dim ? std::min( dim, this->Dim ) : this->Dim; for ( size_t i = 0; i < nCopy; ++i ) { this->Elements[i] = static_cast( ptr[i] ); } } /// Set all vector elements to constant value. void SetAll( const T value ) { #ifndef __SUNPRO_CC #pragma omp parallel for if (Dim>1e5) #endif for ( int i=0; i < static_cast( this->Dim ); ++i ) this->Elements[i] = value; } /// Get vector element by coordinate index. T& operator [] ( const size_t index ) { return this->Elements[index]; } /// Get constant vector element by coordinate index. const T& operator [] ( const size_t index ) const { return this->Elements[index]; } /// Increment vector by another. Vector& operator+= ( const Vector& delta ) { assert( Dim == delta.Dim ); #ifndef __SUNPRO_CC #pragma omp parallel for if (Dim>1e4) #endif for ( int i=0; i( this->Dim ); ++i ) Elements[i] += delta.Elements[i]; return *this; } /// Decrement vector by another. Vector& operator-= ( const Vector& delta ) { assert( Dim == delta.Dim ); #ifndef __SUNPRO_CC #pragma omp parallel for if (Dim>1e4) #endif for ( int i=0; i < static_cast( this->Dim ); ++i ) Elements[i] -= delta.Elements[i]; return *this; } /// Multiply by a scalar. Vector& operator*= ( const T a ) { #ifndef __SUNPRO_CC #pragma omp parallel for if (Dim>1e4) #endif for ( int i=0; i( this->Dim ); ++i ) this->Elements[i] *= a; return *this; } void Print ( FILE *const fp = NULL, const char* format = " %f" ) const { if ( fp ) { for ( size_t idx=0; idx < Dim; ++idx ) fprintf( fp, format, (float) Elements[idx] ); fputs( "\n", fp ); } else { for ( size_t idx=0; idx < Dim; ++idx ) printf( format, (float) Elements[idx] ); puts( "" ); } } /** Sort values in the vector. * Using the two parameters, from and len, this function can be used to sort * only a subrange of values in this vector. In particular, it can be used to * sort the first len elements if from == 0. *\param from Index of first element in the range to sort. *\param len Number of elements to be sorted. */ void Sort( const size_t from = 0, const size_t len = 0 ) { T *ptr = Elements+from; if ( len ) qsort( ptr, len, sizeof( T ), Vector::Compare ); else qsort( ptr, Dim-from, sizeof( T ), Vector::Compare ); } private: /// Flag for memory deallocation of value array. bool FreeElements; /// Compare two vector elements; this is needed for sorting. static int Compare( const void* a, const void* b ) { const T *Ta = (const T *) a; const T *Tb = (const T *) b; return (*Ta > *Tb) - (*Ta < *Tb); } }; /** Shortcut definition. * This typedef defines a name for the frequently used vectors over the * Types::Coordinate type. This is used for all kinds of parameters vectors etc. */ typedef Vector CoordinateVector; /** Shortcut definition. * This typedef defines a name for the frequently used vectors over the * float type. */ typedef Vector FloatVector; //@} } // namespace cmtk #include "cmtkVector.txx" #endif // #ifndef __cmtkVector_h_included_ cmtk-3.3.1/libs/Base/cmtkVector.txx000066400000000000000000000075041276303427400171630ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2011 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 3517 $ // // $LastChangedDate: 2011-10-27 12:42:15 -0700 (Thu, 27 Oct 2011) $ // // $LastChangedBy: torstenrohlfing $ // */ namespace cmtk { /** \addtogroup Base */ //@{ /** Vector addition operator. * Two vectors are added elementwise. A newly created vector object is * returned. This operator is rather inefficient in terms of allocation and * destruction of objects; use -= instead if possible. */ template Vector operator+ ( const Vector& p, const Vector& delta ) { assert ( p.Dim == delta.Dim ); T* Result = Memory::ArrayC::Allocate( p.Dim ); #pragma omp parallel for if (p.Dim>1e4) for ( size_t i=0; i( p.Dim, Result ); } /** Vector subtraction operator. * Two vectors are subtracted elementwise. A newly created vector object is * returned. This operator is rather inefficient in terms of allocation and * destruction of objects; use += instead if possible. */ template inline Vector operator- ( const Vector& p, const Vector& delta ) { assert ( p.Dim == delta.Dim ); T* Result = Memory::ArrayC::Allocate( p.Dim ); #pragma omp parallel for if (p.Dim>1e4) for ( size_t i=0; i( p.Dim, Result ); } /** Scalar-to-vector multiplication operator. * Every element of a vector is multiplies by the same scalar factor. The * result is returned as an automatically created object. This operator is * rather inefficient in terms of allocation and destruction of objects; use * *= instead if possible. */ template Vector operator* ( const T c, const Vector& p ) { T* Result = Memory::ArrayC::Allocate( p.Dim ); #pragma omp parallel for if (p.Dim>1e4) for ( size_t i=0; i( p.Dim, Result ); } /** Coordinatewise multiplication operator. * Two vectors are multiplied element by element. The result is returned as an * automatic variable. */ template Vector CoordMult ( const Vector& p, const Vector& q ) { assert ( p.Dim == q.Dim ); T* Result = Memory::ArrayC::Allocate( p.Dim ); #pragma omp parallel for if (p.Dim>1e4) for ( size_t i=0; i( p.Dim, Result ); } /** Scalar product. * This operator computes the standard scalar product of two vectors over the * same primitive type. As only a primitive object is returned as the result of * this operator, it is time- and memory-efficient. */ template inline T operator* ( const Vector& p, const Vector& q ) { assert ( p.Dim == q.Dim ); T Result = 0; #pragma omp parallel for if (p.Dim>1e4) for ( int i=0; i( p.Dim ); ++i ) Result += p.Elements[i] * q.Elements[i]; return Result; } } // namespace cmtk cmtk-3.3.1/libs/Base/cmtkVector3D.h000066400000000000000000000033441276303427400167540ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2010 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4746 $ // // $LastChangedDate: 2013-05-14 21:09:18 -0700 (Tue, 14 May 2013) $ // // $LastChangedBy: torsten_at_home $ // */ #ifndef __cmtkVector3D_h_included_ #define __cmtkVector3D_h_included_ #include #include #include namespace cmtk { /** \addtogroup Base */ //@{ /// Convenience typedef: vectors in 3D coordinate space. typedef FixedVector<3,Types::Coordinate> Vector3D; /// Cross-product of two 3D vectors. template FixedVector<3,T> CrossProduct( const FixedVector<3,T>& u, const FixedVector<3,T>& v ) { const T result[3] = { u[1]*v[2] - u[2]*v[1], u[2]*v[0] - u[0]*v[2], u[0]*v[1] - u[1]*v[0] }; return FixedVector<3,T>::FromPointer( result ); } //@} } // namespace cmtk #endif // #ifdef __cmtkVector3D_h_included_ cmtk-3.3.1/libs/Base/cmtkVolume.cxx000066400000000000000000000053771276303427400171550ustar00rootroot00000000000000/* // // Copyright 2016 Google, Inc. // // Copyright 1997-2010 Torsten Rohlfing // // Copyright 2004-2012 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 5409 $ // // $LastChangedDate: 2016-01-16 14:51:41 -0800 (Sat, 16 Jan 2016) $ // // $LastChangedBy: torstenrohlfing $ // */ #include "cmtkVolume.h" namespace cmtk { /** \addtogroup Base */ //@{ bool Volume::GetTrilinear ( ProbeInfo& probeInfo, const Types::GridIndexType X, const Types::GridIndexType Y, const Types::GridIndexType Z, const Vector3D& Location, const Types::Coordinate* from, const Types::Coordinate* to ) const { const TypedArray* data = this->GetData(); Types::GridIndexType offset = X+this->m_Dims[0]*(Y+this->m_Dims[1]*Z); bool data_present = data->Get( probeInfo.Values[0], offset ); if ( Xm_Dims[0]-1 ) { data_present &= data->Get( probeInfo.Values[1], offset+nextI ); if ( Ym_Dims[1]-1 ) { data_present &= data->Get( probeInfo.Values[3], offset+nextIJ ); if ( Zm_Dims[2]-1 ) data_present &= data->Get( probeInfo.Values[7], offset+nextIJK ); } if ( Zm_Dims[2]-1 ) data_present &= data->Get( probeInfo.Values[5], offset+nextIK ); } if ( Ym_Dims[1]-1 ) { data_present &= data->Get( probeInfo.Values[2], offset+nextJ ); if ( Zm_Dims[2]-1 ) data_present &= data->Get( probeInfo.Values[6], offset+nextJK ); } if ( Zm_Dims[2]-1 ) data_present &= data->Get( probeInfo.Values[4], offset+nextK ); if (data_present) { for ( int i=0; i<3; ++i ) { probeInfo.Deltas[i] = 1.0/(to[i]-from[i]); probeInfo.Offsets[i] = 1- (probeInfo.Offsets[3+i] = probeInfo.Deltas[i]*(Location[i]-from[i]) ); } probeInfo.Location = Location; return true; } return false; } Vector3D Volume::GetCenter () const { return this->m_Offset + 0.5 * this->m_Size; } } // namespace cmtk cmtk-3.3.1/libs/Base/cmtkVolume.h000066400000000000000000000111151276303427400165650ustar00rootroot00000000000000/* // // Copyright 2016 Google, Inc. // // Copyright 1997-2010 Torsten Rohlfing // // Copyright 2004-2013 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 5409 $ // // $LastChangedDate: 2016-01-16 14:51:41 -0800 (Sat, 16 Jan 2016) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkVolume_h_included_ #define __cmtkVolume_h_included_ #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace cmtk { /** \addtogroup Base */ //@{ /** General 3D volume. * This class handles three-dimensional volume data with a coordinate * transformation and associated distance measure. Methods to retrieve data and * general structural information are provided. *\author Torsten Rohlfing */ class Volume : /// Inherit from 3-D data grid. public DataGrid { public: /// This class type. typedef Volume Self; /// Superclass. typedef DataGrid Superclass; /// Smart pointer to Volume. typedef SmartPointer SmartPtr; /// Region type. typedef Region<3,Types::Coordinate> CoordinateRegionType; /// Index type. typedef CoordinateRegionType::IndexType CoordinateVectorType; /** Volume offset (coordinate of first voxel in RAS standard space). *\note This offset is NOT included in the volume size, m_Size. */ Self::CoordinateVectorType m_Offset; /// Set volume offset. void SetOffset( const Vector3D& o ) { this->m_Offset = o; } /** Spatial extent of the volume in world coordinates *\note This is the actual size of the volume between first and last pixel. Therefore, * if a non-zero volume coordinate offset is set in m_Offset, this does not affect * this field, because the volume size as the product of pixel size times number of pixels * per dimension minus one remains unaffected. */ Self::CoordinateVectorType m_Size; /// Check whether a location is inside the image. bool IsInside( const Self::CoordinateVectorType& v ) const { return (this->m_Offset <= v) && ((v-this->m_Offset) < this->m_Size); } /// Copy constructor. Volume( const Self& other ) : Superclass( other ), m_Offset( other.m_Offset ), m_Size( other.m_Size ) {} /// Grid and data constructor. Volume( const Self::IndexType& dims, const Self::CoordinateVectorType& size, TypedArray::SmartPtr& data = TypedArray::SmartPtr::Null() ) : DataGrid( dims, data ), m_Offset( 0.0 ), m_Size( size ) {} /** Destructor. * Do nothing really; just be present and virtual. */ virtual ~Volume () {}; /** Get total volume. *\return Product of the spatial extents in all three coordinate directions. */ virtual Types::Coordinate TotalVolume () const { return m_Size[0] * m_Size[1] * m_Size[2]; } /// Return average volume of all voxels. virtual Types::Coordinate AverageVoxelVolume () const { return ( (m_Size[0]*m_Size[1]*m_Size[2]) / ((this->m_Dims[0]-1)*(this->m_Dims[1]-1)*(this->m_Dims[2]-1)) ); } /** Calculate volume center. *\return Returned is the center of the bounding box. */ Vector3D GetCenter () const; protected: /** Get information needed for trilinear interpolation. *\return 1 if operation was successful, 0 if no valid data could be found * at the given location. */ bool GetTrilinear ( ProbeInfo&, const Types::GridIndexType, const Types::GridIndexType, const Types::GridIndexType, const Vector3D&, const Types::Coordinate*, const Types::Coordinate* ) const; }; //@} } // namespace cmtk #endif cmtk-3.3.1/libs/Base/cmtkVolume.txx000066400000000000000000000032531276303427400171650ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2012 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 3812 $ // // $LastChangedDate: 2012-02-02 15:52:58 -0800 (Thu, 02 Feb 2012) $ // // $LastChangedBy: torstenrohlfing $ // */ template ScalarImage* Volume::ComputeProjection( const Types::CoordinateAxis axis ) const { ScalarImage* projectImage = DataGrid::ComputeProjection( axis ); switch ( axis ) { case AXIS_X: projectImage->SetPixelSize( this->GetDelta( AXIS_Y, 0 ), this->GetDelta( AXIS_Z, 0 ) ); break; case AXIS_Y: projectImage->SetPixelSize( this->GetDelta( AXIS_X, 0 ), this->GetDelta( AXIS_Z, 0 ) ); break; case AXIS_Z: projectImage->SetPixelSize( this->GetDelta( AXIS_X, 0 ), this->GetDelta( AXIS_Y, 0 ) ); break; } return projectImage; } cmtk-3.3.1/libs/Base/cmtkVolumeClipping.cxx000066400000000000000000000130611276303427400206300ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2010 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 2398 $ // // $LastChangedDate: 2010-10-05 14:54:37 -0700 (Tue, 05 Oct 2010) $ // // $LastChangedBy: torstenrohlfing $ // */ #include "cmtkVolumeClipping.h" #include #include namespace cmtk { /** \addtogroup Base */ //@{ void VolumeClipping::SetClippingBoundaries ( const UniformVolume::CoordinateRegionType& region ) { this->m_ClippingRegion = region; } int VolumeClipping::ClipX ( Types::Coordinate& fromFactor, Types::Coordinate& toFactor, const Vector3D& offset, const Types::Coordinate initFromFactor, const Types::Coordinate initToFactor, const bool lowerClosed, const bool upperClosed ) const { fromFactor = initFromFactor; toFactor = initToFactor; for ( int dim=0; dim<3; ++dim ) { if ( DeltaX[dim] > 0 ) { fromFactor = std::max( fromFactor, (this->m_ClippingRegion.From()[dim] - offset[dim]) / DeltaX[dim] ); toFactor = std::min( toFactor, (this->m_ClippingRegion.To()[dim] - offset[dim]) / DeltaX[dim] ); } else if ( DeltaX[dim] < 0 ) { fromFactor = std::max( fromFactor, (this->m_ClippingRegion.To()[dim] - offset[dim]) / DeltaX[dim] ); toFactor = std::min( toFactor, (this->m_ClippingRegion.From()[dim]-offset[dim]) / DeltaX[dim] ); } else { if ( (offset[dim] < this->m_ClippingRegion.From()[dim]) || ( (offset[dim] == this->m_ClippingRegion.From()[dim] ) && lowerClosed ) || (offset[dim] > this->m_ClippingRegion.To()[dim]) || ( (offset[dim] == this->m_ClippingRegion.To()[dim]) && upperClosed ) ) { fromFactor = toFactor = 0; return 0; } } } return !( fromFactor > toFactor ); } int VolumeClipping::ClipY ( Types::Coordinate& fromFactor, Types::Coordinate& toFactor, const Vector3D& offset, const Types::Coordinate initFromFactor, const Types::Coordinate initToFactor ) const { fromFactor = initFromFactor; toFactor = initToFactor; for ( int dim=0; dim<3; ++dim ) { #ifdef _MSC_VER const Types::Coordinate axmin = offset[dim] + std::min( (Types::Coordinate) 0, DeltaX[dim] ); const Types::Coordinate axmax = offset[dim] + std::max( (Types::Coordinate) 0, DeltaX[dim] ); #else const Types::Coordinate axmin = offset[dim] + std::min( 0, DeltaX[dim] ); const Types::Coordinate axmax = offset[dim]+ std::max( 0, DeltaX[dim] ); #endif if ( DeltaY[dim] > 0 ) { fromFactor = std::max( fromFactor, ( this->m_ClippingRegion.From()[dim] - axmax) / DeltaY[dim] ); toFactor = std::min( toFactor, ( this->m_ClippingRegion.To()[dim] - axmin) / DeltaY[dim] ); } else if ( DeltaY[dim] < 0 ) { fromFactor = std::max( fromFactor, (this->m_ClippingRegion.To()[dim] - axmin) / DeltaY[dim] ); toFactor = std::min( toFactor, (this->m_ClippingRegion.From()[dim] - axmax) / DeltaY[dim] ); } else { if ( (axmax < this->m_ClippingRegion.From()[dim]) || (axmin > this->m_ClippingRegion.To()[dim]) ) { fromFactor = toFactor = 0; return 0; } } } return !( fromFactor > toFactor ); } int VolumeClipping::ClipZ ( Types::Coordinate& fromFactor, Types::Coordinate& toFactor, const Vector3D& offset, const Types::Coordinate initFromFactor, const Types::Coordinate initToFactor ) const { fromFactor = initFromFactor; toFactor = initToFactor; for ( int dim=0; dim<3; ++dim ) { #ifdef _MSC_VER const Types::Coordinate axbymin = offset[dim] + std::min( (Types::Coordinate) 0, DeltaX[dim] ) + std::min( (Types::Coordinate) 0, DeltaY[dim] ); const Types::Coordinate axbymax = offset[dim] + std::max( (Types::Coordinate) 0, DeltaX[dim] ) + std::max( (Types::Coordinate) 0, DeltaY[dim] ); #else const Types::Coordinate axbymin = offset[dim] + std::min( 0, DeltaX[dim] ) + std::min( 0, DeltaY[dim] ); const Types::Coordinate axbymax = offset[dim] + std::max( 0, DeltaX[dim] ) + std::max( 0, DeltaY[dim] ); #endif if ( DeltaZ[dim] > 0 ) { fromFactor = std::max( fromFactor, (this->m_ClippingRegion.From()[dim] - axbymax) / DeltaZ[dim] ); toFactor = std::min( toFactor, (this->m_ClippingRegion.To()[dim] - axbymin) / DeltaZ[dim] ); } else if ( DeltaZ[dim] < 0 ) { fromFactor = std::max( fromFactor, (this->m_ClippingRegion.To()[dim] - axbymin) / DeltaZ[dim] ); toFactor = std::min( toFactor, (this->m_ClippingRegion.From()[dim] - axbymax) / DeltaZ[dim] ); } else { if ( (axbymax < this->m_ClippingRegion.From()[dim]) || (axbymin > this->m_ClippingRegion.To()[dim]) ) { fromFactor = toFactor = 0; return 0; } } } return !( fromFactor > toFactor ); } } // namespace cmtk cmtk-3.3.1/libs/Base/cmtkVolumeClipping.h000066400000000000000000000174171276303427400202660ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2011 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 2752 $ // // $LastChangedDate: 2011-01-17 11:33:31 -0800 (Mon, 17 Jan 2011) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkVolumeClipping_h_included_ #define __cmtkVolumeClipping_h_included_ #include #include #include #include namespace cmtk { /** \addtogroup Base */ //@{ /** Utility class for volume clipping. * This class provides functions to compute intersections of volumes with * lines, planes, and other volumes. Line-to-volume intersection can be * computed by a single function call. * * Plane to volume intersection is done by first computing a range of parallel * lines that intersect the volume. Then, for each line the line to volume * intersection has to be computed to determine the exact intersection. * * Analog, for volume to volume intersection, first a range of parallel planes * is determined. Those then have to be examined in separate as described * before. * * This class' main application is ray clipping for DRR computation and volume * clipping for voxel based volume similarity computation. All member functions * are static, so they can be used without contructing an object. *\author T. Rohlfing */ class VolumeClipping { public: /** Set clipping boundaries. * This function sets the boundaries by which clipping is performed. */ void SetClippingBoundaries( const UniformVolume::CoordinateRegionType& region /*!< The clipping region in image coordinates */ ); /** Set clipped volumes spanning vector in x-direction. *\param deltaX The direction vector of the rows in the volume to be clipped. * The coordinate transformation applied to the clipped volume must have been * applied to this vector previously inorder for the clipping to be performed * correctly. */ void SetDeltaX( const Vector3D& deltaX ) { this->DeltaX = deltaX; } /** Set clipped volumes spanning vector in y-direction. *\param deltaY The direction vector of the columns in the volume to be * clipped. This is the vector from the origin of the first row in each * plane to the origin of the last row in that same plane. * The coordinate transformation applied to the clipped volume must have been * applied to this vector previously inorder for the clipping to be performed * correctly. */ void SetDeltaY( const Vector3D& deltaY ) { this->DeltaY = deltaY; } /** Set clipped volumes spanning vector in z-direction. *\param deltaZ The direction vector of the planes in the volume to be * clipped. This is the vector from the origin of the first plane in the * volume to the origin of the last plane. * The coordinate transformation applied to the clipped volume must have been * applied to this vector previously inorder for the clipping to be performed * correctly. */ void SetDeltaZ( const Vector3D& deltaZ ) { this->DeltaZ = deltaZ; } /** Compute line-to-volume intersection. * This function computes the part of a lines that lies within a given * volume. *\return This function returns 1 if and only if there is a non-empty * intersection of line and volume. In this case, the intersection is * described by fromFactor and toFactor. *\param fromFactor If the function returned 1, this variable holds the * relative distance to the entrance point of the line into the volume. * 0 means the line's starting point, 1 means its end. *\param toFactor If the function returned 1, this variable holds the * relative distance to the exit point of the line from the volume. Possible * values range from 0 to 1 and have the same meaning as fromFactor. *\param offset This 3D vector is the line's starting point. *\param initFromFactor The fromFactor parameter's value is initialized * with this value. It is therefore the lower bound of the parameter range * that is available for intersection. *\param initToFactor The toFactor parameter's value is initialized * with this value. It is therefore the upper bound of the parameter range * that is available for intersection. One application for this parameter * is to use a value bigger than 1, even if [0,1] is the allowed range. Then, * by testing if toFactor == 1.0, it can be determined whether clipping * set the value to 1. This, for example allows to tell closed from open * intervals, which may be important for subsequent computation such as * volume probing. *\param lowerClosed This flag defines whether lower range boundaries are * open (value 0, default) or closed (value 1). In case of an open range, * the bounding value itself is not an element of the range. Thus, if an * intersection is entirely on the boundary, then it is empty in case of an * open range. *\param upperClosed This flag has the same meaning and default as * lowerClosed, but refers to the ranges' upper bounds. */ int ClipX ( Types::Coordinate& fromFactor, Types::Coordinate& toFactor, const Vector3D& offset, const Types::Coordinate initFromFactor = 0, const Types::Coordinate initToFactor = 1, const bool lowerClosed = false, const bool upperClosed = false ) const; /** Compute plane-to-volume intersection. * This function computes the part of a plane that intersects with a given * volume. The intersection is only tested with respect to the dY direction. * for the range of relative positions returned by fromFactor and toFactor, * IntersectX can be used to computed the exact 2D intersection. * * Parameters and return value are identical to IntersectionX. */ int ClipY ( Types::Coordinate& fromFactor, Types::Coordinate& toFactor, const Vector3D& offset, const Types::Coordinate initFromFactor = 0, const Types::Coordinate initToFactor = 1 ) const; /** Compute volume-to-volume intersection. * This function computes the part of a volume that intersects with a given * volume. The intersection is only tested with respect to the dZ direction. * for the range of relative positions returned by fromFactor and toFactor, * IntersectY and afterwards IntersectX can be used to computed the exact 3D * intersection. * * Parameters and return value are identical to IntersectionX. */ int ClipZ ( Types::Coordinate& fromFactor, Types::Coordinate& toFactor, const Vector3D& offset, const Types::Coordinate initFromFactor = 0, const Types::Coordinate initToFactor = 1 ) const; private: /// Clipping boundaries. UniformVolume::CoordinateRegionType m_ClippingRegion; /// Spanning vector of the clipped volume in x-direction. Vector3D DeltaX; /// Spanning vector of the clipped volume in y-direction. Vector3D DeltaY; /// Spanning vector of the clipped volume in z-direction. Vector3D DeltaZ; }; //@} } // namespace cmtk #endif // #ifndef __cmtkVolumeClipping_h_included_ cmtk-3.3.1/libs/Base/cmtkVolumeGridToGridLookup.cxx000066400000000000000000000063211276303427400222540ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2012 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4381 $ // // $LastChangedDate: 2012-05-30 14:18:15 -0700 (Wed, 30 May 2012) $ // // $LastChangedBy: torstenrohlfing $ // */ #include "cmtkVolumeGridToGridLookup.h" #include namespace cmtk { /** \addtogroup Base */ //@{ VolumeGridToGridLookup ::VolumeGridToGridLookup( const UniformVolume& fromVolume, const UniformVolume& toVolume ) : m_SourceCount( 3 ), m_FromIndex( 3 ), m_Weight( 3 ), m_Length( 3 ) { for ( int dim = 0; dim < 3; ++dim ) { const Types::Coordinate fromGridDelta = fromVolume.m_Delta[dim]; const Types::Coordinate toGridDelta = toVolume.m_Delta[dim]; this->m_SourceCount[dim].resize( toVolume.m_Dims[dim]+1 ); this->m_FromIndex[dim].resize( toVolume.m_Dims[dim]+1 ); this->m_Weight[dim].resize( toVolume.m_Dims[dim]+1 ); this->m_Length[dim].resize( toVolume.m_Dims[dim]+1 ); std::vector weightList( fromVolume.m_Dims[dim] ); int fromIdx = 0; for ( int toIdx = 0; toIdx < toVolume.m_Dims[dim]; ++toIdx ) { const Types::Coordinate toGridLo = std::max( 0.0, (toIdx-0.5) * toGridDelta ); const Types::Coordinate toGridHi = std::min( toVolume.m_Size[dim], (0.5+toIdx) * toGridDelta ); this->m_Length[dim][toIdx] = toGridHi - toGridLo; Types::Coordinate fromGridHi = std::min( toVolume.m_Size[dim], (0.5+fromIdx) * fromGridDelta ); while ( toGridLo>=fromGridHi ) { ++fromIdx; fromGridHi += fromGridDelta; } this->m_FromIndex[dim][toIdx] = fromIdx; fromGridHi = std::min( fromVolume.m_Size[dim], fromGridHi ); int idx = 0; Types::Coordinate fromGridLo = std::max( 0.0, (fromIdx-0.5) * fromGridDelta ); for ( int p = fromIdx; (p < fromVolume.m_Dims[dim]) && (fromGridLo < toGridHi); ++p, ++idx ) { weightList[idx] = MathUtil::Intersect( toGridLo, toGridHi, fromGridLo, fromGridHi ); fromGridLo = (p+0.5) * fromGridDelta; fromGridHi += fromGridDelta; } this->m_SourceCount[dim][toIdx] = idx; this->m_Weight[dim][toIdx].resize( idx ); for ( int i = 0; i < idx; ++i ) this->m_Weight[dim][toIdx][i] = weightList[i]; } this->m_Weight[dim][toVolume.m_Dims[dim]].resize(0); } } } // namespace cmtk cmtk-3.3.1/libs/Base/cmtkVolumeGridToGridLookup.h000066400000000000000000000063141276303427400217030ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2010 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 2398 $ // // $LastChangedDate: 2010-10-05 14:54:37 -0700 (Tue, 05 Oct 2010) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkVolumeGridToGridLookup_h_included_ #define __cmtkVolumeGridToGridLookup_h_included_ #include #include namespace cmtk { /** \addtogroup Base */ //@{ /** Class for grid to grid lookup tables. * This class is only for internal use by the Resmple function(s) of * the UniformVolume class. The lookup table basically records for * each pixel in the target grid, which pixels in the source grid it * depends on, and with what relative weight each source pixel contributes * to the target pixel. The contributions are computed as relative overlaps * of boxcar-shaped pixel profiles. */ class VolumeGridToGridLookup { public: /// Constructor: takes original and new image grids. VolumeGridToGridLookup( const UniformVolume& fromGrid, const UniformVolume& toGrid ); /// Get number of source pixels that contribute to the given target pixel. int GetSourceCount( const int dim, const int idx ) const { return this->m_SourceCount[dim][idx]; } /// Get index of first source pixel that contributes to the given target pixel. int GetFromIndex( const int dim, const int idx ) const { return this->m_FromIndex[dim][idx]; } /// Get weight with which a given source pixel contributes to the given target pixel. Types::Coordinate GetWeight( const int dim, const int idx, const int fromIdx ) const { return this->m_Weight[dim][idx][fromIdx]; } /// Length (width) of a given target pixel. Types::Coordinate GetLength( const int dim, const int idx ) const { return this->m_Length[dim][idx]; } private: /// Array of arrays of numbers of source pixels that contribute to the given target pixel. std::vector< std::vector< int > > m_SourceCount; /// Array of arrays of first source pixels that contributes to the given target pixels. std::vector< std::vector< int > > m_FromIndex; /// Array of arrays of weight arrays. std::vector< std::vector< std::vector > > m_Weight; /// Array of array of target pixel lengths. std::vector< std::vector< Types::Coordinate > > m_Length; }; //@} } // namespace cmtk #endif // #define __cmtkUniformVolume_h_included_ cmtk-3.3.1/libs/Base/cmtkWarpXform.cxx000066400000000000000000000223451276303427400176250ustar00rootroot00000000000000/* // // Copyright 1997-2010 Torsten Rohlfing // // Copyright 2004-2013 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4927 $ // // $LastChangedDate: 2013-10-04 10:14:11 -0700 (Fri, 04 Oct 2013) $ // // $LastChangedBy: torstenrohlfing $ // */ #include "cmtkWarpXform.h" #include #include #include #include #include namespace cmtk { /** \addtogroup Base */ //@{ WarpXform::ControlPointRegionType WarpXform::GetAllControlPointsRegion() const { return Self::ControlPointRegionType( (Self::ControlPointIndexType( 0 )), this->m_Dims ); } void WarpXform::InitGrid ( const FixedVector<3,Types::Coordinate>& domain, const Self::ControlPointIndexType& dims ) { this->m_Domain = domain; this->m_Dims = dims; std::fill( this->m_Offset.begin(), this->m_Offset.end(), 0 ); this->m_NumberOfControlPoints = this->m_Dims[0] * this->m_Dims[1] * this->m_Dims[2]; this->AllocateParameterVector( 3 * this->m_NumberOfControlPoints ); this->Update(); } void WarpXform::Update( const bool ) { nextI = 3; nextJ = nextI * this->m_Dims[0]; nextK = nextJ * this->m_Dims[1]; nextIJ = nextJ + nextI; nextIK = nextK + nextI; nextJK = nextK + nextJ; nextIJK = nextJK + nextI; } void WarpXform::GetDerivativeLandmarksMSD ( double& lowerMSD, double& upperMSD, const LandmarkPairList& ll, const unsigned int idx, const Types::Coordinate step ) { upperMSD = lowerMSD = 0; const size_t numberOfLandmarks = ll.size(); if ( numberOfLandmarks ) { Types::Coordinate pOld = this->m_Parameters[idx]; this->m_Parameters[idx] += step; for ( LandmarkPairList::const_iterator it = ll.begin(); it != ll.end(); ++it ) { upperMSD += (this->Apply( it->m_Location ) - it->m_TargetLocation).SumOfSquares(); } this->m_Parameters[idx] = pOld - step; for ( LandmarkPairList::const_iterator it = ll.begin(); it != ll.end(); ++it ) { lowerMSD += (this->Apply( it->m_Location ) - it->m_TargetLocation).SumOfSquares(); } this->m_Parameters[idx] = pOld; upperMSD /= numberOfLandmarks; lowerMSD /= numberOfLandmarks; } } Types::Coordinate WarpXform::GetInverseConsistencyError ( const Self* inverse, const UniformVolume* volume, const UniformVolume::RegionType* voi ) const { Self::SpaceVectorType v, vv; Types::Coordinate result = 0.0; int count = 0; DataGrid::RegionType myVoi; const DataGrid::RegionType *pVoi = &myVoi; if ( voi ) { pVoi = voi; } else { myVoi = volume->GetWholeImageRegion(); } for ( int z = pVoi->From()[2]; z < pVoi->To()[2]; ++z ) for ( int y = pVoi->From()[1]; y < pVoi->To()[1]; ++y ) for ( int x = pVoi->From()[0]; x < pVoi->To()[0]; ++x ) { v = volume->GetGridLocation( x, y, z ); vv = this->Apply( v ); if ( inverse->InDomain( vv ) ) { v -= inverse->Apply( vv ); result += v.RootSumOfSquares(); ++count; } } return count ? result / count : 0.0; } void WarpXform::GetDerivativeInverseConsistencyError ( double& lower, double& upper, const Self* inverse, const UniformVolume* volume, const UniformVolume::RegionType* voi, const unsigned int idx, const Types::Coordinate step ) { const Types::Coordinate pOld = this->m_Parameters[idx]; upper = lower = (-this->GetInverseConsistencyError( inverse, volume, voi )); this->m_Parameters[idx] += step; upper += this->GetInverseConsistencyError( inverse, volume, voi ); this->m_Parameters[idx] = pOld - step; lower+= this->GetInverseConsistencyError( inverse, volume, voi ); this->m_Parameters[idx] = pOld; } Types::Coordinate WarpXform::GetParamStep ( const size_t idx, const Self::SpaceVectorType&, const Types::Coordinate mmStep ) const { if ( this->m_ActiveFlags && ! (*this->m_ActiveFlags)[idx] ) return 0; int controlPointIdx = idx / 3; unsigned short x = ( controlPointIdx % this->m_Dims[0] ); unsigned short y = ( (controlPointIdx / this->m_Dims[0]) % this->m_Dims[1] ); unsigned short z = ( (controlPointIdx / this->m_Dims[0]) / this->m_Dims[1] ); if ( (x>=this->m_IgnoreEdge) && (x<(this->m_Dims[0]-this->m_IgnoreEdge)) && (y>=this->m_IgnoreEdge) && (y<(this->m_Dims[1]-this->m_IgnoreEdge)) && (z>=this->m_IgnoreEdge) && (z<(this->m_Dims[2]-this->m_IgnoreEdge)) ) { return mmStep; } else { return 0; } } void WarpXform::SetParametersActive() { if ( !this->m_ActiveFlags ) { this->m_ActiveFlags = BitVector::SmartPtr( new BitVector( this->m_NumberOfParameters, true ) ); } this->m_ActiveFlags->Set(); } void WarpXform::SetParameterActive ( const size_t index, const bool active ) { if ( !this->m_ActiveFlags ) { this->m_ActiveFlags = BitVector::SmartPtr( new BitVector( this->m_NumberOfParameters, true ) ); } this->m_ActiveFlags->Set( index, active ); } void WarpXform::SetParametersActive( const DataGrid::RegionType& ) { if ( !this->m_ActiveFlags ) { this->m_ActiveFlags = BitVector::SmartPtr( new BitVector( this->m_NumberOfParameters, true ) ); } } void WarpXform::SetParametersActive ( const int axis, const bool active ) { if ( !this->m_ActiveFlags ) { this->m_ActiveFlags = BitVector::SmartPtr( new BitVector( this->m_NumberOfParameters, true ) ); } for ( unsigned int idx = (unsigned int)axis; idx < this->m_NumberOfParameters; idx += 3 ) this->m_ActiveFlags->Set( idx, active ); } void WarpXform::SetParametersActive( const char* axes ) { if ( !this->m_ActiveFlags ) { this->m_ActiveFlags = BitVector::SmartPtr( new BitVector( this->m_NumberOfParameters, false ) ); } if ( axes ) { if ( strchr( axes, 'x' ) || strchr( axes, 'X' ) ) this->SetParametersActive( AXIS_X ); if ( strchr( axes, 'y' ) || strchr( axes, 'Y' ) ) this->SetParametersActive( AXIS_Y ); if ( strchr( axes, 'z' ) || strchr( axes, 'Z' ) ) this->SetParametersActive( AXIS_Z ); } } void WarpXform::SetParameterInactive( const size_t index ) { if ( !this->m_ActiveFlags ) { this->m_ActiveFlags = BitVector::SmartPtr( new BitVector( this->m_NumberOfParameters, true ) ); } this->m_ActiveFlags->Reset( index ); } int WarpXform::GetParameterActive( const size_t index ) const { if ( this->m_ActiveFlags ) return (*this->m_ActiveFlags)[index]; else return 1; } void WarpXform::DeleteParameterActiveFlags() { this->m_ActiveFlags = BitVector::SmartPtr::Null(); } void WarpXform::ReplaceInitialAffine( const AffineXform* newAffineXform ) { AffineXform change; // First, put new affine transformation if ( newAffineXform ) change = *newAffineXform; // Second, concat inverse of current initial affine transformation to undo it if ( this->m_InitialAffineXform ) { change.Concat( *(this->m_InitialAffineXform->GetInverse()) ); } // apply effective change to all control points. Types::Coordinate *coeff = this->m_Parameters; for ( unsigned int idx = 0; idx < this->m_NumberOfControlPoints; ++idx, coeff+=3 ) { const Self::SpaceVectorType p = change.Apply( Self::SpaceVectorType::FromPointer( coeff ) ); coeff[0] = p[0]; coeff[1] = p[1]; coeff[2] = p[2]; } // Finally, copy new transformation. We want to create a new object here // if the current transformation is linked somewhere else. if ( newAffineXform ) { this->m_InitialAffineXform = AffineXform::SmartPtr::DynamicCastFrom( newAffineXform->Clone() ); } else { this->m_InitialAffineXform = AffineXform::SmartPtr( new AffineXform ); } this->m_InitialAffineXform->CopyMetaInfo( *this, META_XFORM_FIXED_IMAGE_PATH ); this->m_InitialAffineXform->CopyMetaInfo( *this, META_XFORM_MOVING_IMAGE_PATH ); } void WarpXform::ConcatAffine( const AffineXform* affineXform ) { // apply effective change to all control points. Types::Coordinate *coeff = this->m_Parameters; for ( unsigned int idx = 0; idx < this->m_NumberOfControlPoints; ++idx, coeff+=3 ) { const Self::SpaceVectorType p = affineXform->Apply( Self::SpaceVectorType::FromPointer( coeff ) ); coeff[0] = p[0]; coeff[1] = p[1]; coeff[2] = p[2]; } // Finally, generate combined affine transformation. We want to create a new // object here if the current transformation is linked somewhere else. if ( this->m_InitialAffineXform.GetReferenceCount() != 1 ) this->m_InitialAffineXform = this->m_InitialAffineXform->Clone(); this->m_InitialAffineXform->Concat( *affineXform ); } } // namespace cmtk cmtk-3.3.1/libs/Base/cmtkWarpXform.h000066400000000000000000000272631276303427400172560ustar00rootroot00000000000000/* // // Copyright 1997-2010 Torsten Rohlfing // // Copyright 2004-2013 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 5160 $ // // $LastChangedDate: 2014-01-12 14:33:52 -0800 (Sun, 12 Jan 2014) $ // // $LastChangedBy: torsten_at_home $ // */ #ifndef __cmtkWarpXform_h_included_ #define __cmtkWarpXform_h_included_ #include #include #include #include #include #include #include #include #include #include #include namespace cmtk { /** \addtogroup Base */ //@{ /** Common base class for free-form-deformation-based warps. */ class WarpXform : /// Inherit generic transformation interface. public Xform { public: /// This class. typedef WarpXform Self; /// Smart pointer to WarpXform typedef SmartPointer SmartPtr; /// Smart pointer to const WarpXform typedef SmartConstPointer SmartConstPtr; /// Region type. typedef Region<3,int> ControlPointRegionType; /// Index type. typedef ControlPointRegionType::IndexType ControlPointIndexType; /// Dimensions of control point grid. Self::ControlPointIndexType m_Dims; /// Domain of control point grid in world coordinates. Self::SpaceVectorType m_Domain; /// Array of spacings between the control points. Self::SpaceVectorType m_Spacing; /// Array of spacings between the control points. Self::SpaceVectorType m_Offset; /// Get global scaling factor. virtual Types::Coordinate GetGlobalScaling() const { return this->m_GlobalScaling; } /// Initial affine transformation. cmtkGetSetMacro(AffineXform::SmartPtr,InitialAffineXform); /** Replace initial affine transformation. * If the new transformation is not given (or a NULL pointer), then * the new initial affine transformation is the identity transformation. * This means that the transformation is converted into its pure * nonrigid form. */ void ReplaceInitialAffine( const AffineXform* newAffineXform = NULL ); /// Concat affine transformation. void ConcatAffine( const AffineXform* affineXform ); /// Get number of control points. size_t GetNumberOfControlPoints() const { return this->m_NumberOfControlPoints; } /// Get offset of a control point from its index. size_t GetOffsetFromIndex( const Self::ControlPointIndexType& index ) const { return index[0] * nextI + index[1] * this->nextJ + index[2] * nextK; } protected: /// Number of control points. size_t m_NumberOfControlPoints; /** Inverted spacings between the control points. * These values are used for multiplication instead of division by those in * Spacing[]. */ Self::SpaceVectorType m_InverseSpacing; /// Number of edge planes in the control point grid to keep unmoved. cmtkGetSetMacro(unsigned int,IgnoreEdge); /// Flag for fast but inaccurate computation. cmtkGetSetMacroDefault(bool,FastMode,true); /// Precomputed global scaling of initial affine transformation. Types::Coordinate m_GlobalScaling; /// Stored scale factors of the initial affine transformation. Self::SpaceVectorType m_InverseAffineScaling; /// Offset of next control grid column. int nextI; /// Offset of next control grid row. int nextJ; /// Offset for next row and column. int nextIJ; /// Offset for next plane. int nextK; /// Offset for next plane and column. int nextIK; /// Offset for next plane and row. int nextJK; /// Offset for next plane, row, and column. int nextIJK; public: /// Default constructor. WarpXform () : m_InitialAffineXform( NULL ), m_NumberOfControlPoints( 0 ), m_GlobalScaling( 1.0 ), m_ActiveFlags( NULL ) { this->m_IgnoreEdge = 0; this->m_FastMode = false; this->m_Dims[0] = this->m_Dims[1] = this->m_Dims[2] = 0; this->m_InverseSpacing[0] = this->m_InverseSpacing[1] = this->m_InverseSpacing[2] = 0.0; this->nextI = this->nextJ = this->nextK = this->nextIJ = this->nextIK = this->nextJK = this->nextIJK = 0; } /// Destructor. virtual ~WarpXform () {} /// Initialized internal data structures for new control point grid. virtual void InitGrid( const FixedVector<3,Types::Coordinate>& domain, const Self::ControlPointIndexType& dims ); /// Get region containing all control point indexes. virtual Self::ControlPointRegionType GetAllControlPointsRegion() const; /** Get region containing all "inside" control point indexes. * The "inside" control points are those for which the transformation can be evaluated. * For higher-order interpolation kernels, e.g., cubic spline, this region may be smaller * than that returned by GetAllControlPointsRegion, but for general transformations we * default to return the same region here. */ virtual Self::ControlPointRegionType GetInsideControlPointsRegion() const { return this->GetAllControlPointsRegion(); } /// Check whether coordinate is in domain of transformation. virtual bool InDomain( const Self::SpaceVectorType& v ) const { return ( v[0] >= 0 ) && ( v[0] <= this->m_Domain[0] ) && ( v[1] >= 0 ) && ( v[1] <= this->m_Domain[1] ) && ( v[2] >= 0 ) && ( v[2] <= this->m_Domain[2] ); } /** Project coordinate to domain of transformation. */ virtual void ProjectToDomain( Self::SpaceVectorType& v ) const { for ( int dim = 0; dim < 3; ++dim ) { v[dim] = std::max( 0, std::min( v[dim], this->m_Domain[dim] ) ); } } /// Update internal representation. virtual void Update( const bool exactDelta = false ); /// Refine control point grid, but maintain transformation exactly. virtual void Refine() {} /** Return derivative of registration error with respect to one parameter. */ virtual void GetDerivativeLandmarksMSD( double& lowerMSD, double& upperMSD, const LandmarkPairList& ll, const unsigned int idx, const Types::Coordinate step ); /** Return inverse consistency. */ virtual Types::Coordinate GetInverseConsistencyError( const Self* inverse, const UniformVolume* volume, const UniformVolume::RegionType* voi = NULL ) const; /** Return derivative of inverse consistency. */ virtual void GetDerivativeInverseConsistencyError ( double& lower, double& upper, const Self* inverse, const UniformVolume* volume, const UniformVolume::RegionType* voi, const unsigned int idx, const Types::Coordinate step ); /// Get the original position of a control point. virtual Self::SpaceVectorType GetOriginalControlPointPosition( const Types::Coordinate x, const Types::Coordinate y, const Types::Coordinate z) const { Self::SpaceVectorType cp; cp[0] = this->m_Offset[0] + x*this->m_Spacing[0]; cp[1] = this->m_Offset[1] + y*this->m_Spacing[1]; cp[2] = this->m_Offset[2] + z*this->m_Spacing[2]; return cp; } /// Get the original position of a control point by index. virtual Self::SpaceVectorType GetOriginalControlPointPositionByOffset( const size_t offset ) const { return this->GetOriginalControlPointPosition( offset % this->m_Dims[0], (offset % (this->m_Dims[0]*this->m_Dims[1])) / this->m_Dims[0], offset / (this->m_Dims[0]*this->m_Dims[1]) ); } /// Get shifted control point position. virtual Self::SpaceVectorType GetShiftedControlPointPosition( const int x, const int y, const int z ) const { return this->GetShiftedControlPointPositionByOffset( x + this->m_Dims[0] * (y + this->m_Dims[1] * z ) ); } /// Get shifted control point position by offset. virtual Self::SpaceVectorType GetShiftedControlPointPositionByOffset( const size_t offset ) const { return Self::SpaceVectorType::FromPointer( this->m_Parameters + 3 * offset ); } /// Set shifted control point position. virtual void SetShiftedControlPointPosition( const Self::SpaceVectorType& v, const int x, const int y, const int z ) const { this->SetShiftedControlPointPositionByOffset( v, x + this->m_Dims[0] * (y + this->m_Dims[1] * z ) ); } /// Set shifted control point position by offset. virtual void SetShiftedControlPointPositionByOffset( const Self::SpaceVectorType& v, const size_t offset ) const { for ( int idx = 0; idx < 3; ++idx ) this->m_Parameters[idx+offset*3] = v[idx]; } /** Get the deformed position of a transformation control point. *\note This function does not necessarily return the shifted control point position, * but rather it applies the current transformation to the given control * point. */ virtual Self::SpaceVectorType GetDeformedControlPointPosition( const int, const int, const int ) const = 0; /// Get parameter step given a transformed volume size. virtual Types::Coordinate GetParamStep( const size_t, const Self::SpaceVectorType& volSize, const Types::Coordinate mmStep = 1 ) const; /// Free bitset for active parameter flags if it exists. void DeleteParameterActiveFlags(); /// Set all parameters as active. void SetParametersActive(); /// Set only those parameters as active that influence a given ROI. void SetParametersActive( const UniformVolume::RegionType& roi ); /// Set parameters for one spatial dimension as active or inactive. void SetParametersActive( const int axis, const bool active = true ); /** Set a particular parameter as active (or passive). *\param index Index of the parameter. *\param active Flag whether to set the parameter as active (non-zero) or * passive (zero). */ void SetParameterActive( const size_t index, const bool active = true ); /// Set a particular parameter as inactive. void SetParameterInactive( const size_t index ); /** Set parameters for spatial dimensions as active. *\param axes This parameter defiend the activated dimensions in this * transformation. If it contains the characters x, y, or z, then the x, y, * and z-directions, respectively, are activated. The remaining directions * (if any) are deativated. The order of the axes is not relevant. Both * upper- and lowercase characters will be accepted. */ void SetParametersActive( const char* axes ); /** Test whether a particular parameter is active. *\param index Index of the parameter. *\return Non-zero if and only if the given parameter is active. */ int GetParameterActive( const size_t index ) const; private: /** Flags for active (and passive) parameters. * This bitset contains one bit for each parameter in this transformation. * Every parameter is considered an active (variable) of passive (fixed) * parameter. Passive parameters are not considered for gradient computations * etc. and can therefore save a significant amount of computation time. */ cmtkGetSetMacro(BitVector::SmartPtr,ActiveFlags); /// Friend declaration. friend class SplineWarpXformUniformVolume; }; //@} } // namespace cmtk #endif // #ifndef __cmtkWarpXform_h_included_ cmtk-3.3.1/libs/Base/cmtkXform.cxx000066400000000000000000000060041276303427400167650ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2013 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4929 $ // // $LastChangedDate: 2013-10-04 10:18:35 -0700 (Fri, 04 Oct 2013) $ // // $LastChangedBy: torstenrohlfing $ // */ #include "cmtkXform.h" #include #include #include #include namespace cmtk { /** \addtogroup Base */ //@{ void Xform::AllocateParameterVector ( const size_t numberOfParameters ) { this->m_NumberOfParameters = numberOfParameters; if ( this->m_NumberOfParameters ) { this->m_ParameterVector = CoordinateVector::SmartPtr( new CoordinateVector( this->m_NumberOfParameters ) ); this->m_Parameters = this->m_ParameterVector->Elements; } else { this->m_ParameterVector = CoordinateVector::SmartPtr::Null(); this->m_Parameters = NULL; } } void Xform::SetParamVector ( CoordinateVector& v ) { if ( this->m_ParameterVector ) { *this->m_ParameterVector = v; } else { this->m_ParameterVector = CoordinateVector::SmartPtr( new CoordinateVector( v ) ); } this->m_Parameters = this->m_ParameterVector->Elements; } void Xform::SetParamVector ( const CoordinateVector& v ) { if ( this->m_ParameterVector ) { *this->m_ParameterVector = v; } else { this->m_ParameterVector = CoordinateVector::SmartPtr( new CoordinateVector( v ) ); } this->m_Parameters = this->m_ParameterVector->Elements; } CoordinateVector& Xform::GetParamVector ( CoordinateVector& v, const size_t targetOffset ) const { v.AdjustDimension( std::max( v.Dim, targetOffset + this->ParamVectorDim() ) ); v.CopyToOffset( *this->m_ParameterVector, targetOffset, this->ParamVectorDim() ); return v; } Types::Coordinate Xform::GetLandmarksMSD( const LandmarkPairList& ll ) const { Types::Coordinate msd = 0; const size_t numberOfLandmarks = ll.size(); if ( numberOfLandmarks ) { for ( LandmarkPairList::const_iterator it = ll.begin(); it != ll.end(); ++it ) { msd += ( this->Apply( it->m_Location ) - it->m_TargetLocation ).SumOfSquares(); } msd /= numberOfLandmarks; } return msd; } } // namespace cmtk cmtk-3.3.1/libs/Base/cmtkXform.h000066400000000000000000000176301276303427400164210ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2012, 2014 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 5165 $ // // $LastChangedDate: 2014-01-13 13:05:29 -0800 (Mon, 13 Jan 2014) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkXform_h_included_ #define __cmtkXform_h_included_ #include #include #include #include #include #include #include #include #include #include namespace cmtk { /** \addtogroup Base */ //@{ /** General 3D coordinate transformation. */ class Xform : /// Inherit from meta data information container. public MetaInformationObject { public: /// This class. typedef Xform Self; /// Smart pointer to this class. typedef SmartPointer SmartPtr; /// Smart pointer-to-const for this class. typedef SmartConstPointer SmartConstPtr; /// Region type. typedef Region<3,Types::Coordinate> SpaceRegionType; /// Three-dimensional vector type. typedef SpaceRegionType::IndexType SpaceVectorType; /// Pointer to warp parameter array. Types::Coordinate *m_Parameters; /// Total number of parameters, ie. the values in Coefficients. size_t m_NumberOfParameters; /// Copy constructor. Xform( const Xform& other ) : MetaInformationObject( other ), m_NumberOfParameters( other.m_NumberOfParameters ), m_ParameterVector( other.m_ParameterVector ) { this->m_Parameters = this->m_ParameterVector->Elements; this->SetMetaInfo( cmtk::META_SPACE, cmtk::AnatomicalOrientationBase::ORIENTATION_STANDARD ); } /// Default constructor. Xform() : m_Parameters( NULL ), m_NumberOfParameters( 0 ) { this->SetMetaInfo( cmtk::META_SPACE, cmtk::AnatomicalOrientationBase::ORIENTATION_STANDARD ); } /// Virtual destructor. virtual ~Xform() {} /// Check whether coordinate is in domain of transformation. virtual bool InDomain( const Self::SpaceVectorType& ) const { return true; } /** Project coordinate to domain of transformation (if bounded). * We provide here an implementation for global transformations, i.e., we * do nothing. */ virtual void ProjectToDomain( Self::SpaceVectorType& ) const {} /// Get global scaling factor. virtual Types::Coordinate GetGlobalScaling() const { return 1.0; } /// Apply transformation to vector. virtual Self::SpaceVectorType Apply ( const Self::SpaceVectorType& ) const = 0; /** Return inverse-transformed vector. */ virtual bool ApplyInverse ( const Self::SpaceVectorType&, Self::SpaceVectorType&, const Types::Coordinate = 0.01 ) const = 0; /** Return origin of warped vector. * Note that since not every class of transformation is closed under inversion, * this function computes only a more or less accurate numerical * approximation to the actual origin of a transformed vector. Note also that this * computation is everything but computationally efficient. *\note This function expects that derived classes will implement the GetJacobian member function, since the iterative search relies on the Jacobian matrix. *\param v Input location; is replaced with the inverse transformation applied to it upon return. *\param initial Initial estimate for the original location. Search goes * from here. This is useful for looking up the original locations of * a large number of closely located vectors, for example all pixels in an * image. *\param accuracy Accuracy of the inversion, i.e., residual inverse consistency error threshold. *\return True is the given inverse was succesfully comuted, false if the * given warped vector was outside the target domain of this transformation. */ virtual bool ApplyInverseWithInitial( const Self::SpaceVectorType& v, Self::SpaceVectorType& u, const Self::SpaceVectorType& initial, const Types::Coordinate accuracy = 0.01 ) const; /// Clone and return smart pointer. Self::SmartPtr Clone () const { return Self::SmartPtr( this->CloneVirtual() ); } /// Return number of coefficients in parameter vector. virtual size_t ParamVectorDim () const { return this->m_NumberOfParameters; } /** Get number of variable parameters in parameter vector. * The variable parameters are those that may be modified i.e. for an * optimization. They are located at the beginning of the complete parameter * vector. */ virtual size_t VariableParamVectorDim () const { return this->ParamVectorDim(); } /** Set Xform by parameter vector. * Be careful: This is NOT a one-way function. The Xform object may change * the parameter vector in order to ensure internal consistency * (AffineXform) or to enhance efficiency. */ virtual void SetParamVector ( CoordinateVector& v ); /** Copy parameter vector from other transformation. * THERE ARE NO CHECKS WHETHER THE TWO TRANSFORMATIONS MATCH!! */ virtual void CopyParamVector ( const Xform* other ) { *(this->m_ParameterVector) = *(other->m_ParameterVector); this->m_Parameters = this->m_ParameterVector->Elements; } /// Set the parameter vector. virtual void SetParamVector ( const CoordinateVector& v ); /// Set a single parameter value. virtual void SetParameter ( const size_t idx, const Types::Coordinate p ) { this->m_Parameters[idx] = p; } /// Get a single parameter value. virtual Types::Coordinate GetParameter ( const size_t idx ) const { return this->m_Parameters[idx]; } /// Copy parameter vector to existing vector object. virtual CoordinateVector& GetParamVector( CoordinateVector& v, const size_t targetOffset = 0 ) const; /// Get parameter step given a transformed volume size. virtual Types::Coordinate GetParamStep( const size_t, const Self::SpaceVectorType&, const Types::Coordinate step_mm = 1 ) const { return step_mm; } /// Get local Jacobian matrix. virtual const CoordinateMatrix3x3 GetJacobian( const Self::SpaceVectorType& ) const = 0; /// Compute Jacobian determinant at a certain location. virtual Types::Coordinate GetJacobianDeterminant ( const Self::SpaceVectorType& ) const = 0; /** Return registration error for set of source/target landmarks. * What is actually returned is the mean squared distance of source * landmark after transformation and desired target landmark. */ virtual Types::Coordinate GetLandmarksMSD( const LandmarkPairList& ll ) const; protected: /** Encapsulated representation of the transformation parameters. * This vector object contains the parameter array pointed at by the public * member Coefficients. The latter is used for more efficient direct access * to the parameters where necessary. */ CoordinateVector::SmartPtr m_ParameterVector; /** Allocate parameter vector. */ void AllocateParameterVector( const size_t numberOfParameters ); /// Actual virtual clone constructor function. virtual Self* CloneVirtual () const = 0; }; //@} } // namespace cmtk #endif // #ifdef __cmtkXform_h_included_ cmtk-3.3.1/libs/Base/cmtkXformList.cxx000066400000000000000000000112241276303427400176210ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2012, 2014 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 5368 $ // // $LastChangedDate: 2014-07-28 17:06:35 -0700 (Mon, 28 Jul 2014) $ // // $LastChangedBy: torstenrohlfing $ // */ #include "cmtkXformList.h" void cmtk::XformList::Add ( const Xform::SmartConstPtr& xform, const bool inverse, const Types::Coordinate globalScale ) { this->push_back( XformListEntry::SmartConstPtr( new XformListEntry( xform, inverse, globalScale ) ) ); } void cmtk::XformList::AddToFront ( const Xform::SmartConstPtr& xform, const bool inverse, const Types::Coordinate globalScale ) { this->push_front( XformListEntry::SmartConstPtr( new XformListEntry( xform, inverse, globalScale ) ) ); } bool cmtk::XformList::ApplyInPlace( Xform::SpaceVectorType& v ) const { for ( const_iterator it = this->begin(); it != this->end(); ++it ) { if ( (*it)->Inverse ) { // is this an affine transformation that has an inverse? if ( (*it)->InverseAffineXform ) { // apply inverse v = (*it)->InverseAffineXform->Apply( v ); } else { // not affine: use approximate inverse if ( ! (*it)->m_Xform->ApplyInverse( v, v, this->m_Epsilon ) ) return false; } } else { // are we outside xform domain? then return failure. if ( !(*it)->m_Xform->InDomain( v ) ) return false; v = (*it)->m_Xform->Apply( v ); } } return true; } bool cmtk::XformList::GetJacobian ( const Xform::SpaceVectorType& v, Types::DataItem& jacobian, const bool correctGlobalScale ) const { Xform::SpaceVectorType vv( v ); jacobian = static_cast( 1.0 ); for ( const_iterator it = this->begin(); it != this->end(); ++it ) { if ( (*it)->Inverse ) { if ( correctGlobalScale ) jacobian *= static_cast( (*it)->GlobalScale ); // is this an affine transformation that has an inverse? if ( (*it)->InverseAffineXform ) { // apply inverse vv = (*it)->InverseAffineXform->Apply( vv ); } else { // not affine: use approximate inverse if ( ! (*it)->m_Xform->ApplyInverse( vv, vv, this->m_Epsilon ) ) return false; } // compute Jacobian at destination and invert jacobian /= static_cast( (*it)->m_Xform->GetJacobianDeterminant( vv ) ); } else { // are we outside xform domain? then return failure. if ( !(*it)->m_Xform->InDomain( v ) ) return false; jacobian *= static_cast( (*it)->m_Xform->GetJacobianDeterminant( vv ) ); if ( correctGlobalScale ) jacobian /= static_cast( (*it)->GlobalScale ); vv = (*it)->m_Xform->Apply( vv ); } } return true; } bool cmtk::XformList::AllAffine() const { for ( const_iterator it = this->begin(); it != this->end(); ++it ) { if ( !(*it)->IsAffine() ) return false; } return true; } cmtk::XformList cmtk::XformList::MakeAllAffine() const { cmtk::XformList allAffine; for ( const_iterator it = this->begin(); it != this->end(); ++it ) { allAffine.push_back( (*it)->CopyAsAffine() ); } return allAffine; } std::string cmtk::XformList::GetFixedImagePath() const { const XformListEntry& first = **(this->begin()); // if transformation is inverse, get original "moving" path instead. if ( first.Inverse ) return first.m_Xform->GetMetaInfo( META_XFORM_MOVING_IMAGE_PATH, "" ); else return first.m_Xform->GetMetaInfo( META_XFORM_FIXED_IMAGE_PATH, "" ); } std::string cmtk::XformList::GetMovingImagePath() const { const XformListEntry& last = **(this->rbegin()); // if transformation is inverse, get original "fixed" path instead. if ( last.Inverse ) return last.m_Xform->GetMetaInfo( META_XFORM_FIXED_IMAGE_PATH, "" ); else return last.m_Xform->GetMetaInfo( META_XFORM_MOVING_IMAGE_PATH, "" ); } cmtk-3.3.1/libs/Base/cmtkXformList.h000066400000000000000000000065151276303427400172550ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2011 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 3542 $ // // $LastChangedDate: 2011-11-03 13:33:41 -0700 (Thu, 03 Nov 2011) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkXformList_h_included_ #define __cmtkXformList_h_included_ #include #include #include #include namespace cmtk { /** \addtogroup Base */ //@{ /// A transformation list. class XformList : /// Inherit STL list. public std::deque< XformListEntry::SmartConstPtr > { private: /// Error threshold for inverse approximation. Types::Coordinate m_Epsilon; public: /// This class. typedef XformList Self; /// Smart pointer. typedef SmartPointer SmartPtr; /// Smart pointer to const. typedef SmartConstPointer SmartConstPtr; /// Constructor. XformList( const Types::Coordinate epsilon = 0.0 ) : m_Epsilon( epsilon ) {}; /// Set epsilon. void SetEpsilon( const Types::Coordinate epsilon ) { this->m_Epsilon = epsilon; } /// Add a transformation the the end of the list, i.e., to be applied after the current list of transformations void Add( const Xform::SmartConstPtr& xform, const bool inverse = false, const Types::Coordinate globalScale = 1.0 ); /// Add a transformation the the end of the list, i.e., to be applied before the current list of transformations void AddToFront( const Xform::SmartConstPtr& xform, const bool inverse = false, const Types::Coordinate globalScale = 1.0 ); /// Apply a sequence of (inverse) transformations. bool ApplyInPlace( Xform::SpaceVectorType& v ) const; /// Get the Jacobian determinant of a sequence of transformations. bool GetJacobian( const Xform::SpaceVectorType& v, Types::DataItem& jacobian, const bool correctGlobalScale = true ) const; /// Is this transformation list all affine? bool AllAffine() const; /// Make all-affine copy of this transformation list. Self MakeAllAffine() const; /** Get fixed image path, if available. * Not every transformation file format stores the fixed image path, in which case * an empty string is returned here. */ std::string GetFixedImagePath() const; /** Get moving image path, if available. * Not every transformation file format stores the moving image path, in which case * an empty string is returned here. */ std::string GetMovingImagePath() const; }; //@} } // namespace cmtk #endif // #ifndef __cmtkXformList_h_included_ cmtk-3.3.1/libs/Base/cmtkXformListEntry.cxx000066400000000000000000000055421276303427400206510ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2010, 2014 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 5192 $ // // $LastChangedDate: 2014-01-29 15:22:26 -0800 (Wed, 29 Jan 2014) $ // // $LastChangedBy: torstenrohlfing $ // */ #include "cmtkXformListEntry.h" #include cmtk::XformListEntry::XformListEntry ( const Xform::SmartConstPtr& xform, const bool inverse, const Types::Coordinate globalScale ) : m_Xform( xform ), InverseAffineXform( NULL ), m_PolyXform( NULL ), m_WarpXform( NULL ), Inverse( inverse ), GlobalScale( globalScale ) { if ( this->m_Xform ) { this->m_WarpXform = dynamic_cast( this->m_Xform.GetConstPtr() ); this->m_PolyXform = dynamic_cast( this->m_Xform.GetConstPtr() ); AffineXform::SmartConstPtr affineXform( AffineXform::SmartConstPtr::DynamicCastFrom( this->m_Xform ) ); if ( affineXform ) { this->InverseAffineXform = affineXform->MakeInverse(); } } } cmtk::XformListEntry::~XformListEntry() { // we got the inverse affine from AffineXform::MakeInverse, so we // need to get rid of it explicitly. delete this->InverseAffineXform; } cmtk::XformListEntry::SmartPtr cmtk::XformListEntry::CopyAsAffine() const { try { if ( this->m_WarpXform ) { return Self::SmartPtr( new Self( this->m_WarpXform->m_InitialAffineXform, this->Inverse, this->GlobalScale ) ); } else if ( this->m_PolyXform ) { return Self::SmartPtr( new Self( Xform::SmartPtr( new AffineXform( this->m_PolyXform->GetGlobalAffineMatrix() ) ), this->Inverse, this->GlobalScale ) ); } else { return Self::SmartPtr( new Self( this->m_Xform, this->Inverse, this->GlobalScale ) ); } } catch ( const AffineXform::MatrixType::SingularMatrixException& ) { cmtk::StdErr << "ERROR: singular matrix encountered in cmtk::XformListEntry::CopyAsAffine() - this should not be happening!\n"; throw cmtk::ExitException( 1 ); } } cmtk-3.3.1/libs/Base/cmtkXformListEntry.h000066400000000000000000000053751276303427400203020ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2011, 2014 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 5166 $ // // $LastChangedDate: 2014-01-13 14:26:33 -0800 (Mon, 13 Jan 2014) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkXformListEntry_h_included_ #define __cmtkXformListEntry_h_included_ #include #include #include #include #include #include namespace cmtk { /** \addtogroup Base */ //@{ /// An entry in a transformation sequence. class XformListEntry { public: /// This class. typedef XformListEntry Self; /// Smart pointer. typedef SmartPointer SmartPtr; /// Smart pointer-to-const. typedef SmartConstPointer SmartConstPtr; /// Constructor. XformListEntry( const Xform::SmartConstPtr& xform = Xform::SmartConstPtr::Null(), const bool inverse = false, const Types::Coordinate globalScale = 1.0 ); /// Destructor. ~XformListEntry(); /// The actual transformation. const Xform::SmartConstPtr m_Xform; /// The actual inverse if transformation is affine. const AffineXform* InverseAffineXform; /// The actual transformation as polynomial transformation. const PolynomialXform* m_PolyXform; /// The actual transformation as spline warp. const WarpXform* m_WarpXform; /// Apply forward (false) or inverse (true) transformation. bool Inverse; /// Global scale for normalizing the Jacobian. Types::Coordinate GlobalScale; /// Is this an affine transformation? bool IsAffine() const { return (this->m_WarpXform == NULL) && (this->m_PolyXform == NULL); } /// Make a copy of this entry in which all nonrigid transformations are replaced with their associated affine initializers. Self::SmartPtr CopyAsAffine() const; }; //@} } // namespace cmtk #endif // #ifndef __cmtkXformListEntry_h_included_ cmtk-3.3.1/libs/Base/cmtkXformUniformVolume.h000066400000000000000000000043751276303427400211530ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2010 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 2453 $ // // $LastChangedDate: 2010-10-18 10:33:06 -0700 (Mon, 18 Oct 2010) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkXformUniformVolume_h_included_ #define __cmtkXformUniformVolume_h_included_ #include #include #include #include namespace cmtk { /** \addtogroup Base */ //@{ /** Pre-compute transformation for grid locations in a uniform volume. */ class XformUniformVolume : /// Inherit from class to prevent copying. private CannotBeCopied { public: /// This class. typedef XformUniformVolume Self; /// Smart pointer to this class. typedef SmartPointer SmartPtr; /// Smart pointer-to-const to this class. typedef SmartConstPointer SmartConstPtr; /// Virtual destructor. virtual ~XformUniformVolume() {} /** Get transformed location of linked grid pixel. */ virtual void GetTransformedGrid( Vector3D& v, const int idxX, const int idxY, const int idxZ ) const = 0; /** Get transformed locations of a series (scanline) of linked grid pixels. */ virtual void GetTransformedGridRow( Vector3D *const v, const size_t numPoints, const int idxX, const int idxY, const int idxZ ) const = 0; }; //@} } // namespace cmtk #endif // #ifdef __cmtkXformUniformVolume_h_included_ cmtk-3.3.1/libs/Base/cmtkXform_Inverse.cxx000066400000000000000000000043751276303427400204710ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2014 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4929 $ // // $LastChangedDate: 2013-10-04 10:18:35 -0700 (Fri, 04 Oct 2013) $ // // $LastChangedBy: torstenrohlfing $ // */ #include "cmtkXform.h" bool cmtk::Xform::ApplyInverseWithInitial ( const Self::SpaceVectorType& target, Self::SpaceVectorType& source, const Self::SpaceVectorType& initial, const Types::Coordinate accuracy ) const { Self::SpaceVectorType u( initial ); this->ProjectToDomain( u ); Self::SpaceVectorType vu( this->Apply( initial ) ), delta; ((delta = vu) -= target); Types::Coordinate error = delta.RootSumOfSquares(); Types::Coordinate step = 1.0; while ( ( error > accuracy) && (step > 0.001) ) { // transform difference vector into original coordinate system using inverse Jacobian. delta *= this->GetJacobian( u ).GetInverse().GetTranspose(); // initialize line search (vu = u) -= (delta *= step); // project back into transformation domain, if necessary this->ProjectToDomain( vu ); // line search along transformed error direction Self::SpaceVectorType uNext( vu ); vu = this->Apply( vu ); (delta = vu) -= target; if ( error > delta.RootSumOfSquares() ) { error = delta.RootSumOfSquares(); u = uNext; } else { step *= 0.5; } } source = u; return !(error > accuracy); } cmtk-3.3.1/libs/Base/doxygen.h000066400000000000000000000023511276303427400161160ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2011 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 2734 $ // // $LastChangedDate: 2011-01-14 10:23:45 -0800 (Fri, 14 Jan 2011) $ // // $LastChangedBy: torstenrohlfing $ // */ /**\defgroup Base cmtkBase Library * This library provides classes for basic objects, such as volumes, transformations, * vectors, functionals, and meta information. */ cmtk-3.3.1/libs/CMakeLists.txt000066400000000000000000000027721276303427400161650ustar00rootroot00000000000000## ## Copyright 1997-2009 Torsten Rohlfing ## ## Copyright 2004-2011 SRI International ## ## This file is part of the Computational Morphometry Toolkit. ## ## http://www.nitrc.org/projects/cmtk/ ## ## The Computational Morphometry Toolkit is free software: you can ## redistribute it and/or modify it under the terms of the GNU General Public ## License as published by the Free Software Foundation, either version 3 of ## the License, or (at your option) any later version. ## ## The Computational Morphometry Toolkit is distributed in the hope that it ## will be useful, but WITHOUT ANY WARRANTY; without even the implied ## warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License along ## with the Computational Morphometry Toolkit. If not, see ## . ## ## $Revision: 3644 $ ## ## $LastChangedDate: 2011-12-22 09:23:24 -0800 (Thu, 22 Dec 2011) $ ## ## $LastChangedBy: torstenrohlfing $ ## SET(BUILD_LIBS_LIST System Numerics Base IO Registration Segmentation Recon) IF(CMTK_USE_QT) SET(BUILD_LIBS_LIST ${BUILD_LIBS_LIST} Pipeline Qt) ENDIF(CMTK_USE_QT) IF(CMTK_USE_CUDA) SET(BUILD_LIBS_LIST ${BUILD_LIBS_LIST} GPU) ENDIF(CMTK_USE_CUDA) IF(CMTK_BUILD_UNSTABLE) SET(BUILD_LIBS_LIST ${BUILD_LIBS_LIST} Unstable) ENDIF(CMTK_BUILD_UNSTABLE) FOREACH(lib ${BUILD_LIBS_LIST}) ADD_SUBDIRECTORY(${lib}) ENDFOREACH(lib ${BUILD_LIBS_LIST}) cmtk-3.3.1/libs/GPU/000077500000000000000000000000001276303427400140505ustar00rootroot00000000000000cmtk-3.3.1/libs/GPU/CMakeLists.txt000066400000000000000000000050011276303427400166040ustar00rootroot00000000000000## ## Copyright 1997-2011 Torsten Rohlfing ## ## Copyright 2004-2011 SRI International ## ## This file is part of the Computational Morphometry Toolkit. ## ## http://www.nitrc.org/projects/cmtk/ ## ## The Computational Morphometry Toolkit is free software: you can ## redistribute it and/or modify it under the terms of the GNU General Public ## License as published by the Free Software Foundation, either version 3 of ## the License, or (at your option) any later version. ## ## The Computational Morphometry Toolkit is distributed in the hope that it ## will be useful, but WITHOUT ANY WARRANTY; without even the implied ## warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License along ## with the Computational Morphometry Toolkit. If not, see ## . ## ## $Revision: 3644 $ ## ## $LastChangedDate: 2011-12-22 09:23:24 -0800 (Thu, 22 Dec 2011) $ ## ## $LastChangedBy: torstenrohlfing $ ## # Sources of non-specific classes. SET(cmtkGPU_SRCS cmtkDeviceHistogram.cxx cmtkDeviceUniformVolume.cxx cmtkDeviceUniformVolumeArray.cxx cmtkEntropyMinimizationIntensityCorrectionFunctionalDevice.cxx cmtkImagePairAffineRegistrationFunctionalDevice.cxx cmtkImageSymmetryPlaneFunctionalDevice.cxx cmtkSimpleLevelsetDevice.cxx ) IF(CMTK_USE_CUDA) SET(cmtkGPU_SRCS_CUDA cmtkDeviceImageConvolution_kernels.cu cmtkDeviceHistogram_kernels.cu cmtkDeviceMemoryCUDA.cxx cmtkDeviceArrayCUDA.cxx cmtkEntropyMinimizationIntensityCorrectionFunctionalDevice_kernels.cu cmtkImagePairAffineRegistrationFunctionalDevice_kernels.cu cmtkSimpleLevelsetDevice_kernels.cu cmtkSumReduction_kernel.cu ) CUDA_ADD_LIBRARY(cmtkGPU ${cmtkGPU_SRCS_CUDA} ${cmtkGPU_SRCS}) ENDIF(CMTK_USE_CUDA) TARGET_LINK_LIBRARIES(cmtkGPU cmtkSegmentation cmtkRegistration cmtkBase cmtkSystem cmtkNumerics) IF(CMTK_LIBRARY_PROPERTIES) SET_TARGET_PROPERTIES(cmtkGPU PROPERTIES ${CMTK_LIBRARY_PROPERTIES}) ENDIF(CMTK_LIBRARY_PROPERTIES) INSTALL(TARGETS cmtkGPU RUNTIME DESTINATION ${CMTK_INSTALL_BIN_DIR} COMPONENT runtime LIBRARY DESTINATION ${CMTK_INSTALL_LIB_DIR} COMPONENT runtime ARCHIVE DESTINATION ${CMTK_INSTALL_LIB_DIR} COMPONENT libraries) FILE(GLOB files_h "${CMAKE_CURRENT_SOURCE_DIR}/*.h") FILE(GLOB files_txx "${CMAKE_CURRENT_SOURCE_DIR}/*.txx") INSTALL(FILES ${files_h} ${files_txx} DESTINATION ${CMTK_INSTALL_INCLUDE_DIR}/GPU COMPONENT headers) cmtk-3.3.1/libs/GPU/cmtkCUDA.h000066400000000000000000000034601276303427400156170ustar00rootroot00000000000000/* // // Copyright 2010 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 2589 $ // // $LastChangedDate: 2010-12-03 09:59:14 -0800 (Fri, 03 Dec 2010) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkCUDA_h_included_ #define __cmtkCUDA_h_included_ #include #include #include #include #include "System/cmtkStackBacktrace.h" #define cmtkCheckCallCUDA(cmd) \ { const cudaError_t cudaError = cmd; if ( cudaError != cudaSuccess ) { fprintf( stderr, "CUDA command failed with error '%s' at %s:%d\n", cudaGetErrorString( cudaError ), __FILE__, __LINE__ ); cmtk::StackBacktrace::PrintBacktrace(); exit(1); } } #define cmtkCheckLastErrorCUDA \ { const cudaError_t cudaError = cudaGetLastError(); if ( cudaError != cudaSuccess ) { fprintf( stderr, "CUDA error '%s' at %s:%d\n", cudaGetErrorString( cudaError ), __FILE__, __LINE__ ); cmtk::StackBacktrace::PrintBacktrace(); exit( 1 ); } } #endif // #ifndef __cmtkCUDA_h_included_ cmtk-3.3.1/libs/GPU/cmtkDeviceArray.h000066400000000000000000000053371276303427400173060ustar00rootroot00000000000000/* // // Copyright 2010 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 2184 $ // // $LastChangedDate: 2010-08-06 16:03:41 -0700 (Fri, 06 Aug 2010) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkDeviceArray_h_included_ #define __cmtkDeviceArray_h_included_ #include #ifdef CMTK_USE_CUDA # include "cmtkDeviceArrayCUDA.h" #else # include "cmtkDeviceArrayCL.h" #endif namespace cmtk { /** \addtogroup GPU */ //@{ /// Resource managing class template for type-specific memory allocated on a GPU device through . template class DeviceArrayTemplate : /// Inherit from GPU-specific pointer base class. public DeviceArrayGPU { public: /// This class. typedef DeviceArrayTemplate Self; /// Smart pointer-to-const. typedef SmartConstPointer SmartConstPtr; /// Smart pointer. typedef SmartPointer SmartPtr; /// Base class. typedef DeviceArrayGPU Superclass; /// Constructor: allocate memory on device through base class. DeviceArrayTemplate( const FixedVector<3,int>& dims3 ) : DeviceArrayGPU( dims3 ) {} /// Create new object and allocate memory. static typename Self::SmartPtr Create( const FixedVector<3,int>& dims3 /*!< Array dimensions */ ) { return typename Self::SmartPtr( new Self( dims3 ) ); } /// Create new object, allocate, and initialize memory. static typename Self::SmartPtr Create( const FixedVector<3,int>& dims3, /*!< Array dimensions */ const float* initFrom /*!< Initialize from this region in host memory.*/ ) { Self* newObject = new Self( dims3 ); newObject->CopyToDevice( initFrom ); return typename Self::SmartPtr( newObject ); } }; #ifdef CMTK_USE_CUDA typedef DeviceArrayTemplate DeviceArray; #else typedef DeviceArrayTemplate DeviceArray; #endif //@} } // namespace cmtk #endif // #ifndef __cmtkDeviceArray_h_included_ cmtk-3.3.1/libs/GPU/cmtkDeviceArrayCUDA.cxx000066400000000000000000000060041276303427400203060ustar00rootroot00000000000000/* // // Copyright 2010 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 2398 $ // // $LastChangedDate: 2010-10-05 14:54:37 -0700 (Tue, 05 Oct 2010) $ // // $LastChangedBy: torstenrohlfing $ // */ #include "cmtkDeviceArrayCUDA.h" #include #include #include cmtk::DeviceArrayCUDA ::DeviceArrayCUDA( const FixedVector<3,int>& dims3 ) : m_Dims( dims3 ) { const struct cudaChannelFormatDesc desc = cudaCreateChannelDesc(); cmtkCheckCallCUDA( cudaMalloc3DArray( &(this->m_DeviceArrayPtr), &desc, make_cudaExtent( this->m_Dims[0], this->m_Dims[1], this->m_Dims[2] ) ) ); } cmtk::DeviceArrayCUDA ::~DeviceArrayCUDA() { if ( this->m_DeviceArrayPtr ) cmtkCheckCallCUDA( cudaFreeArray( this->m_DeviceArrayPtr ) ); } void cmtk::DeviceArrayCUDA ::CopyToDevice( const float* data ) { cudaMemcpy3DParms copyParams = {0}; copyParams.srcPtr = make_cudaPitchedPtr( (void*)data, this->m_Dims[0]*sizeof(float), this->m_Dims[0], this->m_Dims[1] ); copyParams.dstArray = this->m_DeviceArrayPtr; copyParams.extent = make_cudaExtent( this->m_Dims[0], this->m_Dims[1], this->m_Dims[2] ); copyParams.kind = cudaMemcpyHostToDevice; cmtkCheckCallCUDA( cudaMemcpy3D( ©Params ) ); } void cmtk::DeviceArrayCUDA ::CopyOnDeviceToArray( const float* data ) { cudaMemcpy3DParms copyParams = {0}; copyParams.srcPtr = make_cudaPitchedPtr( (void*)data, this->m_Dims[0]*sizeof(float), this->m_Dims[0], this->m_Dims[1] ); copyParams.dstArray = this->m_DeviceArrayPtr; copyParams.extent = make_cudaExtent( this->m_Dims[0], this->m_Dims[1], this->m_Dims[2] ); copyParams.kind = cudaMemcpyDeviceToDevice; cmtkCheckCallCUDA( cudaMemcpy3D( ©Params ) ); } void cmtk::DeviceArrayCUDA ::CopyOnDeviceToLinear( float* data ) { cudaMemcpy3DParms copyParams = {0}; copyParams.dstPtr = make_cudaPitchedPtr( (void*)data, this->m_Dims[0]*sizeof(float), this->m_Dims[0], this->m_Dims[1] ); copyParams.srcArray = this->m_DeviceArrayPtr; copyParams.extent = make_cudaExtent( this->m_Dims[0], this->m_Dims[1], this->m_Dims[2] ); copyParams.kind = cudaMemcpyDeviceToDevice; cmtkCheckCallCUDA( cudaMemcpy3D( ©Params ) ); } cmtk-3.3.1/libs/GPU/cmtkDeviceArrayCUDA.h000066400000000000000000000052151276303427400177360ustar00rootroot00000000000000/* // // Copyright 2010 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 2398 $ // // $LastChangedDate: 2010-10-05 14:54:37 -0700 (Tue, 05 Oct 2010) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkDeviceArrayCUDA_h_included_ #define __cmtkDeviceArrayCUDA_h_included_ #include #include #include #include /// Forward declaration. struct cudaArray; namespace cmtk { /** \addtogroup GPU */ //@{ /// Resource managing class for raw memory allocated on a GPU device through CUDA. class DeviceArrayCUDA /// Make sure this is never copied. : private CannotBeCopied { public: /// This class. typedef DeviceArrayCUDA Self; /// Smart pointer-to-const. typedef SmartConstPointer SmartConstPtr; /// Smart pointer. typedef SmartPointer SmartPtr; /// Device array pointer. typedef struct cudaArray* DeviceArrayPointer; /// Exception for failed allocation. class bad_alloc : public std::bad_alloc {}; /// Constructor: allocate array through CUDA. DeviceArrayCUDA( const FixedVector<3,int>& dims3 ); /// Destructor: free array through CUDA. virtual ~DeviceArrayCUDA(); /// Copy host linear memory to device. void CopyToDevice( const float* data ); /// Copy from linear device memory to device array. void CopyOnDeviceToArray( const float* data ); /// Copy from device array to linear device memory. void CopyOnDeviceToLinear( float* data ); /// Get device array pointer. DeviceArrayPointer GetArrayOnDevice() { return this->m_DeviceArrayPtr; } private: /// Array dimensions. FixedVector<3,int> m_Dims; /// Opaque pointer to array on device. DeviceArrayPointer m_DeviceArrayPtr; }; } // namespace cmtk #endif // #ifndef __cmtkDeviceArrayCUDA_h_included_ cmtk-3.3.1/libs/GPU/cmtkDeviceHistogram.cxx000066400000000000000000000053431276303427400205350ustar00rootroot00000000000000/* // // Copyright 2010 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 2398 $ // // $LastChangedDate: 2010-10-05 14:54:37 -0700 (Tue, 05 Oct 2010) $ // // $LastChangedBy: torstenrohlfing $ // */ #include "cmtkDeviceHistogram.h" #include "cmtkDeviceHistogram_kernels.h" #include #include cmtk::DeviceHistogram ::DeviceHistogram( const size_t numberOfBins ) { this->m_NumberOfBins = numberOfBins; this->m_NumberOfBinsPadded = cmtk::Memory::GetNextPowerOfTwo( this->m_NumberOfBins ); if ( this->m_NumberOfBinsPadded > 512 ) { throw Exception( "Exceeded maximum number of histogram bins (512)" ); } this->m_OnDeviceData = DeviceMemory::Create( this->m_NumberOfBinsPadded ); this->m_OnDeviceData->SetToZero(); this->m_OnDeviceResult = DeviceMemory::Create( this->m_NumberOfBinsPadded ); this->m_OnDeviceResult->SetToZero(); } void cmtk::DeviceHistogram ::Reset() { this->m_OnDeviceData->SetToZero(); } void cmtk::DeviceHistogram ::Populate( const DeviceMemory& dataOnDevice, const float rangeFrom, const float rangeTo, const bool logScale ) { cmtkDeviceHistogramPopulate( this->m_OnDeviceData->Ptr(), dataOnDevice.Ptr(), rangeFrom, rangeTo, logScale, this->m_NumberOfBins, dataOnDevice.GetNumberOfItems() ); } void cmtk::DeviceHistogram ::Populate( const DeviceMemory& dataOnDevice, const DeviceMemory& maskOnDevice, const float rangeFrom, const float rangeTo, const bool logScale ) { cmtkDeviceHistogramPopulate( this->m_OnDeviceData->Ptr(), dataOnDevice.Ptr(), maskOnDevice.Ptr(), rangeFrom, rangeTo, logScale, this->m_NumberOfBins, dataOnDevice.GetNumberOfItems() ); } float cmtk::DeviceHistogram ::GetEntropy() const { cmtkDeviceHistogramEntropy( this->m_OnDeviceResult->Ptr(), this->m_OnDeviceData->Ptr(), this->m_NumberOfBinsPadded ); float result; this->m_OnDeviceResult->CopyToHost( &result, 1 ); return result; } cmtk-3.3.1/libs/GPU/cmtkDeviceHistogram.h000066400000000000000000000057221276303427400201630ustar00rootroot00000000000000/* // // Copyright 2010 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 2184 $ // // $LastChangedDate: 2010-08-06 16:03:41 -0700 (Fri, 06 Aug 2010) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkDeviceHistogram_h_included_ #define __cmtkDeviceHistogram_h_included_ #include #include "cmtkDeviceMemory.h" namespace cmtk { /** \addtogroup GPU */ //@{ /// Device memory representation of a uniform volume with static helper functions. class DeviceHistogram { public: /// This class. typedef DeviceHistogram Self; /// Smart pointer to this class. typedef SmartPointer SmartPtr; /// Create device representation of volume object. static Self::SmartPtr Create( const size_t numberOfBins /*!< Allocate device memory for data as multiple of this value.*/ ) { return Self::SmartPtr( new Self( numberOfBins ) ); } /// Return device data pointer. DeviceMemory& GetDataOnDevice() { return *(this->m_OnDeviceData); } /// Return device data pointer. const DeviceMemory& GetDataOnDevice() const { return *(this->m_OnDeviceData); } /// Reset histogram, i.e., set all bins to zero. void Reset(); /// Populate histogram from data on device. void Populate( const DeviceMemory& dataOnDevice, const float rangeFrom, const float rangeTo, const bool logScale = false ); /// Populate histogram from data on device using binary mask. void Populate( const DeviceMemory& dataOnDevice, const DeviceMemory& maskOnDevice, const float rangeFrom, const float rangeTo, const bool logScale = false ); /// Get entropy. float GetEntropy() const; private: /// Constructor. DeviceHistogram( const size_t numberOfBins ); /// User-selected number of bins. size_t m_NumberOfBins; /// Number of bins after padding to power of 2. size_t m_NumberOfBinsPadded; /// Managed device memory pointer to histogram data. DeviceMemory::SmartPtr m_OnDeviceData; /// Managed device memory pointer to result of histogram operations. mutable DeviceMemory::SmartPtr m_OnDeviceResult; }; } // namespace cmtk #endif // #ifndef __cmtkDeviceHistogram_h_included_ cmtk-3.3.1/libs/GPU/cmtkDeviceHistogram_kernels.cu000066400000000000000000000236561276303427400220740ustar00rootroot00000000000000/* // // Copyright 2010 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 2012 $ // // $LastChangedDate: 2010-07-19 15:14:26 -0700 (Mon, 19 Jul 2010) $ // // $LastChangedBy: torstenrohlfing $ // */ #include "cmtkDeviceHistogram_kernels.h" #include #include extern __shared__ float shared[]; __global__ void cmtkDeviceHistogramEntropyKernel( float* result, const float *dataPtr ) { int tx = threadIdx.x; // first, load data into shared memory float *working = &shared[0]; working[tx] = dataPtr[tx]; __syncthreads(); // second, compute sum of all bin values via butterfly for ( int bit = 1; bit < blockDim.x; bit <<= 1 ) { const float sum = working[tx] + working[tx^bit]; __syncthreads(); working[tx] = sum; __syncthreads(); } // third, normalize if ( working[tx] ) { working[tx] = dataPtr[tx] / working[tx]; } // fourth, do p*log(p) if ( working[tx] > 0 ) { working[tx] *= log( working[tx] ); } else { working[tx] = 0; } __syncthreads(); // fifth, another butterfly to compute \sum[p*log(p)] for ( int bit = 1; bit < blockDim.x; bit <<= 1 ) { const float sum = working[tx] + working[tx^bit]; __syncthreads(); working[tx] = sum; __syncthreads(); } result[tx] = -working[tx]; } void cmtkDeviceHistogramEntropy( float* result, const float* dataPtr, int numberOfBins ) { dim3 dimBlock( numberOfBins, 1 ); dim3 dimGrid( 1, 1 ); // start kernel and allocate shared memory for "numberOfBins" floats cmtkDeviceHistogramEntropyKernel<<>>( result, dataPtr ); } __global__ void cmtkDeviceHistogramConsolidate( float* histPtr, float* localPtr, const int numberOfBins, const int numberOfThreads ) { int tx = threadIdx.x; // finally, add all thread working histograms to output histogram for ( int idx = 1+tx; idx <= numberOfBins; idx += blockDim.x ) { float sum = 0; for ( int hx = 0; hx < numberOfThreads; ++hx ) { sum += localPtr[ idx + hx*(1+numberOfBins) ]; } histPtr[idx-1] += sum; } } __global__ void cmtkDeviceHistogramPopulateKernel( float* histPtr, float* localPtr, const float *dataPtr, const float rangeFrom, const float rangeTo, const int numberOfBins, const int numberOfSamples ) { int tx = threadIdx.x; int offs = tx + blockDim.x * blockIdx.x; int skip = blockDim.x * gridDim.x; // working histogram for this thread float* working = localPtr + (numberOfBins+1)*offs; // start by resetting all histogram bins to 0 for ( int i = 0; i < numberOfBins; ++i ) working[i] = 0; // populate histogram bins const float binScale = (numberOfBins-1) / (rangeTo - rangeFrom); for ( int offset = offs; offset < numberOfSamples; offset += skip ) { int index = 1+truncf( fmaxf( 0, fminf( numberOfBins-1, (dataPtr[offset] - rangeFrom) * binScale ) ) ); // 1+... for consistency with masked computation; bin0 is ignored in final analysis. ++working[ index ]; } } __global__ void cmtkDeviceHistogramPopulateLogKernel( float* histPtr, float* localPtr, const float *dataPtr, const float rangeFrom, const float rangeTo, const int numberOfBins, const int numberOfSamples ) { int tx = threadIdx.x; int offs = tx + blockDim.x * blockIdx.x; int skip = blockDim.x * gridDim.x; // working histogram for this thread float* working = localPtr + (numberOfBins+1)*offs; // start by resetting all histogram bins to 0 for ( int i = 0; i < numberOfBins; ++i ) working[i] = 0; // populate histogram bins const float binScale = (numberOfBins-1) / (rangeTo - rangeFrom); const float logNumBins = log( static_cast( numberOfBins ) ); for ( int offset = offs; offset < numberOfSamples; offset += skip ) { int index = 1+truncf( (numberOfBins-1) * fmaxf( 0, fminf( 1, log((1 + dataPtr[offset]-rangeFrom)*binScale)/logNumBins ) ) ); ++working[ index ]; } } void cmtkDeviceHistogramPopulate( float* histPtr, const float* dataPtr, const float rangeFrom, const float rangeTo, const bool logScale, const int numberOfBins, const int numberOfSamples ) { dim3 dimBlock( 256, 1 ); dim3 dimGrid( 16, 1 ); const size_t nThreads = 16*256; const size_t lBytes = sizeof(float) * (numberOfBins+1) * nThreads; float* localPtr; if ( cudaMalloc( &localPtr, lBytes ) != cudaSuccess ) { fprintf( stderr, "ERROR: cudaMalloc() failed with error %s\n",cudaGetErrorString( cudaGetLastError() ) ); exit( 1 ); } if ( cudaMemset( localPtr, 0, lBytes ) != cudaSuccess ) { fprintf( stderr, "ERROR: cudaMemset() failed with error %s\n",cudaGetErrorString( cudaGetLastError() ) ); exit( 1 ); } if ( logScale ) cmtkDeviceHistogramPopulateLogKernel<<>>( histPtr, localPtr, dataPtr, rangeFrom, rangeTo, numberOfBins, numberOfSamples ); else cmtkDeviceHistogramPopulateKernel<<>>( histPtr, localPtr, dataPtr, rangeFrom, rangeTo, numberOfBins, numberOfSamples ); cudaError_t kernelError = cudaGetLastError(); if ( kernelError != cudaSuccess ) { fprintf( stderr, "ERROR: CUDA kernel failed with error %s\n", cudaGetErrorString( kernelError ) ); exit( 1 ); } dim3 dimBlock2( 256, 1 ); dim3 dimGrid2( 1, 1 ); cmtkDeviceHistogramConsolidate<<>>( histPtr, localPtr, numberOfBins, nThreads ); kernelError = cudaGetLastError(); if ( kernelError != cudaSuccess ) { fprintf( stderr, "ERROR: CUDA kernel failed with error %s\n", cudaGetErrorString( kernelError ) ); exit( 1 ); } cudaFree( localPtr ); } __global__ void cmtkDeviceHistogramPopulateWithMaskKernel( float* histPtr, float* localPtr, const float *dataPtr, const int *maskPtr, const float rangeFrom, const float rangeTo, const int numberOfBins, const int numberOfSamples ) { int tx = threadIdx.x; int offs = tx + blockDim.x * blockIdx.x; int skip = blockDim.x * gridDim.x; // working histogram for this thread in shared memory float* working = localPtr + (numberOfBins+1)*offs; // populate histogram bins const float binScale = (numberOfBins-1) / (rangeTo - rangeFrom); for ( int offset = offs; offset < numberOfSamples; offset += skip ) { const float d = (dataPtr[offset] - rangeFrom) * binScale; const float m = maskPtr[offset]; float binIndex = fmaxf( 0, fminf( numberOfBins-1, d ) ); const int index = truncf( (1+binIndex) * m ); ++working[ index ]; } } __global__ void cmtkDeviceHistogramPopulateLogWithMaskKernel( float* histPtr, float* localPtr, const float *dataPtr, const int *maskPtr, const float rangeFrom, const float rangeTo, const int numberOfBins, const int numberOfSamples ) { int tx = threadIdx.x; int offs = tx + blockDim.x * blockIdx.x; int skip = blockDim.x * gridDim.x; // working histogram for this thread in shared memory float* working = localPtr + (numberOfBins+1)*offs; // populate histogram bins const float binScale = (numberOfBins-1) / (rangeTo - rangeFrom); const float logNumBins = log( static_cast( numberOfBins ) ); for ( int offset = offs; offset < numberOfSamples; offset += skip ) { const float d = log((1 + dataPtr[offset]-rangeFrom)*binScale); const float m = maskPtr[offset]; float binIndex = (numberOfBins-1) * fmaxf( 0, fminf( 1, d / logNumBins ) ); const int index = truncf( (1+binIndex) * m ); ++working[ index ]; } } void cmtkDeviceHistogramPopulate( float* histPtr, const float* dataPtr, const int* maskPtr, const float rangeFrom, const float rangeTo, const bool logScale, const int numberOfBins, const int numberOfSamples ) { dim3 dimBlock( 256, 1 ); dim3 dimGrid( 16, 1 ); const size_t nThreads = 16*256; const size_t lBytes = sizeof(float) * (numberOfBins+1) * nThreads; float* localPtr; if ( cudaMalloc( &localPtr, lBytes ) != cudaSuccess ) { fprintf( stderr, "ERROR: cudaMalloc() failed with error %s\n",cudaGetErrorString( cudaGetLastError() ) ); exit( 1 ); } if ( cudaMemset( localPtr, 0, lBytes ) != cudaSuccess ) { fprintf( stderr, "ERROR: cudaMemset() failed with error %s\n",cudaGetErrorString( cudaGetLastError() ) ); exit( 1 ); } if ( logScale ) cmtkDeviceHistogramPopulateLogWithMaskKernel<<>>( histPtr, localPtr, dataPtr, maskPtr, rangeFrom, rangeTo, numberOfBins, numberOfSamples ); else cmtkDeviceHistogramPopulateWithMaskKernel<<>>( histPtr, localPtr, dataPtr, maskPtr, rangeFrom, rangeTo, numberOfBins, numberOfSamples ); cudaError_t kernelError = cudaGetLastError(); if ( kernelError != cudaSuccess ) { fprintf( stderr, "ERROR: CUDA kernel failed with error %s\n", cudaGetErrorString( kernelError ) ); exit( 1 ); } dim3 dimBlock2( 256, 1 ); dim3 dimGrid2( 1, 1 ); cmtkDeviceHistogramConsolidate<<>>( histPtr, localPtr, numberOfBins, nThreads ); kernelError = cudaGetLastError(); if ( kernelError != cudaSuccess ) { fprintf( stderr, "ERROR: CUDA kernel failed with error %s\n", cudaGetErrorString( kernelError ) ); exit( 1 ); } cudaFree( localPtr ); } cmtk-3.3.1/libs/GPU/cmtkDeviceHistogram_kernels.cxx000066400000000000000000000031231276303427400222520ustar00rootroot00000000000000/* // // Copyright 2010 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 2152 $ // // $LastChangedDate: 2010-08-04 10:19:33 -0700 (Wed, 04 Aug 2010) $ // // $LastChangedBy: torstenrohlfing $ // */ #include "cmtkDeviceHistogram_kernels.h" #include #include void cmtkDeviceHistogramEntropy( float* result, const float* dataPtr, int numberOfBins ) { } void cmtkDeviceHistogramPopulate( float* histPtr, const float* dataPtr, const float rangeFrom, const float rangeTo, const bool logScale, const int numberOfBins, const int numberOfSamples ) { } void cmtkDeviceHistogramPopulate( float* histPtr, const float* dataPtr, const int* maskPtr, const float rangeFrom, const float rangeTo, const bool logScale, const int numberOfBins, const int numberOfSamples ) { } cmtk-3.3.1/libs/GPU/cmtkDeviceHistogram_kernels.h000066400000000000000000000052321276303427400217020ustar00rootroot00000000000000/* // // Copyright 2010 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 2184 $ // // $LastChangedDate: 2010-08-06 16:03:41 -0700 (Fri, 06 Aug 2010) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkDeviceHistogram_kernels_h_included_ #define __cmtkDeviceHistogram_kernels_h_included_ #include /** \addtogroup GPU */ //@{ /// Populate histogram from data, entirely on device. void cmtkDeviceHistogramPopulate( float* histPtr, /*!< Device pointer to histogram data (output). */ const float* dataPtr, /*!< Device pointer to input image data. */ const float rangeFrom, /*!< Histogram range from this value. */ const float rangeTo, /*!< Histogram range to this value. */ const bool logScale, /*!< Use log scale for value-to-bin mapping to make entropy scale-invariant. */ const int numberOfBins, /*!< Number of histogram bins. */ const int numberOfSamples /*!< Number of data samples */ ); /// Populate histogram from data using binary mask, entirely on device. void cmtkDeviceHistogramPopulate( float* histPtr, /*!< Device pointer to histogram data (output). */ const float* dataPtr, /*!< Device pointer to input image data. */ const int* maskPtr, /*!< Device pointer to binary mask data (0=background, 1=foreground). */ const float rangeFrom, /*!< Histogram range from this value. */ const float rangeTo, /*!< Histogram range to this value. */ const bool logScale, /*!< Use log scale for value-to-bin mapping to make entropy scale-invariant. */ const int numberOfBins, /*!< Number of histogram bins. */ const int numberOfSamples /*!< Number of data samples */ ); /// Compute entropy from histogram on device. void cmtkDeviceHistogramEntropy( float* result, const float* dataPtr, int numberOfBins ); //@} #endif // #ifndef __cmtkDeviceHistogram_kernels_h_included_ cmtk-3.3.1/libs/GPU/cmtkDeviceImageConvolution_kernels.cu000066400000000000000000000144121276303427400234070ustar00rootroot00000000000000/* // // Copyright 2010 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 3168 $ // // $LastChangedDate: 2011-04-22 12:51:51 -0700 (Fri, 22 Apr 2011) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifdef _WIN32 // This fixes a strange compile error using VisualStudio 2010 Express. // See http://forums.nvidia.com/index.php?showtopic=67822 #define WIN32_LEAN_AND_MEAN #endif #include "cmtkDeviceImageConvolution_kernels.h" #include "System/cmtkMemory.h" #include "GPU/cmtkCUDA.h" #include "GPU/cmtkDeviceMemory.h" #include /// Texture reference to volume data. texture texRef; __constant__ float deviceKernel[128]; __global__ void cmtkDeviceImageConvolutionKernelX( float* dest, int dims0, int dims1, int dims2, int kernelLength, int kernelCenter ) { const int offs = threadIdx.x + blockDim.x * blockIdx.x; const int x = offs % dims0; const int y = (offs / dims0) % dims1; const int z = offs / (dims0 * dims1); float sum = 0, total = 0; for ( int i = 0; i < kernelLength; ++i ) { const int xx = x + i - kernelCenter; const float w = ( (xx>=0) && (xx=0) && (yy=0) && (zz() ) ); const int nPixels = dims3[0] * dims3[1] * dims3[2]; cmtkCheckCallCUDA( cudaMemcpyToSymbol( deviceKernel, kernelX, kernelLengthX * sizeof( float ), 0, cudaMemcpyHostToDevice ) ); dim3 threads( 512 ); dim3 blocks( nPixels/512+1 ); cmtkDeviceImageConvolutionKernelX<<>>( dest, dims3[0], dims3[1], dims3[2], kernelLengthX, (kernelLengthX-1)>>1 ); cmtkCheckLastErrorCUDA; cudaMemcpy3DParms copyParams = {0}; copyParams.srcPtr = make_cudaPitchedPtr( (void*)dest, dims3[0]*sizeof(float), dims3[0], dims3[1] ); copyParams.dstArray = (struct cudaArray*) array; copyParams.extent = make_cudaExtent( dims3[0], dims3[1], dims3[2] ); copyParams.kind = cudaMemcpyDeviceToDevice; cmtkCheckCallCUDA( cudaMemcpy3D( ©Params ) ); cmtkCheckCallCUDA( cudaMemcpyToSymbol( deviceKernel, kernelY, kernelLengthY * sizeof( float ), 0, cudaMemcpyHostToDevice ) ); cmtkDeviceImageConvolutionKernelY<<>>( dest, dims3[0], dims3[1], dims3[2], kernelLengthY, (kernelLengthY-1)>>1 ); cmtkCheckLastErrorCUDA; cmtkCheckCallCUDA( cudaMemcpy3D( ©Params ) ); cmtkCheckCallCUDA( cudaMemcpyToSymbol( deviceKernel, kernelZ, kernelLengthZ * sizeof( float ), 0, cudaMemcpyHostToDevice ) ); cmtkDeviceImageConvolutionKernelZ<<>>( dest, dims3[0], dims3[1], dims3[2], kernelLengthZ, (kernelLengthZ-1)>>1 ); cmtkCheckLastErrorCUDA; cmtkCheckCallCUDA( cudaUnbindTexture( texRef ) ); } void cmtk::DeviceImageConvolutionInPlace( const int* dims3, void* array, const int kernelLengthX, const float* kernelX, const int kernelLengthY, const float* kernelY, const int kernelLengthZ, const float* kernelZ ) { const int nPixels = dims3[0] * dims3[1] * dims3[2]; cmtk::DeviceMemory temporary( nPixels ); // call out-of-place-place convolution DeviceImageConvolution( temporary.Ptr(), dims3, array, kernelLengthX, kernelX, kernelLengthY, kernelY, kernelLengthZ, kernelZ ); // copy back into original array cudaMemcpy3DParms copyParams = {0}; copyParams.srcPtr = make_cudaPitchedPtr( (void*)temporary.Ptr(), dims3[0]*sizeof(float), dims3[0], dims3[1] ); copyParams.dstArray = (struct cudaArray*) array; copyParams.extent = make_cudaExtent( dims3[0], dims3[1], dims3[2] ); copyParams.kind = cudaMemcpyDeviceToDevice; cmtkCheckCallCUDA( cudaMemcpy3D( ©Params ) ); cmtkCheckCallCUDA( cudaUnbindTexture( texRef ) ); } cmtk-3.3.1/libs/GPU/cmtkDeviceImageConvolution_kernels.h000066400000000000000000000040261276303427400232270ustar00rootroot00000000000000/* // // Copyright 2010 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 2119 $ // // $LastChangedDate: 2010-07-30 11:38:04 -0700 (Fri, 30 Jul 2010) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkDeviceImageConvolution_kernels_h_included_ #define __cmtkDeviceImageConvolution_kernels_h_included_ #include namespace cmtk { /** \addtogroup GPU */ //@{ /** Convolution of a 3D image (CUDA array) with a separable 3D kernel. *\warning Even though the convolution result is ultimately stored out-of-place in the * given target memory, the input array's content is destroyed in the process. */ void DeviceImageConvolution( float* dest, const int* dims3, void* array, const int kernelLengthX, const float* kernelX, const int kernelLengthY, const float* kernelY, const int kernelLengthZ, const float* kernelZ ); /** In-place convolution of a 3D image (CUDA array) with a separable 3D kernel. */ void DeviceImageConvolutionInPlace( const int* dims3, void* array, const int kernelLengthX, const float* kernelX, const int kernelLengthY, const float* kernelY, const int kernelLengthZ, const float* kernelZ ); //@} } // namespace cmtk #endif // #ifndef __cmtkDeviceImageConvolution_kernels_h_included_ cmtk-3.3.1/libs/GPU/cmtkDeviceMemory.h000066400000000000000000000102551276303427400174730ustar00rootroot00000000000000/* // // Copyright 2010 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 2184 $ // // $LastChangedDate: 2010-08-06 16:03:41 -0700 (Fri, 06 Aug 2010) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkDeviceMemory_h_included_ #define __cmtkDeviceMemory_h_included_ #include #ifdef CMTK_USE_CUDA # include "cmtkDeviceMemoryCUDA.h" #else # include "cmtkDeviceMemoryCL.h" #endif namespace cmtk { /** \addtogroup GPU */ //@{ /// Resource managing class template for type-specific memory allocated on a GPU device through . #ifdef CMTK_USE_CUDA template #else template #endif class DeviceMemory : /// Inherit privately from raw pointer base class. private DeviceMemoryGPU { public: /// This class. typedef DeviceMemory Self; /// Smart pointer-to-const. typedef SmartConstPointer SmartConstPtr; /// Smart pointer. typedef SmartPointer SmartPtr; /// Base class. typedef DeviceMemoryGPU Superclass; /// Constructor: allocate memory on device through base class. DeviceMemory( const size_t n, /*!< Number of items.*/ const size_t padToMultiple = 1 ) : DeviceMemoryGPU( n * sizeof( T ), padToMultiple * sizeof( T ) ), m_NumberOfItems( n ) {} /// Create new object and allocate memory. static typename Self::SmartPtr Create( const size_t nItems, /*!< Allocate (at least) this many items of type T.*/ const size_t padToMultiple = 1 /*!< Pad number of allocated elements to next multiple of this number.*/ ) { return typename Self::SmartPtr( new Self( nItems, padToMultiple ) ); } /// Create new object, allocate, and initialize memory. static typename Self::SmartPtr Create( const size_t nItems, /*!< Allocate (at least) this many items of type T.*/ const T* initFrom, /*!< Initialize from this region in host memory.*/ const size_t padToMultiple = 1 /*!< Pad number of allocated elements to next multiple of this number.*/ ) { Self* newObject = new Self( nItems, padToMultiple ); newObject->CopyToDevice( initFrom, nItems ); return typename Self::SmartPtr( newObject ); } /// Get const pointer. const T* Ptr() const { return static_cast( this->m_PointerDevice ); } /// Get non-const pointer. T* Ptr() { return static_cast( this->m_PointerDevice ); } /// Copy from host to device memory. void CopyToDevice( const T *const srcPtrHost, const size_t count ) { this->Superclass::CopyToDevice( srcPtrHost, count * sizeof( T ) ); } /// Copy from device to host memory. void CopyToHost( void *const dstPtrHost, const size_t count ) const { this->Superclass::CopyToHost( dstPtrHost, count * sizeof( T ) ); } /// Copy between two device memory locations. void CopyOnDevice( const Self& srcPtrDevice, const size_t count ) { this->Superclass::CopyOnDevice( srcPtrDevice, count * sizeof( T ) ); } /// Clear device memory (set to all zeroes). void SetToZero() { this->Superclass::Memset( 0, this->m_NumberOfItems * sizeof( T ) ); } /// Get number of items. size_t GetNumberOfItems() const { return this->m_NumberOfItems; } private: /// Number of items allocated. size_t m_NumberOfItems; }; //@} } // namespace cmtk #endif // #ifndef __cmtkDeviceMemory_h_included_ cmtk-3.3.1/libs/GPU/cmtkDeviceMemoryCUDA.cxx000066400000000000000000000044451276303427400205070ustar00rootroot00000000000000/* // // Copyright 2010 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 2398 $ // // $LastChangedDate: 2010-10-05 14:54:37 -0700 (Tue, 05 Oct 2010) $ // // $LastChangedBy: torstenrohlfing $ // */ #include "cmtkDeviceMemoryCUDA.h" #include #include namespace cmtk { DeviceMemoryCUDA ::DeviceMemoryCUDA( const size_t nBytes, const size_t padToMultiple ) { this->m_NumberOfBytesAllocated = (((nBytes-1) / padToMultiple)+1) * padToMultiple; cmtkCheckCallCUDA( cudaMalloc( &(this->m_PointerDevice), this->m_NumberOfBytesAllocated ) ); } DeviceMemoryCUDA ::~DeviceMemoryCUDA() { if ( this->m_PointerDevice ) cmtkCheckCallCUDA( cudaFree( this->m_PointerDevice ) ); } void DeviceMemoryCUDA ::CopyToDevice( const void *const srcPtrHost, const size_t nBytes ) { cmtkCheckCallCUDA( cudaMemcpy( this->m_PointerDevice, srcPtrHost, nBytes, cudaMemcpyHostToDevice ) ); } void DeviceMemoryCUDA ::CopyToHost( void *const dstPtrHost, const size_t nBytes ) const { cmtkCheckCallCUDA( cudaMemcpy( dstPtrHost, this->m_PointerDevice, nBytes, cudaMemcpyDeviceToHost ) ); } void DeviceMemoryCUDA ::CopyOnDevice( const Self& srcPtrDevice, const size_t nBytes ) { cmtkCheckCallCUDA( cudaMemcpy( this->m_PointerDevice, srcPtrDevice.m_PointerDevice, nBytes, cudaMemcpyDeviceToDevice ) ); } void DeviceMemoryCUDA ::Memset( const int value, const size_t nBytes ) { cmtkCheckCallCUDA( cudaMemset( this->m_PointerDevice, value, nBytes ) ); } } // namespace cmtk cmtk-3.3.1/libs/GPU/cmtkDeviceMemoryCUDA.h000066400000000000000000000063171276303427400201340ustar00rootroot00000000000000/* // // Copyright 2010 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 2398 $ // // $LastChangedDate: 2010-10-05 14:54:37 -0700 (Tue, 05 Oct 2010) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkDeviceMemoryCUDA_h_included_ #define __cmtkDeviceMemoryCUDA_h_included_ #include #include #include #include #include namespace cmtk { /** \addtogroup GPU */ //@{ /// Resource managing class for raw memory allocated on a GPU device through CUDA. class DeviceMemoryCUDA /// Make sure this is never copied. : private CannotBeCopied { public: /// This class. typedef DeviceMemoryCUDA Self; /// Smart pointer-to-const. typedef SmartConstPointer SmartConstPtr; /// Smart pointer. typedef SmartPointer SmartPtr; /// Exception for failed allocation. class bad_alloc : public std::bad_alloc {}; /// Constructor: allocate memory through CUDA. DeviceMemoryCUDA( const size_t nBytes /*!< Number of bytes to allocate */, const size_t padToMultiple = 1 /*!< Pad to allocate nearest multiple of this many bytes. */ ); /// Destructor: free memory through CUDA. virtual ~DeviceMemoryCUDA(); /// Get number of bytes allocated on device. size_t GetNumberOfBytesAllocated() { return this->m_NumberOfBytesAllocated; } protected: /// Create new object and allocate memory. Self::SmartPtr Alloc( const size_t nBytes, const size_t padToMultiple = 1 ) { return Self::SmartPtr( new Self( nBytes, padToMultiple ) ); } /// Copy from host to device memory. void CopyToDevice( const void *const srcPtrHost, const size_t nBytes ); /// Copy from device to host memory. void CopyToHost( void *const dstPtrHost, const size_t nBytes ) const; /// Copy between two device memory locations. void CopyOnDevice( const Self& srcPtrDevice, const size_t nBytes ); /// Copy between two device memory locations. void Memset( const int value, const size_t nBytes ); /** Raw pointer to allocated device memory. * Note that this is a device memory space pointer, which is not valid in * host memory and can, therefore, not be dereferenced in host code. */ void* m_PointerDevice; /// Total number of bytes allocated on device. size_t m_NumberOfBytesAllocated; }; //@} } // namespace cmtk #endif // #ifndef __cmtkDeviceMemoryCUDA_h_included_ cmtk-3.3.1/libs/GPU/cmtkDeviceThresholdData_kernels.cu000066400000000000000000000031641276303427400226550ustar00rootroot00000000000000/* // // Copyright 2010 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 2118 $ // // $LastChangedDate: 2010-07-30 11:37:15 -0700 (Fri, 30 Jul 2010) $ // // $LastChangedBy: torstenrohlfing $ // */ #include "cmtkDeviceThresholdData_kernels.h" #include "GPU/cmtkCUDA.h" #include __device__ void cmtkDeviceThresholdDataKernel( float* dest, const int n, const float lowerThreshold, const float upperThreshold ) { for ( int i = threadIdx.x; i < n; i += blockDim.x ) { dest[idx] = fmaxf( lowerThreshold, fminf( upperThreshold, dest[idx] ) ); } } void cmtk::DeviceThresholdData( float* dest, const int n, const float lowerThreshold, const float upperThreshold ) { cmtkDeviceThresholdDataKernel<<<1,512>>>( dest, n, lowerThreshold, upperThreshold ); cmtkCheckLastErrorCUDA; } cmtk-3.3.1/libs/GPU/cmtkDeviceThresholdData_kernels.h000066400000000000000000000026551276303427400225010ustar00rootroot00000000000000/* // // Copyright 2010 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 2118 $ // // $LastChangedDate: 2010-07-30 11:37:15 -0700 (Fri, 30 Jul 2010) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkDeviceThresholdData_kernels_h_included_ #define __cmtkDeviceThresholdData_kernels_h_included_ #include namespace cmtk { /** \addtogroup GPU */ //@{ /** Threshold data on device. */ void DeviceThresholdData( float* dest, const int n, const float lowerThreshold, const float upperThreshold ); } // namespace cmtk //@} #endif // #ifndef __cmtkDeviceThresholdData_kernels_h_included_ cmtk-3.3.1/libs/GPU/cmtkDeviceUniformVolume.cxx000066400000000000000000000036341276303427400214100ustar00rootroot00000000000000/* // // Copyright 2010, 2012 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 3977 $ // // $LastChangedDate: 2012-03-06 15:30:28 -0800 (Tue, 06 Mar 2012) $ // // $LastChangedBy: torstenrohlfing $ // */ #include "cmtkDeviceUniformVolume.h" cmtk::DeviceUniformVolume:: DeviceUniformVolume( const UniformVolume& volume, const size_t padDataToMultiple ) { // set volume parameters UniformVolumeOnDevice onDevice; for ( size_t i = 0; i < 3; ++i ) { onDevice.m_Dims[i] = volume.m_Dims[i]; onDevice.m_Delta[i] = static_cast( volume.m_Delta[i] ); } // convert volume data to float and copy to device if ( volume.GetData() ) { TypedArray::SmartPtr floatData = volume.GetData()->Convert( TYPE_FLOAT ); this->m_OnDeviceData = DeviceMemory::Create( volume.GetNumberOfPixels(), static_cast( floatData->GetDataPtr() ), padDataToMultiple ); // set device pointer to data, then copy whole structure to device. onDevice.m_Data = this->m_OnDeviceData->Ptr(); } this->m_OnDevice = DeviceMemory::Create( 1, &onDevice ); } cmtk-3.3.1/libs/GPU/cmtkDeviceUniformVolume.h000066400000000000000000000047421276303427400210360ustar00rootroot00000000000000/* // // Copyright 2010 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 2398 $ // // $LastChangedDate: 2010-10-05 14:54:37 -0700 (Tue, 05 Oct 2010) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkDeviceUniformVolume_h_included_ #define __cmtkDeviceUniformVolume_h_included_ #include #include "cmtkDeviceMemory.h" #include "cmtkUniformVolumeOnDevice.h" #include namespace cmtk { /** \addtogroup GPU */ //@{ /// Device memory representation of a uniform volume with static helper functions. class DeviceUniformVolume { public: /// This class. typedef DeviceUniformVolume Self; /// Smart pointer to this class. typedef SmartPointer SmartPtr; /// Create device representation of volume object. static Self::SmartPtr Create( const UniformVolume& volume, const size_t padDataToMultiple = 1 /*!< Allocate device memory for data as multiple of this value.*/ ) { return Self::SmartPtr( new Self( volume, padDataToMultiple ) ); } /// Return device representation of volume. DeviceMemory& GetOnDevice() { return *(this->m_OnDevice); } /// Return device data pointer. DeviceMemory& GetDataOnDevice() { return *(this->m_OnDeviceData); } private: /// Constructor. DeviceUniformVolume( const UniformVolume& volume, const size_t padDataToMultiple = 1 ); /// Managed device memory pointer to parameter block. DeviceMemory::SmartPtr m_OnDevice; /// Managed device memory pointer to volume data. DeviceMemory::SmartPtr m_OnDeviceData; }; } // namespace cmtk #endif // #ifndef __cmtkDeviceUniformVolume_h_included_ cmtk-3.3.1/libs/GPU/cmtkDeviceUniformVolumeArray.cxx000066400000000000000000000032341276303427400224030ustar00rootroot00000000000000/* // // Copyright 2010 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 3517 $ // // $LastChangedDate: 2011-10-27 12:42:15 -0700 (Thu, 27 Oct 2011) $ // // $LastChangedBy: torstenrohlfing $ // */ #include "cmtkDeviceUniformVolumeArray.h" #include cmtk::DeviceUniformVolumeArray:: DeviceUniformVolumeArray( const UniformVolume& volume ) : m_DeviceArrayPointer( DeviceArray::Create( volume.m_Dims ) ) { const TypedArray::SmartPtr data = volume.GetData(); if ( data ) { if ( data->GetType() == TYPE_FLOAT ) { m_DeviceArrayPointer->CopyToDevice( static_cast( data->GetDataPtr() ) ); } else { float* fData = static_cast( data->ConvertArray( TYPE_FLOAT ) ); m_DeviceArrayPointer->CopyToDevice( fData ); Memory::ArrayC::Delete( fData ); } } } cmtk-3.3.1/libs/GPU/cmtkDeviceUniformVolumeArray.h000066400000000000000000000040331276303427400220260ustar00rootroot00000000000000/* // // Copyright 2010 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 2398 $ // // $LastChangedDate: 2010-10-05 14:54:37 -0700 (Tue, 05 Oct 2010) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkDeviceUniformVolumeArray_h_included_ #define __cmtkDeviceUniformVolumeArray_h_included_ #include #include "cmtkDeviceArray.h" #include namespace cmtk { /** \addtogroup GPU */ //@{ /// Representation of a uniform volume as 3D texture in device memory. class DeviceUniformVolumeArray { public: /// This class. typedef DeviceUniformVolumeArray Self; /// Smart pointer to this class. typedef SmartPointer SmartPtr; /// Create device representation of volume object. static Self::SmartPtr Create( const UniformVolume& volume ) { return Self::SmartPtr( new Self( volume ) ); } /// Get volume array on device. DeviceArray::SmartPtr& GetDeviceArrayPtr() { return this->m_DeviceArrayPointer; } private: /// Constructor. DeviceUniformVolumeArray( const UniformVolume& volume ); /// Device array pointer. DeviceArray::SmartPtr m_DeviceArrayPointer; }; } // namespace cmtk #endif // #ifndef __cmtkDeviceUniformVolumeArray_h_included_ cmtk-3.3.1/libs/GPU/cmtkEntropyMinimizationIntensityCorrectionFunctionalDevice.cxx000066400000000000000000000113361276303427400306110ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2010, 2013 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4870 $ // // $LastChangedDate: 2013-09-24 11:01:11 -0700 (Tue, 24 Sep 2013) $ // // $LastChangedBy: torstenrohlfing $ // */ #include "cmtkEntropyMinimizationIntensityCorrectionFunctionalDevice.h" #include namespace cmtk { /** \addtogroup GPU */ //@{ template EntropyMinimizationIntensityCorrectionFunctionalBase::SmartPtr CreateEntropyMinimizationIntensityCorrectionFunctionalDevice ( const unsigned int polynomialDegreeAdd ) { typedef EntropyMinimizationIntensityCorrectionFunctionalBase::SmartPtr FunctionalPointer; FunctionalPointer functional; switch ( polynomialDegreeAdd ) { case 0 : functional = FunctionalPointer( new EntropyMinimizationIntensityCorrectionFunctionalDevice<0,NDegreeMul> ); break; case 1 : functional = FunctionalPointer( new EntropyMinimizationIntensityCorrectionFunctionalDevice<1,NDegreeMul> ); break; case 2 : functional = FunctionalPointer( new EntropyMinimizationIntensityCorrectionFunctionalDevice<2,NDegreeMul> ); break; case 3 : functional = FunctionalPointer( new EntropyMinimizationIntensityCorrectionFunctionalDevice<3,NDegreeMul> ); break; case 4 : functional = FunctionalPointer( new EntropyMinimizationIntensityCorrectionFunctionalDevice<4,NDegreeMul> ); break; default: StdErr.printf( "ERROR: combination of polynomial degrees %u (add) and %u (mul) not supported.\n", polynomialDegreeAdd, NDegreeMul ); exit( 1 ); } return functional; } EntropyMinimizationIntensityCorrectionFunctionalBase::SmartPtr CreateEntropyMinimizationIntensityCorrectionFunctionalDevice ( const unsigned int polynomialDegreeAdd, const unsigned int polynomialDegreeMul ) { typedef EntropyMinimizationIntensityCorrectionFunctionalBase::SmartPtr FunctionalPointer; FunctionalPointer functional; switch ( polynomialDegreeMul ) { case 0 : functional = CreateEntropyMinimizationIntensityCorrectionFunctionalDevice<0>( polynomialDegreeAdd ); break; case 1 : functional = CreateEntropyMinimizationIntensityCorrectionFunctionalDevice<1>( polynomialDegreeAdd ); break; case 2 : functional = CreateEntropyMinimizationIntensityCorrectionFunctionalDevice<2>( polynomialDegreeAdd ); break; case 3 : functional = CreateEntropyMinimizationIntensityCorrectionFunctionalDevice<3>( polynomialDegreeAdd ); break; case 4 : functional = CreateEntropyMinimizationIntensityCorrectionFunctionalDevice<4>( polynomialDegreeAdd ); break; default: StdErr.printf( "ERROR: polynomial degree %u (mul) not supported.\n", polynomialDegreeMul ); exit( 1 ); } return functional; } EntropyMinimizationIntensityCorrectionFunctionalBase::SmartPtr CreateEntropyMinimizationIntensityCorrectionFunctionalDevice ( const unsigned int polynomialDegreeAdd, const unsigned int polynomialDegreeMul, EntropyMinimizationIntensityCorrectionFunctionalBase::SmartPtr oldFunctional ) { EntropyMinimizationIntensityCorrectionFunctionalBase::SmartPtr newFunctional = CreateEntropyMinimizationIntensityCorrectionFunctionalDevice( polynomialDegreeAdd, polynomialDegreeMul ); if ( oldFunctional ) { CoordinateVector vOld; oldFunctional->GetParamVector( vOld ); CoordinateVector vNew( newFunctional->ParamVectorDim() ); vNew.SetAll( 0.0 ); for ( size_t degreeAdd = 0; degreeAdd < oldFunctional->GetNumberOfMonomialsAdd(); ++degreeAdd ) { vNew[degreeAdd] = vOld[degreeAdd]; } for ( size_t degreeMul = 0; degreeMul < oldFunctional->GetNumberOfMonomialsMul(); ++degreeMul ) { vNew[newFunctional->GetNumberOfMonomialsAdd() + degreeMul] = vOld[oldFunctional->GetNumberOfMonomialsAdd() + degreeMul]; } } return newFunctional; } } // namespace cmtk cmtk-3.3.1/libs/GPU/cmtkEntropyMinimizationIntensityCorrectionFunctionalDevice.h000066400000000000000000000117021276303427400302330ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2010 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 2398 $ // // $LastChangedDate: 2010-10-05 14:54:37 -0700 (Tue, 05 Oct 2010) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkEntropyMinimizationIntensityCorrectionFunctionalDevice_h_included_ #define __cmtkEntropyMinimizationIntensityCorrectionFunctionalDevice_h_included_ #include #include #include "cmtkDeviceMemory.h" #include "cmtkDeviceHistogram.h" #include "cmtkDeviceUniformVolume.h" namespace cmtk { /** \addtogroup GPU */ //@{ /// Base class for GPU implementation entropy-minimzation MR bias correction functional using Device. template class EntropyMinimizationIntensityCorrectionFunctionalDevice /// Inherit non-GPU base class. : public EntropyMinimizationIntensityCorrectionFunctional { public: /// This class type. typedef EntropyMinimizationIntensityCorrectionFunctionalDevice Self; /// Pointer to this class. typedef SmartPointer SmartPtr; /// Superclass type. typedef EntropyMinimizationIntensityCorrectionFunctional Superclass; /// Return type of the functional evaluation. typedef typename Superclass::ReturnType ReturnType; /// Virtual destructor. virtual ~EntropyMinimizationIntensityCorrectionFunctionalDevice() {} /// Set input image. virtual void SetInputImage( UniformVolume::SmartConstPtr& inputImage ); /// Set foreground mask. virtual void SetForegroundMask( const UniformVolume& foregroundMask ); /// GPU-based functional evaluation for given parameter vector. virtual typename Self::ReturnType EvaluateAt( CoordinateVector& v ) { this->SetParamVector( v ); this->UpdateOutputImageDevice(); return this->EvaluateDevice(); } /** GPU-based implementation of gradient evaluation. * This function uses UpdateOutputImageDevice to update the output image using * the computation device. */ virtual typename Self::ReturnType EvaluateWithGradient( CoordinateVector& v, CoordinateVector& g, const Types::Coordinate step ); protected: /// Number of image pixels. size_t m_NumberOfPixels; /// Input image in device memory. DeviceUniformVolume::SmartPtr m_InputImageDevice; /// Binary foreground mask in device memory. DeviceMemory::SmartPtr m_ForegroundMaskDevice; /// Output image data. DeviceMemory::SmartPtr m_OutputDataDevice; /// Image histogram on device. DeviceHistogram::SmartPtr m_HistogramDevice; /// Update output image on device. void UpdateOutputImageDevice(); /// Evaluate corrected image entropy on device. typename Self::ReturnType EvaluateDevice(); }; /// Create functional templated over polynomial degrees. template EntropyMinimizationIntensityCorrectionFunctionalBase::SmartPtr CreateEntropyMinimizationIntensityCorrectionFunctionalDevice ( const unsigned int polynomialDegreeAdd ); /// Create functional templated over polynomial degrees. EntropyMinimizationIntensityCorrectionFunctionalBase::SmartPtr CreateEntropyMinimizationIntensityCorrectionFunctionalDevice ( const unsigned int polynomialDegreeAdd, const unsigned int polynomialDegreeMul ); /** Create functional templated over polynomial degrees with initialization from old functional. * This function creates a new functional and copies the polynomial coefficients from an existing * functional of equal or lower polynomial degrees into the correct locations of the new functional's * parameter vector. This is for incremental computation. */ EntropyMinimizationIntensityCorrectionFunctionalBase::SmartPtr CreateEntropyMinimizationIntensityCorrectionFunctionalDevice ( const unsigned int polynomialDegreeAdd, const unsigned int polynomialDegreeMul, EntropyMinimizationIntensityCorrectionFunctionalBase::SmartPtr oldFunctional ); //@} } // namespace cmtk #include "cmtkEntropyMinimizationIntensityCorrectionFunctionalDevice.txx" #endif // #ifndef __cmtkEntropyMinimizationIntensityCorrectionFunctionalDevice_h_included_ cmtk-3.3.1/libs/GPU/cmtkEntropyMinimizationIntensityCorrectionFunctionalDevice.txx000066400000000000000000000136771276303427400306440ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2012, 2014 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 5137 $ // // $LastChangedDate: 2014-01-10 11:38:43 -0800 (Fri, 10 Jan 2014) $ // // $LastChangedBy: torstenrohlfing $ // */ #include "cmtkEntropyMinimizationIntensityCorrectionFunctionalDevice.h" #include "cmtkEntropyMinimizationIntensityCorrectionFunctionalDevice_kernels.h" template void cmtk::EntropyMinimizationIntensityCorrectionFunctionalDevice ::SetInputImage( UniformVolume::SmartConstPtr& inputImage ) { this->Superclass::SetInputImage( inputImage ); this->m_InputImageDevice = DeviceUniformVolume::Create( *inputImage, 512 ); this->m_NumberOfPixels = inputImage->GetNumberOfPixels(); this->m_HistogramDevice = DeviceHistogram::Create( this->m_NumberOfHistogramBins ); } template void cmtk::EntropyMinimizationIntensityCorrectionFunctionalDevice ::SetForegroundMask( const UniformVolume& foregroundMask ) { this->Superclass::SetForegroundMask( foregroundMask ); std::vector maskCopy( this->m_NumberOfPixels ); for ( size_t i = 0; i < this->m_NumberOfPixels; ++i ) { if ( this->m_ForegroundMask[i] ) maskCopy[i] = 1; else maskCopy[i] = 0; } this->m_ForegroundMaskDevice = DeviceMemory::Create( this->m_NumberOfPixels, &maskCopy[0], 512 ); } #pragma GCC diagnostic ignored "-Wtype-limits" template typename cmtk::EntropyMinimizationIntensityCorrectionFunctionalDevice::ReturnType cmtk::EntropyMinimizationIntensityCorrectionFunctionalDevice ::EvaluateWithGradient ( CoordinateVector& v, CoordinateVector& g, const Types::Coordinate step ) { const typename Self::ReturnType baseValue = this->EvaluateAt( v ); for ( size_t dim = 0; dim < this->VariableParamVectorDim(); ++dim ) { const Types::Coordinate stepScale = this->GetParamStep( dim, step ); if ( stepScale <= 0 ) { g[dim] = 0; } else { const Types::Coordinate v0 = v[dim]; v[dim] += stepScale; const typename Self::ReturnType upper = this->EvaluateAt( v ); v[dim] = v0 - stepScale; const typename Self::ReturnType lower = this->EvaluateAt( v ); v[dim] = v0; if ( (upper > baseValue) || (lower > baseValue) ) { g[dim] = upper-lower; } else { g[dim] = 0; } } } return baseValue; } template void cmtk::EntropyMinimizationIntensityCorrectionFunctionalDevice ::UpdateOutputImageDevice() { if ( !this->m_OutputDataDevice ) this->m_OutputDataDevice = DeviceMemory::Create( this->m_NumberOfPixels, 512 ); float* input = this->m_InputImageDevice->GetDataOnDevice().Ptr(); float* output = this->m_OutputDataDevice->Ptr(); const int dims0 = this->m_InputImage->m_Dims[0]; const int dims1 = this->m_InputImage->m_Dims[1]; const int dims2 = this->m_InputImage->m_Dims[2]; if ( Self::PolynomialTypeMul::NumberOfMonomials > 1 ) { std::vector parameters( Self::PolynomialTypeMul::NumberOfMonomials ), corrections( Self::PolynomialTypeMul::NumberOfMonomials ); for ( size_t i = 1; i < Self::PolynomialTypeMul::NumberOfMonomials; ++i ) { parameters[i] = static_cast( this->m_CoefficientsMul[i] ); corrections[i] = static_cast( this->m_AddCorrectionMul[i] ); } EntropyMinimizationIntensityCorrectionFunctionalDeviceUpdateOutputImage( output, input, dims0, dims1, dims2, NOrderMul, 1 /*multiply*/, Self::PolynomialTypeMul::NumberOfMonomials, ¶meters[1], &corrections[1] ); input = output; // if additive bias also, apply to output of multiplicative stage } if ( Self::PolynomialTypeAdd::NumberOfMonomials > 1 ) { std::vector parameters( Self::PolynomialTypeAdd::NumberOfMonomials ), corrections( Self::PolynomialTypeAdd::NumberOfMonomials ); for ( size_t i = 1; i < Self::PolynomialTypeAdd::NumberOfMonomials; ++i ) { parameters[i] = static_cast( this->m_CoefficientsAdd[i] ); corrections[i] = static_cast( this->m_AddCorrectionAdd[i] ); } EntropyMinimizationIntensityCorrectionFunctionalDeviceUpdateOutputImage( output, input, dims0, dims1, dims2, NOrderAdd, 0 /*multiply*/, Self::PolynomialTypeAdd::NumberOfMonomials, ¶meters[1], &corrections[1] ); } } template typename cmtk::EntropyMinimizationIntensityCorrectionFunctionalDevice::ReturnType cmtk::EntropyMinimizationIntensityCorrectionFunctionalDevice ::EvaluateDevice() { const Types::DataItemRange range = this->m_EntropyHistogram->GetRange(); this->m_HistogramDevice->Reset(); this->m_HistogramDevice->Populate( *this->m_OutputDataDevice, *this->m_ForegroundMaskDevice, static_cast( range.m_LowerBound ), static_cast( range.m_UpperBound ), this->m_UseLogIntensities ); return -this->m_HistogramDevice->GetEntropy(); } cmtk-3.3.1/libs/GPU/cmtkEntropyMinimizationIntensityCorrectionFunctionalDevice_kernels.cu000066400000000000000000000125201276303427400321350ustar00rootroot00000000000000/* // // Copyright 2010 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 2113 $ // // $LastChangedDate: 2010-07-30 11:22:13 -0700 (Fri, 30 Jul 2010) $ // // $LastChangedBy: torstenrohlfing $ // */ #include "cmtkEntropyMinimizationIntensityCorrectionFunctionalDevice_kernels.h" #include "GPU/cmtkCUDA.h" __constant__ float deviceWeights[34]; __constant__ float deviceCorrections[34]; __global__ void cmtkEntropyMinimizationIntensityCorrectionFunctionalUpdateOutputImageKernel ( float* output, float* input, int degree, int multiply, int nPixels, int dims0, int dims1, int dims2 ) { const int offset = blockIdx.x * blockDim.x + threadIdx.x; if ( offset < nPixels ) { const int x = offset % dims0; const int y = (offset / dims0) % dims1; const int z = offset / (dims0 * dims1); const float X = 2.0f * (x-dims0/2) / dims0; const float Y = 2.0f * (y-dims1/2) / dims1; const float Z = 2.0f * (z-dims2/2) / dims2; const float in = input[offset]; float bias = deviceWeights[0] * (X - deviceCorrections[0]) + deviceWeights[1] * (Y - deviceCorrections[1]) + deviceWeights[2] * (Z - deviceCorrections[2]); if ( degree > 1 ) { bias += deviceWeights[3] * (X * X - deviceCorrections[3]) + deviceWeights[4] * (X * Y - deviceCorrections[4]) + deviceWeights[5] * (X * Z - deviceCorrections[5]) + deviceWeights[6] * (Y * Y - deviceCorrections[6]) + deviceWeights[7] * (Y * Z - deviceCorrections[7]) + deviceWeights[8] * (Z * Z - deviceCorrections[8]); } if ( degree > 2 ) { bias += deviceWeights[ 9] * (X * X * X - deviceCorrections[ 9]) + deviceWeights[10] * (X * X * Y - deviceCorrections[10]) + deviceWeights[11] * (X * X * Z - deviceCorrections[11]) + deviceWeights[12] * (X * Y * Y - deviceCorrections[12]) + deviceWeights[13] * (X * Y * Z - deviceCorrections[13]) + deviceWeights[14] * (X * Z * Z - deviceCorrections[14]) + deviceWeights[15] * (Y * Y * Y - deviceCorrections[15]) + deviceWeights[16] * (Y * Y * Z - deviceCorrections[16]) + deviceWeights[17] * (Y * Z * Z - deviceCorrections[17]) + deviceWeights[18] * (Z * Z * Z - deviceCorrections[18]); } if ( degree > 3 ) { bias += deviceWeights[19] * (X * X * X * X - deviceCorrections[19]) + deviceWeights[20] * (X * X * X * Y - deviceCorrections[20]) + deviceWeights[21] * (X * X * X * Z - deviceCorrections[21]) + deviceWeights[22] * (X * X * Y * Y - deviceCorrections[22]) + deviceWeights[23] * (X * X * Y * Z - deviceCorrections[23]) + deviceWeights[24] * (X * X * Z * Z - deviceCorrections[24]) + deviceWeights[25] * (X * Y * Y * Y - deviceCorrections[25]) + deviceWeights[26] * (X * Y * Y * Z - deviceCorrections[26]) + deviceWeights[27] * (X * Y * Z * Z - deviceCorrections[27]) + deviceWeights[28] * (X * Z * Z * Z - deviceCorrections[28]) + deviceWeights[29] * (Y * Y * Y * Y - deviceCorrections[29]) + deviceWeights[30] * (Y * Y * Y * Z - deviceCorrections[30]) + deviceWeights[31] * (Y * Y * Z * Z - deviceCorrections[31]) + deviceWeights[32] * (Y * Z * Z * Z - deviceCorrections[32]) + deviceWeights[33] * (Z * Z * Z * Z - deviceCorrections[33]); } if ( multiply ) { output[offset] = in * (bias+1); } else { output[offset] = in + bias; } } } void cmtk::EntropyMinimizationIntensityCorrectionFunctionalDeviceUpdateOutputImage ( float* output, float* input, const int dims0, const int dims1, const int dims2, const int degree, const int multiply, const int nargs, const float* weights, const float* corrections ) { cmtkCheckCallCUDA( cudaMemcpyToSymbol( deviceWeights, weights, nargs * sizeof( *weights ), 0, cudaMemcpyHostToDevice ) ); cmtkCheckCallCUDA( cudaMemcpyToSymbol( deviceCorrections, corrections, nargs * sizeof( *corrections ), 0, cudaMemcpyHostToDevice ) ); const int nPixels = dims0 * dims1 * dims2; // how many local copies of the histogram can we fit in shared memory? int device; cmtkCheckCallCUDA( cudaGetDevice( &device ) ); cudaDeviceProp dprop; cmtkCheckCallCUDA( cudaGetDeviceProperties( &dprop, device ) ); int nThreads = nPixels; if ( nThreads > dprop.maxThreadsPerBlock ) nThreads = dprop.maxThreadsPerBlock; dim3 dimBlock( nThreads, 1, 1 ); dim3 dimGrid( (nPixels+nThreads-1)/nThreads, 1 ); cmtkEntropyMinimizationIntensityCorrectionFunctionalUpdateOutputImageKernel<<>>( output, input, degree, multiply, nPixels, dims0, dims1, dims2 ); cmtkCheckLastErrorCUDA; } cmtk-3.3.1/libs/GPU/cmtkEntropyMinimizationIntensityCorrectionFunctionalDevice_kernels.h000066400000000000000000000033241276303427400317570ustar00rootroot00000000000000/* // // Copyright 2010 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 2113 $ // // $LastChangedDate: 2010-07-30 11:22:13 -0700 (Fri, 30 Jul 2010) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkEntropyMinimizationIntensityCorrectionFunctionalDevice_kernels_included_ #define __cmtkEntropyMinimizationIntensityCorrectionFunctionalDevice_kernels_included_ #include /** \addtogroup GPU */ //@{ namespace cmtk { /// Update output image using either additive or multiplicative bias field. void EntropyMinimizationIntensityCorrectionFunctionalDeviceUpdateOutputImage ( float* output, float* input, const int dims0, const int dims1, const int dims2, const int degree, const int multiply, const int nargs, const float* weights, const float* corrections ); } // namespace cmtk //@} #endif // #ifndef __cmtkEntropyMinimizationIntensityCorrectionFunctionalDevice_kernels_included_ cmtk-3.3.1/libs/GPU/cmtkImagePairAffineRegistrationFunctionalDevice.cxx000066400000000000000000000056021276303427400261630ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2012 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4381 $ // // $LastChangedDate: 2012-05-30 14:18:15 -0700 (Wed, 30 May 2012) $ // // $LastChangedBy: torstenrohlfing $ // */ #include "cmtkImagePairAffineRegistrationFunctionalDevice.h" #include namespace cmtk { /** \addtogroup Registration */ //@{ ImagePairAffineRegistrationFunctionalDevice::ImagePairAffineRegistrationFunctionalDevice ( UniformVolume::SmartConstPtr& fixedVolume, UniformVolume::SmartConstPtr& movingVolume ) : ImagePairRegistrationFunctional( fixedVolume, movingVolume ), m_FixedVolumeOnDevice( DeviceUniformVolumeArray::Create( *(fixedVolume) ) ), m_MovingVolumeOnDevice( DeviceUniformVolumeArray::Create( *(movingVolume) ) ) { } ImagePairAffineRegistrationFunctionalDevice::ReturnType ImagePairAffineRegistrationFunctionalDevice::Evaluate() { const AffineXform::MatrixType xformMatrix; float matrix[4][4]; for ( size_t j = 0; j < 4; ++j ) { for ( size_t i = 0; i < 4; ++i ) { matrix[j][i] = static_cast( xformMatrix[j][i] ); } } FixedVector<3,float> deltas = this->m_ReferenceGrid->Deltas(); // multiply deltas for index-to-image space conversion for ( size_t j = 0; j < 3; ++j ) { for ( size_t i = 0; i < 3; ++i ) { matrix[j][i] *= deltas[j]; } } // divide by size to get to normalized image coordinates after mirror for ( size_t j = 0; j < 4; ++j ) // here, need to run up to 3 because translation is also in output space { for ( size_t i = 0; i < 3; ++i ) { matrix[j][i] /= static_cast( this->m_FloatingGrid->m_Size[i] ); } } return -ImagePairAffineRegistrationFunctionalDeviceEvaluateMSD( this->m_ReferenceGrid->m_Dims.begin(), this->m_FixedVolumeOnDevice->GetDeviceArrayPtr()->GetArrayOnDevice(), this->m_FloatingGrid->m_Dims.begin(), this->m_MovingVolumeOnDevice->GetDeviceArrayPtr()->GetArrayOnDevice(), matrix ); } } // namespace cmtk cmtk-3.3.1/libs/GPU/cmtkImagePairAffineRegistrationFunctionalDevice.h000066400000000000000000000046061276303427400256130ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2010 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 2567 $ // // $LastChangedDate: 2010-11-29 14:45:58 -0800 (Mon, 29 Nov 2010) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkImagePairAffineRegistrationFunctionalDevice_h_included_ #define __cmtkImagePairAffineRegistrationFunctionalDevice_h_included_ #include #include #include namespace cmtk { /** \addtogroup GPU */ //@{ /** Functional for affine registration of two images on the GPU. */ class ImagePairAffineRegistrationFunctionalDevice : public ImagePairRegistrationFunctional { public: /// This class. typedef ImagePairAffineRegistrationFunctionalDevice Self; /// Smart pointer to this class. typedef SmartPointer SmartPtr; /// Superclass typedef ImagePairRegistrationFunctional Superclass; /// Constructor. ImagePairAffineRegistrationFunctionalDevice( UniformVolume::SmartConstPtr& fixedVolume, UniformVolume::SmartConstPtr& movingVolume ); /// Destructor. virtual ~ImagePairAffineRegistrationFunctionalDevice() {} /// Compute functional value. virtual Self::ReturnType Evaluate(); private: /// Fixed volume on compute device. DeviceUniformVolumeArray::SmartPtr m_FixedVolumeOnDevice; /// Moving volume on compute device. DeviceUniformVolumeArray::SmartPtr m_MovingVolumeOnDevice; }; //@} } // namespace cmtk #endif // #ifndef __cmtkImagePairAffineRegistrationFunctionalDevice_h_included_ cmtk-3.3.1/libs/GPU/cmtkImagePairAffineRegistrationFunctionalDevice_kernels.cu000066400000000000000000000111401276303427400275050ustar00rootroot00000000000000/* // // Copyright 2010 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 3168 $ // // $LastChangedDate: 2011-04-22 12:51:51 -0700 (Fri, 22 Apr 2011) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifdef _WIN32 // This fixes a strange compile error using VisualStudio 2010 Express. // See http://forums.nvidia.com/index.php?showtopic=67822 #define WIN32_LEAN_AND_MEAN #endif #include "cmtkImagePairAffineRegistrationFunctionalDevice_kernels.h" #include "System/cmtkMemory.h" #include "GPU/cmtkCUDA.h" #include "GPU/cmtkDeviceMemory.h" #include "GPU/cmtkSumReduction_kernel.h" #include /// Texture reference to volume data. texture texRefMov; texture texRefFix; __constant__ float deviceMatrix[4][4]; __global__ void cmtkImagePairAffineRegistrationFunctionalDeviceEvaluateMSDKernel( float* squares, const int dims0, const int dims1, const int dims2 ) { const int tx = threadIdx.x; const int ty = threadIdx.y; float sq = 0; for ( int z = blockIdx.x; z < dims2; z += blockDim.x ) { const float mXz = z * deviceMatrix[2][0] + deviceMatrix[3][0]; const float mYz = z * deviceMatrix[2][1] + deviceMatrix[3][1]; const float mZz = z * deviceMatrix[2][2] + deviceMatrix[3][2]; for ( int y = ty; y < dims1; y += blockDim.y ) { const float mXyz = y * deviceMatrix[1][0] + mXz; const float mYyz = y * deviceMatrix[1][1] + mYz; const float mZyz = y * deviceMatrix[1][2] + mZz; for ( int x = tx; x < dims0; x += blockDim.x ) { const float dataFix = tex3D( texRefFix, x, y, z ); const float mX = x * deviceMatrix[0][0] + mXyz; const float mY = x * deviceMatrix[0][1] + mYyz; const float mZ = x * deviceMatrix[0][2] + mZyz; const float dataMov = tex3D( texRefMov, mX, mY, mZ ); const float diff = dataMov-dataFix; sq += diff*diff; } } } const int idx = tx + blockDim.x * ( ty + blockDim.y * blockIdx.x ); squares[idx] = sq; } float cmtk::ImagePairAffineRegistrationFunctionalDeviceEvaluateMSD( const int* fixedDims3, void* fixedArray, const int* movingDims3, void* movingArray, const float matrix[4][4] ) { cmtkCheckCallCUDA( cudaMemcpyToSymbol( deviceMatrix, matrix, 16 * sizeof( float ), 0, cudaMemcpyHostToDevice ) ); // Set texture parameters for fixed image indexed access texRefFix.addressMode[0] = cudaAddressModeClamp; texRefFix.addressMode[1] = cudaAddressModeClamp; texRefFix.addressMode[2] = cudaAddressModeClamp; texRefFix.filterMode = cudaFilterModePoint; texRefFix.normalized = false; cmtkCheckCallCUDA( cudaBindTextureToArray( texRefFix, (struct cudaArray*) fixedArray, cudaCreateChannelDesc() ) ); // Set texture parameters for moving image interpolated access texRefMov.addressMode[0] = cudaAddressModeWrap; texRefMov.addressMode[1] = cudaAddressModeWrap; texRefMov.addressMode[2] = cudaAddressModeWrap; texRefMov.filterMode = cudaFilterModeLinear; texRefMov.normalized = true; // Bind the array to the texture reference cmtkCheckCallCUDA( cudaBindTextureToArray( texRefMov, (struct cudaArray*) movingArray, cudaCreateChannelDesc() ) ); // alocate memory for partial sums of squares dim3 dimBlock( 16, 16 ); const int nPartials = dimBlock.x * dimBlock.y * dimBlock.z * fixedDims3[2]; cmtk::DeviceMemory partialSums( nPartials ); cmtkImagePairAffineRegistrationFunctionalDeviceEvaluateMSDKernel<<>>( partialSums.Ptr(), fixedDims3[0], fixedDims3[1], fixedDims3[2] ); cmtkCheckLastErrorCUDA; cmtkCheckCallCUDA( cudaUnbindTexture( texRefMov ) ); cmtkCheckCallCUDA( cudaUnbindTexture( texRefFix ) ); return cmtk::SumReduction( partialSums.Ptr(), nPartials ) / fixedDims3[0]*fixedDims3[1]*fixedDims3[2]; } cmtk-3.3.1/libs/GPU/cmtkImagePairAffineRegistrationFunctionalDevice_kernels.h000066400000000000000000000035331276303427400273340ustar00rootroot00000000000000/* // // Copyright 2010 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 2567 $ // // $LastChangedDate: 2010-11-29 14:45:58 -0800 (Mon, 29 Nov 2010) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkImagePairAffineRegistrationFunctionalDevice_kernels_h_included_ #define __cmtkImagePairAffineRegistrationFunctionalDevice_kernels_h_included_ /** \addtogroup GPU */ //@{ namespace cmtk { /// Evaluate Mean Squared Difference for symmetry plane computation on GPU. float ImagePairAffineRegistrationFunctionalDeviceEvaluateMSD( const int* fixedDims3 /*!< Fixed volume dimensions */, void* fixedArray /*!< Device array with fixed volume data */, const int* movingDims3 /*!< Movingvolume dimensions */, void* movingArray /*!< Device array with moving volume data */, const float matrix[4][4] /*!< Mirror matrix: from index to image coordinates, then mirror, then to normalized [0..1] coordinates */ ); } // namespace cmtk //@} #endif // #ifndef __cmtkImagePairAffineRegistrationFunctionalDevice_kernels_h_included_ cmtk-3.3.1/libs/GPU/cmtkImageSymmetryPlaneFunctionalDevice.cxx000066400000000000000000000061071276303427400243760ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2012 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4381 $ // // $LastChangedDate: 2012-05-30 14:18:15 -0700 (Wed, 30 May 2012) $ // // $LastChangedBy: torstenrohlfing $ // */ #include "cmtkImageSymmetryPlaneFunctionalDevice.h" #include namespace cmtk { /** \addtogroup Registration */ //@{ ImageSymmetryPlaneFunctionalDevice::ImageSymmetryPlaneFunctionalDevice ( UniformVolume::SmartConstPtr& volume ) : ImageSymmetryPlaneFunctionalBase( volume ) { this->m_VolumeOnDevice = DeviceUniformVolumeArray::SmartPtr( DeviceUniformVolumeArray::Create( *(this->m_Volume) ) ); } ImageSymmetryPlaneFunctionalDevice::ImageSymmetryPlaneFunctionalDevice ( UniformVolume::SmartConstPtr& volume, const Types::DataItemRange& valueRange ) : ImageSymmetryPlaneFunctionalBase( volume, valueRange ) { this->m_VolumeOnDevice = DeviceUniformVolumeArray::SmartPtr( DeviceUniformVolumeArray::Create( *(this->m_Volume) ) ); } ImageSymmetryPlaneFunctionalDevice::ReturnType ImageSymmetryPlaneFunctionalDevice::Evaluate() { const AffineXform::MatrixType mirrorMatrix = this->m_ParametricPlane.GetMirrorXformMatrix(); float matrix[4][4]; for ( size_t j = 0; j < 4; ++j ) { for ( size_t i = 0; i < 4; ++i ) { matrix[j][i] = static_cast( mirrorMatrix[j][i] ); } } FixedVector<3,float> deltas = this->m_Volume->Deltas(); // multiply deltas for index-to-image space conversion for ( size_t j = 0; j < 3; ++j ) { for ( size_t i = 0; i < 3; ++i ) { matrix[j][i] *= deltas[j]; } } // divide by size to get to normalized image coordinates after mirror for ( size_t j = 0; j < 4; ++j ) // here, need to run up to 3 because translation is also in output space { for ( size_t i = 0; i < 3; ++i ) { matrix[j][i] /= static_cast( this->m_Volume->m_Size[i] ); } } return -ImagePairAffineRegistrationFunctionalDeviceEvaluateMSD( this->m_Volume->m_Dims.begin(), this->m_VolumeOnDevice->GetDeviceArrayPtr()->GetArrayOnDevice(), this->m_Volume->m_Dims.begin(), this->m_VolumeOnDevice->GetDeviceArrayPtr()->GetArrayOnDevice(), matrix ); } } // namespace cmtk cmtk-3.3.1/libs/GPU/cmtkImageSymmetryPlaneFunctionalDevice.h000066400000000000000000000045661276303427400240320ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2010 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 2398 $ // // $LastChangedDate: 2010-10-05 14:54:37 -0700 (Tue, 05 Oct 2010) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkImageSymmetryPlaneFunctionalDevice_h_included_ #define __cmtkImageSymmetryPlaneFunctionalDevice_h_included_ #include #include #include namespace cmtk { /** \addtogroup GPU */ //@{ /** Functional for finding a symmetry plane in 3-D volumes using GPU support. */ class ImageSymmetryPlaneFunctionalDevice : /// Inherit functional interface. public ImageSymmetryPlaneFunctionalBase { public: /// This class. typedef ImageSymmetryPlaneFunctionalDevice Self; /// Smart pointer to this class. typedef SmartPointer SmartPtr; /// Superclass typedef ImageSymmetryPlaneFunctionalBase Superclass; /// Constructor. ImageSymmetryPlaneFunctionalDevice( UniformVolume::SmartConstPtr& volume ); /// Constructor with value range limits. ImageSymmetryPlaneFunctionalDevice( UniformVolume::SmartConstPtr& volume, const Types::DataItemRange& valueRange ); /// Destructor. virtual ~ImageSymmetryPlaneFunctionalDevice() {} /// Compute functional value. virtual Self::ReturnType Evaluate(); private: /// Volume on compute device. DeviceUniformVolumeArray::SmartPtr m_VolumeOnDevice; }; //@} } // namespace cmtk #endif // #ifndef __cmtkImageSymmetryPlaneFunctionalDevice_h_included_ cmtk-3.3.1/libs/GPU/cmtkSimpleLevelsetDevice.cxx000066400000000000000000000067611276303427400215420ustar00rootroot00000000000000/* // // Copyright 2010, 2012, 2013 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4931 $ // // $LastChangedDate: 2013-10-04 10:20:54 -0700 (Fri, 04 Oct 2013) $ // // $LastChangedBy: torstenrohlfing $ // */ #include "cmtkSimpleLevelsetDevice.h" #include #include #include #include #include #include #include #include #include #include void cmtk::SimpleLevelsetDevice ::Evolve( const int numberOfIterations, const bool forceIterations ) { FixedArray< 3, std::vector > kernels; for ( int dim = 0; dim < 3; ++dim ) { kernels[dim] = GaussianKernel::GetSymmetricKernel( this->m_FilterSigma / this->m_Volume->Deltas()[dim], 0.01f /*maxError*/ ); } const size_t numberOfPixels = this->m_Levelset->GetNumberOfPixels(); DeviceUniformVolume::SmartPtr deviceVolume = DeviceUniformVolume::Create( *(this->m_Volume) ); DeviceUniformVolumeArray::SmartPtr deviceLevelset = DeviceUniformVolumeArray::Create( *(this->m_Levelset) ); DeviceMemory::SmartPtr temporary = DeviceMemory::Create( numberOfPixels ); int nInsideOld = 0, nInside = 1; Progress::Begin( 0, numberOfIterations, 1, "Levelset Evolution" ); for ( int it = 0; (it < numberOfIterations) && ((nInside!=nInsideOld) || forceIterations); ++it ) { Progress::SetProgress( it ); DeviceImageConvolution( temporary->Ptr(), this->m_Volume->GetDims().begin(), deviceLevelset->GetDeviceArrayPtr()->GetArrayOnDevice(), kernels[0].size(), &kernels[0][0], kernels[1].size(), &kernels[1][0], kernels[2].size(), &kernels[2][0] ); float insideSum, outsideSum; SimpleLevelsetDeviceUpdateInsideOutside( temporary->Ptr(), deviceVolume->GetDataOnDevice().Ptr(), numberOfPixels, &insideSum, &outsideSum, &nInside ); if ( nInside == 0 ) throw Self::DegenerateLevelsetException(); const int nOutside = numberOfPixels - nInside; if ( nOutside == 0 ) throw Self::DegenerateLevelsetException(); SimpleLevelsetDeviceUpdateLevelset( temporary->Ptr(), deviceVolume->GetDataOnDevice().Ptr(), numberOfPixels, insideSum / nInside, outsideSum / nOutside, 1.0f * nInside / nOutside, static_cast( this->m_TimeDelta ), static_cast( this->m_LevelsetThreshold ) ); deviceLevelset->GetDeviceArrayPtr()->CopyOnDeviceToArray( temporary->Ptr() ); } temporary->CopyToHost( this->m_Levelset->GetData()->GetDataPtr(), numberOfPixels ); Progress::Done(); } cmtk-3.3.1/libs/GPU/cmtkSimpleLevelsetDevice.h000066400000000000000000000037361276303427400211660ustar00rootroot00000000000000/* // // Copyright 2010 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 2398 $ // // $LastChangedDate: 2010-10-05 14:54:37 -0700 (Tue, 05 Oct 2010) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkSimpleLevelsetDevice_h_included_ #define __cmtkSimpleLevelsetDevice_h_included_ #include #include namespace cmtk { /** \addtogroup GPU */ //@{ /** Class for computing a simple levelset evolution on the GPU */ class SimpleLevelsetDevice /// Inherit from CPU-based levelset class. : public SimpleLevelset { public: /// This class. typedef SimpleLevelsetDevice Self; /// Parent class. typedef SimpleLevelset Superclass; /// Constructor. SimpleLevelsetDevice( UniformVolume::SmartConstPtr& volume ) : Superclass( volume ) {} /// Levelset evolution on GPU. virtual void Evolve( const int numberOfIterations /*!< Number of iterations */, const bool forceIterations = false /*!< If this is set, evolution continues until maximum iteration count is reached, even when convergence is detected */ ); }; } // namespace cmtk #endif // #ifndef __cmtkSimpleLevelsetDevice_h_included_ cmtk-3.3.1/libs/GPU/cmtkSimpleLevelsetDevice_kernels.cu000066400000000000000000000074441276303427400230710ustar00rootroot00000000000000/* // // Copyright 2010 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 3168 $ // // $LastChangedDate: 2011-04-22 12:51:51 -0700 (Fri, 22 Apr 2011) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifdef _WIN32 // This fixes a strange compile error using VisualStudio 2010 Express. // See http://forums.nvidia.com/index.php?showtopic=67822 #define WIN32_LEAN_AND_MEAN #endif #include "cmtkSimpleLevelsetDevice_kernels.h" #include "GPU/cmtkCUDA.h" #include "GPU/cmtkDeviceMemory.h" #include "GPU/cmtkSumReduction_kernel.h" #include __global__ void cmtkSimpleLevelsetDeviceUpdateInsideOutsideKernel( float* partialInsideSum, float* partialOutsideSum, int* partialInside, float* levelset, float* data, const int nPixels ) { int nInside = 0; float insideSum = 0; float outsideSum = 0; for ( int idx = threadIdx.x; idx < nPixels; idx += blockDim.x ) { const float l = levelset[idx]; const float d = data[idx]; const int flag = (l>0) ? 1 : 0; nInside += flag; insideSum += flag*d; outsideSum += (1-flag)*d; } partialInside[threadIdx.x] = nInside; partialInsideSum[threadIdx.x] = insideSum; partialOutsideSum[threadIdx.x] = outsideSum; } void cmtk::SimpleLevelsetDeviceUpdateInsideOutside( float* levelset, float* data, const int nPixels, float* insideSum, float* outsideSum, int* nInside ) { const int nThreads = 512; DeviceMemory partialInside( nThreads ); DeviceMemory partialInsideSum( nThreads ); DeviceMemory partialOutsideSum( nThreads ); cmtkSimpleLevelsetDeviceUpdateInsideOutsideKernel<<<1,nThreads>>>( partialInsideSum.Ptr(), partialOutsideSum.Ptr(), partialInside.Ptr(), levelset, data, nPixels ); cmtkCheckLastErrorCUDA; *nInside = SumReduction( partialInside.Ptr(), nThreads ); *insideSum = SumReduction( partialInsideSum.Ptr(), nThreads ); *outsideSum = SumReduction( partialOutsideSum.Ptr(), nThreads ); } __global__ void cmtkSimpleLevelsetDeviceUpdateInsideOutsideKernel( float* levelset, float* data, const int nPixels, const float mInside, const float mOutside, const float ratioInOut, const float timeDelta, const float levelsetThreshold ) { for ( size_t n = threadIdx.x; n < nPixels; n += blockDim.x ) { const float d = data[n]; const float l = levelset[n]; const float zInside = fabsf( mInside - d ); const float zOutside = fabs( mOutside - d ); const float delta = ( zInside>zOutside ) ? -timeDelta * ratioInOut : timeDelta / ratioInOut; levelset[n] = fminf( levelsetThreshold, fmaxf( -levelsetThreshold, l+delta ) ); } } void cmtk::SimpleLevelsetDeviceUpdateLevelset( float* levelset, float* data, const int nPixels, const float mInside, const float mOutside, const float ratioInOut, const float timeDelta, const float levelsetThreshold ) { cmtkSimpleLevelsetDeviceUpdateInsideOutsideKernel<<<1,512>>>( levelset, data, nPixels, mInside, mOutside, ratioInOut, timeDelta, levelsetThreshold ); cmtkCheckLastErrorCUDA; } cmtk-3.3.1/libs/GPU/cmtkSimpleLevelsetDevice_kernels.h000066400000000000000000000050051276303427400227000ustar00rootroot00000000000000/* // // Copyright 2010 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 2184 $ // // $LastChangedDate: 2010-08-06 16:03:41 -0700 (Fri, 06 Aug 2010) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkSimpleLevelsetDevice_kernels_h_included_ #define __cmtkSimpleLevelsetDevice_kernels_h_included_ #include /** \addtogroup GPU */ //@{ namespace cmtk { /// Count inside pixels and compute the sums of inside and outside pixels. void SimpleLevelsetDeviceUpdateInsideOutside( float* levelset /*!< Input: current levelset */, float* volume /*!< Input: image data */, const int nPixels /*!< Input: number of pixels */, float* insideSum /*!< Output: sum of data values in the "inside" region */, float* outsideSum /*!< Output: sum of data values in the "outside" region */, int* nInside /*!< Output: number of pixels in the "inside" region */ ); /// Update levelset based on mean values in inside and outside regions, then threshold the result. void SimpleLevelsetDeviceUpdateLevelset( float* levelset /*!< Input/output: current levelset, is updated by this function */, float* volume /*!< Input: image data */, const int nPixels /*!< Input: number of pixels */, const float mInside /*!< Input: mean value of data in the "inside" region */, const float mOutside /*!< Input: mean value of data in the "outside" region */, const float ratioInOut /*!< Input: ratio of inside and outside pixel counts. */, const float timeDelta /*!< Input: levelset evolution time constant */, const float levelsetThreshold /*!< Input: levelset threshold */ ); } // namespace cmtk //@} #endif // #ifndef __cmtkSimpleLevelsetDevice_kernels_h_included_ cmtk-3.3.1/libs/GPU/cmtkSumReduction_kernel.cu000066400000000000000000000034541276303427400212470ustar00rootroot00000000000000/* // // Copyright 2010 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 2124 $ // // $LastChangedDate: 2010-07-30 15:04:33 -0700 (Fri, 30 Jul 2010) $ // // $LastChangedBy: torstenrohlfing $ // */ #include "cmtkSumReduction_kernel.h" #include "GPU/cmtkCUDA.h" #include template __global__ void cmtkSumReductionKernel( T* data, const int n ) { const int tx = threadIdx.x; for ( int i = tx + blockDim.x; i < n; i += blockDim.x ) { data[tx] += data[i]; } __syncthreads(); if ( tx == 0 ) { for ( int i = 1; i < blockDim.x; ++i ) data[0] += data[i]; } } template T cmtk::SumReduction( T* data, const int n ) { cmtkSumReductionKernel<<<1,512>>>( data, n ); T result; cmtkCheckCallCUDA( cudaMemcpy( &result, data, sizeof( T ), cudaMemcpyDeviceToHost ) ); return result; } template int cmtk::SumReduction( int* data, const int n ); template float cmtk::SumReduction( float* data, const int n ); cmtk-3.3.1/libs/GPU/cmtkSumReduction_kernel.h000066400000000000000000000026031276303427400210620ustar00rootroot00000000000000/* // // Copyright 2010-2011 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 2731 $ // // $LastChangedDate: 2011-01-13 16:22:47 -0800 (Thu, 13 Jan 2011) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkSumReduction_kernel_h_included_ #define __cmtkSumReduction_kernel_h_included_ /** \addtogroup GPU */ //@{ namespace cmtk { /** Compute sum reduction of data. *\warning The data is destroyed in the process. */ template T SumReduction( T* data, const int n ); } // namespace cmtk //@} #endif // #ifndef __cmtkSumReduction_kernel_h_included_ cmtk-3.3.1/libs/GPU/cmtkUniformVolumeOnDevice.h000066400000000000000000000027011276303427400213240ustar00rootroot00000000000000/* // // Copyright 2010 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 1896 $ // // $LastChangedDate: 2010-06-25 10:29:55 -0700 (Fri, 25 Jun 2010) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkUniformVolumeOnDevice_h_included_ #define __cmtkUniformVolumeOnDevice_h_included_ /** \addtogroup GPU */ //@{ namespace cmtk { /// Copy from host to device. typedef struct { /// Volume dimensions. int m_Dims[3]; /// Pixel sizes. float m_Delta[3]; /// Pointer to volume data. float* m_Data; } UniformVolumeOnDevice; } // namespace cmtk //@} #endif // #ifndef __cmtkUniformVolumeOnDevice_h_included_ cmtk-3.3.1/libs/GPU/doxygen.h000066400000000000000000000023521276303427400157000ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2011 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 2734 $ // // $LastChangedDate: 2011-01-14 10:23:45 -0800 (Fri, 14 Jan 2011) $ // // $LastChangedBy: torstenrohlfing $ // */ /** \defgroup GPU cmtkGPU library * This library provides classes related to computations on GPU hardware via the CUDA * and potentially OpenCL programming paradigms. */ cmtk-3.3.1/libs/IO/000077500000000000000000000000001276303427400137245ustar00rootroot00000000000000cmtk-3.3.1/libs/IO/CMakeLists.txt000066400000000000000000000061651276303427400164740ustar00rootroot00000000000000## ## Copyright 2004-2012, 2014 SRI International ## ## Copyright 1997-2009 Torsten Rohlfing ## ## This file is part of the Computational Morphometry Toolkit. ## ## http://www.nitrc.org/projects/cmtk/ ## ## The Computational Morphometry Toolkit is free software: you can ## redistribute it and/or modify it under the terms of the GNU General Public ## License as published by the Free Software Foundation, either version 3 of ## the License, or (at your option) any later version. ## ## The Computational Morphometry Toolkit is distributed in the hope that it ## will be useful, but WITHOUT ANY WARRANTY; without even the implied ## warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License along ## with the Computational Morphometry Toolkit. If not, see ## . ## ## $Revision: 5224 $ ## ## $LastChangedDate: 2014-03-11 16:38:30 -0700 (Tue, 11 Mar 2014) $ ## ## $LastChangedBy: torstenrohlfing $ ## # Sources of non-templated classes. SET(cmtkIO_SRCS cmtkAffineXformITKIO.cxx cmtkSplineWarpXformITKIO.cxx cmtkClassStreamAffineXform.cxx cmtkClassStreamParametricPlane.cxx cmtkClassStreamPolynomialXform.cxx cmtkClassStreamStudyList.cxx cmtkClassStreamWarpXform.cxx cmtkFileFormat.cxx cmtkGroupwiseRegistrationFunctionalIO.cxx cmtkImageFileDICOM.cxx cmtkImageOperationApplyMask.cxx cmtkImageOperationMatchIntensities.cxx cmtkImageStackDICOM.cxx cmtkLandmarkIO.cxx cmtkLandmarkListIO.cxx cmtkPhantomIO_MagphanEMR051.cxx cmtkSegmentationLabelIO.cxx cmtkSiemensCSAHeader.cxx cmtkStudy.cxx cmtkStudyList.cxx cmtkTypedStream.cxx cmtkTypedStreamInput.cxx cmtkTypedStreamOutput.cxx cmtkTypedStreamStudylist.cxx cmtkVolumeFromFile.cxx cmtkVolumeFromFileAnalyze.cxx cmtkVolumeFromFileBioRad.cxx cmtkVolumeFromFileMetaFile.cxx cmtkVolumeFromFileNRRD.cxx cmtkVolumeFromFileNifti.cxx cmtkVolumeFromFileVanderbilt.cxx cmtkVolumeFromSlices.cxx cmtkVolumeIO.cxx cmtkXformIO.cxx cmtkXformListIO.cxx cmtkXformIONifti.cxx cmtkXformIONrrd.cxx nifti1_io_math.c ) IF(CMTK_USE_DCMTK) SET(cmtkIO_SRCS ${cmtkIO_SRCS} cmtkDICOM.cxx cmtkVolumeFromStudy.cxx cmtkVolumeFromFileDICOM.cxx) ENDIF(CMTK_USE_DCMTK) IF(CMTK_USE_SQLITE) SET(cmtkIO_SRCS ${cmtkIO_SRCS} cmtkSQLite.cxx) ENDIF(CMTK_USE_SQLITE) ADD_LIBRARY(cmtkIO ${cmtkIO_SRCS}) TARGET_LINK_LIBRARIES(cmtkIO cmtkBase cmtkSystem cmtkNumerics ${NRRD_LIBRARIES} ${CMTK_SQLITE_LIB} ${DCMTK_LIBRARIES} ${CMTK_ZLIB_LIB}) IF(CMTK_LIBRARY_PROPERTIES) SET_TARGET_PROPERTIES(cmtkIO PROPERTIES ${CMTK_LIBRARY_PROPERTIES}) ENDIF(CMTK_LIBRARY_PROPERTIES) INSTALL(TARGETS cmtkIO RUNTIME DESTINATION ${CMTK_INSTALL_BIN_DIR} COMPONENT runtime LIBRARY DESTINATION ${CMTK_INSTALL_LIB_DIR} COMPONENT runtime ARCHIVE DESTINATION ${CMTK_INSTALL_LIB_DIR} COMPONENT libraries) FILE(GLOB files_h "${CMAKE_CURRENT_SOURCE_DIR}/*.h") FILE(GLOB files_txx "${CMAKE_CURRENT_SOURCE_DIR}/*.txx") INSTALL(FILES ${files_h} ${files_txx} DESTINATION ${CMTK_INSTALL_INCLUDE_DIR}/IO COMPONENT headers) cmtk-3.3.1/libs/IO/cmtkAffineXformITKIO.cxx000066400000000000000000000070561276303427400203430ustar00rootroot00000000000000/* // // Copyright 2009-2010, 2013 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 5129 $ // // $LastChangedDate: 2014-01-09 13:48:15 -0800 (Thu, 09 Jan 2014) $ // // $LastChangedBy: torstenrohlfing $ // */ #include "cmtkAffineXformITKIO.h" #include #include #include void cmtk::AffineXformITKIO ::Write( const std::string& filename, const AffineXform& affineXform ) { std::ofstream stream( filename.c_str() ); if ( stream.good() ) { // write header stream << "#Insight Transform File V1.0\n"; Self::Write( stream, affineXform, 0 ); stream.close(); } } void cmtk::AffineXformITKIO ::Write( std::ofstream& stream, const AffineXform& affineXform, const size_t idx ) { stream << "# Transform " << idx << "\n"; // write ID depending on whether CMTK is using single or double precision floats for coordinates if ( typeid( Types::Coordinate ) == typeid( double ) ) { stream << "Transform: AffineTransform_double_3_3\n"; } else { stream << "Transform: AffineTransform_float_3_3\n"; } // write parameters, 3x3 transformation matrix first stream << "Parameters: "; for ( int i = 0; i < 3; ++i ) { for ( int j = 0; j < 3; ++j ) { stream << affineXform.Matrix[j][i] << " "; } } // write translations for ( int i = 0; i < 3; ++i ) { stream << affineXform.Matrix[3][i] << " "; } // finish up with (all-zero) fixed parameters stream << "\n" << "FixedParameters: 0 0 0\n"; } cmtk::AffineXform::SmartPtr cmtk::AffineXformITKIO ::Read( const std::string& filename ) { std::ifstream stream( filename.c_str() ); if ( stream.good() ) { std::string line; std::getline( stream, line ); if ( line != "#Insight Transform File V1.0" ) return AffineXform::SmartPtr( NULL ); std::getline( stream, line ); if ( line != "# Transform 0" ) return AffineXform::SmartPtr( NULL ); std::getline( stream, line ); if ( line == "Transform: AffineTransform_double_3_3" || line == "Transform: AffineTransform_float_3_3" ) { std::getline( stream, line, ' ' ); Types::Coordinate matrix[4][4] = { {0,0,0,0}, {0,0,0,0}, {0,0,0,0}, {0,0,0,1} }; for ( int i = 0; i < 3; ++i ) { for ( int j = 0; j < 3; ++j ) { stream >> matrix[j][i]; } } for ( int i = 0; i < 3; ++i ) { stream >> matrix[3][i]; } try { AffineXform::SmartPtr xform( new AffineXform( matrix ) ); xform->SetMetaInfo( META_SPACE, AnatomicalOrientationBase::SPACE_ITK ); return xform; } catch ( const AffineXform::MatrixType::SingularMatrixException& ) { StdErr << "ERROR: singular matrix in cmtk::AffineXformITKIO::Read()\n"; } } } return AffineXform::SmartPtr( NULL ); } cmtk-3.3.1/libs/IO/cmtkAffineXformITKIO.h000066400000000000000000000043201276303427400177570ustar00rootroot00000000000000/* // // Copyright 2009-2011 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 2994 $ // // $LastChangedDate: 2011-03-14 11:27:30 -0700 (Mon, 14 Mar 2011) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkAffineXformITKIO_h_included__ #define __cmtkAffineXformITKIO_h_included__ #include #include #include namespace cmtk { /** Class for reading and writing affine transformations from and to ITK's file format. * This should also be understood by Slicer3 for transformation exchange with CMTK tools * run as plugins. */ class AffineXformITKIO { public: /// This class. typedef AffineXformITKIO Self; /// Write transformation to ITK file. static void Write( const std::string& filename, const AffineXform& affineXform ); /// Write transformation to open stream, e.g., for writing more than one transformation to the same file. static void Write( std::ofstream& stream /*!< An open stream to which the ITK file header has already been written.*/, const AffineXform& affineXform /*!< Transformation to write next.*/, const size_t idx = 0 /*!< Index of transformation, i.e., its relative position in file when it is written.*/ ); /// Read transformation from ITK file. static AffineXform::SmartPtr Read( const std::string& filename ); }; } // namespace cmtk #endif // #ifndef __cmtkAffineXformITKIO_h_included__ cmtk-3.3.1/libs/IO/cmtkAnalyze.h000066400000000000000000000037361276303427400163700ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // Copyright 2004-2009 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 11 $ // // $LastChangedDate: 2009-05-30 11:30:08 -0700 (Sat, 30 May 2009) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkAnalyze_h_included_ #define __cmtkAnalyze_h_included_ #include namespace cmtk { /** \addtogroup IO */ //@{ /// IDs for data types in Analyze image file. typedef enum { ANALYZE_TYPE_NONE = 0, ANALYZE_TYPE_BINARY = 1, ANALYZE_TYPE_UNSIGNED_CHAR = 2, ANALYZE_TYPE_SIGNED_SHORT = 4, ANALYZE_TYPE_SIGNED_INT = 8, ANALYZE_TYPE_FLOAT = 16, ANALYZE_TYPE_COMPLEX = 32, ANALYZE_TYPE_DOUBLE = 64, ANALYZE_TYPE_RGB = 128, ANALYZE_TYPE_USHORT = 132, //SPM extension ANALYZE_TYPE_UINT = 136, // SPM extension ANALYZE_TYPE_ALL = 255 } AnalyzeDataType; /// IDs for slice orientations in Analyze image file. typedef enum { ANALYZE_AXIAL = 0, ANALYZE_CORONAL = 1, ANALYZE_SAGITTAL = 2, ANALYZE_AXIAL_FLIP = 3, ANALYZE_CORONAL_FLIP = 4, ANALYZE_SAGITTAL_FLIP = 5, ANALYZE_UNKNOWN = 255 } AnalyzeOrientation; //@} } // namespace cmtk #endif // #ifndef __cmtkAnalyze_h_included_ cmtk-3.3.1/libs/IO/cmtkClassStreamAffineXform.cxx000066400000000000000000000113141276303427400216750ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2013 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 5046 $ // // $LastChangedDate: 2013-11-27 14:59:04 -0800 (Wed, 27 Nov 2013) $ // // $LastChangedBy: torsten_at_home $ // */ #include "cmtkClassStreamAffineXform.h" #include namespace cmtk { /** \addtogroup IO */ //@{ ClassStreamOutput& operator << ( ClassStreamOutput& stream, const AffineXform& affineXform ) { stream.Begin( "affine_xform" ); stream.WriteCoordinateArray( "xlate", affineXform.RetXlate(), 3 ); stream.WriteCoordinateArray( "rotate", affineXform.RetAngles(), 3 ); if ( affineXform.GetUseLogScaleFactors() ) stream.WriteCoordinateArray( "log_scale", affineXform.RetScales(), 3 ); else stream.WriteCoordinateArray( "scale", affineXform.RetScales(), 3 ); stream.WriteCoordinateArray( "shear", affineXform.RetShears(), 3 ); stream.WriteCoordinateArray( "center", affineXform.RetCenter(), 3 ); stream.End(); return stream; } ClassStreamInput& operator >> ( ClassStreamInput& stream, AffineXform::SmartPtr& affineXform ) { try { affineXform = AffineXform::SmartPtr( new AffineXform ); stream >> (*affineXform); } catch (...) { affineXform = AffineXform::SmartPtr::Null(); } return stream; } ClassStreamInput& operator >> ( ClassStreamInput& stream, AffineXform& affineXform ) { CoordinateVector pVector( 15 ); Types::Coordinate* parameters = pVector.Elements; const char *referenceStudy = NULL; const char *floatingStudy = NULL; if ( stream.Seek( "affine_xform", true /*forward*/ ) != TypedStream::CONDITION_OK ) { stream.Rewind(); if ( stream.Seek( "registration", true /*forward*/ ) != TypedStream::CONDITION_OK ) { throw Exception( "Did not find 'registration' section in affine xform archive" ); } referenceStudy = stream.ReadString( "reference_study", NULL ); floatingStudy = stream.ReadString( "floating_study", NULL ); if ( stream.Seek( "affine_xform", false /*forward*/ ) != TypedStream::CONDITION_OK ) { throw Exception( "Did not find 'affine_xform' section in affine xform archive" ); } } if ( stream.ReadCoordinateArray( "xlate", parameters, 3 ) != TypedStream::CONDITION_OK ) { parameters[0] = parameters[1] = parameters[2] = 0; } if ( stream.ReadCoordinateArray( "rotate", parameters+3, 3 ) != TypedStream::CONDITION_OK ) { parameters[3] = parameters[4] = parameters[5] = 0; } bool logScaleFactors = false; if ( stream.ReadCoordinateArray( "scale", parameters+6, 3 ) != TypedStream::CONDITION_OK ) { if ( stream.ReadCoordinateArray( "log_scale", parameters+6, 3 ) == TypedStream::CONDITION_OK ) { logScaleFactors = true; } else { parameters[6] = parameters[7] = parameters[8] = 1; } } if ( stream.ReadCoordinateArray( "shear", parameters+9, 3 ) != TypedStream::CONDITION_OK ) { parameters[9] = parameters[10] = parameters[11] = 0; } if ( stream.ReadCoordinateArray( "center", parameters+12, 3 ) != TypedStream::CONDITION_OK ) { parameters[12] = parameters[13] = parameters[14] = 0; } stream.End(); if ( stream.GetReleaseMajor() < 2 ) { CompatibilityMatrix4x4 matrix( pVector, logScaleFactors ); Types::Coordinate newParameters[15]; matrix.Decompose( newParameters, pVector.Elements+12, logScaleFactors ); pVector.SetFromArray( newParameters, 15 ); } affineXform.SetUseLogScaleFactors( logScaleFactors ); affineXform.SetParamVector( pVector ); affineXform.SetMetaInfo( META_SPACE, AnatomicalOrientation::ORIENTATION_STANDARD ); if ( referenceStudy ) affineXform.SetMetaInfo( META_XFORM_FIXED_IMAGE_PATH, referenceStudy ); if ( floatingStudy ) affineXform.SetMetaInfo( META_XFORM_MOVING_IMAGE_PATH, floatingStudy ); return stream; } } // namespace cmtk cmtk-3.3.1/libs/IO/cmtkClassStreamAffineXform.h000066400000000000000000000034531276303427400213270ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2010 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4344 $ // // $LastChangedDate: 2012-05-11 14:51:08 -0700 (Fri, 11 May 2012) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkClassStreamAffineXform_h_included_ #define __cmtkClassStreamAffineXform_h_included_ #include #include #include #include namespace cmtk { /** \addtogroup IO */ //@{ /// Write affine transformation object. ClassStreamOutput& operator << ( ClassStreamOutput& stream, const AffineXform& affineXform ); /// Read affine transformation. ClassStreamInput& operator >> ( ClassStreamInput& stream, AffineXform::SmartPtr& affineXform ); /// Read affine transformation. ClassStreamInput& operator >> ( ClassStreamInput& stream, AffineXform& affineXform ); //@} } // namespace cmtk #endif // #ifndef __cmtkClassStreamAffineXform_h_included_ cmtk-3.3.1/libs/IO/cmtkClassStreamInput.h000066400000000000000000000056401276303427400202220ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2012 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4344 $ // // $LastChangedDate: 2012-05-11 14:51:08 -0700 (Fri, 11 May 2012) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkClassStreamInput_h_included_ #define __cmtkClassStreamInput_h_included_ #include #include #include #include #include #include #include #include namespace cmtk { /** \addtogroup IO */ //@{ /** Class for reading various library classes to and from disk. */ class ClassStreamInput : /// Inherit basic functionality from typed stream. public TypedStreamInput { public: /// This class. typedef ClassStreamInput Self; /// Parent class. typedef TypedStreamInput Superclass; /// Default constructor. ClassStreamInput() : TypedStreamInput() {} /** Open constructor. *\param filename Name of the archive to open. */ ClassStreamInput( const std::string& filename ) : TypedStreamInput( filename ) {} /** Open constructor for separate path and archive names. *\param dir Directory to open archive in. *\param archive Name of the archive to open. */ ClassStreamInput( const std::string& dir, const std::string& archive ) : TypedStreamInput( dir, archive ) {} /// Read (spline or linear) warp transformation. ClassStreamInput& operator >> ( WarpXform::SmartPtr& warpXform ); /// Read (spline or linear) warp transformation. ClassStreamInput& operator >> ( WarpXform*& warpXform ); /// Actually read warp transformation object. ClassStreamInput& Get ( WarpXform::SmartPtr& warpXform, const AffineXform* affineXform = NULL ); /// Actually read warp transformation object. ClassStreamInput& Get ( WarpXform*& warpXform, const AffineXform* affineXform = NULL ); /// Read parametric plane. ClassStreamInput& operator >> ( ParametricPlane*& parametricPlane ); }; //@} } // namespace cmtk #endif // #ifndef __cmtkClassStreamInput_h_included_ cmtk-3.3.1/libs/IO/cmtkClassStreamMultiChannelRegistration.h000066400000000000000000000046631276303427400241050ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2012 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4344 $ // // $LastChangedDate: 2012-05-11 14:51:08 -0700 (Fri, 11 May 2012) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkClassStreamMultiChannelRegistration_h_included_ #define __cmtkClassStreamMultiChannelRegistration_h_included_ #include #include #include #include namespace cmtk { /** \addtogroup IO */ //@{ /** Write file names and transformations from affine multi-channel registration functional. */ template ClassStreamOutput& operator << ( ClassStreamOutput& stream, const AffineMultiChannelRegistrationFunctional& functional ); /** Read file names and transformations from archive to multi-channel affine registration functional. */ template ClassStreamInput& operator >> ( ClassStreamInput& stream, AffineMultiChannelRegistrationFunctional& functional ); /** Write file names and transformations from spline warp multi-channel registration functional. */ template ClassStreamOutput& operator << ( ClassStreamOutput& stream, const SplineWarpMultiChannelRegistrationFunctional& functional ); //@} } // namespace cmtk #include "cmtkClassStreamMultiChannelRegistration.txx" #endif // #ifndef __cmtkClassStreamMultiChannelRegistration_h_included_ cmtk-3.3.1/libs/IO/cmtkClassStreamMultiChannelRegistration.txx000066400000000000000000000111341276303427400244700ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2013 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 5023 $ // // $LastChangedDate: 2013-11-23 09:52:08 -0800 (Sat, 23 Nov 2013) $ // // $LastChangedBy: torsten_at_home $ // */ #include #include #include #include namespace cmtk { /** \addtogroup IO */ //@{ template ClassStreamOutput& operator << ( ClassStreamOutput& stream, const AffineMultiChannelRegistrationFunctional& functional ) { stream.Begin( "registration" ); stream.WriteInt( "reference_channel_count", functional.GetNumberOfReferenceChannels() ); for ( size_t idx = 0; idx < functional.GetNumberOfReferenceChannels(); ++idx ) { stream.WriteString( "reference_channel", functional.GetReferenceChannel( idx )->GetMetaInfo( META_FS_PATH ).c_str() ); } stream.WriteInt( "floating_channel_count", functional.GetNumberOfFloatingChannels() ); for ( size_t idx = 0; idx < functional.GetNumberOfFloatingChannels(); ++idx ) { stream.WriteString( "floating_channel", functional.GetFloatingChannel( idx )->GetMetaInfo( META_FS_PATH ).c_str() ); } stream << functional.GetTransformation(); stream.End(); return stream; } template ClassStreamInput& operator >> ( ClassStreamInput& stream, AffineMultiChannelRegistrationFunctional& functional ) { stream.Seek( "registration" ); const size_t referenceChannelCount = stream.ReadInt( "reference_channel_count", 0 ); for ( size_t idx = 0; idx < referenceChannelCount; ++idx ) { const char* channel = stream.ReadString( "reference_channel", NULL, true /*forward*/ ); if ( channel ) { UniformVolume::SmartPtr volume( VolumeIO::ReadOriented( channel ) ); if ( !volume || !volume->GetData() ) { StdErr << "ERROR: Cannot read image " << channel << "\n"; throw ExitException( 1 ); } functional.AddReferenceChannel( volume ); } } const size_t floatingChannelCount = stream.ReadInt( "floating_channel_count", 0 ); for ( size_t idx = 0; idx < floatingChannelCount; ++idx ) { const char* channel = stream.ReadString( "floating_channel", NULL, true /*forward*/ ); if ( channel ) { UniformVolume::SmartPtr volume( VolumeIO::ReadOriented( channel ) ); if ( !volume || !volume->GetData() ) { StdErr << "ERROR: Cannot read image " << channel << "\n"; throw ExitException( 1 ); } functional.AddFloatingChannel( volume ); } } try { stream >> functional.GetTransformation(); } catch ( const Exception& ex ) { StdErr << "ERROR: could not get transformation from file - " << ex.what() << "\n"; throw ExitException( 1 ); } stream.End(); return stream; } template ClassStreamOutput& operator << ( ClassStreamOutput& stream, const SplineWarpMultiChannelRegistrationFunctional& functional ) { stream.Begin( "registration" ); stream.WriteInt( "reference_channel_count", functional.GetNumberOfReferenceChannels() ); for ( size_t idx = 0; idx < functional.GetNumberOfReferenceChannels(); ++idx ) { stream.WriteString( "reference_channel", functional.GetReferenceChannel( idx )->GetMetaInfo( META_FS_PATH ).c_str() ); } stream.WriteInt( "floating_channel_count", functional.GetNumberOfFloatingChannels() ); for ( size_t idx = 0; idx < functional.GetNumberOfFloatingChannels(); ++idx ) { stream.WriteString( "floating_channel", functional.GetFloatingChannel( idx )->GetMetaInfo( META_FS_PATH ).c_str() ); } stream << functional.GetTransformation(); stream.End(); return stream; } } // namespace cmtk cmtk-3.3.1/libs/IO/cmtkClassStreamOutput.h000066400000000000000000000072361276303427400204260ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2012, 2014 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 5357 $ // // $LastChangedDate: 2014-06-16 12:23:42 -0700 (Mon, 16 Jun 2014) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkClassStreamOutput_h_included_ #define __cmtkClassStreamOutput_h_included_ #include #include #include #include #include #include #include #include namespace cmtk { /** \addtogroup IO */ //@{ /** Class for writing various library classes to and from disk. */ class ClassStreamOutput : /// Inherit basic functionality from typed stream. public TypedStreamOutput { public: /// This class. typedef ClassStreamOutput Self; /// Parent class. typedef TypedStreamOutput Superclass; /// Default constructor. ClassStreamOutput() : TypedStreamOutput() {} /// Virtual destructor - to make sure base class destructor is properly run. ~ClassStreamOutput() {} /** Open constructor. *\param filename Name of the archive to open. */ ClassStreamOutput( const std::string& filename, const Superclass::Mode mode ) : TypedStreamOutput( filename, mode ) {} /** Open constructor for separate path and archive names. *\param dir Directory to open archive in. *\param archive Name of the archive to open. */ ClassStreamOutput( const std::string& dir, const std::string& archive, const Superclass::Mode mode ) : TypedStreamOutput( dir, archive, mode ) {} /** Write generic transformation object. * This function determines the virtual type of the transformation object * (spline or linear deformation) using a dynamic_cast. It then calls the * appropriate specialized output function. */ ClassStreamOutput& operator << ( const WarpXform *warpXform ); /** Write spline transformation object. * This function works on a reference rather than a pointer. It immediately * calls the pointer-based function defined above for the actual writing. */ ClassStreamOutput& operator << ( const SplineWarpXform& splineWarpXform ) { return (*this) << &splineWarpXform; } /** Write parametric plane object. */ ClassStreamOutput& operator << ( const ParametricPlane *parametricPlane ); /** Write parametric plane object. * This function works on a reference rather than a pointer. It immediately * calls the pointer-based function defined above for the actual writing. */ ClassStreamOutput& operator << ( const ParametricPlane& parametricPlane ) { return (*this) << ¶metricPlane; } private: /// Write actual warp transformation object. ClassStreamOutput& PutWarp( const WarpXform* warpXform ); }; //@} } // namespace cmtk #endif // #ifndef __cmtkClassStreamOutput_h_included_ cmtk-3.3.1/libs/IO/cmtkClassStreamParametricPlane.cxx000066400000000000000000000045401276303427400225430ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2012 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4427 $ // // $LastChangedDate: 2012-06-12 11:23:22 -0700 (Tue, 12 Jun 2012) $ // // $LastChangedBy: torstenrohlfing $ // */ #include #include namespace cmtk { ClassStreamInput& ClassStreamInput::operator>>( ParametricPlane*& parametricPlane ) { parametricPlane = NULL; if ( this->Seek( "plane" ) != TypedStream::CONDITION_OK ) return *this; parametricPlane = new ParametricPlane(); Types::Coordinate planeOrigin[3]; this->ReadCoordinateArray( "origin", planeOrigin, 3 ); parametricPlane->SetOrigin( FixedVector<3,Types::Coordinate>::FromPointer( planeOrigin ) ); parametricPlane->SetRho( this->ReadCoordinate( "rho" ) ); parametricPlane->SetTheta( Units::Degrees( this->ReadCoordinate( "theta" ) ) ); parametricPlane->SetPhi( Units::Degrees( this->ReadCoordinate( "phi" ) ) ); return *this; } ClassStreamOutput& ClassStreamOutput::operator<<( const ParametricPlane* parametricPlane ) { this->Begin( "plane" ); this->WriteCoordinateArray( "origin", parametricPlane->GetOrigin().begin(), 3 ); this->WriteDouble( "rho", parametricPlane->GetRho() ); this->WriteDouble( "theta", Units::Degrees( parametricPlane->GetTheta() ).Value() ); this->WriteDouble( "phi", Units::Degrees( parametricPlane->GetPhi() ).Value() ); this->WriteCoordinateArray( "normal", parametricPlane->GetNormal().begin(), 3 ); return *this; } } // namespace cmtk cmtk-3.3.1/libs/IO/cmtkClassStreamPolynomialXform.cxx000066400000000000000000000064661276303427400226440ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2014 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 5046 $ // // $LastChangedDate: 2013-11-27 14:59:04 -0800 (Wed, 27 Nov 2013) $ // // $LastChangedBy: torsten_at_home $ // */ #include "cmtkClassStreamPolynomialXform.h" namespace cmtk { /** \addtogroup IO */ //@{ ClassStreamOutput& operator << ( ClassStreamOutput& stream, const PolynomialXform& xform ) { stream.Begin( "polynomial_xform" ); stream.WriteInt( "degree", xform.Degree() ); stream.WriteCoordinateArray( "center", xform.Center().begin(), 3 ); stream.WriteCoordinateArray( "coefficients", xform.m_Parameters, xform.m_NumberOfParameters ); stream.End(); return stream; } ClassStreamInput& operator >> ( ClassStreamInput& stream, PolynomialXform& xform ) { const char *fixedImage = NULL; const char *movingImage = NULL; if ( stream.Seek( "polynomial_xform", true /*forward*/ ) != TypedStream::CONDITION_OK ) { stream.Rewind(); if ( stream.Seek( "registration", true /*forward*/ ) != TypedStream::CONDITION_OK ) { throw Exception( "Did not find 'registration' section in archive" ); } fixedImage = stream.ReadString( "reference_study", NULL ); movingImage = stream.ReadString( "floating_study", NULL ); if ( stream.Seek( "polynomial_xform", false /*forward*/ ) != TypedStream::CONDITION_OK ) { throw Exception( "Did not find 'polynomial_xform' section in archive" ); } } int degree = stream.ReadInt( "degree", -1 ); if ( degree == -1 ) { throw Exception( "Did not find 'degree' value in polynomial xform archive" ); } xform = PolynomialXform( degree ); Types::Coordinate center[3]; if ( stream.ReadCoordinateArray( "center", center, 3 ) != TypedStream::CONDITION_OK ) { throw Exception( "Could not read 'center' array from polynomial xform archive" ); } xform.SetCenter( PolynomialXform::SpaceVectorType::FromPointer( center ) ); if ( stream.ReadCoordinateArray( "coefficients", xform.m_Parameters, xform.m_NumberOfParameters ) != TypedStream::CONDITION_OK ) { throw Exception( "Could not read 'coeffients' array from polynomial xform archive" ); } stream.End(); xform.SetMetaInfo( META_SPACE, AnatomicalOrientation::ORIENTATION_STANDARD ); if ( fixedImage ) xform.SetMetaInfo( META_XFORM_FIXED_IMAGE_PATH, fixedImage ); if ( movingImage ) xform.SetMetaInfo( META_XFORM_MOVING_IMAGE_PATH, movingImage ); return stream; } } // namespace cmtk cmtk-3.3.1/libs/IO/cmtkClassStreamPolynomialXform.h000066400000000000000000000032741276303427400222630ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2010, 2014 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4344 $ // // $LastChangedDate: 2012-05-11 14:51:08 -0700 (Fri, 11 May 2012) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkClassStreamPolynomialXform_h_included_ #define __cmtkClassStreamPolynomialXform_h_included_ #include #include #include #include namespace cmtk { /** \addtogroup IO */ //@{ /// Write affine transformation object. ClassStreamOutput& operator << ( ClassStreamOutput& stream, const PolynomialXform& xform ); /// Read affine transformation. ClassStreamInput& operator >> ( ClassStreamInput& stream, PolynomialXform& xform ); //@} } // namespace cmtk #endif // #ifndef __cmtkClassStreamPolynomialXform_h_included_ cmtk-3.3.1/libs/IO/cmtkClassStreamStudyList.cxx000066400000000000000000000063231276303427400214410ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2013 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4879 $ // // $LastChangedDate: 2013-09-26 15:08:17 -0700 (Thu, 26 Sep 2013) $ // // $LastChangedBy: torstenrohlfing $ // */ #include "cmtkClassStreamStudyList.h" #include #include #include namespace cmtk { /** \addtogroup IO */ //@{ void ClassStreamStudyList::Write ( const std::string& path, const StudyList* studyList ) { ClassStreamOutput stream; stream.Open( path, "studylist", ClassStreamOutput::MODE_WRITE ); if ( stream.IsValid() ) { StudyList::const_iterator it = studyList->begin(); while ( it != studyList->end() ) { stream.Begin( "source" ); stream.WriteString( "studyname", it->first->GetFileSystemPath() ); stream.End(); ++it; } stream.Close(); } else { StdErr << "ERROR: could not open archive " << path << "/studylist\n"; } stream.Open( path, "registration", ClassStreamOutput::MODE_WRITE_ZLIB ); if ( stream.IsValid() ) { StudyList::const_iterator it = studyList->begin(); while ( it != studyList->end() ) { StudyToXform targetList = it->second; std::map seen; StudyToXform::const_iterator tit; for ( tit = targetList.begin(); tit != targetList.end(); ++tit ) { if ( seen.find( tit->first ) == seen.end() ) { seen[tit->first] = true; stream.Begin( "registration" ); stream.WriteString( "reference_study", it->first->GetFileSystemPath() ); stream.WriteString( "floating_study", tit->first->GetFileSystemPath() ); StudyToXform::const_iterator tit2; for ( tit2 = targetList.begin(); tit2 != targetList.end(); ++tit2 ) { if ( tit2->first == tit->first ) { Xform::SmartPtr xform = tit2->second; AffineXform::SmartPtr affine = AffineXform::SmartPtr::DynamicCastFrom( xform ); if ( affine ) { stream << (*affine); } WarpXform::SmartPtr warp = WarpXform::SmartPtr::DynamicCastFrom( xform ); if ( warp ) { stream << warp; } } } stream.End(); } } ++it; } stream.Close(); } else { StdErr << "ERROR: could not open archive " << path << "/registration\n"; } } } // namespace cmtk cmtk-3.3.1/libs/IO/cmtkClassStreamStudyList.h000066400000000000000000000030611276303427400210620ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2013 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4879 $ // // $LastChangedDate: 2013-09-26 15:08:17 -0700 (Thu, 26 Sep 2013) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkClassStreamStudyList_h_included_ #define __cmtkClassStreamStudyList_h_included_ #include #include #include namespace cmtk { /** \addtogroup IO */ //@{ /** StudyList with class stream file system interface. */ class ClassStreamStudyList { public: /// Write studylist. static void Write( const std::string& path, const StudyList *studyList ); }; //@} } // namespace cmtk #endif // #ifndef __cmtkClassStreamStudyList_h_included_ cmtk-3.3.1/libs/IO/cmtkClassStreamWarpXform.cxx000066400000000000000000000136111276303427400214200ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2012 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4427 $ // // $LastChangedDate: 2012-06-12 11:23:22 -0700 (Tue, 12 Jun 2012) $ // // $LastChangedBy: torstenrohlfing $ // */ #include #include #include namespace cmtk { ClassStreamOutput& ClassStreamOutput::operator << ( const WarpXform *warpXform ) { return this->PutWarp( warpXform ); } ClassStreamOutput& ClassStreamOutput::PutWarp ( const WarpXform *warpXform ) { const Types::Coordinate *nCoeff = warpXform->m_Parameters; if ( dynamic_cast( warpXform ) ) this->Begin( "spline_warp" ); if ( warpXform->GetInitialAffineXform() ) *this << (*warpXform->GetInitialAffineXform()); this->WriteBool ( "absolute", true ); this->WriteIntArray( "dims", warpXform->m_Dims.begin(), 3 ); this->WriteCoordinateArray( "domain", warpXform->m_Domain.begin(), 3 ); this->WriteCoordinateArray( "origin", warpXform->m_Offset.begin(), 3 ); this->WriteCoordinateArray( "coefficients", nCoeff, warpXform->m_NumberOfParameters, 3 ); const BitVector* activeFlags = warpXform->GetActiveFlags(); if ( activeFlags ) { this->WriteBoolArray( "active", activeFlags->GetBitVector(), warpXform->m_NumberOfParameters, 30 ); } this->End(); return *this; } ClassStreamInput& ClassStreamInput::Get ( WarpXform::SmartPtr& warpXform, const AffineXform* initialXform ) { WarpXform* warp; this->Get( warp, initialXform ); warpXform = WarpXform::SmartPtr( warp ); return *this; } ClassStreamInput& ClassStreamInput::Get ( WarpXform*& warpXform, const AffineXform* initialXform ) { warpXform = NULL; int WarpType = -1; if ( this->Seek( "spline_warp" ) == TypedStream::CONDITION_OK ) WarpType = 1; else if ( this->Seek( "linear_warp" ) == TypedStream::CONDITION_OK ) WarpType = 0; else { this->Rewind(); if ( this->Seek( "registration", true /*forward*/ ) != TypedStream::CONDITION_OK ) { return *this; } if ( this->Seek( "spline_warp" ) == TypedStream::CONDITION_OK ) WarpType = 1; else if ( this->Seek( "linear_warp" ) == TypedStream::CONDITION_OK ) WarpType = 0; else return *this; } AffineXform::SmartPtr initialInverse( NULL ); if ( initialXform == NULL ) { AffineXform::SmartPtr newInitialXform; *this >> newInitialXform; initialInverse = AffineXform::SmartPtr( newInitialXform ); } else { initialInverse = AffineXform::SmartPtr( initialXform->MakeInverse() ); } int absolute = this->ReadBool( "absolute", 0 ); int dims[3]; if ( TypedStream::CONDITION_OK != this->ReadIntArray( "dims", dims, 3 ) ) { return *this; } int numControlPoints = dims[0] * dims[1] * dims[2]; int numberOfParameters = 3 * numControlPoints; CoordinateVector::SmartPtr parameters( new CoordinateVector( numberOfParameters ) ); Types::Coordinate *Coefficients = parameters->Elements; Vector3D domain; Vector3D origin; if ( this->ReadCoordinateArray( "domain", domain.begin(), 3 ) != TypedStream::CONDITION_OK ) this->ReadCoordinateArray( "extent", domain.begin(), 3 ); int readOrigin = this->ReadCoordinateArray( "origin", origin.begin(), 3 ); this->ReadCoordinateArray( "coefficients", Coefficients, numberOfParameters ); if ( !absolute && (readOrigin == TypedStream::CONDITION_OK) ) { Types::Coordinate *p = Coefficients; for ( int z=0; z( (numberOfParameters / 8)+1 ); if ( this->ReadBoolArray( "active", active, numberOfParameters ) == TypedStream::CONDITION_OK ) { BitVector::SmartPtr bitSet( new BitVector( numberOfParameters, active ) ); warpXform->SetActiveFlags( bitSet ); } else { Memory::ArrayC::Delete( active ); } this->End(); if ( warpXform ) { warpXform->SetMetaInfo( META_SPACE, AnatomicalOrientation::ORIENTATION_STANDARD ); } return *this; } ClassStreamInput& ClassStreamInput::operator >> ( WarpXform::SmartPtr& warpXform ) { this->Get( warpXform ); return *this; } ClassStreamInput& ClassStreamInput::operator >> ( WarpXform*& warpXform ) { this->Get( warpXform ); return *this; } } // namespace cmtk cmtk-3.3.1/libs/IO/cmtkDICOM.cxx000066400000000000000000000406011276303427400161630ustar00rootroot00000000000000/* // // Copyright 2014-2015 Google Inc. // // Copyright 2004-2014 SRI International // // Copyright 1997-2009 Torsten Rohlfing // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 5391 $ // // $LastChangedDate: 2015-11-02 19:37:40 -0800 (Mon, 02 Nov 2015) $ // // $LastChangedBy: torstenrohlfing $ // */ #include "cmtkDICOM.h" #include #include #include #include #include #ifdef CMTK_USE_DCMTK_JPEG # include #endif #ifdef HAVE_SYS_TYPES_H # include #endif #include #include #include #ifndef DCM_ACR_NEMA_ImagePosition # define DCM_ACR_NEMA_ImagePosition DcmTagKey(0x0020, 0x0030) #endif #ifndef DCM_ACR_NEMA_ImageOrientation # define DCM_ACR_NEMA_ImageOrientation DcmTagKey(0x0020, 0x0035) #endif #ifndef DCM_ACR_NEMA_Location # define DCM_ACR_NEMA_Location DcmTagKey(0x0020, 0x0050) #endif #ifndef DCM_ACR_NEMA_2C_VariablePixelData # define DCM_ACR_NEMA_2C_VariablePixelData DcmTagKey(0x7f00, 0x0010) #endif namespace cmtk { /** \addtogroup IO */ //@{ void DICOM::InitFromFile( const std::string& path ) { this->m_Path = path; #ifdef CMTK_USE_DCMTK_JPEG // register global decompression codecs static bool decodersRegistered = false; if ( ! decodersRegistered ) { DJDecoderRegistration::registerCodecs( EDC_photometricInterpretation, EUC_default, EPC_default, 1 ); decodersRegistered = true; } #endif std::auto_ptr fileformat( new DcmFileFormat ); if (!fileformat.get()) { throw Exception( "Could not create DICOM file format object." ); } OFCondition status = fileformat->loadFile( path.c_str() ); if ( !status.good() ) { throw Exception( "Cannot read DICOM file.." ); // StdErr << "Error: cannot read DICOM file " << path << " (" << status.text() << ")\n"; } this->m_Dataset = fileformat->getAndRemoveDataset(); if ( !this->m_Dataset ) { throw Exception( "File format has NULL dataset." ); } this->m_Document = std::auto_ptr( new DiDocument( this->m_Dataset, this->m_Dataset->getOriginalXfer(), CIF_AcrNemaCompatibility ) ); if ( ! this->m_Document.get() || ! this->m_Document->good() ) { throw Exception( "Could not create document representation." ); } } const FixedVector<3,int> DICOM::GetDims() const { FixedVector<3,int> dims( 0 ); Uint16 tempUint16 = 1; if ( this->Document().getValue( DCM_Columns, tempUint16 ) ) { dims[0] = static_cast( tempUint16 ); } if ( this->Document().getValue( DCM_Rows, tempUint16 ) ) { dims[1] = static_cast( tempUint16 ); } // detect and treat multi-frame files if ( ! this->Document().getValue( DCM_NumberOfFrames, tempUint16 ) ) { // unlike Rows/Columns, NumberofFrames defaults to 1 tempUint16 = 1; } dims[2] = tempUint16; return dims; } const FixedVector<3,double> DICOM::GetPixelSize() const { FixedVector<3,double> pixelSize( 0.0 ); // get calibration from image const bool hasPixelSpacing = (this->Document().getValue(DCM_PixelSpacing, pixelSize[0], 0) > 0); if ( hasPixelSpacing ) { if (this->Document().getValue(DCM_PixelSpacing, pixelSize[1], 1) < 2) { throw Exception( "DICOM file does not have two elements in pixel size tag" ); } } else throw Exception( "DICOM file does not specify pixel size" ); // get slice spacing from multi-slice images. if ( ! this->Document().getValue( DCM_SpacingBetweenSlices, pixelSize[2] ) ) { pixelSize[2] = 0; } return pixelSize; } const FixedVector<3,double> DICOM::GetImageOrigin() const { FixedVector<3,double> imageOrigin( 0.0 ); const char *image_position_s = NULL; if ( ! this->Document().getValue( DCM_ImagePositionPatient, image_position_s ) ) { // ImagePositionPatient tag not present, try ImagePosition instead #ifdef DCM_ImagePosition if ( ! this->Document().getValue( DCM_ImagePosition, image_position_s ) ) image_position_s = NULL; #else if ( ! this->Document().getValue( DCM_ACR_NEMA_ImagePosition, image_position_s ) ) image_position_s = NULL; #endif } if ( image_position_s ) { double xyz[3]; if ( 3 == sscanf( image_position_s,"%20lf%*c%20lf%*c%20lf", xyz, xyz+1, xyz+2 ) ) { imageOrigin = FixedVector<3,double>::FromPointer( xyz ); } } return imageOrigin; } const FixedArray< 2, FixedVector<3,double> > DICOM::GetImageOrientation() const { FixedArray< 2, FixedVector<3,double> > orientation; orientation[0] = FixedVector<3,double>( 0.0 ); orientation[1] = FixedVector<3,double>( 0.0); orientation[0][0] = 1; orientation[1][1] = 1; const char *image_orientation_s = NULL; #ifdef DCM_ImageOrientation if ( ! this->Document().getValue( DCM_ImageOrientation, image_orientation_s ) ) #else if ( ! this->Document().getValue( DCM_ACR_NEMA_ImageOrientation, image_orientation_s ) ) #endif { // ImageOrientation tag not present, try ImageOrientationPatient instead if ( ! this->Document().getValue( DCM_ImageOrientationPatient, image_orientation_s ) ) image_orientation_s = NULL; } if ( image_orientation_s ) { double dx[3], dy[3]; if ( 6 == sscanf( image_orientation_s, "%20lf%*c%20lf%*c%20lf%*c%20lf%*c%20lf%*c%20lf", dx, dx+1, dx+2, dy, dy+1, dy+2 ) ) { orientation[0] = ( FixedVector<3,double>::FromPointer( dx ) ); orientation[1] = ( FixedVector<3,double>::FromPointer( dy ) ); } } return orientation; } TypedArray::SmartPtr DICOM::GetPixelDataArray( const size_t pixelDataLength ) { DcmElement *delem = NULL; unsigned short bitsAllocated = 0; if ( ( delem = this->m_Document->search( DCM_BitsAllocated ) ) ) { delem->getUint16( bitsAllocated ); } else { // No "BitsAllocated" tag; use "BitsStored" instead. if ( ( delem = this->m_Document->search( DCM_BitsStored ) ) ) { delem->getUint16( bitsAllocated ); } } bool pixelDataSigned = false; Uint16 pixelRepresentation = 0; if ( this->m_Document->getValue( DCM_PixelRepresentation, pixelRepresentation ) > 0) pixelDataSigned = (pixelRepresentation == 1); double rescaleIntercept, rescaleSlope; const bool haveRescaleIntercept = (0 != this->m_Document->getValue( DCM_RescaleIntercept, rescaleIntercept )); if ( ! haveRescaleIntercept ) rescaleIntercept = 0; const bool haveRescaleSlope = (0 != this->m_Document->getValue( DCM_RescaleSlope, rescaleSlope )); if ( ! haveRescaleSlope ) rescaleSlope = 1; pixelDataSigned = pixelDataSigned || (rescaleIntercept < 0); Uint16 paddingValue = 0; const bool paddingFlag = (this->m_Dataset->findAndGetUint16( DCM_PixelPaddingValue, paddingValue )).good(); TypedArray::SmartPtr pixelDataArray; #ifdef DCM_VariablePixelData delem = this->m_Document->search( DCM_VariablePixelData ); #else delem = this->m_Document->search( DCM_ACR_NEMA_2C_VariablePixelData ); #endif if (!delem) delem = this->m_Document->search( DCM_PixelData ); if (delem) { if ( (delem->getTag().getEVR() == EVR_OW) || (bitsAllocated > 8) ) { Uint16 *pdata = NULL; delem->getUint16Array(pdata); if ( pixelDataSigned ) { const short paddingShort = static_cast( paddingValue ); pixelDataArray = TypedArray::Create( TYPE_SHORT, pdata, pixelDataLength, paddingFlag, &paddingShort, Memory::ArrayCXX::DeleteWrapper ); } else { const unsigned short paddingUShort = static_cast( paddingValue ); pixelDataArray = TypedArray::Create( TYPE_USHORT, pdata, pixelDataLength, paddingFlag, &paddingUShort, Memory::ArrayCXX::DeleteWrapper ); } } else { Uint8 *pdata = NULL; delem->getUint8Array(pdata); if ( pixelDataSigned ) { const char paddingChar = static_cast( paddingValue ); pixelDataArray = TypedArray::Create( TYPE_CHAR, pdata, pixelDataLength, paddingFlag, &paddingChar, Memory::ArrayCXX::DeleteWrapper ); } else { const char paddingByte = static_cast( paddingValue ); pixelDataArray = TypedArray::Create( TYPE_BYTE, pdata, pixelDataLength, paddingFlag, &paddingByte, Memory::ArrayCXX::DeleteWrapper ); } } delem->detachValueField(); } if ( ! pixelDataArray ) { throw Exception( "Could not read pixel data from DICOM file" ); } if ( haveRescaleIntercept || haveRescaleSlope ) { double intpart = 0; if ( fabs(modf(rescaleSlope, &intpart) / rescaleSlope) > 1e-5 ) { pixelDataArray = pixelDataArray->Convert(TYPE_FLOAT); } pixelDataArray->Rescale( rescaleSlope, rescaleIntercept ); } return pixelDataArray; } const FixedVector<3,double> DICOM::DemosaicAndGetNormal ( const FixedArray< 2, FixedVector<3,double> >& imageOrientation, const FixedVector<3,Types::Coordinate>& deltas, FixedVector<3,int>& dims, TypedArray::SmartPtr& pixelDataArray, FixedVector<3,double>& imageOrigin ) { // without further information, we "guess" the image normal vector FixedVector<3,double> sliceNormal = SurfaceNormal( imageOrientation[0], imageOrientation[1] ).Get(); // detect and treat Siemens multi-slice mosaics const char* tmpStr = NULL; if ( this->Document().getValue( DCM_Manufacturer, tmpStr ) ) { if ( !strncmp( tmpStr, "SIEMENS", 7 ) ) { Uint16 tempUint16 = 0; const DcmTagKey nSlicesTag(0x0019,0x100a); if ( this->Document().getValue( nSlicesTag, tempUint16 ) ) { dims[2] = tempUint16; } // check for mosaic if ( dims[2] || (this->Document().getValue( DCM_ImageType, tmpStr ) && strstr( tmpStr, "MOSAIC" )) ) { int unmosaicImageRows; int unmosaicImageCols; const DcmTagKey mosaicTag(0x0051,0x100b); if ( this->Document().getValue( mosaicTag, tmpStr ) ) { if ( 2 != sscanf( tmpStr, "%6dp*%6ds", &unmosaicImageRows, &unmosaicImageCols) ) { if ( 2 != sscanf( tmpStr, "%6d*%6ds", &unmosaicImageRows, &unmosaicImageCols) ) { StdErr << "ERROR: unable to parse mosaic size from (0x0051,0x100b): " << tmpStr << "\n"; } } } // For the following, see here: http://nipy.sourceforge.net/nibabel/dicom/siemens_csa.html#csa-header this->ParseSiemensCSA( DcmTagKey(0x0029,0x1020), unmosaicImageCols, unmosaicImageRows, dims[2], sliceNormal, imageOrigin ); // series information this->ParseSiemensCSA( DcmTagKey(0x0029,0x1010), unmosaicImageCols, unmosaicImageRows, dims[2], sliceNormal, imageOrigin ); // image information // hopefully we have figured out the mosaic dimensions by now. if ( (unmosaicImageCols > 0) && (unmosaicImageRows > 0 ) ) { const int xMosaic = dims[0] / unmosaicImageCols; dims[0] = unmosaicImageCols; dims[1] = unmosaicImageRows; // de-mosaic the data array const unsigned long imageSizePixels = dims[0] * dims[1] * dims[2]; TypedArray::SmartPtr newDataArray( TypedArray::Create( pixelDataArray->GetType(), imageSizePixels ) ); const size_t pixelsPerSlice = unmosaicImageCols * unmosaicImageRows; size_t toOffset = 0; for ( int slice = 0; slice < dims[2]; ++slice ) { for ( int j = 0; j < unmosaicImageRows; ++j, toOffset += dims[0] ) { const size_t iPatch = slice % xMosaic; const size_t jPatch = slice / xMosaic; const size_t fromOffset = jPatch * xMosaic * pixelsPerSlice + j * xMosaic * unmosaicImageCols + iPatch * unmosaicImageCols; pixelDataArray->BlockCopy( *newDataArray, toOffset, fromOffset, unmosaicImageCols ); } } pixelDataArray = newDataArray; // convert CSA-header center-of-image origin to corner-of-image standard origin (Issue #6754) imageOrigin -= (0.5 * ((dims[0]-1) * deltas[0] * imageOrientation[0] + (dims[1]-1) * deltas[1] * imageOrientation[1]) ); } } } } return sliceNormal; } void DICOM::ParseSiemensCSA( const DcmTagKey& tagKey, int& unmosaicImageCols, int& unmosaicImageRows, int& slices, FixedVector<3,double>& sliceNormal, FixedVector<3,double>& imageOrigin ) { const Uint8* csaHeaderInfo = NULL; unsigned long csaHeaderLength = 0; if ( this->Dataset().findAndGetUint8Array ( tagKey, csaHeaderInfo, &csaHeaderLength ).status() == OF_ok ) { SiemensCSAHeader csaHeader( (const char*)csaHeaderInfo, csaHeaderLength ); SiemensCSAHeader::const_iterator it = csaHeader.find( "AcquisitionMatrixText" ); if ( (it != csaHeader.end()) && !it->second.empty() ) { if ( 2 != sscanf( it->second[0].c_str(), "%6dp*%6ds", &unmosaicImageRows, &unmosaicImageCols) ) { if ( 2 != sscanf( it->second[0].c_str(), "%6d*%6ds", &unmosaicImageRows, &unmosaicImageCols) ) { StdErr << "ERROR: unable to parse mosaic size from CSA field AcquisitionMatrixText: " << it->second[0] << " in file " << this->m_Path << "\n"; } } } it = csaHeader.find( "NumberOfImagesInMosaic" ); if ( (it != csaHeader.end()) && !it->second.empty() ) slices = atof( it->second[0].c_str() ); it = csaHeader.find( "SliceNormalVector" ); if ( (it != csaHeader.end()) && (it->second.size() >= 3) ) { for ( size_t i = 0; i < 3; ++i ) sliceNormal[i] = atof( it->second[i].c_str() ); } // get true slice0 location from CSA header it = csaHeader.find( "MrPhoenixProtocol" ); if ( (it != csaHeader.end()) && !it->second.empty() ) { // ID strings for three axes in CSA header const std::string sliceOrientationString[] = { "dSag", "dCor", "dTra" }; for ( int i = 0; i < 3; ++i ) { const size_t sliceTagPos = it->second[0].find( std::string( "sSliceArray.asSlice[0].sPosition." ) + sliceOrientationString[i] ); if ( sliceTagPos != std::string::npos ) { const size_t equalPos = it->second[0].find( '=', sliceTagPos ); if ( equalPos != std::string::npos ) { imageOrigin[i] = atof( it->second[0].substr( equalPos + 1 ).c_str() ); } else { StdErr << "ERROR: unable to get image origin component from: " << it->second[0] << " in file " << this->m_Path << "\nAssuming zero.\n"; imageOrigin[i] = 0; } } else { StdErr << "ERROR: unable to get image origin tag for component " << sliceOrientationString[i] << " from CSA header in file " << this->m_Path << "\nAssuming zero.\n"; imageOrigin[i] = 0; } } } } } ScalarImage* DICOM::Read ( const char *path ) { ScalarImage* image = NULL; Self dicom( path ); FixedVector<3,int> dims = dicom.GetDims(); FixedVector<3,double> pixelSize = dicom.GetPixelSize(); ScalarImage::SpaceVectorType imageOrigin = dicom.GetImageOrigin(); image = new ScalarImage( dims[0], dims[1], dims[2] ); image->SetPixelSize( pixelSize[0], pixelSize[1] ); image->SetFrameToFrameSpacing( pixelSize[2] ); TypedArray::SmartPtr pixelDataArray = dicom.GetPixelDataArray( dims[0] * dims[1] * dims[2] ); image->SetPixelData( pixelDataArray ); // now some more manual readings... // get original table position from image. double sliceLocation = 0; if ( ! dicom.Document().getValue( DCM_SliceLocation, sliceLocation ) ) { #ifdef DCM_Location dicom.Document().getValue( DCM_Location, sliceLocation ); #else dicom.Document().getValue( DCM_ACR_NEMA_Location, sliceLocation ); #endif } image->SetImageSlicePosition( sliceLocation ); image->SetImageOrigin( imageOrigin ); // get original image direction from file. FixedArray< 2, FixedVector<3,double> > imageOrientation = dicom.GetImageOrientation(); image->SetImageDirectionX( imageOrientation[0] ); image->SetImageDirectionY( imageOrientation[1] ); return image; } //@} } // namespace cmtk cmtk-3.3.1/libs/IO/cmtkDICOM.h000066400000000000000000000113361276303427400156130ustar00rootroot00000000000000/* // // Copyright 2004-2012, 2013 SRI International // // Copyright 1997-2009 Torsten Rohlfing // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4943 $ // // $LastChangedDate: 2013-10-04 13:46:53 -0700 (Fri, 04 Oct 2013) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkDICOM_h_included_ #define __cmtkDICOM_h_included_ #include #include #include #include #include #include namespace cmtk { /** \addtogroup IO */ //@{ /** Reader/writer class for DICOM images. */ class DICOM { public: /// This class. typedef DICOM Self; /// Default constructor. DICOM() : m_Dataset( NULL ) {} /// Read file constructor. DICOM( const std::string& path ) { this->InitFromFile( path ); } /// Read object from DICOM file. void InitFromFile( const std::string& path ); /// Get image dimensions (number of pixels per axis). const FixedVector<3,int> GetDims() const; /// Get image pixel size. const FixedVector<3,double> GetPixelSize() const; /// Get image origin in scanner coordinates. const FixedVector<3,double> GetImageOrigin() const; /// Get image orientation of the i and j grid axes. const FixedArray< 2, FixedVector<3,double> > GetImageOrientation() const; /** Get pixel data array. * The pixel data type is determined automatically based on bits allocated and signed vs. unsigned representation. * * If the RescaleSlope or RescaleIntercept tags are present, intensity rescaling is applied. * * If a padding value is defined in the DICOM file, this value is also set as padding in the output array. * *\warning As a side effect, this function releases the pixel data array pointer from the DICOM object, i.e., * this function can only be called ONCE for each object. */ TypedArray::SmartPtr GetPixelDataArray( const size_t pixelDataLength ); /// Demosaic (if necessary) the image and return slice normal vector. const FixedVector<3,double> DemosaicAndGetNormal( const FixedArray< 2, FixedVector<3,double> >& imageOrientation /*!< Image orientation vectors (read-only, for default normal computation)*/, const FixedVector<3,Types::Coordinate>& deltas /*!< Pixel size - this is needed for computing image origin coordinates from image center location */, FixedVector<3,int>& dims /*!< Image dimensions - these may be modified if the image is a mosaic */, TypedArray::SmartPtr& pixelDataArray /*!< Pixel data array - a new array will be returned if the image is a mosaic.*/, FixedVector<3,double>& imageOrigin /*!< True image origin from CSA header if this is a mosaic file. */); /// Get const DICOM dataset. const DcmDataset& Dataset() const { return *(this->m_Dataset); } /// Get DICOM dataset. DcmDataset& Dataset() { return *(this->m_Dataset); } /// Get const DICOM document. const DiDocument& Document() const { return *(this->m_Document); } /// Get DICOM document. DiDocument& Document() { return *(this->m_Document); } /** Read ScalarImage from DICOM file. */ static ScalarImage* Read( const char *path ); private: /// Path of this DICOM file. std::string m_Path; /// Pointer to the DICOM dataset object DcmDataset* m_Dataset; /// Pointer to the DICOM document object std::auto_ptr m_Document; /// Parse private Siemens CSA data. void ParseSiemensCSA( const DcmTagKey& tagKey /*!< DCM tag with the Siemens CSA header data */, int& unmosaicImageCols /*!< Return demosaiced image columns */, int& unmosaicImageRows /*!< Return demosaiced image rows */, int& slices /*!< Return demosaiced image slices */, FixedVector<3,double>& sliceNormal /*!< Return demosaiced image slice normal */, FixedVector<3,double>& imageOrigin /*!< Return demosaiced image origin */ ); }; //@} } // namespace cmtk #endif // #ifndef __cmtkDICOM_h_included_ cmtk-3.3.1/libs/IO/cmtkFileConstHeader.h000066400000000000000000000065231276303427400177610ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2011 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4542 $ // // $LastChangedDate: 2012-10-04 12:56:58 -0700 (Thu, 04 Oct 2012) $ // // $LastChangedBy: torsten_at_home $ // */ #ifndef __cmtkFileConstHeader_h_included_ #define __cmtkFileConstHeader_h_included_ #include #include #include namespace cmtk { /** \addtogroup IO */ //@{ /** Read-only access to fields in a binary file header. */ class FileConstHeader { public: /// Constructor. FileConstHeader( const void *const header, const bool isBigEndian = true ) { this->m_ConstHeader = static_cast( header ); this->m_IsBigEndian = isBigEndian; } /// Retrieve field of arbitrary type from header. template T GetField( const size_t offset ) { T result; memcpy( &result, this->m_ConstHeader+offset, sizeof( T ) ); #ifdef WORDS_BIGENDIAN if ( ! this->m_IsBigEndian ) result = Memory::ByteSwap( result ); #else if ( this->m_IsBigEndian ) result = Memory::ByteSwap( result ); #endif return result; } /// Get null-terminated string from header. char* GetFieldString( const size_t offset, char* value, const size_t maxlen = 0 ) { if ( maxlen ) strncpy( value, this->m_ConstHeader+offset, maxlen ); else strcpy( value, this->m_ConstHeader+offset ); return value; } /** Compare n-character string. *\return The comparison result code as returned by memcmp(), i.e., the result is 0 if the given string matches * the respective portion of the header. */ bool CompareFieldStringN( const size_t offset, const char* value, const size_t len ) const { return !memcmp( this->m_ConstHeader+offset, value, len ); } /// Retrieve array of arbitrary type from header. template void GetArray ( T *const target, const size_t offset, const size_t length = 1 ) { memcpy( target, this->m_ConstHeader+offset, length * sizeof( T ) ); #ifdef WORDS_BIGENDIAN if ( ! this->m_IsBigEndian ) { for ( size_t i = 0; i < length; ++i ) target[i] = Memory::ByteSwap( target[i] ); } #else if ( this->m_IsBigEndian ) { for ( size_t i = 0; i < length; ++i ) target[i] = Memory::ByteSwap( target[i] ); } #endif } private: /// Pointer to the binary header. const char *m_ConstHeader; protected: /// Flag for endianness. bool m_IsBigEndian; }; //@} } // namespace cmtk #endif // #ifndef __cmtkFileConstHeader_h_included_ cmtk-3.3.1/libs/IO/cmtkFileFormat.cxx000066400000000000000000000177131276303427400173700ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2013 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 5036 $ // // $LastChangedDate: 2013-11-25 15:15:37 -0800 (Mon, 25 Nov 2013) $ // // $LastChangedBy: torstenrohlfing $ // */ #include "cmtkFileFormat.h" #include #include #ifdef HAVE_SYS_TYPES_H # include #endif #ifdef HAVE_SYS_STAT_H # include #endif #include #include #include namespace cmtk { /** \addtogroup IO */ //@{ /// Structure that holds information on magic file number. typedef struct { /// Offset of the magic number within the file. unsigned short offset; /// Magic number as string of characters. const char *magicString; /// Length of magic number string (necessary due to Null characters). const size_t magicStringLength; } FileFormatMagic; /// Magic number records for known file types. const FileFormatMagic FileFormatMagicNumbers[] = { { 0, NULL, 0 }, // NEXIST { 0, NULL, 0 }, // ARCHIVE { 0, NULL, 0 }, // STUDY { 0, NULL, 0 }, // STUDYLIST { 0, "! TYPEDSTREAM", 13 }, // TypedStream archive { 0, "P5", 2 }, // PGM { 128, "DICM", 4 }, // DICOM { 0, "Modality :=", 11 }, // VANDERBILT { 0, "# AmiraMesh 3D", 14 }, { 0, NULL, 0 }, // RAW { 0, NULL, 0 }, // RAW3D { 54, "\x39\x30", 2 }, // BIORAD { 344, "ni1\x00", 4 }, // Nifti, detached header { 344, "n+1\x00", 4 }, // Nifti, single file { 0, "AVW_ImageFile", 13 }, // Analyze AVW file. { 0, NULL, 0 }, // MetaImage { 0, "NRRD", 4 }, //NRRD { 0, "\x5C\x01\x00\x00", 4 }, // Analyze little endian { 0, "\x00\x00\x01\x5C", 4 }, // Analyze big endian { 0, "#Insight Transform File V1.0", 28 }, // ITK transformation file { 0, NULL, 0 } // Unknown. }; const char* FileFormatName[] = { /// File of this name does not exist. "", /// File is a compressed archive. "COMPRESSED-ARCHIVE", /// Path is a typedstream study directory. "STUDY", /// Path is a typedstream studylist directory. "STUDYLIST", /// Path is a typedstream archive. "TYPEDSTREAM", /// Path is a PGM image. "PGM", /// Path is a DICOM image. "DICOM", /// Path is a Vanderbilt image description file. "VANDERBILT", /// Path is a Amira image file. "AMIRA", /// Path is some raw binary file (2-D). "RAW-DATA", /// Path is some raw binary file (3-D). "RAW3D", /// Path is a BioRad .PIC file. "BIORAD", /// Nifti, detached header "NIFTI-DETACHED-HEADER", /// Nifti, single file "NIFTI-SINGLE-FILE", /// Path is an Analyze AVW file. "ANALYZE-AVW", /// MetaImage "METAIMAGE", /// NRRD "NRRD", /// Path is an Analyze 7.5 file in little endian. "ANALYZE-HDR-LITTLEENDIAN", /// Path is an Analyze 7.5 file in big endian. "ANALYZE-HDR-BIGENDIAN", /** File type cannot be determined. * This ID always has to be the last one! */ NULL }; FileFormatID FileFormat::Identify( const std::string& path, const bool decompress ) { CompressedStream::StatType buf; if ( CompressedStream::Stat( path, &buf ) < 0 ) return FILEFORMAT_NEXIST; if ( buf.st_mode & S_IFDIR ) return FileFormat::IdentifyDirectory( path ); else if ( buf.st_mode & S_IFREG ) return FileFormat::IdentifyFile( path, decompress ); return FILEFORMAT_NEXIST; } std::string FileFormat::Describe( const FileFormatID id ) { switch ( id ) { case FILEFORMAT_NEXIST: return "File or directory does not exist."; case FILEFORMAT_STUDY: return "Typedstream study archive [Directory]."; case FILEFORMAT_STUDYLIST: return "Typedstream studylist archive [Directory]."; case FILEFORMAT_PGM: return "PGM image file [File]."; case FILEFORMAT_DICOM: return "DICOM image file [File]."; case FILEFORMAT_VANDERBILT: return "Vanderbilt header/image file combination [File]."; case FILEFORMAT_AMIRA: return "AmiraMesh image file [File]."; case FILEFORMAT_BIORAD: return "BioRad image file [File]."; case FILEFORMAT_NIFTI_DETACHED: return "NIFTI detached header+image [File]"; case FILEFORMAT_NIFTI_SINGLEFILE: return "NIFTI single file [File]"; case FILEFORMAT_ANALYZE_HDR: return "Analyze 7.5 file [Header+Binary File/Little Endian]."; case FILEFORMAT_ANALYZE_HDR_BIGENDIAN: return "Analyze 7.5 file [Header+Binary File/Big Endian]."; case FILEFORMAT_ANALYZE_AVW: return "Analyze AVW file [File]."; case FILEFORMAT_RAW: return "RAW image file [File]."; case FILEFORMAT_NRRD: return "Nrrd image file [File]."; case FILEFORMAT_UNKNOWN: default: break; } return "ILLEGAL ID tag in FileFormat::Describe()."; } FileFormatID FileFormat::IdentifyDirectory( const std::string& path ) { char filename[PATH_MAX]; struct stat buf; snprintf( filename, sizeof( filename ), "%s%cimages", path.c_str(), (int)CMTK_PATH_SEPARATOR ); if ( (!stat( filename, &buf )) && ( buf.st_mode & S_IFREG ) ) return FILEFORMAT_STUDY; snprintf( filename, sizeof( filename ), "%s%cimages.gz", path.c_str(), (int)CMTK_PATH_SEPARATOR ); if ( (!stat( filename, &buf )) && ( buf.st_mode & S_IFREG ) ) return FILEFORMAT_STUDY; snprintf( filename, sizeof( filename ), "%s%cstudylist", path.c_str(), (int)CMTK_PATH_SEPARATOR ); if ( (!stat( filename, &buf )) && ( buf.st_mode & S_IFREG ) ) return FILEFORMAT_STUDYLIST; snprintf( filename, sizeof( filename ), "%s%cstudylist.gz", path.c_str(), (int)CMTK_PATH_SEPARATOR ); if ( (!stat( filename, &buf )) && ( buf.st_mode & S_IFREG ) ) return FILEFORMAT_STUDYLIST; return FILEFORMAT_UNKNOWN; } FileFormatID FileFormat::IdentifyFile( const std::string& path, const bool decompress ) { CompressedStream stream( path ); if ( ! stream.IsValid() ) return FILEFORMAT_NEXIST; if ( stream.IsCompressed() && !decompress ) return FILEFORMAT_COMPRESSED_ARCHIVE; char buffer[348]; memset( buffer, 0, sizeof( buffer ) ); stream.Read( buffer, 1, 348 ); FileFormatID id = FILEFORMAT_NEXIST; while ( id != FILEFORMAT_UNKNOWN ) { if ( FileFormatMagicNumbers[id].magicString ) { if ( !memcmp( buffer+FileFormatMagicNumbers[id].offset, FileFormatMagicNumbers[id].magicString, FileFormatMagicNumbers[id].magicStringLength ) ) return id; } char cid = static_cast( id ); id = static_cast( ++cid ); } return FILEFORMAT_UNKNOWN; } bool FileFormat::IsImage( const FileFormatID& id ) { switch ( id ) { case FILEFORMAT_STUDY: case FILEFORMAT_PGM: case FILEFORMAT_DICOM: case FILEFORMAT_VANDERBILT: case FILEFORMAT_AMIRA: case FILEFORMAT_BIORAD: case FILEFORMAT_NIFTI_DETACHED: case FILEFORMAT_NIFTI_SINGLEFILE: case FILEFORMAT_ANALYZE_AVW: case FILEFORMAT_METAIMAGE: case FILEFORMAT_NRRD: case FILEFORMAT_ANALYZE_HDR: case FILEFORMAT_ANALYZE_HDR_BIGENDIAN: return true; default: break; } return false; } bool FileFormat::IsXform( const FileFormatID& id ) { switch ( id ) { case FILEFORMAT_STUDYLIST: case FILEFORMAT_TYPEDSTREAM: return true; default: break; } return false; } } // namespace cmtk cmtk-3.3.1/libs/IO/cmtkFileFormat.h000066400000000000000000000103771276303427400170140ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2013 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4817 $ // // $LastChangedDate: 2013-09-10 11:28:43 -0700 (Tue, 10 Sep 2013) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkFileFormat_h_included_ #define __cmtkFileFormat_h_included_ #include #include namespace cmtk { /** \addtogroup IO */ //@{ /// ID codes for known file formats. typedef enum { /// File of this name does not exist. FILEFORMAT_NEXIST = 0, /// File is a compressed archive file. FILEFORMAT_COMPRESSED_ARCHIVE = 1, /// Path is a typedstream study directory. FILEFORMAT_STUDY = 2, /// Path is a typedstream studylist directory. FILEFORMAT_STUDYLIST = 3, /// Path is a typedstream archive file. FILEFORMAT_TYPEDSTREAM = 4, /// Path is a PGM image. FILEFORMAT_PGM = 5, /// Path is a DICOM image. FILEFORMAT_DICOM = 6, /// Path is a Vanderbilt image description file. FILEFORMAT_VANDERBILT = 7, /// Path is a Amira image file. FILEFORMAT_AMIRA = 8, /// Path is some raw binary file (2-D). FILEFORMAT_RAW = 9, /// Path is some raw binary file (3-D). FILEFORMAT_RAW3D = 10, /// Path is a BioRad .PIC image file. FILEFORMAT_BIORAD = 11, /// Path is a NRRD file. FILEFORMAT_NIFTI_DETACHED = 12, /// Path is a NRRD file. FILEFORMAT_NIFTI_SINGLEFILE = 13, /// Path is an Analyze AVW file. FILEFORMAT_ANALYZE_AVW = 14, /// Path is a MetaImage file. FILEFORMAT_METAIMAGE = 15, /// Path is a NRRD file. FILEFORMAT_NRRD = 16, /// Path is an Analyze header file in little endian. FILEFORMAT_ANALYZE_HDR = 17, /// Path is an Analyze header file in little endian. FILEFORMAT_ANALYZE_HDR_BIGENDIAN = 18, /// Path is an ITK transformation file. FILEFORMAT_ITK_TFM = 19, /** File type cannot be determined. * This ID always has to be the last one! */ FILEFORMAT_UNKNOWN } FileFormatID; /// Table of file format ID names. extern const char* FileFormatName[]; /** Identify file (and directory) formats. */ class FileFormat { public: /** Identify file or directory with given path. * Compressed files as supported by CompressedStream are handled. If both * an uncompressed and a compressed file exist for the same path prefix, then * the uncompressed file has precedence. */ static FileFormatID Identify( const std::string& path /*!< Image path. */, const bool decompress = true /*!< If set, compressed files are decompressed before determining their file type.*/ ); /** Return textual description of identified file format. */ static std::string Describe( const FileFormatID id ); /** Return textual description of a file's format. */ static std::string Describe( const std::string& path ) { return Describe( Identify( path ) ); } /// Say whether given file format is an image file format. static bool IsImage( const FileFormatID& id ); /// Say whether given file format is a transformation file format. static bool IsXform( const FileFormatID& id ); private: /** Identify directory with given path. */ static FileFormatID IdentifyDirectory( const std::string& path ); /** Identify regular file with given path. */ static FileFormatID IdentifyFile( const std::string& path /*!< Image path. */, const bool decompress = true /*!< If set, compressed files are decompressed before determining their file type.*/ ); }; //@} } // namespace cmtk #endif // #ifndef __cmtkFileFormat_h_included_ cmtk-3.3.1/libs/IO/cmtkFileHeader.h000066400000000000000000000045231276303427400167500ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2011 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 3513 $ // // $LastChangedDate: 2011-10-26 15:28:43 -0700 (Wed, 26 Oct 2011) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkFileHeader_h_included_ #define __cmtkFileHeader_h_included_ #include #include namespace cmtk { /** \addtogroup IO */ //@{ /** Read and write ccess to fields in a binary file header. */ class FileHeader : public FileConstHeader { public: /// Constructor. FileHeader( void *const header, const bool isBigEndian = true ) : FileConstHeader( header, isBigEndian ) { this->m_Header = static_cast( header ); } /// Store field of arbitrary type to header. template void StoreField( const size_t offset, T value ) { #ifdef WORDS_BIGENDIAN if ( (sizeof(T) > 1) && ! this->m_IsBigEndian ) value = Memory::ByteSwap( value ); #else if ( (sizeof(T) > 1) && this->m_IsBigEndian ) value = Memory::ByteSwap( value ); #endif memcpy( this->m_Header+offset, &value, sizeof( T ) ); } /// Store null-terminated string to header. void StoreFieldString( const size_t offset, const char* value, const size_t maxlen = 0 ) { if ( maxlen ) strncpy( this->m_Header+offset, value, maxlen ); else strcpy( this->m_Header+offset, value ); } private: /// Pointer to the binary header. char *m_Header; }; //@} } // namespace cmtk #endif // #ifndef __cmtkFileHeader_h_included_ cmtk-3.3.1/libs/IO/cmtkGroupwiseRegistrationFunctionalIO.cxx000066400000000000000000000046601276303427400241670ustar00rootroot00000000000000/* // // Copyright 2016 Google, Inc. // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2012 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 5398 $ // // $LastChangedDate: 2016-01-14 18:29:18 -0800 (Thu, 14 Jan 2016) $ // // $LastChangedBy: torstenrohlfing $ // */ #include "cmtkGroupwiseRegistrationFunctionalIO.h" #include #include #include namespace cmtk { /** \addtogroup IO */ //@{ ClassStreamOutput& operator<< ( ClassStreamOutput& stream, const GroupwiseRegistrationFunctionalBase& func ) { const UniformVolume* templateGrid = func.GetTemplateGrid(); stream.Begin( "template" ); stream.WriteIntArray( "dims", templateGrid->GetDims().begin(), 3 ); stream.WriteCoordinateArray( "delta", templateGrid->Deltas().begin(), 3 ); stream.WriteCoordinateArray( "size", templateGrid->m_Size.begin(), 3 ); stream.WriteCoordinateArray( "origin", templateGrid->m_Offset.begin(), 3 ); stream.End(); for ( size_t idx = 0; idx < func.GetNumberOfTargetImages(); ++idx ) { const UniformVolume* target = func.GetOriginalTargetImage( idx ); stream.WriteString( "target", target->GetMetaInfo( META_FS_PATH ).c_str() ); const Xform* xform = func.GetGenericXformByIndex( idx ); const AffineXform* affineXform = dynamic_cast( xform ); if ( affineXform ) stream << (*affineXform); const SplineWarpXform* splineXform = dynamic_cast( xform ); if ( splineXform ) stream << splineXform; } return stream; } } // namespace cmtk cmtk-3.3.1/libs/IO/cmtkGroupwiseRegistrationFunctionalIO.h000066400000000000000000000033011276303427400236030ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2012 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4344 $ // // $LastChangedDate: 2012-05-11 14:51:08 -0700 (Fri, 11 May 2012) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkGroupwiseRegistrationFunctionalIO_h_included_ #define __cmtkGroupwiseRegistrationFunctionalIO_h_included_ #include #include #include #include namespace cmtk { /** \addtogroup IO */ //@{ ClassStreamOutput& operator<<( ClassStreamOutput& stream, const GroupwiseRegistrationFunctionalBase& func ); ClassStreamInput& operator>>( ClassStreamInput& stream, GroupwiseRegistrationFunctionalBase& func ); //@} } // namespace cmtk #endif // #ifndef __cmtkGroupwiseRegistrationFunctionalIO_h_included_ cmtk-3.3.1/libs/IO/cmtkImageFileDICOM.cxx000066400000000000000000000407651276303427400177410ustar00rootroot00000000000000/* // // Copyright 2004-2014 SRI International // // Copyright 1997-2009 Torsten Rohlfing // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4497 $ // // $LastChangedDate: 2012-08-24 13:46:21 -0700 (Fri, 24 Aug 2012) $ // // $LastChangedBy: torstenrohlfing $ // */ #include "cmtkImageFileDICOM.h" #include #include #include #include #include namespace cmtk { void ImageFileDICOM::Print() const { cmtk::DebugOutput( 1 ) << " File Name = [" << this->m_FileDir << "/" << this->m_FileName << "]\n"; cmtk::DebugOutput( 1 ) << " SeriesID = [" << this->GetTagValue( DCM_SeriesInstanceUID ) << "]\n"; cmtk::DebugOutput( 1 ) << " StudyID = [" << this->GetTagValue( DCM_StudyInstanceUID ) << "]\n"; cmtk::DebugOutput( 1 ) << " ImagePositionPatient = [" << this->GetTagValue( DCM_ImagePositionPatient ) << "]\n"; cmtk::DebugOutput( 1 ) << " AcquisitionNumber = [" << this->m_AcquisitionNumber << "]\n"; cmtk::DebugOutput( 1 ) << " Modality = [" << this->GetTagValue( DCM_Modality ) << "]\n"; if ( this->GetTagValue( DCM_Modality ) == "MR" ) { cmtk::DebugOutput( 1 ) << " EchoTime = [" << this->GetTagValue( DCM_EchoTime ) << "]\n"; cmtk::DebugOutput( 1 ) << " RepetitionTime = [" << this->GetTagValue( DCM_RepetitionTime ) << "]\n"; } } bool ImageFileDICOM::Match( const Self& other, const Types::Coordinate numericalTolerance, const bool disableCheckOrientation, const bool ignoreAcquisitionNumber ) const { // do not stack multislice images if ( this->m_IsMultislice || other.m_IsMultislice ) return false; if ( ! disableCheckOrientation ) { double orientThis[6]; if ( 6 != sscanf( this->GetTagValue( DCM_ImageOrientationPatient ).c_str(), "%30lf%*c%30lf%*c%30lf%*c%30lf%*c%30lf%*c%30lf", orientThis, orientThis+1, orientThis+2, orientThis+3, orientThis+4, orientThis+5 ) ) { StdErr << "ERROR: unable to parse image orientation from '" << this->GetTagValue( DCM_ImageOrientationPatient ).c_str() << "'\n"; return false; } double orientOther[6]; if ( 6 != sscanf( other.GetTagValue( DCM_ImageOrientationPatient ).c_str(), "%30lf%*c%30lf%*c%30lf%*c%30lf%*c%30lf%*c%30lf", orientOther, orientOther+1, orientOther+2, orientOther+3, orientOther+4, orientOther+5 ) ) { StdErr << "ERROR: unable to parse image orientation from '" << other.GetTagValue( DCM_ImageOrientationPatient ).c_str() << "'\n"; return false; } for ( int i = 0; i < 6; ++i ) { if ( fabs( orientThis[i] - orientOther[i] ) > numericalTolerance ) return false; } } return ( this->m_FileDir == other.m_FileDir ) && // do not stack across directories ( this->GetTagValue( DCM_FrameOfReferenceUID ) == other.GetTagValue( DCM_FrameOfReferenceUID ) ) && ( this->GetTagValue( DCM_SeriesInstanceUID ) == other.GetTagValue( DCM_SeriesInstanceUID ) ) && ( this->GetTagValue( DCM_StudyInstanceUID ) == other.GetTagValue( DCM_StudyInstanceUID ) ) && ( this->GetTagValue( DCM_EchoTime ) == other.GetTagValue( DCM_EchoTime ) ) && ( this->GetTagValue( DCM_RepetitionTime ) == other.GetTagValue( DCM_RepetitionTime ) ) && ( this->GetTagValue( DCM_InversionTime ) == other.GetTagValue( DCM_InversionTime ) ) && ( this->m_BValue == other.m_BValue ) && ( this->m_BVector == other.m_BVector ) && (( this->m_AcquisitionNumber == other.m_AcquisitionNumber ) || ignoreAcquisitionNumber) && ( this->m_RawDataType == other.m_RawDataType ); } ImageFileDICOM::ImageFileDICOM( const std::string& filepath ) :m_IsMultislice( false ), m_IsDWI( false ), m_DwellTime( 0.0 ), m_PhaseEncodeDirectionSign( "" ), m_BValue( 0 ), m_BVector( 0.0 ), m_HasBVector( false ), m_RawDataType( "unknown" ) { if ( cmtk::FileFormat::Identify( filepath, false /*decompress*/ ) != cmtk::FILEFORMAT_DICOM ) // need to disable "decompress" in Identify() because DCMTK cannot currently read using on-the-fly decompression. throw(0); this->m_FileName = filepath; this->m_FileDir = ""; const size_t lastSlash = this->m_FileName.rfind( CMTK_PATH_SEPARATOR ); if ( lastSlash != std::string::npos ) { this->m_FileDir = this->m_FileName.substr( 0, lastSlash ); // trim trailing slashes, both forward and back const size_t lastNotSlash = this->m_FileDir.find_last_not_of( "/\\" ); if ( lastNotSlash != std::string::npos ) { this->m_FileDir.erase( lastNotSlash+1 ); } else { this->m_FileDir.clear(); } this->m_FileName = this->m_FileName.substr( lastSlash+1 ); const size_t suffix = this->m_FileName.rfind( '.' ); if ( suffix != std::string::npos ) { const std::string suffixStr = this->m_FileName.substr( suffix+1 ); if ( (suffixStr == ".Z") || (suffixStr == ".gz") ) { this->m_FileName = this->m_FileName.erase( suffix ); } } } std::auto_ptr fileformat( new DcmFileFormat ); OFCondition status = fileformat->loadFile( filepath.c_str() ); if ( !status.good() ) { cmtk::StdErr << "Error: cannot read DICOM file " << filepath << " (" << status.text() << ")\n"; throw (0); } this->m_Dataset = std::auto_ptr( fileformat->getAndRemoveDataset() ); if ( !this->m_Dataset.get() ) { throw(1); } this->m_Document = std::auto_ptr( new DiDocument( this->m_Dataset.get(), this->m_Dataset->getOriginalXfer(), CIF_AcrNemaCompatibility ) ); if ( ! this->m_Document.get() || ! this->m_Document->good() ) { throw(2); } // read the string tags that we need for later const DcmTagKey defaultStringTags[] = { DCM_Manufacturer, DCM_ManufacturerModelName, DCM_DeviceSerialNumber, DCM_StationName, DCM_AcquisitionTime, DCM_PatientsName, DCM_Modality, DCM_EchoTime, DCM_RepetitionTime, DCM_InversionTime, DCM_ImagingFrequency, DCM_SequenceName, DCM_InPlanePhaseEncodingDirection, DCM_StudyInstanceUID, DCM_StudyID, DCM_StudyDate, DCM_FrameOfReferenceUID, DCM_SeriesInstanceUID, DCM_SeriesDescription, DCM_ImagePositionPatient, DCM_ImageOrientationPatient, DCM_RescaleIntercept, DCM_RescaleSlope, // GE-specific tags DCM_GE_PulseSequenceName, DCM_GE_PulseSequenceDate, DCM_GE_InternalPulseSequenceName, DCM_GE_AssetRFactors, // Siemens-specific tags // Philips-specific tags // Zero-tag ends the array DcmTagKey( 0, 0 ) }; for ( size_t tagIdx = 0; (defaultStringTags[tagIdx].getGroup() != 0) && (defaultStringTags[tagIdx].getElement() != 0); ++tagIdx ) { const char* tmpStr = NULL; if ( this->m_Document->getValue( defaultStringTags[tagIdx], tmpStr ) ) this->m_TagToStringMap[defaultStringTags[tagIdx]] = tmpStr; } // check for multi-slice DICOMs Uint16 nFrames = 0; if ( this->m_Document->getValue( DCM_NumberOfFrames, nFrames ) ) { this->m_IsMultislice = (nFrames > 1 ); } // read integer tags that we also need if ( ! this->m_Document->getValue( DCM_InstanceNumber, this->m_InstanceNumber ) ) this->m_InstanceNumber = 0; if ( ! this->m_Document->getValue( DCM_AcquisitionNumber, this->m_AcquisitionNumber ) ) this->m_AcquisitionNumber = 0; // check for which vendor and deal with specifics elsewhere if ( this->m_TagToStringMap[DCM_Manufacturer].substr( 0, 7 ) == "SIEMENS" ) { this->DoVendorTagsSiemens(); } if ( this->m_TagToStringMap[DCM_Manufacturer].substr( 0, 2 ) == "GE" ) { this->DoVendorTagsGE(); } if ( this->m_TagToStringMap[DCM_Manufacturer].substr( 0, 7 ) == "Philips" ) { this->DoVendorTagsPhilips(); } } void ImageFileDICOM::DoVendorTagsSiemens() { Uint16 nFrames = 0; double tmpDbl = 0; const char* tmpStr = NULL; this->m_IsMultislice = (0 != this->m_Document->getValue( DcmTagKey (0x0019,0x100a), nFrames )); // Number of Slices tag this->m_IsMultislice |= ( this->m_Document->getValue( DCM_ImageType, tmpStr ) && strstr( tmpStr, "MOSAIC" ) ); // mosaics are always multi-slice // Retrieve slice times if this tag exists (it's an array of double-precision floats) for ( size_t slice = 0, nslices = this->m_Document->getVM( DCM_Siemens_MosaicRefAcqTimes ); slice < nslices; ++slice ) { if ( this->m_Document->getValue( DCM_Siemens_MosaicRefAcqTimes, tmpDbl, slice ) > 0 ) this->m_SliceTimes.push_back( tmpDbl ); } if ( this->GetTagValue( DCM_Modality ) == "MR" ) { // try to extract raw data type from "ImageType" if ( this->m_Document->getValue( DCM_ImageType, tmpStr ) ) { if ( strstr( tmpStr, "\\P\\" ) ) this->m_RawDataType = "phase"; else if ( strstr( tmpStr, "\\M\\" ) ) this->m_RawDataType = "magnitude"; else if ( strstr( tmpStr, "\\R\\" ) ) this->m_RawDataType = "real"; } // Get dwell time if cas header exists and contains it const Uint8* csaImageHeaderInfo = NULL; unsigned long csaImageHeaderLength = 0; if ( this->m_Dataset->findAndGetUint8Array ( DcmTagKey(0x0029,0x1010), csaImageHeaderInfo, &csaImageHeaderLength ).status() == OF_ok ) // the "Image" CSA header, not the "Series" header. { SiemensCSAHeader csaImageHeader( (const char*)csaImageHeaderInfo, csaImageHeaderLength ); SiemensCSAHeader::const_iterator it = csaImageHeader.find( "RealDwellTime" ); if ( (it != csaImageHeader.end()) && !it->second.empty() ) { this->m_DwellTime = 1.0 / atof( it->second[0].c_str() ); } else { this->m_DwellTime = 0.0; } it = csaImageHeader.find( "PhaseEncodingDirectionPositive" ); if ( (it != csaImageHeader.end()) && !it->second.empty() ) { switch ( it->second[0][0] ) { case '0': this->m_PhaseEncodeDirectionSign = "NEG"; break; case '1': this->m_PhaseEncodeDirectionSign = "POS"; break; default: this->m_PhaseEncodeDirectionSign = "UNKNOWN"; break; } } } // for DWI, first check standard DICOM vendor tags if ( (this->m_IsDWI = (this->m_Document->getValue( DcmTagKey(0x0019,0x100d), tmpStr )!=0)) ) // "Directionality" tag { if ( this->m_Document->getValue( DcmTagKey(0x0019,0x100c), tmpStr ) != 0 ) // bValue tag { this->m_BValue = atof( tmpStr ); this->m_IsDWI |= (this->m_BValue > 0); } if ( this->m_BValue > 0 ) { for ( int idx = 0; idx < 3; ++idx ) { this->m_IsDWI |= (this->m_Document->getValue( DcmTagKey(0x0019,0x100e), this->m_BVector[idx], idx ) != 0); } } } else { // no hint of DWI in standard tags, look into CSA header to confirm if ( csaImageHeaderInfo ) { SiemensCSAHeader csaImageHeader( (const char*)csaImageHeaderInfo, csaImageHeaderLength ); SiemensCSAHeader::const_iterator it = csaImageHeader.find( "DiffusionDirectionality" ); if ( (it != csaImageHeader.end()) && !it->second.empty() ) { this->m_IsDWI = (0 == it->second[0].compare( 0, 11, "DIRECTIONAL" )); } it = csaImageHeader.find( "B_value" ); if ( (it != csaImageHeader.end()) && !it->second.empty() ) { this->m_BValue = atof( it->second[0].c_str() ); } it = csaImageHeader.find( "DiffusionGradientDirection" ); if ( (it != csaImageHeader.end()) && (it->second.size() >= 3) ) { for ( int idx = 0; idx < 3; ++idx ) { this->m_BVector[idx] = atof( it->second[idx].c_str() ); } } } } this->m_HasBVector = this->m_IsDWI; } } void ImageFileDICOM::DoVendorTagsGE() { int tmpInt = 0; double tmpDbl = 0; if ( this->GetTagValue( DCM_Modality ) == "MR" ) { // raw data type Sint16 rawTypeIdx = 3; if ( ! this->m_Document->getValue( DCM_GE_RawDataType_ImageType, rawTypeIdx ) ) rawTypeIdx = 0; // assume this is a magnitude image rawTypeIdx = std::min( 3, std::max( 0, (int)rawTypeIdx ) ); const char *const RawDataTypeString[4] = { "magnitude", "phase", "real", "imaginary" }; this->m_RawDataType = RawDataTypeString[rawTypeIdx]; Sint16 effEchoSpacing = 0; if ( this->m_Document->getValue( DCM_GE_EffectiveEchoSpacing, effEchoSpacing ) ) { std::ostringstream ss; ss << effEchoSpacing; this->m_TagToStringMap[DCM_GE_EffectiveEchoSpacing] = ss.str(); // Default (no acceleration) - dwell time is effective echo spacing (convert microseconds to seconds here) this->m_DwellTime = 1e-6 * effEchoSpacing; // Check for "asset" acceleration const std::string asset = this->GetTagValue( DCM_GE_AssetRFactors, "" ); if ( asset != "" ) { float rFactor; if ( 1 == sscanf( asset.c_str(), "%10f\\%*c", &rFactor ) ) { // With acceleration - dwell time is effective echo spacing multiplied by acceleration factor (e.g., 0.5 for 2-fold acceleration) this->m_DwellTime *= rFactor; } } } // dwi information this->m_IsDWI = false; const char* tmpStr = NULL; if ( this->m_Document->getValue( DcmTagKey(0x0019,0x10e0), tmpStr ) > 0 ) // Number of Diffusion Directions { const int nDirections = atoi( tmpStr ); if ( nDirections > 0 ) { this->m_IsDWI = true; if ( this->m_Document->getValue( DcmTagKey(0x0043,0x1039), tmpStr ) > 0 ) // bValue tag { if ( 1 == sscanf( tmpStr, "%10d\\%*c", &tmpInt ) ) { this->m_BValue = static_cast( tmpInt ); this->m_HasBVector = true; for ( int i = 0; i < 3; ++i ) { if ( this->m_Document->getValue( DcmTagKey(0x0019,0x10bb+i), tmpStr ) > 0 ) // bVector tags { this->m_BVector[i] = atof( tmpStr ); } else { this->m_BVector[i] = 0; this->m_HasBVector = false; } } this->m_BVector[2] *= -1; // for some reason z component apparently requires negation of sign on GE } } } } } } void ImageFileDICOM::DoVendorTagsPhilips() { double tmpDbl = 0; if ( this->GetTagValue( DCM_Modality ) == "MR" ) { if ( this->m_Document->getValue( DcmTagKey(0x0018,0x9087), tmpDbl ) > 0 ) // bValue tag { this->m_IsDWI = true; this->m_BValue = tmpDbl; } this->m_HasBVector = true; if ( this->m_BValue > 0 ) { for ( size_t i = 0; this->m_IsDWI && (i < 3); ++i ) { if ( this->m_Document->getValue( DcmTagKey(0x0018,0x9089), tmpDbl, i ) > 0 ) this->m_BVector[i] = tmpDbl; else this->m_IsDWI = false; } const char* tmpStr = NULL; if ( this->m_Document->getValue( DcmTagKey(0x2001,0x1004), tmpStr ) > 0 ) // DiffusionDirection tag - "O", oblique or "I", isotropic. Uses "I" with bValue!=0 to identify average diffusion image { if ( tmpStr ) { this->m_HasBVector = (tmpStr[0] != 'I'); } } } } } bool ImageFileDICOM::MatchAnyPattern( const std::map& patterns ) const { // check for positive include list if ( !patterns.empty() ) { for ( std::map::const_iterator it = patterns.begin(); it != patterns.end(); ++it ) { // if tag not found, do not include const char* tmpStr = NULL; if ( this->m_Document->getValue( it->first, tmpStr ) ) { // if tag value matches, then return true if ( strstr( tmpStr, it->second.c_str() ) ) return true; } } } return false; // did not find a match or list was empty } bool ImageFileDICOM::MatchAllPatterns( const std::map& patterns ) const { // check for positive include list if ( !patterns.empty() ) { for ( std::map::const_iterator it = patterns.begin(); it != patterns.end(); ++it ) { // if tag not found, do not include const char* tmpStr = NULL; if ( this->m_Document->getValue( it->first, tmpStr ) ) { // if tag value matches, then return true if ( !strstr( tmpStr, it->second.c_str() ) ) return false; } } } return true; // all matched or list empty } } // namespace CMTK cmtk-3.3.1/libs/IO/cmtkImageFileDICOM.h000066400000000000000000000135171276303427400173610ustar00rootroot00000000000000/* // // Copyright 2004-2014 SRI International // // Copyright 1997-2009 Torsten Rohlfing // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4497 $ // // $LastChangedDate: 2012-08-24 13:46:21 -0700 (Fri, 24 Aug 2012) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkImageFileDICOM_h_included_ #define __cmtkImageFileDICOM_h_included_ #include #include #include #include #include #include #define DCM_GE_EffectiveEchoSpacing DcmTagKey(0x0043,0x102c) #define DCM_GE_RawDataType_ImageType DcmTagKey(0x0043,0x102f) #define DCM_GE_AssetRFactors DcmTagKey(0x0043,0x1083) #define DCM_GE_PulseSequenceName DcmTagKey(0x0019,0x109c) #define DCM_GE_PulseSequenceDate DcmTagKey(0x0019,0x109d) #define DCM_GE_InternalPulseSequenceName DcmTagKey(0x0019,0x109e) #ifndef DCM_ManufacturerModelName #define DCM_ManufacturerModelName DcmTagKey(0x0008,0x1090) #endif #ifndef DCM_PatientsName #define DCM_PatientsName DCM_PatientName #endif #ifndef DCM_Siemens_MosaicRefAcqTimes // Siemens tag for relative timing of all slices in a mosaic file #define DCM_Siemens_MosaicRefAcqTimes DcmTagKey(0x0019,0x1029) #endif #include #include #include namespace cmtk { /** \addtogroup IO */ //@{ /// Class handling a single DICOM image file and its meta data. class ImageFileDICOM { public: /// This class. typedef ImageFileDICOM Self; /// Smart pointer to this class. typedef SmartPointer SmartPtr; /// Smart pointer to constant object of this class. typedef SmartConstPointer SmartConstPtr; /// File name. std::string m_FileName; /// File directory. std::string m_FileDir; /// Flag for multislice images bool m_IsMultislice; /// DICOM acquisition number. Sint32 m_AcquisitionNumber; /// DICOM image number (index in volume). Sint32 m_InstanceNumber; /// Flag for diffusion-weighted images. bool m_IsDWI; /// Dwell time for EPI double m_DwellTime; /** Sign for phase encode direction. * This is "pos" for "positive" phase encoding, "neg" for "negative", or "unknown" */ std::string m_PhaseEncodeDirectionSign; /// B value for DWI. double m_BValue; /// B vector for DWI. cmtk::FixedVector<3,double> m_BVector; /// Flag whether this image has a valid B vector (false for average DWI images). bool m_HasBVector; /// Raw data type string. std::string m_RawDataType; /// Slice time(s) for this file. std::vector m_SliceTimes; /// Constructor. ImageFileDICOM( const std::string& filename ); /// Determine whether two images match, i.e., belong to the same volume. bool Match( const Self& other, const Types::Coordinate numericalTolerance = 0, /*!< Numerical comparison tolerance; values with absolute difference less than this threshold are considered equal. */ const bool disableCheckOrientation = false /*!< Flag for disabling the checking of image orientation vectors.*/, const bool ignoreAcquisitionNumber = false /*!< When this flag is set, the AcquisitionNumber DICOM tag is ignore for matching images*/ ) const; /// Test if this image matches at least one from a list of DICOM tag patterns. bool MatchAnyPattern( const std::map& patterns ) const; /// Test if this image matches all from a list of DICOM tag patterns (or list is empty). bool MatchAllPatterns( const std::map& patterns ) const; /// Compare order based on file name (for lexicographic sorting). static bool lessFileName( const Self* lhs, const Self* rhs ) { return lhs->m_FileName < rhs->m_FileName; } /// Compare order based on image instace (for sorting in acquisition order). static bool lessInstanceNumber( const Self* lhs, const Self* rhs ) { return lhs->m_InstanceNumber < rhs->m_InstanceNumber; } /// Print informatiomn about this object. void Print() const; /// Release memory allocated for complete DICOM document. void ReleaseDocument() { this->m_Document = std::auto_ptr( NULL ); } /// Get tag value. const std::string& GetTagValue( const DcmTagKey& tag /*!< Find value string for this tag in DICOM file */, const std::string& defaultString = "" /*!< Return this default value if tag does not exist */ ) const { std::map::const_iterator it = this->m_TagToStringMap.find( tag ); if ( it != this->m_TagToStringMap.end() ) return it->second; else return defaultString; } private: /// Pointer to DICOM dataset object std::auto_ptr m_Dataset; /// DICOM document object. std::auto_ptr m_Document; /// Map DCMTK tags to their string values in this image file. std::map m_TagToStringMap; /// Handle Siemens private tags. void DoVendorTagsSiemens(); /// Handle GE private tags. void DoVendorTagsGE(); /// Handle Philips private tags. void DoVendorTagsPhilips(); }; //@} } // namespace cmtk #endif // #ifndef __cmtkImageFileDICOM_h_included_ cmtk-3.3.1/libs/IO/cmtkImageOperationApplyMask.cxx000066400000000000000000000060301276303427400220530ustar00rootroot00000000000000/* // // Copyright 2010-2011 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 3499 $ // // $LastChangedDate: 2011-10-21 12:48:54 -0700 (Fri, 21 Oct 2011) $ // // $LastChangedBy: torsten_at_home $ // */ #include "cmtkImageOperationApplyMask.h" #include cmtk::UniformVolume::SmartPtr cmtk::ImageOperationApplyMask ::Apply( cmtk::UniformVolume::SmartPtr& volume ) { const std::string maskOrientation = this->m_MaskVolume->GetMetaInfo( META_IMAGE_ORIENTATION ); const std::string workingOrientation = volume->GetMetaInfo( META_IMAGE_ORIENTATION ); if ( maskOrientation != workingOrientation ) { this->m_MaskVolume = cmtk::UniformVolume::SmartPtr( this->m_MaskVolume->GetReoriented( workingOrientation.c_str() ) ); } for ( int dim = 0; dim < 3; ++dim ) { if ( this->m_MaskVolume->m_Dims[dim] != volume->m_Dims[dim] ) { cmtk::StdErr << "ERROR: mask volume dimensions do not match working volume dimensions.\n"; exit( 1 ); } } const cmtk::TypedArray& maskData = *(this->m_MaskVolume->GetData()); cmtk::TypedArray& volumeData = *(volume->GetData()); const size_t nPixels = volume->GetNumberOfPixels(); for ( size_t i = 0; i < nPixels; ++i ) if ( maskData.IsPaddingOrZeroAt( i ) ) volumeData.SetPaddingAt( i ); return volume; } cmtk::UniformVolume::SmartPtr cmtk::ImageOperationApplyMask ::ReadMaskFile( const char* maskFileName, const bool inverse ) { cmtk::UniformVolume::SmartPtr maskVolume( cmtk::VolumeIO::ReadOriented( maskFileName ) ); if ( !maskVolume || !maskVolume->GetData() ) { cmtk::StdErr << "ERROR: could not read mask from file " << maskFileName << "\nProgram will terminate now, just to be safe.\n"; exit( 1 ); } // binarize mask to 1/0, convert to char, and also consider "inverse" flag in the process. cmtk::TypedArray::SmartPtr& maskData = maskVolume->GetData(); const size_t nPixels = maskData->GetDataSize(); for ( size_t n = 0; n < nPixels; ++n ) { if ( maskData->IsPaddingOrZeroAt( n ) == inverse ) maskData->Set( 1, n ); else maskData->Set( 0, n ); } maskVolume->SetData( cmtk::TypedArray::SmartPtr( maskData->Convert( cmtk::TYPE_BYTE ) ) ); return maskVolume; } cmtk-3.3.1/libs/IO/cmtkImageOperationApplyMask.h000066400000000000000000000046301276303427400215040ustar00rootroot00000000000000/* // // Copyright 2009-2010 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 2398 $ // // $LastChangedDate: 2010-10-05 14:54:37 -0700 (Tue, 05 Oct 2010) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkImageOperationApplyMask_h_included_ #define __cmtkImageOperationApplyMask_h_included_ #include #include namespace cmtk { /// Apply mask image. class ImageOperationApplyMask /// Inherit generic image operation. : public ImageOperation { public: /// This class. typedef ImageOperationApplyMask Self; /// Constructor. ImageOperationApplyMask( const cmtk::UniformVolume::SmartPtr& maskVolume ) : m_MaskVolume( maskVolume ) {} /// Apply this operation to an image in place. virtual cmtk::UniformVolume::SmartPtr Apply( cmtk::UniformVolume::SmartPtr& volume ); /// Create new mask operation. static void New( const char* maskFileName ) { ImageOperation::m_ImageOperationList.push_back( SmartPtr( new ImageOperationApplyMask( Self::ReadMaskFile( maskFileName ) ) ) ); } /// Create new inverse mask operation. static void NewInverse( const char* maskFileName ) { ImageOperation::m_ImageOperationList.push_back( SmartPtr( new ImageOperationApplyMask( Self::ReadMaskFile( maskFileName, true /*inverse*/ ) ) ) ); } private: /// The mask volume. cmtk::UniformVolume::SmartPtr m_MaskVolume; /// Read the actual mask file. static cmtk::UniformVolume::SmartPtr ReadMaskFile( const char* maskFileName, const bool inverse = false ); }; } // namespace cmtk #endif // #ifndef __cmtkImageOperationApplyMask_h_included_ cmtk-3.3.1/libs/IO/cmtkImageOperationMatchIntensities.cxx000066400000000000000000000061601276303427400234310ustar00rootroot00000000000000/* // // Copyright 2010 Torsten Rohlfing // // Copyright 2011, 2014 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 3097 $ // // $LastChangedDate: 2011-04-06 13:07:22 -0700 (Wed, 06 Apr 2011) $ // // $LastChangedBy: torstenrohlfing $ // */ #include "cmtkImageOperationMatchIntensities.h" #include #include #include #include cmtk::ImageOperationMatchIntensities::ImageOperationMatchIntensities( const Self::Mode mode, const std::string& referenceImagePath ) : m_Mode( mode ) { UniformVolume::SmartPtr referenceImage = VolumeIO::Read( referenceImagePath ); if ( ! referenceImage ) { StdErr << "ERROR: cannot read image " << referenceImagePath << "\n"; throw ExitException( 1 ); } this->m_ReferenceData = referenceImage->GetData(); if ( ! this->m_ReferenceData ) { StdErr << "ERROR: read geometry but could not read pixel data from " << referenceImagePath << "\n"; throw ExitException( 1 ); } } cmtk::UniformVolume::SmartPtr cmtk::ImageOperationMatchIntensities::Apply( cmtk::UniformVolume::SmartPtr& volume ) { TypedArray& volumeData = *(volume->GetData()); switch ( this->m_Mode ) { case Self::MATCH_HISTOGRAMS: volumeData.ApplyFunctionObject( cmtk::TypedArrayFunctionHistogramMatching( volumeData, *(this->m_ReferenceData) ) ); break; case Self::MATCH_MEAN_SDEV: { cmtk::Types::DataItem rMean, rVar; this->m_ReferenceData->GetStatistics( rMean, rVar ); cmtk::Types::DataItem mMean, mVar; volumeData.GetStatistics( mMean, mVar ); const cmtk::Types::DataItem scale = sqrt( rVar / mVar ); const cmtk::Types::DataItem offset = rMean - scale * mMean; volumeData.Rescale( scale, offset ); break; } } return volume; } void cmtk::ImageOperationMatchIntensities::NewMatchHistograms( const char* referenceImagePath ) { ImageOperation::m_ImageOperationList.push_back( SmartPtr( new Self( Self::MATCH_HISTOGRAMS, referenceImagePath ) ) ); } void cmtk::ImageOperationMatchIntensities::NewMatchMeanSDev( const char* referenceImagePath ) { ImageOperation::m_ImageOperationList.push_back( SmartPtr( new Self( Self::MATCH_MEAN_SDEV, referenceImagePath ) ) ); } cmtk-3.3.1/libs/IO/cmtkImageOperationMatchIntensities.h000066400000000000000000000052171276303427400230600ustar00rootroot00000000000000/* // // Copyright 2010 Torsten Rohlfing // // Copyright 2011, 2014 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 3097 $ // // $LastChangedDate: 2011-04-06 13:07:22 -0700 (Wed, 06 Apr 2011) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkImageOperationMatchIntensities_h_included_ #define __cmtkImageOperationMatchIntensities_h_included_ #include #include namespace cmtk { /** \addtogroup Base */ //@{ /// Image operation: match intensities to another image. class ImageOperationMatchIntensities /// Inherit from image operation base class. : public ImageOperation { public: /// This class. typedef ImageOperationMatchIntensities Self; /// Superclass. typedef ImageOperation Superclass; /// Operation mode. typedef enum { /// Match histograms. MATCH_HISTOGRAMS, /// Match mean and standard deviation MATCH_MEAN_SDEV } Mode; /// Constructor. ImageOperationMatchIntensities( const Self::Mode mode /*!< Operation mode.*/, const std::string& referenceImagePath /*!< Path of the reference image to match intensites to.*/ ); /// Apply this operation to an image in place. virtual cmtk::UniformVolume::SmartPtr Apply( cmtk::UniformVolume::SmartPtr& volume ); /// Create new operation to match histograms. static void NewMatchHistograms( const char* referenceImagePath /*!< Path of the reference image to match intensites to.*/ ); /// Create new operation to match image mean and standard deviation. static void NewMatchMeanSDev( const char* referenceImagePath /*!< Path of the reference image to match intensites to.*/ ); private: /// Operation mode. Self::Mode m_Mode; /// Reference image data. TypedArray::SmartPtr m_ReferenceData; }; //@} } // namespace cmtk #endif // #ifndef __cmtkImageOperationMatchIntensities_h_included_ cmtk-3.3.1/libs/IO/cmtkImageStackDICOM.cxx000066400000000000000000000525041276303427400201210ustar00rootroot00000000000000/* // // Copyright 2004-2014 SRI International // // Copyright 1997-2009 Torsten Rohlfing // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4497 $ // // $LastChangedDate: 2012-08-24 13:46:21 -0700 (Fri, 24 Aug 2012) $ // // $LastChangedBy: torstenrohlfing $ // */ #include "cmtkImageStackDICOM.h" #include #include #include #include #include #include #include #include #include #include namespace cmtk { bool ImageStackDICOM::Match ( const ImageFileDICOM& newImage, const Types::Coordinate numericalTolerance, const bool disableCheckOrientation, const bool ignoreAcquisitionNumber ) const { if ( this->empty() ) return true; // first image always matches ImageFileDICOM::SmartConstPtr check = this->front(); if ( check ) { if ( !check->Match( newImage, numericalTolerance, disableCheckOrientation, ignoreAcquisitionNumber ) ) return 0; for ( const_iterator it = this->begin(); it != this->end(); ++it ) { // if we already have an image in same location in this study, // then bump to next study if ( (*it)->GetTagValue( DCM_ImagePositionPatient ) == newImage.GetTagValue( DCM_ImagePositionPatient ) ) return 0; } return true; } else return false; } void ImageStackDICOM::AddImageFile ( ImageFileDICOM::SmartConstPtr& newImage ) { iterator it = begin(); for ( ; it != end(); ++it ) if ( newImage->m_InstanceNumber < (*it)->m_InstanceNumber ) break; insert( it, newImage ); } std::vector ImageStackDICOM::AssembleSliceTimes() const { std::vector sliceTimes; for ( const_iterator it = this->begin(); it != this->end(); ++it ) { // add this file's slice times to stack slice time vector - this should be safe even if file slice time vector is empty sliceTimes.insert( sliceTimes.end(), (*it)->m_SliceTimes.begin(), (*it)->m_SliceTimes.end() ); } return sliceTimes; } const char * ImageStackDICOM::WhitespaceWriteMiniXML( mxml_node_t* node, int where) { const char* name = node->value.element.name; typedef struct _wsLookupType { /// XML element name. const char* name; /// Table of whitespace sequences. const char* ws[4]; } wsLookupType; static const wsLookupType wsLookup[] = { { "dicom:Manufacturer", { "\t", NULL, NULL, "\n" } }, { "dicom:ManufacturerModel", { "\t", NULL, NULL, "\n" } }, { "dicom:DeviceSerialNumber", { "\t", NULL, NULL, "\n" } }, { "dicom:StationName", { "\t", NULL, NULL, "\n" } }, { "dicom:RepetitionTime", { "\t", NULL, NULL, "\n" } }, { "dicom:EchoTime", { "\t", NULL, NULL, "\n" } }, { "dicom:InversionTime", { "\t", NULL, NULL, "\n" } }, { "dicom:ImagingFrequency", { "\t", NULL, NULL, "\n" } }, { "dwellTime", { "\t", NULL, NULL, "\n" } }, { "phaseEncodeDirection", { "\t", NULL, NULL, "\n" } }, { "phaseEncodeDirectionSign", { "\t", NULL, NULL, "\n" } }, { "dicom:SequenceName", { "\t", NULL, NULL, "\n" } }, { "dicom:GE:PulseSequenceName", { "\t", NULL, NULL, "\n" } }, { "dicom:GE:PulseSequenceDate", { "\t", NULL, NULL, "\n" } }, { "dicom:GE:InternalPulseSequenceName", { "\t", NULL, NULL, "\n" } }, { "dicom:GE:EffectiveEchoSpacing", { "\t", NULL, NULL, "\n" } }, { "type", { "\t", NULL, NULL, "\n" } }, { "dwi", { "\t", "\n", "\t", "\n" } }, { "bValue", { "\t\t", NULL, NULL, "\n" } }, { "bVector", { "\t\t", NULL, NULL, "\n" } }, { "bVectorImage", { "\t\t", NULL, NULL, "\n" } }, { "bVectorStandard", { "\t\t", NULL, NULL, "\n" } }, { "dcmFileDirectory", { "\t", NULL, NULL, "\n" } }, { "dicom:StudyInstanceUID", { "\t", NULL, NULL, "\n" } }, { "dicom:SeriesInstanceUID", { "\t", NULL, NULL, "\n" } }, { "dicom:FrameOfReferenceUID", { "\t", NULL, NULL, "\n" } }, { "dicom:ImageOrientationPatient", { "\t", NULL, NULL, "\n" } }, { "sliceTime", { "\t", NULL, NULL, "\n" } }, { "image", { "\t", "\n", "\t", "\n" } }, { "dcmFile", { "\t\t", NULL, NULL, "\n" } }, { "dicom:AcquisitionTime", { "\t\t", NULL, NULL, "\n" } }, { "dicom:ImagePositionPatient", { "\t\t", NULL, NULL, "\n" } }, { "dicom:RescaleIntercept", { "\t\t", NULL, NULL, "\n" } }, { "dicom:RescaleSlope", { "\t\t", NULL, NULL, "\n" } }, { NULL, {NULL, NULL, NULL, NULL} } }; if ( (where >= 0) && (where < 4) ) { for ( size_t idx = 0; wsLookup[idx].name; ++idx ) { if ( ! strcmp( name, wsLookup[idx].name ) ) return wsLookup[idx].ws[where]; } } switch ( where ) { case MXML_WS_BEFORE_OPEN: return NULL; case MXML_WS_AFTER_OPEN: return "\n"; case MXML_WS_BEFORE_CLOSE: return NULL; case MXML_WS_AFTER_CLOSE: return "\n"; } return NULL; } // wrap tolower() - on Mac, system function is not compatible with std::transform() static int cmtkWrapToLower( const int c ) { return tolower( c ); } void ImageStackDICOM::WriteXML( const std::string& fname, const UniformVolume& volume, const bool includeIdentifiers ) const { mxmlSetWrapMargin( 120 ); // make enough room for indented bVectorStandard mxml_node_t *x_root = mxmlNewElement( NULL, "?xml version=\"1.0\" encoding=\"utf-8\"?" ); // convenience pointer to first image in series const ImageFileDICOM* firstImage = this->front(); if ( includeIdentifiers ) { mxml_node_t *x_device = mxmlNewElement( x_root, "device" ); Coverity::FakeFree( mxmlNewText( mxmlNewElement( x_device, "dicom:Manufacturer" ), 0, firstImage->GetTagValue( DCM_Manufacturer ).c_str() ) ); Coverity::FakeFree( mxmlNewText( mxmlNewElement( x_device, "dicom:ManufacturerModel" ), 0, firstImage->GetTagValue( DCM_ManufacturerModelName ).c_str() ) ); Coverity::FakeFree( mxmlNewText( mxmlNewElement( x_device, "dicom:StationName" ), 0, firstImage->GetTagValue( DCM_StationName ).c_str() ) ); Coverity::FakeFree( mxmlNewText( mxmlNewElement( x_device, "dicom:DeviceSerialNumber" ), 0, firstImage->GetTagValue( DCM_DeviceSerialNumber ).c_str() ) ); } std::string modality = firstImage->GetTagValue( DCM_Modality ); std::transform( modality.begin(), modality.end(), modality.begin(), cmtkWrapToLower ); mxml_node_t *x_modality = mxmlNewElement( x_root, modality.c_str() ); if ( modality == "mr" ) { mxml_node_t *x_tr = mxmlNewElement( x_modality, "dicom:RepetitionTime"); mxmlNewReal( x_tr, atof( firstImage->GetTagValue( DCM_RepetitionTime ).c_str() ) ); mxmlElementSetAttr( x_tr, "units", "ms" ); mxml_node_t *x_te = mxmlNewElement( x_modality, "dicom:EchoTime"); mxmlNewReal( x_te, atof( firstImage->GetTagValue( DCM_EchoTime ).c_str() ) ); mxmlElementSetAttr( x_te, "units", "ms" ); mxml_node_t *x_ti = mxmlNewElement( x_modality, "dicom:InversionTime"); mxmlNewReal( x_ti, atof( firstImage->GetTagValue( DCM_InversionTime ).c_str() ) ); mxmlElementSetAttr( x_ti, "units", "ms" ); Coverity::FakeFree( mxmlNewReal( mxmlNewElement( x_modality, "dicom:ImagingFrequency"), atof( firstImage->GetTagValue( DCM_ImagingFrequency ).c_str() ) ) ); if ( firstImage->m_DwellTime > 0 ) { mxml_node_t *x_dwell_time = mxmlNewElement( x_modality, "dwellTime"); mxmlNewReal( x_dwell_time, firstImage->m_DwellTime ); mxmlElementSetAttr( x_dwell_time, "units", "s" ); } const std::string phaseEncodeDirection = firstImage->GetTagValue( DCM_InPlanePhaseEncodingDirection ); if ( phaseEncodeDirection != "" ) { Coverity::FakeFree( mxmlNewText( mxmlNewElement( x_modality, "phaseEncodeDirection"), 0, phaseEncodeDirection.c_str() ) ); } if ( firstImage->m_PhaseEncodeDirectionSign != "" ) { Coverity::FakeFree( mxmlNewText( mxmlNewElement( x_modality, "phaseEncodeDirectionSign"), 0, firstImage->m_PhaseEncodeDirectionSign.c_str() ) ); } if ( firstImage->GetTagValue( DCM_GE_EffectiveEchoSpacing ) != "" ) { Coverity::FakeFree( mxmlNewReal( mxmlNewElement( x_modality, "dicom:GE:EffectiveEchoSpacing"), atof( firstImage->GetTagValue( DCM_GE_EffectiveEchoSpacing ).c_str() ) ) ); } if ( firstImage->GetTagValue( DCM_SequenceName ) != "" && includeIdentifiers ) { Coverity::FakeFree( mxmlNewText( mxmlNewElement( x_modality, "dicom:SequenceName"), 0, firstImage->GetTagValue( DCM_SequenceName ).c_str() ) ); } if ( firstImage->GetTagValue( DCM_GE_PulseSequenceName ) != "" && includeIdentifiers ) { Coverity::FakeFree( mxmlNewText( mxmlNewElement( x_modality, "dicom:GE:PulseSequenceName"), 0, firstImage->GetTagValue( DCM_GE_PulseSequenceName ).c_str() ) ); } if ( firstImage->GetTagValue( DCM_GE_PulseSequenceDate ) != "" && includeIdentifiers ) { Coverity::FakeFree( mxmlNewText( mxmlNewElement( x_modality, "dicom:GE:PulseSequenceDate"), 0, firstImage->GetTagValue( DCM_GE_PulseSequenceDate ).c_str() ) ); } if ( firstImage->GetTagValue( DCM_GE_InternalPulseSequenceName ) != "" && includeIdentifiers ) { Coverity::FakeFree( mxmlNewText( mxmlNewElement( x_modality, "dicom:GE:InternalPulseSequenceName"), 0, firstImage->GetTagValue( DCM_GE_InternalPulseSequenceName ).c_str() ) ); } if ( firstImage->m_RawDataType != "unknown" ) { Coverity::FakeFree( mxmlNewText( mxmlNewElement( x_modality, "type"), 0, firstImage->m_RawDataType.c_str() ) ); } if ( firstImage->m_IsDWI ) { mxml_node_t *x_dwi = mxmlNewElement( x_modality, "dwi" ); Coverity::FakeFree( mxmlNewInteger( mxmlNewElement( x_dwi, "bValue"), firstImage->m_BValue ) ); if ( firstImage->m_HasBVector ) { mxml_node_t *x_bvec = mxmlNewElement( x_dwi, "bVector"); mxmlElementSetAttr( x_bvec, "coordinateSpace", "LPS" ); for ( size_t idx = 0; idx < 3; ++idx ) { Coverity::FakeFree( mxmlNewReal( x_bvec, firstImage->m_BVector[idx] ) ); } // Determine bVector in image LPS coordinate space: // First, create copy of image grid UniformVolume::SmartPtr gridLPS = volume.CloneGrid(); // Make sure still in LPS DICOM coordinate space gridLPS->ChangeCoordinateSpace( "LPS" ); try { // Apply inverse of remaining image-to-space matrix to original bVector const UniformVolume::CoordinateVectorType bVectorImage = firstImage->m_BVector * Matrix3x3( gridLPS->GetImageToPhysicalMatrix().GetInverse() ); mxml_node_t *x_bvec_image = mxmlNewElement( x_dwi, "bVectorImage"); mxmlElementSetAttr( x_bvec_image, "imageOrientation", gridLPS->GetMetaInfo( META_IMAGE_ORIENTATION ).c_str() ); for ( size_t idx = 0; idx < 3; ++idx ) { Coverity::FakeFree( mxmlNewReal( x_bvec_image, bVectorImage[idx] ) ); } } catch ( const AffineXform::MatrixType::SingularMatrixException& ) { StdErr << "WARNING: singular image-to-physical matrix; cannot determine b vector orientation in image space (bVectorImage).\n"; } // Determine bVector in image RAS standard coordinate space: // First, create copy of image grid UniformVolume::SmartPtr gridRAS = gridLPS->GetReoriented(); try { // Apply inverse of remaining image-to-space matrix to original bVector const UniformVolume::CoordinateVectorType bVectorStandard = firstImage->m_BVector * Matrix3x3( gridRAS->GetImageToPhysicalMatrix().GetInverse() ); mxml_node_t *x_bvec_std = mxmlNewElement( x_dwi, "bVectorStandard" ); mxmlElementSetAttr( x_bvec_std, "imageOrientation", gridRAS->GetMetaInfo( META_IMAGE_ORIENTATION ).c_str() ); for ( size_t idx = 0; idx < 3; ++idx ) { Coverity::FakeFree( mxmlNewReal( x_bvec_std, bVectorStandard[idx] ) ); } } catch ( const AffineXform::MatrixType::SingularMatrixException& ) { StdErr << "WARNING: singular image-to-physical matrix; cannot determine b vector orientation in standard space (bVectorStandard).\n"; } } } } mxml_node_t *x_stack = mxmlNewElement( x_root, "stack" ); if ( includeIdentifiers ) { Coverity::FakeFree( mxmlNewText( mxmlNewElement( x_stack, "dcmFileDirectory" ), 0, firstImage->m_FileDir.c_str() ) ); Coverity::FakeFree( mxmlNewText( mxmlNewElement( x_stack, "dicom:StudyInstanceUID" ), 0, firstImage->GetTagValue( DCM_StudyInstanceUID ).c_str() ) ); Coverity::FakeFree( mxmlNewText( mxmlNewElement( x_stack, "dicom:SeriesInstanceUID" ), 0, firstImage->GetTagValue( DCM_SeriesInstanceUID ).c_str() ) ); if ( firstImage->GetTagValue( DCM_FrameOfReferenceUID, "missing" ) != "missing" ) { Coverity::FakeFree( mxmlNewText( mxmlNewElement( x_stack, "dicom:FrameOfReferenceUID" ), 0, firstImage->GetTagValue( DCM_FrameOfReferenceUID ).c_str() ) ); } } Coverity::FakeFree( mxmlNewText( mxmlNewElement( x_stack, "dicom:ImageOrientationPatient" ), 0, firstImage->GetTagValue( DCM_ImageOrientationPatient ).c_str() ) ); for ( const_iterator it = this->begin(); it != this->end(); ++it ) { mxml_node_t *x_image = mxmlNewElement( x_stack, "image" ); if ( includeIdentifiers ) { Coverity::FakeFree( mxmlNewText( mxmlNewElement( x_image, "dcmFile" ), 0, (*it)->m_FileName.c_str() ) ); } Coverity::FakeFree( mxmlNewText( mxmlNewElement( x_image, "dicom:AcquisitionTime" ), 0, (*it)->GetTagValue( DCM_AcquisitionTime ).c_str() ) ); Coverity::FakeFree( mxmlNewText( mxmlNewElement( x_image, "dicom:ImagePositionPatient" ), 0, (*it)->GetTagValue( DCM_ImagePositionPatient ).c_str() ) ); if ( (*it)->GetTagValue( DCM_RescaleIntercept, "missing" ) != "missing" ) { Coverity::FakeFree( mxmlNewReal( mxmlNewElement( x_image, "dicom:RescaleIntercept" ), atof( (*it)->GetTagValue( DCM_RescaleIntercept ).c_str() ) ) ); } if ( (*it)->GetTagValue( DCM_RescaleSlope, "missing" ) != "missing" ) { Coverity::FakeFree( mxmlNewReal( mxmlNewElement( x_image, "dicom:RescaleSlope" ), atof( (*it)->GetTagValue( DCM_RescaleSlope ).c_str() ) ) ); } } // put slice times into XML (if we have them) const std::vector stackSliceTimes = this->AssembleSliceTimes(); if ( ! stackSliceTimes.empty() ) { const double baseTime = *std::min_element( stackSliceTimes.begin(),stackSliceTimes.end() ); for ( size_t stackSlice = 0; stackSlice < stackSliceTimes.size(); ++stackSlice ) { mxml_node_t *x_slice_time = mxmlNewElement( x_stack, "sliceTime" ); Coverity::FakeFree( mxmlNewReal( x_slice_time, stackSliceTimes[stackSlice]-baseTime ) ); char slice_str[10]; snprintf( slice_str, 9, "%u", static_cast( stackSlice ) ); mxmlElementSetAttr( x_slice_time, "slice", slice_str ); } } FILE *file = fopen( fname.c_str(), "w" ); if ( file ) { mxmlSaveFile( x_root, file, Self::WhitespaceWriteMiniXML ); fputs( "\n", file ); // end last line fclose( file ); } else { StdErr << "ERROR: could not open file " << fname << " for writing\n"; } Coverity::FakeFree( x_modality ); mxmlDelete( x_root ); } cmtk::UniformVolume::SmartConstPtr ImageStackDICOM::WriteImage( const std::string& fname, const Self::EmbedInfoEnum embedInfo ) const { const ImageFileDICOM *first = this->front(); UniformVolume::SmartPtr volume; if ( !first->m_IsMultislice ) { StudyImageSet studyImageSet; studyImageSet.SetImageFormat( FILEFORMAT_DICOM ); studyImageSet.SetImageDirectory( first->m_FileDir.c_str() ); studyImageSet.SetMultiFile( true ); for ( const_iterator it = this->begin(); it != this->end(); ++it ) { studyImageSet.push_back( (*it)->m_FileName ); } volume = VolumeFromStudy::Read( &studyImageSet, this->m_Tolerance ); } else { char fullPath[PATH_MAX]; #ifdef MSC_VER snprintf( fullPath, sizeof( fullPath ), "%s\\%s", first->m_FileDir.c_str(), first->m_FileName.c_str() ); #else snprintf( fullPath, sizeof( fullPath ), "%s/%s", first->m_FileDir.c_str(), first->m_FileName.c_str() ); #endif volume = VolumeFromFile::ReadDICOM( fullPath ); } if ( volume ) { switch ( embedInfo ) { default: case EMBED_NONE: break; case EMBED_STUDYID_STUDYDATE: volume->SetMetaInfo( META_IMAGE_DESCRIPTION, first->GetTagValue( DCM_StudyID ) + "_" + first->GetTagValue( DCM_StudyDate ) ); break; case EMBED_PATIENTNAME: volume->SetMetaInfo( META_IMAGE_DESCRIPTION, first->GetTagValue( DCM_PatientsName ) ); break; case EMBED_SERIESDESCR: volume->SetMetaInfo( META_IMAGE_DESCRIPTION, first->GetTagValue( DCM_SeriesDescription ) ); break; } // see if we have Phase Encode direction and set metadata (for NIFTI only at this time) const std::string phaseEncodeDirection = first->GetTagValue( DCM_InPlanePhaseEncodingDirection ); if ( ! phaseEncodeDirection.empty() ) { volume->SetMetaInfo( META_IMAGE_SLICE_PEDIRECTION, phaseEncodeDirection ); } // see if we have slice times and set metadata accordingly (relevant for NIFTI only at this time) const std::vector sliceTimes = this->AssembleSliceTimes(); if ( sliceTimes.size() > 1 ) // need at least 2 slices for meaningful slice order { std::vector sliceTimesSorted = sliceTimes; std::sort( sliceTimesSorted.begin(), sliceTimesSorted.end() ); // This next bit inspired by Xiangru Li's Matlab DICOM-to-NIfTI converter, http://www.mathworks.com/matlabcentral/fileexchange/42997-dicom-to-nifti-converter double duration = 0; for ( size_t i = 1; i < sliceTimes.size(); ++i ) { duration += fabs(sliceTimes[i]-sliceTimes[i-1]); } duration /= (sliceTimes.size()-1); const double difference = (sliceTimesSorted[sliceTimesSorted.size()-1]-sliceTimesSorted[0]) / (sliceTimes.size()-1); std::string sliceOrder; if ( fabs( difference - duration ) < 1e-6 ) { sliceOrder = META_IMAGE_SLICEORDER_SI; } else if ( fabs( difference + duration ) < 1e-6 ) { sliceOrder = META_IMAGE_SLICEORDER_SD; } else if ( difference > 0 ) { if ( sliceTimes[0] < sliceTimes[1] ) { // EVEN slices acquired first. sliceOrder = META_IMAGE_SLICEORDER_AI; } else { // ODD slices acquired first sliceOrder = META_IMAGE_SLICEORDER_AI2; } } else { if ( sliceTimes[sliceTimes.size()-1] < sliceTimes[sliceTimes.size()-2] ) { // EVEN slices acquired first. sliceOrder = META_IMAGE_SLICEORDER_AD; } else { // ODD slices acquired first sliceOrder = META_IMAGE_SLICEORDER_AD2; } } if ( ! sliceOrder.empty() ) { volume->SetMetaInfo( META_IMAGE_SLICEORDER, sliceOrder ); std::ostringstream strm; strm << difference; volume->SetMetaInfo( META_IMAGE_SLICEDURATION, strm.str() ); } } VolumeIO::Write( *volume, fname.c_str() ); DebugOutput( 1 ).GetStream().printf( "\nOutput file:%s\nImage size: %3dx%3dx%3d pixels\nPixel size: %.4fx%.4fx%.4f mm\n\n", fname.c_str(), volume->m_Dims[0], volume->m_Dims[1], volume->m_Dims[2], volume->m_Delta[0], volume->m_Delta[1], volume->m_Delta[2] ); } else { // No longer need to warn - now warn at lower level // StdErr << "WARNING: No valid volume was read.\n"; } DebugOutput( 1 ) << "DICOM Information: \n" << " Description: " << first->GetTagValue( DCM_SeriesDescription ) << "\n" << " Series: " << first->GetTagValue( DCM_SeriesInstanceUID ) << "\n" << " Study: " << first->GetTagValue( DCM_StudyInstanceUID ) << "\n" << " Acquisition: " << first->m_AcquisitionNumber << "\n" << " TR / TE: " << first->GetTagValue( DCM_RepetitionTime ) << "ms / " << first->GetTagValue( DCM_EchoTime ) << "ms\n" << " Position: " << first->GetTagValue( DCM_ImagePositionPatient ) << "\n" << " Orientation: " << first->GetTagValue( DCM_ImageOrientationPatient ) << "\n" << " Raw Data Type: " << first->m_RawDataType << "\n"; DebugOutput( 1 ) << "\nImage List:\n"; for ( const_iterator it = this->begin(); it != this->end(); ++it ) { DebugOutput( 1 ) << (*it)->m_FileName << " "; } DebugOutput( 1 ) << "\n====================================================\n"; return volume; } } // namespace CMTK cmtk-3.3.1/libs/IO/cmtkImageStackDICOM.h000066400000000000000000000101641276303427400175420ustar00rootroot00000000000000/* // // Copyright 2004-2014 SRI International // // Copyright 1997-2009 Torsten Rohlfing // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4497 $ // // $LastChangedDate: 2012-08-24 13:46:21 -0700 (Fri, 24 Aug 2012) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkImageStackDICOM_h_included_ #define __cmtkImageStackDICOM_h_included_ #include #include #include #include #include #include #include namespace cmtk { /** \addtogroup IO */ //@{ /// Class handling a stack of DICOM image files. class ImageStackDICOM : public std::vector { public: /// This class. typedef ImageStackDICOM Self; /// Smart pointer to this class. typedef SmartPointer SmartPtr; /// Smart pointer to const object of this class. typedef SmartConstPointer SmartConstPtr; /// Enum type to select DICOM information to be embedded into output images as "description". typedef enum { /// No embedding. EMBED_NONE = 0, /// Embed StudyID plus Date. EMBED_STUDYID_STUDYDATE = 1, /// Embed patient name. EMBED_PATIENTNAME = 2, /// Embed series description. EMBED_SERIESDESCR = 3 } EmbedInfoEnum; /// Constructor. ImageStackDICOM( const Types::Coordinate tolerance = 0 /*!< Tolerance for floating point comparisons, e.g., when testing for uniform pixel/slice spacings.*/ ) : m_Tolerance( tolerance ) {} /// Add new DICOM image file to this stack. void AddImageFile( ImageFileDICOM::SmartConstPtr& image ); /// Match new image file against this volume stack. bool Match ( const ImageFileDICOM& newImage /*!< New image - test whether this belongs with the ones already in this stack.*/, const Types::Coordinate numericalTolerance = 0, /*!< Numerical comparison tolerance; values with absolute difference less than this threshold are considered equal. */ const bool disableCheckOrientation = false /*!< Flag for disabling the checking of image orientation vectors.*/, const bool ignoreAcquisitionNumber = false /*!< When this flag is set, the AcquisitionNumber DICOM tag is ignore for matching images*/ ) const; /// Write XML sidecar file. void WriteXML( const std::string& name /*!< Sidecar XML file name. */, const cmtk::UniformVolume& volume /*!< Previously written image volume - provides information about coordinate system etc. */, const bool includeIdentifiers = false /*!< If this is set, protected "identifiers" such as device serial numbers will also be included in the XML output.*/ ) const; /// Write to image file. cmtk::UniformVolume::SmartConstPtr WriteImage ( const std::string& name /*!< File name and path for new image.*/, const Self::EmbedInfoEnum embedInfo /*!< Flag for selecting information embedded into image description.*/ ) const; /// Print stack information. void print() const; private: /// Stored floating point tolerance. Types::Coordinate m_Tolerance; /// Assemble slice times vector from current set of slices. std::vector AssembleSliceTimes() const; /// Generate custom whitespaces for XML output. static const char *WhitespaceWriteMiniXML( mxml_node_t*, int where); }; //@} } // namespace cmtk #endif // #ifndef __cmtkImageStackDICOM_h_included_ cmtk-3.3.1/libs/IO/cmtkLandmarkIO.cxx000066400000000000000000000026521276303427400173150ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2012 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4093 $ // // $LastChangedDate: 2012-03-27 13:05:25 -0700 (Tue, 27 Mar 2012) $ // // $LastChangedBy: torstenrohlfing $ // */ #include "cmtkLandmarkIO.h" std::ostream& operator<<( std::ostream& stream, const cmtk::Landmark& lm ) { stream << lm.m_Location << " " << lm.m_Name << "\n"; return stream; } /// Landmark input operator. std::istream& operator>>( std::istream& stream, cmtk::Landmark& lm ) { stream >> lm.m_Location; std::getline( stream, lm.m_Name ); return stream; } cmtk-3.3.1/libs/IO/cmtkLandmarkIO.h000066400000000000000000000027231276303427400167410ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2012 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4093 $ // // $LastChangedDate: 2012-03-27 13:05:25 -0700 (Tue, 27 Mar 2012) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkLandmarkIO_h_included_ #define __cmtkLandmarkIO_h_included_ #include #include #include /// Landmark output operator std::ostream& operator<<( std::ostream& stream, const cmtk::Landmark& lm ); /// Landmark input operator. std::istream& operator>>( std::istream& stream, cmtk::Landmark& lm ); #endif // #ifndef __cmtkLandmarkIO_h_included_ cmtk-3.3.1/libs/IO/cmtkLandmarkListIO.cxx000066400000000000000000000031471276303427400201510ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2012 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4093 $ // // $LastChangedDate: 2012-03-27 13:05:25 -0700 (Tue, 27 Mar 2012) $ // // $LastChangedBy: torstenrohlfing $ // */ #include "cmtkLandmarkListIO.h" #include std::ostream& operator<<( std::ostream& stream, const cmtk::LandmarkList& lmList ) { for ( cmtk::LandmarkList::const_iterator it = lmList.begin(); it != lmList.end(); ++it ) { stream << *it; } return stream; } /// Landmark input operator. std::istream& operator>>( std::istream& stream, cmtk::LandmarkList& lmList ) { cmtk::Landmark lm; while ( !stream.eof() ) { stream >> lm; if ( ! stream.fail() ) lmList.push_back( lm ); } return stream; } cmtk-3.3.1/libs/IO/cmtkLandmarkListIO.h000066400000000000000000000027751276303427400176040ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2012 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4093 $ // // $LastChangedDate: 2012-03-27 13:05:25 -0700 (Tue, 27 Mar 2012) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkLandmarkListIO_h_included_ #define __cmtkLandmarkListIO_h_included_ #include #include #include /// Landmark list output operator std::ostream& operator<<( std::ostream& stream, const cmtk::LandmarkList& lmList ); /// Landmark list input operator. std::istream& operator>>( std::istream& stream, cmtk::LandmarkList& lmList ); #endif // #ifndef __cmtkLandmarkListIO_h_included_ cmtk-3.3.1/libs/IO/cmtkPhantomIO.h000066400000000000000000000035271276303427400166210ustar00rootroot00000000000000/* // // Copyright 2012 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4283 $ // // $LastChangedDate: 2012-04-30 15:59:43 -0700 (Mon, 30 Apr 2012) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkPhantomIO_h_included_ #define __cmtkPhantomIO_h_included_ #include #include #include namespace cmtk { /** \addtogroup IO */ //@{ /// Read and write imaging phantom descriptions to and from XML files. class PhantomIO { public: /// This class. typedef PhantomIO Self; /// Read detected Magphan EMR051 description. static DetectedPhantomMagphanEMR051::SmartPtr Read( const std::string& path ); /// Write detected Magphan EMR051 description. static void Write( const DetectedPhantomMagphanEMR051& phantom, const std::string& fpath ); private: /// Whitespace callback function for MiniXML. static const char* WhitespaceWriteMiniXML( mxml_node_t* node, int where); }; //@} } // namespace cmtk #endif // #ifndef __cmtkPhantomIO_h_included_ cmtk-3.3.1/libs/IO/cmtkPhantomIO_MagphanEMR051.cxx000066400000000000000000000217701276303427400214210ustar00rootroot00000000000000/* // // Copyright 2012-2014 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 5355 $ // // $LastChangedDate: 2014-05-09 16:55:27 -0700 (Fri, 09 May 2014) $ // // $LastChangedBy: torstenrohlfing $ // */ #include "cmtkPhantomIO.h" #include #include const char * cmtk::PhantomIO::WhitespaceWriteMiniXML( mxml_node_t* node, int where) { const char* name = node->value.element.name; typedef struct _wsLookupType { /// XML element name. const char* name; /// Table of whitespace sequences. const char* ws[4]; } wsLookupType; static const wsLookupType wsLookup[] = { { "phantomType", { NULL, NULL, NULL, "\n" } }, { "fallbackOrientationCNR", { NULL, "\n", NULL, "\n" } }, { "fallbackCentroidCNR", { NULL, "\n", NULL, "\n" } }, { "snr", { NULL, NULL, NULL, "\n" } }, { "cnr", { NULL, NULL, NULL, "\n" } }, { "maxDimming", { NULL, NULL, NULL, "\n" } }, { "scale", { NULL, NULL, NULL, "\n" } }, { "nonlinear", { NULL, NULL, NULL, "\n" } }, { "landmarkList", { NULL, "\n", NULL, "\n" } }, { "landmark", { "\t", "\n", "\t", "\n" } }, { "name", { "\t\t", NULL, NULL, "\n" } }, { "detected", { "\t\t", NULL, NULL, "\n" } }, { "expected", { "\t\t", NULL, NULL, "\n" } }, { "isPrecise", { "\t\t", NULL, NULL, "\n" } }, { "fitResidual", { "\t\t", NULL, NULL, "\n" } }, { NULL, {NULL, NULL, NULL, NULL} } }; if ( (where >= 0) && (where < 4) ) { for ( size_t idx = 0; wsLookup[idx].name; ++idx ) { if ( ! strcmp( name, wsLookup[idx].name ) ) return wsLookup[idx].ws[where]; } } switch ( where ) { case MXML_WS_BEFORE_OPEN: return NULL; case MXML_WS_AFTER_OPEN: return "\n"; case MXML_WS_BEFORE_CLOSE: return NULL; case MXML_WS_AFTER_CLOSE: return "\n"; } return NULL; } void cmtk::PhantomIO::Write( const DetectedPhantomMagphanEMR051& phantom, const std::string& fpath ) { mxmlSetWrapMargin( 120 ); // make enough room for indented landmark locations mxml_node_t *x_root = mxmlNewElement( NULL, "?xml version=\"1.0\" encoding=\"utf-8\"?" ); mxml_node_t *x_phantom = mxmlNewElement( x_root, "phantom" ); Coverity::FakeFree( mxmlNewText( mxmlNewElement( x_phantom, "phantomType" ), 0, "MagphanEMR051" ) ); // put fallback flags into output as a warning if ( phantom.m_StatusFlags.m_FallbackOrientationCNR ) { Coverity::FakeFree( mxmlNewElement( x_phantom, "fallbackOrientationCNR" ) ); } if ( phantom.m_StatusFlags.m_FallbackCentroidCNR ) { mxml_node_t *x_fallback_centroid = mxmlNewElement( x_phantom, "fallbackCentroidCNR" ); char distStr[10]; snprintf( distStr, 10, "%8f", phantom.m_StatusFlags.m_DistanceSNRtoCNR ); mxmlElementSetAttr( x_fallback_centroid, "distance", distStr ); Coverity::FakeFree( x_fallback_centroid ); } Coverity::FakeFree( mxmlNewReal( mxmlNewElement( x_phantom, "snr" ), phantom.m_EstimatedSNR ) ); mxml_node_t *x_cnr = mxmlNewElement( x_phantom, "cnr" ); for ( size_t i=0; i < phantom.m_EstimatedCNR.Size(); ++i ) Coverity::FakeFree( mxmlNewReal( x_cnr, phantom.m_EstimatedCNR[i] ) ); Coverity::FakeFree( mxmlNewReal( mxmlNewElement( x_phantom, "maxDimming" ), phantom.m_MaxDimming ) ); FixedVector<3,Types::Coordinate> scales = phantom.m_LinearFitXform.GetScales(); mxml_node_t *x_scale= mxmlNewElement( x_phantom, "scale"); for ( size_t idx = 0; idx < 3; ++idx ) { Coverity::FakeFree( mxmlNewReal( x_scale, scales[idx] ) ); } mxml_node_t *x_nonlinear= mxmlNewElement( x_phantom, "nonlinear"); for ( size_t idx = 0; idx < 3; ++idx ) { Coverity::FakeFree( mxmlNewReal( x_nonlinear, phantom.m_EstimatedNonLinear[idx] ) ); } mxml_node_t *x_lmpairs = mxmlNewElement( x_phantom, "landmarkList" ); mxmlElementSetAttr( x_lmpairs, "coordinates", "physical" ); mxmlElementSetAttr( x_lmpairs, "space", "RAS" ); const std::list& lmPairs = phantom.LandmarkPairsList(); char lmCntStr[5]; snprintf( lmCntStr, 4, "%d", static_cast( lmPairs.size() ) ); mxmlElementSetAttr( x_lmpairs, "count", lmCntStr ); for ( std::list::const_iterator it = lmPairs.begin(); it != lmPairs.end(); ++it ) { mxml_node_t *x_lm = mxmlNewElement( x_lmpairs, "landmark"); Coverity::FakeFree( mxmlNewText( mxmlNewElement( x_lm, "name" ), 0, it->m_Name.c_str() ) ); mxml_node_t *x_expected = mxmlNewElement( x_lm, "expected"); for ( size_t idx = 0; idx < 3; ++idx ) { Coverity::FakeFree( mxmlNewReal( x_expected, it->m_Location[idx] ) ); } mxml_node_t *x_detected= mxmlNewElement( x_lm, "detected"); for ( size_t idx = 0; idx < 3; ++idx ) { Coverity::FakeFree( mxmlNewReal( x_detected, it->m_TargetLocation[idx] ) ); } Coverity::FakeFree( mxmlNewText( mxmlNewElement( x_lm, "isPrecise" ), 0, it->m_Precise ? "yes" : "no" ) ); Coverity::FakeFree( mxmlNewReal( mxmlNewElement( x_lm, "fitResidual" ), it->m_Residual ) ); } FILE *file = fopen( fpath.c_str(), "w" ); if ( file ) { mxmlSaveFile( x_root, file, Self::WhitespaceWriteMiniXML ); fputs( "\n", file ); // end last line fclose( file ); } else { cmtk::StdErr << "ERROR: could not open file " << fpath << " for writing\n"; } mxmlDelete( x_root ); } cmtk::DetectedPhantomMagphanEMR051::SmartPtr cmtk::PhantomIO::Read( const std::string& fpath ) { FILE *file = fopen( fpath.c_str(), "r" ); if ( !file ) { cmtk::StdErr << "ERROR: could not open file " << fpath << " for reading\n"; return DetectedPhantomMagphanEMR051::SmartPtr( NULL ); } mxml_node_t *x_root = mxmlLoadFile( NULL, file, MXML_TEXT_CALLBACK ); fclose( file ); mxml_node_t *x_landmarks = mxmlFindElement( x_root, x_root, "landmarkList", NULL, NULL, MXML_DESCEND ); if ( ! x_landmarks ) { cmtk::StdErr << "ERROR: could not file 'landmarks' XML element in file " << fpath << "\n"; mxmlDelete( x_root ); return DetectedPhantomMagphanEMR051::SmartPtr( NULL ); } AffineXform xform; DetectedPhantomMagphanEMR051::SmartPtr result( new DetectedPhantomMagphanEMR051( xform ) ); for ( mxml_node_t* x_fiducial = mxmlFindElement( x_landmarks, x_root, "landmark", NULL, NULL, MXML_DESCEND ); x_fiducial != NULL; x_fiducial = mxmlFindElement( x_fiducial, x_root, "landmark", NULL, NULL, MXML_DESCEND ) ) { mxml_node_t* x_name = mxmlFindElement( x_fiducial, x_root, "name", NULL, NULL, MXML_DESCEND ); if ( ! x_name || ! x_name->child ) continue; const std::string name = x_name->child->value.text.string; mxml_node_t* x_expected = mxmlFindElement( x_fiducial, x_root, "expected", NULL, NULL, MXML_DESCEND ); if ( ! x_expected || ! x_expected->child ) continue; Landmark::SpaceVectorType expected; mxml_node_t* x_expected_it = x_expected->child; for ( size_t i = 0; i < 3; ++i, x_expected_it = x_expected_it->next ) expected[i] = atof( x_expected_it->value.text.string ); mxml_node_t* x_detected = mxmlFindElement( x_fiducial, x_root, "detected", NULL, NULL, MXML_DESCEND ); if ( ! x_detected || ! x_detected->child ) continue; Landmark::SpaceVectorType detected; mxml_node_t* x_detected_it = x_detected->child; for ( size_t i = 0; i < 3; ++i, x_detected_it = x_detected_it->next ) detected[i] = atof( x_detected_it->value.text.string ); mxml_node_t* x_precise = mxmlFindElement( x_fiducial, x_root, "isPrecise", NULL, NULL, MXML_DESCEND ); if ( ! x_precise || ! x_precise->child ) continue; const bool precise = !strcmp( x_precise->child->value.text.string, "yes" ); mxml_node_t* x_residual = mxmlFindElement( x_fiducial, x_root, "fitResidual", NULL, NULL, MXML_DESCEND ); if ( ! x_residual || ! x_residual->child ) continue; const Types::Coordinate residual = static_cast( atof( x_residual->child->value.text.string ) ); result->AddLandmarkPair( name, expected, detected, residual, precise ); } mxmlDelete( x_root ); return result; } cmtk-3.3.1/libs/IO/cmtkSQLite.cxx000066400000000000000000000064251276303427400164770ustar00rootroot00000000000000/* // // Copyright 2010 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 2398 $ // // $LastChangedDate: 2010-10-05 14:54:37 -0700 (Tue, 05 Oct 2010) $ // // $LastChangedBy: torstenrohlfing $ // */ #include "cmtkSQLite.h" #include #include cmtk::SQLite::SQLite ( const std::string& dbPath, const bool readOnly ) : m_Good( false ), m_DebugMode( false ) { if ( readOnly ) { this->m_Good = (sqlite3_open_v2( dbPath.c_str(), &this->m_DB, SQLITE_OPEN_READONLY, NULL /*zVFS*/ ) == SQLITE_OK); } else { this->m_Good = (sqlite3_open_v2( dbPath.c_str(), &this->m_DB, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, NULL /*zVFS*/ ) == SQLITE_OK); } } cmtk::SQLite::~SQLite() { if ( this->m_Good ) sqlite3_close( this->m_DB ); } void cmtk::SQLite::Exec( const std::string& sql ) { if ( ! this->Good() ) throw Self::Exception( "Attempting operation on invalid SQLite database object" ); if ( this->m_DebugMode ) { StdErr << sql << "\n"; } char* err = NULL; if ( sqlite3_exec( this->m_DB, sql.c_str(), NULL, NULL, &err ) != SQLITE_OK ) { StdErr << "Exec " << sql << "\nSQL error: " << err << "\n"; sqlite3_free( err ); } } /// Callback for SQLite: add rows to results table. extern "C" int cmtkSQLiteQueryCallback( void* pTable, int ncols, char** rowdata, char** ) { cmtk::SQLite::TableType* table = static_cast( pTable ); std::vector< std::string > tableRow( ncols ); for ( int col = 0; col < ncols; ++col ) { if ( rowdata[col] ) tableRow[col] = std::string( rowdata[col] ); else tableRow[col] = std::string( "NULL" ); } table->push_back( tableRow ); return 0; } void cmtk::SQLite::Query( const std::string& sql, cmtk::SQLite::TableType& table ) const { if ( ! this->Good() ) throw Self::Exception( "Attempting operation on invalid SQLite database object" ); if ( this->m_DebugMode ) { StdErr << sql << "\n"; } table.resize( 0 ); char* err = NULL; if ( sqlite3_exec( this->m_DB, sql.c_str(), cmtkSQLiteQueryCallback, &table, &err ) != SQLITE_OK ) { StdErr << "Query " << sql << "\nSQL error: " << err << "\n"; sqlite3_free( err ); } } bool cmtk::SQLite::TableExists( const std::string& tableName ) const { Self::TableType table; this->Query( "SELECT name FROM SQLite_Master WHERE name='"+tableName+"'", table ); return table.size() && table[0].size() && (table[0][0] == tableName); } cmtk-3.3.1/libs/IO/cmtkSQLite.h000066400000000000000000000063711276303427400161240ustar00rootroot00000000000000/* // // Copyright 2010 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 2398 $ // // $LastChangedDate: 2010-10-05 14:54:37 -0700 (Tue, 05 Oct 2010) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkSQLite_h_included_ #define __cmtkSQLite_h_included_ #include #include #include #include #include namespace cmtk { /** \addtogroup IO */ //@{ /** Wrapper class for SQLite database. */ class SQLite { public: /// This class. typedef SQLite Self; /// Primary key type for the underlying database. This is used to uniquely identify table entries. typedef sqlite3_uint64 PrimaryKeyType; /// Primary key value when object is not found: this should be guaranteed to never be used by the database as an actual primary key. static const PrimaryKeyType NOTFOUND = static_cast( -1 ); /// Table type: matrix of strings. typedef std::vector< std::vector< std::string > > TableType; /// Exception class for class-specific error reporting. class Exception : /// Inherit from library-level exception public cmtk::Exception { public: /// Constructor with error message. Exception( const std::string& error ) : cmtk::Exception( error ) {}; }; /// Constructor: open SQLite database. SQLite( const std::string& dbPath, /*!< Path to the SQLite3 database file. */ const bool readOnly = false /*!< If this flag is set, the database is opened read-only. If false, the database is opened for read/write, and a non-existing database will be created. */); /// Destructor: close database. virtual ~SQLite(); /// Test "good" flag. bool Good() const { return this->m_Good; } /// Execute an SQL command with no return value. void Exec( const std::string& sql ); /// Query database and return table. void Query( const std::string& sql, Self::TableType& table ) const; /// Check if table exists. bool TableExists( const std::string& tableName ) const; /// Turn on debug mode. void DebugModeOn() { this->m_DebugMode = true; } /// Turn off debug mode. void DebugModeOff() { this->m_DebugMode = false; } protected: /// Database object. mutable sqlite3 *m_DB; /// Flag for "good" database object. bool m_Good; /// Debug mode flag: if this is set, all executed SQL queries will be printed to standard error. bool m_DebugMode; }; //@} } // namespace cmtk #endif // #ifndef __cmtkSQLite_h_included_ cmtk-3.3.1/libs/IO/cmtkSegmentationLabelIO.cxx000066400000000000000000000034161276303427400211600ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2010 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 2022 $ // // $LastChangedDate: 2010-07-21 15:26:03 -0700 (Wed, 21 Jul 2010) $ // // $LastChangedBy: torstenrohlfing $ // */ #include "cmtkSegmentationLabelIO.h" #include #include namespace cmtk { /** \addtogroup IO */ //@{ std::istream& operator>> ( std::istream& stream, SegmentationLabelMap& lblMap ) { std::string line; while ( ! stream.eof() ) { std::getline( stream, line ); if ( line.length() && (line[0] != '#') ) { // skip blank and comments int id; std::string name, rs, gs, bs, as; std::istringstream inStr( line ); inStr >> id >> name >> rs >> gs >> bs >> as; lblMap[id].SetName( name.c_str() ); lblMap[id].SetRGB( atoi( rs.c_str() ), atoi( gs.c_str() ), atoi( bs.c_str() ) ); } } return stream; } } // namespace cmtk cmtk-3.3.1/libs/IO/cmtkSegmentationLabelIO.h000066400000000000000000000030501276303427400205770ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2010 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 2398 $ // // $LastChangedDate: 2010-10-05 14:54:37 -0700 (Tue, 05 Oct 2010) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkSegmentationLabelIO_h_included_ #define __cmtkSegmentationLabelIO_h_included_ #include #include #include #include namespace cmtk { /** \addtogroup IO */ //@{ /// Operator to read a list of label IDs from a C++ stream. std::istream& operator>>( std::istream& stream, SegmentationLabelMap& lblMap ); //@} } // namespace cmtk #endif // #ifndef __cmtkSegmentationLabelIO_h_included_ cmtk-3.3.1/libs/IO/cmtkSiemensCSAHeader.cxx000066400000000000000000000072321276303427400203760ustar00rootroot00000000000000/* // // Copyright 2004-2012 SRI International // // Copyright 1997-2009 Torsten Rohlfing // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4527 $ // // $LastChangedDate: 2012-10-02 10:16:03 -0700 (Tue, 02 Oct 2012) $ // // $LastChangedBy: torstenrohlfing $ // */ #include "cmtkSiemensCSAHeader.h" #include #include cmtk::SiemensCSAHeader::SiemensCSAHeader( const char* csaData, const size_t csaLength ) { FileConstHeader fileHeader( csaData, false /*isBigEndian*/ ); // Siemens CSA header is always little endian const char headerID2[] = { 'S', 'V', '1', '0' }; const bool csa2hdr = fileHeader.CompareFieldStringN( 0, headerID2, sizeof( headerID2 ) ); // get number of items according to either CSA1 or CSA2 format const size_t nTags = csa2hdr ? fileHeader.GetField( 8 ) : fileHeader.GetField( 0 ); size_t tagOffset = csa2hdr ? 16 : 8; // start after header: length is 16 bytes for CSA2, 8 bytes for CSA1 for ( size_t tag = 0; tag < nTags; ++tag ) { // make sure we don't run over the end of the header if ( tagOffset+84 >= csaLength ) break; // first, get tag name (up to 64 characters plus 0x0) char tagName[65]; fileHeader.GetFieldString( tagOffset, tagName, 64 ); // find number of items for this tag and make room in allocated vector const size_t nItems = fileHeader.GetField( tagOffset + 76 ); // create new tag object Self::value_type newTag( tagName, std::vector() ); newTag.second.resize( nItems ); tagOffset += 84; for ( size_t item = 0; item < nItems; ++item ) { if ( tagOffset+4 >= csaLength ) break; const size_t itemLen = fileHeader.GetField( tagOffset ); if ( itemLen && (tagOffset+16+itemLen itemStr( itemLen ); fileHeader.GetFieldString( tagOffset+16, &(itemStr[0]), itemLen ); newTag.second[item] = std::string( itemStr.begin(), itemStr.end() ); } tagOffset += 4*((itemLen+3)/4) /*move up to nearest 4-byte boundary*/ + 16 /*the 4 ints at the beginning of item, including itemLength*/; } // finally, insert tag into the map std::pair inserted = this->insert( newTag ); if ( !inserted.second ) { StdErr << "Warning: CSA tag named '" << tagName << "' appears more than once.\n"; } } } std::ostream& cmtk::operator<<( std::ostream& stream, const SiemensCSAHeader& csaHeader ) { for ( SiemensCSAHeader::const_iterator it = csaHeader.begin(); it != csaHeader.end(); ++it ) { stream << it->first << " nitems=" << it->second.size() << "\n"; for ( size_t item = 0; item < it->second.size(); ++item ) { stream << "\t\"" << it->second[item] << "\" [" << it->second[item].length() << "]\n" ; } } return stream; } cmtk-3.3.1/libs/IO/cmtkSiemensCSAHeader.h000066400000000000000000000034111276303427400200160ustar00rootroot00000000000000/* // // Copyright 2004-2012 SRI International // // Copyright 1997-2009 Torsten Rohlfing // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4527 $ // // $LastChangedDate: 2012-10-02 10:16:03 -0700 (Tue, 02 Oct 2012) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkSiemensCSAHeader_h_included_ #define __cmtkSiemensCSAHeader_h_included_ #include #include #include #include #include namespace cmtk { /// Class for handling Siemens CSA headers in DICOM files. class SiemensCSAHeader : public std::map< std::string,std::vector > { public: /// This class. typedef SiemensCSAHeader Self; /// Constructor from binary blob. SiemensCSAHeader( const char* csaData, const size_t csaLength ); }; /// Write header contents to stream. std::ostream& operator<<( std::ostream& stream, const cmtk::SiemensCSAHeader& csaHeader ); } // namespace cmtk #endif // #ifndef __cmtkSiemensCSAHeader_h_included_ cmtk-3.3.1/libs/IO/cmtkSplineWarpXformITKIO.cxx000066400000000000000000000064171276303427400212370ustar00rootroot00000000000000/* // // Copyright 2009-2012 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4538 $ // // $LastChangedDate: 2012-10-02 15:16:13 -0700 (Tue, 02 Oct 2012) $ // // $LastChangedBy: torstenrohlfing $ // */ #include "cmtkSplineWarpXformITKIO.h" #include #include #include #include #include void cmtk::SplineWarpXformITKIO ::Write( const std::string& filename, const SplineWarpXform& xform, const UniformVolume& refVolume, const UniformVolume& fltVolume ) { std::ofstream stream( filename.c_str() ); if ( stream.good() ) { // write header stream << "#Insight Transform File V1.0\n" << "# Transform 0\n"; // write ID depending on whether CMTK is using single or double precision floats for coordinates if ( typeid( Types::Coordinate ) == typeid( double ) ) { stream << "Transform: BSplineDeformableTransform_double_3_3\n"; } else { stream << "Transform: BSplineDeformableTransform_float_3_3\n"; } // write parameters stream << "Parameters:"; Vector3D v, vx; const AffineXform::SmartPtr bulkXform = xform.GetInitialAffineXform(); for ( size_t cp = 0; cp < xform.GetNumberOfControlPoints(); ++cp ) { v = xform.GetOriginalControlPointPositionByOffset( cp ); if ( bulkXform ) v = bulkXform->Apply( v ); vx = xform.GetShiftedControlPointPositionByOffset( cp ); vx -= v; stream << " " << -vx[0] << " " << -vx[1] << " " << vx[2]; // convert from RAS to LPS by writing -x,-y,+z } stream << "\n"; // Origin of the control point grid must be transformed into physical coordinates of the reference image Vector3D origin( xform.m_Offset * refVolume.GetImageToPhysicalMatrix() ); // Fixed parameters: // * Grid Size // * Grid Origin // * Grid Spacing // * Grid Direction stream << "FixedParameters: " << xform.m_Dims[0] << " " << xform.m_Dims[1] << " " << xform.m_Dims[2] << " " << origin[0] << " " << origin[1] << " " << origin[2] << " " << xform.m_Spacing[0] << " " << xform.m_Spacing[1] << " " << xform.m_Spacing[2] << " " << "1 0 0 0 1 0 0 0 1\n"; if ( bulkXform ) { TransformChangeToSpaceAffine toNative( *(bulkXform), refVolume, fltVolume, AnatomicalOrientationBase::SPACE_ITK ); AffineXformITKIO::Write( stream, toNative.GetTransformation(), 1 /*idx*/ ); } stream.close(); } } cmtk-3.3.1/libs/IO/cmtkSplineWarpXformITKIO.h000066400000000000000000000033651276303427400206630ustar00rootroot00000000000000/* // // Copyright 2009-2010 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 2398 $ // // $LastChangedDate: 2010-10-05 14:54:37 -0700 (Tue, 05 Oct 2010) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkSplineWarpXformITKIO_h_included__ #define __cmtkSplineWarpXformITKIO_h_included__ #include #include #include #include namespace cmtk { /** Class for reading and writing affine transformations from and to ITK's file format. * This should also be understood by Slicer3 for transformation exchange with CMTK tools * run as plugins. */ class SplineWarpXformITKIO { public: /// Write transformation to ITK file. static void Write( const std::string& filename, const SplineWarpXform& xform, const UniformVolume& refVolume, const UniformVolume& fltVolume ); }; } // namespace cmtk #endif // #ifndef __cmtkSplineWarpXformITKIO_h_included__ cmtk-3.3.1/libs/IO/cmtkStudy.cxx000066400000000000000000000122301276303427400164350ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2013 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4880 $ // // $LastChangedDate: 2013-09-26 15:11:56 -0700 (Thu, 26 Sep 2013) $ // // $LastChangedBy: torstenrohlfing $ // */ #include "cmtkStudy.h" #include #include #include #include namespace cmtk { /** \addtogroup IO */ //@{ Study::Study() : m_Modality( NULL ), m_Volume( NULL ), m_MinimumValue( 0.0 ), m_MaximumValue( 0.0 ), m_Padding( false ), m_PaddingValue( 0.0 ), m_HaveUserColorMap( false ), m_StandardColormap( 0 ), m_ReverseColormap( false ), m_Black( 0.0 ), m_White( 0.0 ), m_Gamma( 1.0 ), m_DisplayedImageIndex( -1 ), m_ZoomFactor( 1 ), m_SliceNormal( 2 ) { } Study::Study( const std::string& fileSystemPath, const std::string& name ) : m_Modality( NULL ), m_Volume( NULL ), m_MinimumValue( 0.0 ), m_MaximumValue( 0.0 ), m_Padding( false ), m_PaddingValue( 0.0 ), m_HaveUserColorMap( false ), m_StandardColormap( 0 ), m_ReverseColormap( false ), m_Black( 0.0 ), m_White( 0.0 ), m_Gamma( 1.0 ), m_DisplayedImageIndex( -1 ), m_ZoomFactor( 1 ), m_SliceNormal( 2 ) { if ( ! fileSystemPath.empty() ) { this->m_FileSystemPath = fileSystemPath; this->m_Description = FileFormat::Describe( this->m_FileSystemPath ); // cut trailing '/'s off the study path. const size_t lastChar = this->m_FileSystemPath.find_last_not_of( "/" ); if ( lastChar != std::string::npos ) { this->m_FileSystemPath = this->m_FileSystemPath.substr( 0, lastChar+1 ); } this->SetMakeName( name ); } } std::string Study::SetMakeName( const std::string& name, const int suffix ) { char suffixStr[10]; snprintf( suffixStr, 9, "<%d>", suffix ); if ( !name.empty() ) { if ( suffix ) { this->SetName( name + suffixStr ); } else { this->SetName( name ); } } else { std::string studyName = name; const size_t lastChar = studyName.find_last_not_of( "/" ); if ( lastChar != std::string::npos ) { studyName = studyName.substr( 0, lastChar+1 ); } const size_t lastSlash = studyName.rfind( "/" ); if ( lastSlash != std::string::npos ) { studyName = studyName.substr( lastSlash+1 ); } else { studyName = this->m_FileSystemPath; } const size_t dot = studyName.find( "." ); if ( dot != std::string::npos ) { studyName = studyName.substr( 0, dot ); } if ( suffix ) studyName = studyName + suffixStr; this->SetName( studyName ); } return this->m_Name; } bool Study::ReadVolume( const bool reRead, const char* orientation ) { UniformVolume::SmartPtr oldVolume( NULL ); if ( this->m_Volume && reRead ) { oldVolume = this->m_Volume; this->m_Volume = UniformVolume::SmartPtr( NULL ); } if ( !this->m_Volume ) { if ( orientation ) this->m_Volume = UniformVolume::SmartPtr( VolumeIO::ReadOriented( this->m_FileSystemPath, orientation ) ); else this->m_Volume = UniformVolume::SmartPtr( VolumeIO::Read( this->m_FileSystemPath ) ); if ( this->m_Volume ) { this->m_Dims = this->m_Volume->GetDims(); this->m_DisplayedImageIndex = this->m_Dims[AXIS_Z] / 2 ; this->m_ZoomFactor = 1; const TypedArray *dataArray = this->m_Volume->GetData(); if ( dataArray ) { const Types::DataItemRange range = dataArray->GetRange(); this->m_MinimumValue = range.m_LowerBound; this->m_MaximumValue = range.m_UpperBound; this->m_Black = dataArray->GetPercentile( 0.01, 1024 ); this->m_White = dataArray->GetPercentile( 0.99, 1024 ); this->m_StandardColormap = 0; this->m_ReverseColormap = false; } } } if ( this->m_Volume && this->m_Volume->GetData() ) { return true; } this->m_Volume = oldVolume; return false; } void Study::CopyColormap( const Study* other ) { this->m_MinimumValue = other->m_MinimumValue; this->m_MaximumValue = other->m_MaximumValue; this->m_StandardColormap = other->m_StandardColormap; this->m_ReverseColormap = other->m_ReverseColormap; this->m_Black = other->m_Black; this->m_White = other->m_White; this->m_Gamma = other->m_Gamma; } } // namespace cmtk cmtk-3.3.1/libs/IO/cmtkStudy.h000066400000000000000000000124121276303427400160640ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2013 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4881 $ // // $LastChangedDate: 2013-09-26 15:12:14 -0700 (Thu, 26 Sep 2013) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkStudy_h_included_ #define __cmtkStudy_h_included_ #include #include #include #include #include #include #include #include #include #include namespace cmtk { /** \addtogroup IO */ //@{ /** Constants to identify color table. */ enum { /// Grayscale color table. PALETTE_GRAY = 0, /// Red color table. PALETTE_RED = 1, /// Green color table. PALETTE_GREEN = 2, /// Blue color table. PALETTE_BLUE = 3, /// Rainbow color table. PALETTE_RAINBOW = 4, /// Color table for labels. PALETTE_LABELS = 5 }; /** Class for parameters of a general imaging study. */ class Study { public: /// This class. typedef Study Self; /// Path of this study in the file system. cmtkGetSetMacro(std::string,FileSystemPath); /// Short, memorable name assigned to this study. cmtkGetSetMacro(std::string,Name); /// Textual description of study file type. cmtkGetSetMacro(std::string,Description); /// Textual description of study file type. cmtkGetSetMacroString(Modality); /// Volume data associated with this study. cmtkGetSetMacro(UniformVolume::SmartPtr,Volume); /// Landmark list. cmtkGetSetMacro(LandmarkList::SmartPtr,LandmarkList); /// Voxel dimensions of the volume image. DataGrid::IndexType m_Dims; /// Minimum value. cmtkGetSetMacro(Types::DataItem,MinimumValue); /// Maximum value. cmtkGetSetMacro(Types::DataItem,MaximumValue); /// Pixel padding value. cmtkGetSetMacro(bool,Padding); /// Pixel padding value. cmtkGetSetMacro(Types::DataItem,PaddingValue); /// Flag for user-defined colormap. cmtkGetSetMacro(bool,HaveUserColorMap); /// Index of colormap. cmtkGetSetMacro(char,StandardColormap); /// Is colormap reversed? cmtkGetSetMacro(bool,ReverseColormap); /// Value corresponding to "black". cmtkGetSetMacro(Types::DataItem,Black); /// Value corresponding to "white". cmtkGetSetMacro(Types::DataItem,White); /// Gamma value. cmtkGetSetMacro(double,Gamma); /// Index of currently displayed image. cmtkGetSetMacro(unsigned int,DisplayedImageIndex); /// Displayed image zoom. cmtkGetSetMacro(unsigned int,ZoomFactor); /// Slice normal coordinate axis. cmtkGetSetMacro(int,SliceNormal); public: /// Smart pointer to Study. typedef SmartPointer SmartPtr; /// Default constructor. Study(); /// Constructor: Construct study from image file. Study( const std::string& fileSystemPath, const std::string& name = "" ); /// Virtual destructor. virtual ~Study() {} /** Read volume data. *\param reRead If this is false, then the volume is only read if it has not * been read before. Otherwise, it is re-read in any case. *\param orientation Optional three-letter anatomical image orientation. *\return True if reading was successful; the "Volume" field has a pointer to * the resulting image volume. */ bool ReadVolume( const bool reRead = false, const char* orientation = NULL ); /** Set study name; create name if no name given. * This function sets the name of this study. If no name is given (name * parameter is NULL pointer), then a name is constructed from the file * system path of this study. */ std::string SetMakeName( const std::string& name = "" /*!< New study name */, const int suffix = 0 /*!< Unique numerical suffix to be added to study name if other studies with the same name exist. */ ); /// Static study reader function. static Self* Read( const std::string& path ) { return new Self( path ); } /** Copy colormap information from another study object. */ void CopyColormap( const Self* other ); /** Get colormap from label list. */ void SetFromLabelMap( const SegmentationLabelMap& lblMap ) { this->m_HaveUserColorMap = true; UserLabelMap = lblMap; } /// Get user-defined label map. const SegmentationLabelMap& GetUserLabelMap() const { return UserLabelMap; } private: /// User-defined label and colormap. SegmentationLabelMap UserLabelMap; }; //@} } // namespace cmtk #endif // #ifndef __cmtkStudy_h_included_ cmtk-3.3.1/libs/IO/cmtkStudyImageSet.h000066400000000000000000000041511276303427400175040ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2010 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 2398 $ // // $LastChangedDate: 2010-10-05 14:54:37 -0700 (Tue, 05 Oct 2010) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkStudyImageSet_h_included_ #define __cmtkStudyImageSet_h_included_ #include #include #include #include #include namespace cmtk { /** \addtogroup IO */ //@{ /// An imaging study that is constructed from multiple 2-D images. class StudyImageSet : /// Inherit basic study fields and functions. public Study, /// Inherit string list for list of file names. public std::list { private: /// Convenience typedef. typedef Study Superclass; public: /// Is this a single file or a multi-file study? cmtkGetSetMacro(bool,MultiFile); /// Directory that contains the image files. cmtkGetSetMacroString(ImageDirectory); /// Directory that contains the image files. cmtkGetSetMacro(FileFormatID,ImageFormat); /// Default constructor. StudyImageSet() : Study(), m_MultiFile( false ), m_ImageDirectory( NULL ), m_ImageFormat( FILEFORMAT_UNKNOWN ) {} }; //@} } // namespace cmtk #endif // #ifndef __cmtkStudyImageSet_h_included_ cmtk-3.3.1/libs/IO/cmtkStudyList.cxx000066400000000000000000000130441276303427400172750ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2011, 2013 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4878 $ // // $LastChangedDate: 2013-09-24 15:48:06 -0700 (Tue, 24 Sep 2013) $ // // $LastChangedBy: torstenrohlfing $ // */ #include "cmtkStudyList.h" #include #include namespace cmtk { /** \addtogroup IO */ //@{ const Study* StudyList::GetStudy( const unsigned int studyIndex ) const { if ( studyIndex < this->size() ) { const_iterator it = this->begin(); for ( unsigned int i = 0; i < studyIndex; ++i ) ++it; return it->first; } else return NULL; } Study::SmartPtr StudyList::GetStudy( const unsigned int studyIndex ) { if ( studyIndex < this->size() ) { const_iterator it = this->begin(); for ( unsigned int i = 0; i < studyIndex; ++i ) ++it; return it->first; } else return Study::SmartPtr::Null(); } const Study* StudyList::FindStudyPath( const std::string& fileSystemPath ) const { if ( fileSystemPath.empty() ) return NULL; const_iterator it = this->begin(); while ( it != this->end() ) { if ( it->first->GetFileSystemPath() == fileSystemPath ) return it->first; ++it; } // not found: return NULL; return NULL; } Study::SmartPtr StudyList::FindStudyPath( const std::string& fileSystemPath, const bool create ) { if ( fileSystemPath.empty() ) return Study::SmartPtr::Null(); iterator it = this->begin(); while ( it != this->end() ) { if ( it->first->GetFileSystemPath() == fileSystemPath ) return it->first; ++it; } // not found: return NULL or create; if ( !create ) return Study::SmartPtr::Null(); Study::SmartPtr newStudy( new Study ); newStudy->SetFileSystemPath( fileSystemPath ); this->AddStudy( newStudy ); return newStudy; } const Study* StudyList::FindStudyName( const std::string& name ) const { if ( name.empty() ) return NULL; const_iterator it = this->begin(); while ( it != this->end() ) { if ( it->first->GetName() == name ) return it->first; ++it; } // not found: return NULL; return NULL; } Study::SmartPtr StudyList::FindStudyName( const std::string& name ) { if ( name.empty() ) return Study::SmartPtr::Null(); iterator it = this->begin(); while ( it != this->end() ) { if ( it->first->GetName() == name ) return it->first; ++it; } // not found: return NULL; return Study::SmartPtr::Null(); } Study::SmartPtr StudyList::AddStudy( const std::string& fileSystemPath ) { if ( fileSystemPath.empty() ) return Study::SmartPtr::Null(); const_iterator it = this->begin(); while ( it != this->end() ) { // if this study is already in the list, we're done. if ( it->first->GetFileSystemPath() == fileSystemPath ) return Study::SmartPtr::Null(); ++it; } Study::SmartPtr newStudy( Study::Read( fileSystemPath ) ); if ( newStudy ) { int suffix = 0; while ( this->FindStudyName( newStudy->GetName() ) ) { newStudy->SetMakeName( "", suffix++ ); } (*this)[newStudy]; } return newStudy; } void StudyList::AddStudy( Study::SmartPtr& study ) { if ( !study ) return; const std::string& newStudyPath = study->GetFileSystemPath(); const_iterator it = this->begin(); while ( it != this->end() ) { // if this study is already in the list, we're done. if ( it->first->GetFileSystemPath() == newStudyPath ) return; ++it; } // insert new study into map. (*this)[study]; } void StudyList::AddXform ( const std::string& fromStudyPath, const std::string& toStudyPath, AffineXform::SmartPtr& affineXform, WarpXform::SmartPtr& warpXform ) { Study::SmartPtr fromStudy = this->FindStudyPath( fromStudyPath, true /*create*/ ); Study::SmartPtr toStudy = this->FindStudyPath( toStudyPath, true /*create*/ ); this->AddXform( fromStudy, toStudy, affineXform, warpXform ); } void StudyList::AddXform ( Study::SmartPtr& fromStudy, Study::SmartPtr& toStudy, AffineXform::SmartPtr& affineXform, WarpXform::SmartPtr& warpXform ) { if ( !fromStudy || !toStudy ) return; if ( affineXform ) { Xform::SmartPtr xform = affineXform; (*this)[fromStudy].insert( std::multimap::value_type( toStudy, xform ) ); } if ( warpXform ) { Xform::SmartPtr xform = warpXform; (*this)[fromStudy].insert( std::multimap::value_type( toStudy, xform ) ); } } void StudyList::DeleteStudy( const Study* study ) { iterator it = this->begin(); while ( it != this->end() ) { if ( it->first == study ) { this->erase( it ); } break; } } } // namespace cmtk cmtk-3.3.1/libs/IO/cmtkStudyList.h000066400000000000000000000103741276303427400167250ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2011, 2013 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4878 $ // // $LastChangedDate: 2013-09-24 15:48:06 -0700 (Tue, 24 Sep 2013) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkStudyList_h_included_ #define __cmtkStudyList_h_included_ #include #include #include #include #include #include namespace cmtk { /** \addtogroup IO */ //@{ typedef std::multimap StudyToXform; typedef std::map< Study::SmartPtr, StudyToXform > StudyToStudyToXform; class StudyList : /// Also inherit container functions from STL list class. public StudyToStudyToXform { public: /// This class. typedef StudyList Self; /// Smart pointer to StudyList. typedef SmartPointer SmartPtr; /// Parent class. typedef StudyToStudyToXform Superclass; /** Default constructor. * Do nothing really, except be there. */ StudyList() {}; /** Copy constructor. *\todo Implement copying of transformation links between studies. */ StudyList( const StudyList& slist ) : Superclass( slist ) {}; /// Get constant Study object by index. const Study *GetStudy( const unsigned int studyIndex ) const; /// Get non-constant Study object by index. Study::SmartPtr GetStudy( const unsigned int studyIndex ); /// Find constant Study object by file system path. const Study *FindStudyPath( const std::string& fileSystemPath ) const; /** Find non-constant Study object by file system path. */ Study::SmartPtr FindStudyPath( const std::string& fileSystemPath /*!< Path of study to find in filesystem */, const bool create = false /*!< Flag whether to create a study that does not exist already */ ); /// Find constant Study object by file system path. const Study *FindStudyName( const std::string& name /*!< Name of the study to find.*/ ) const; /// Find non-constant Study object by file system path. Study::SmartPtr FindStudyName( const std::string& name /*!< Name of the study to find.*/ ); /// Add a new study entry. Study::SmartPtr AddStudy( const std::string& fileSystemPath /*!< Name of the study to add.*/ ); /// Add an existing study object. void AddStudy( Study::SmartPtr& study /*!< Existing study object to add to the study list.*/ ); /// Add a coordinate transformation between two studies. void AddXform( Study::SmartPtr& fromStudy, Study::SmartPtr& toStudy, AffineXform::SmartPtr& affineXform, WarpXform::SmartPtr& warpXform = WarpXform::SmartPtr::Null() ); /// Add a coordinate transformation between two studies. void AddXform( const std::string& fromStudyPath /*!< Path of the study that the transformation maps from.*/, const std::string& toStudyPath /*!< Path of the study that the transformation maps to.*/, AffineXform::SmartPtr& affineXform /*!< Affine coordinate transformation between the given studies. */, WarpXform::SmartPtr& warpXform = WarpXform::SmartPtr::Null() /*!< Optional nonrigid transformation between the two given studies. */); /// Remove and delete given study object. void DeleteStudy( const Study* study /*!< Study object to delete from the list.*/ ); /// Remove and delete given study object. void DeleteStudy( const unsigned int studyIndex /*!< Index of study to delete from the list.*/ ); }; //@} } // namespace cmtk #endif // #ifndef __cmtkStudyList_h_included_ cmtk-3.3.1/libs/IO/cmtkTypedStream.cxx000066400000000000000000000075211276303427400175750ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2013 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4870 $ // // $LastChangedDate: 2013-09-24 11:01:11 -0700 (Tue, 24 Sep 2013) $ // // $LastChangedBy: torstenrohlfing $ // */ #include "cmtkTypedStream.h" #include #include #include #include #include #include #ifdef HAVE_MALLOC_H # include #endif #ifdef HAVE_SYS_TIME_H # include #endif #ifdef HAVE_SYS_STAT_H # include #endif namespace cmtk { /** \addtogroup IO */ //@{ TypedStream::TypedStream() { File = NULL; GzFile = NULL; PrecisionFloat = 6; PrecisionDouble = 10; this->m_Status = Self::ERROR_NONE; this->m_DebugFlag = Self::DEBUG_OFF; memset( this->Buffer, 0, sizeof( this->Buffer ) ); this->BufferKey = this->BufferValue = NULL; SplitPosition = NULL; } int TypedStream ::StringCmp( const char *s1, const char *s2 ) { for (; *s1 && *s2; s1++, s2++) { if (*s1 == ' ' || *s1 == '\t' || *s1 == '\n' || *s2 == ' ' || *s2 == '\t' || *s2 == '\n') { break; } if (*s1 == *s2) continue; if (*s1 >= 'a' && *s1 <= 'z') { if (*s1 - ('a'-'A') == *s2) continue; } if (*s2 >= 'a' && *s2 <= 'z') { if (*s2 - ('a'-'A') == *s1) continue; } return 1; } if ((*s1 == ' ' || *s1 == '\0' || *s1 == '\t' || *s1 == '\n') && (*s2 == ' ' || *s2 == '\0' || *s2 == '\t' || *s2 == '\n')) { return 0; } return 1; } char* TypedStream ::StringSplit( char * s1 ) const { if (s1) SplitPosition = s1-1; if (SplitPosition == NULL) return NULL; /* skip over leading white space */ for ( SplitPosition++; *SplitPosition == '\0' || *SplitPosition == ' ' || *SplitPosition == '\t' || *SplitPosition == '\n'; SplitPosition++ ) if ( *SplitPosition == '\0' ) return NULL; s1 = SplitPosition; /* find token's end */ if ( *SplitPosition == '\"' ) { /* skip over the special string token */ for ( SplitPosition++; *SplitPosition && *SplitPosition != '\n' && *SplitPosition != '\t'; SplitPosition++) { if ( *SplitPosition == '\\' && *(SplitPosition+1) ) { SplitPosition++; continue; } if ( *SplitPosition == '\"' ) { SplitPosition++; break; } } } else { /* skip over a numeric value */ for ( ; *SplitPosition; SplitPosition++ ) { if ( *SplitPosition == ' ' || *SplitPosition == '\t' || *SplitPosition == '\n') break; } } if ( *SplitPosition ) { *SplitPosition = '\0'; } else { SplitPosition = NULL; } return s1; } void TypedStream ::DebugOutput( const char* format, ... ) { if ( this->m_DebugFlag != Self::DEBUG_ON ) return; static char buffer[1024]; va_list args; va_start(args, format); vsnprintf( buffer, sizeof( buffer ), format, args ); va_end(args); fputs( buffer, stderr ); fputs( "\n", stderr ); } } // namespace cmtk cmtk-3.3.1/libs/IO/cmtkTypedStream.h000066400000000000000000000163101276303427400172160ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2013 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 5048 $ // // $LastChangedDate: 2013-11-27 15:02:05 -0800 (Wed, 27 Nov 2013) $ // // $LastChangedBy: torsten_at_home $ // */ #ifndef __cmtkTypedStream_h_included_ #define __cmtkTypedStream_h_included_ #include #include #include #include #include #ifndef NULL #define NULL 0 #endif #include namespace cmtk { /** \addtogroup IO */ //@{ /**\name TypedStream.h */ //@{ /** base class for reading and writing of "typedstream" archives. */ class TypedStream { public: /// This class. typedef TypedStream Self; /// Condition upon function return. typedef enum { /// There was an error. CONDITION_ERROR, /// No error encountered; operation completed successfully. CONDITION_OK } Condition; /// Classes of error conditions typedef enum { /// No error. ERROR_NONE, /// Unknown error. ERROR_UNKNOWN, /** A call to a system function returned an error condition. * To find out more details, "errno" may be consulted. */ ERROR_SYSTEM, /** Error in the format of the open archive. */ ERROR_FORMAT, /** Wrong or invalid arguments given to a function. */ ERROR_ARG, /** The requested operation is not available in the current stream mode. * This usually means that a write access was requested on a read-only * archive or a Seek() operation on a write-only archive. */ ERROR_MODE, /** Error in a primitive data object. * A value in the archive does not have the correct syntax for the expected * type. */ ERROR_TYPE, /** An internal limit was exhausted. * As we are now using a proper STL stack for keeping track of open levels, * this condition should not occur any more. */ ERROR_LIMIT, /** Close of a level was requested when none was open. */ ERROR_LEVEL, /** The current stream is invalid. * This condition is set when an access is tried without opening a file * first. */ ERROR_INVALID, ERROR_MAX } Status; /// Identifiers for supported primitive data types. typedef enum { /// Interger. TYPE_INT, /// Boolean (Yes/No). TYPE_BOOL, /// Binary boolean (0/1). TYPE_BINARYBOOL, /// Single-precision float. TYPE_FLOAT, /// Double-precision float TYPE_DOUBLE, /// String (char*). TYPE_STRING } Type; /// Identifiers for tokens in archives. typedef enum { /// End-of-file. TOKEN_EOF, /// Section beginning "{". TOKEN_BEGIN, /// Section end "}". TOKEN_END, /// Key (field name). TOKEN_KEY, /// Field value. TOKEN_VALUE, /// Comment. TOKEN_COMMENT } Token; /// Debug flag values. typedef enum { /// There was an error. DEBUG_OFF, /// No error encountered; operation completed successfully. DEBUG_ON } DebugFlag; /// Default constructor. TypedStream(); /** Return validity of archive. *\return 1 if an archive is currently open, 0 if not. */ int IsValid() { return (this->File != NULL) || (this->GzFile != NULL); } /** Return status of last operation. */ Self::Status GetStatus() const { return this->m_Status; } /// Set debugging flag. void SetDebugFlag( const Self::DebugFlag debugFlag = Self::DEBUG_ON /*!< Set the debug flag to this value. */ ) { this->m_DebugFlag = debugFlag; } protected: /// Internal: Length of the read buffer for one archive line. static const int LIMIT_BUFFER = 1024; /// Pointer to the actual file. FILE *File; /// Pointer to the compressed file in decompression mode. gzFile GzFile; /** Holds the status of the last operation. */ Self::Status m_Status; /** Number of significant digits for "float" fields. * As all float numbers are written to the archive as strings, part of the * native resolution is lost. This field determines, how many significant * digits are preserved when converting single precision float numbers to * strings. */ int PrecisionFloat; /** Number of significant digits for "double" fields. * As all float numbers are written to the archive as strings, part of the * native resolution is lost. This field determines, how many significant * digits are preserved when converting double precision float numbers to * strings. */ int PrecisionDouble; /// Buffer for the current line read from the archive. char Buffer[Self::LIMIT_BUFFER]; /// Pointer to the "key" part of the line currently in Buffer. char* BufferKey; /// Pointer to the "value" part of the line currently in Buffer. char* BufferValue; /** Stack of open section levels. * This stack holds the starting positions of all currently open sections. * The entries are byte positions relative to the beginning of the file. */ std::stack LevelStack; /** Compare two strings. * Other than the standard library's strcmp() function, this implementation * ignores upper and lowercase. Also, strings are terminated by either NULL * characters or any white space or newline. *\return 0 for identical strings (up to upper-/lowercase), 1 for * non-identical strings. */ static int StringCmp( const char* s1, const char* s2 ); /** Separate next token. * This function identifies the next token in the given string, sets a NULL * character to mark its end and returns a pointer to the token's first * character. The state between calls is saved in the "SplitPosition" field. * Calling the function with NULL as a parameter resets the internal state. */ char* StringSplit( char* s1 /*!< String to split into tokens. */ ) const; /// Internal position pointer for "StringSplit()". mutable char* SplitPosition; /** Return the identifier for the generated archive format (version). * This is the earliest CMTK version that can read this archive properly. */ static const char* GetTypedStreamIdent() { return "! TYPEDSTREAM 2.4\n"; } /// Debug flag. Self::DebugFlag m_DebugFlag; /// Output diagnostic message if debug flag is set. void DebugOutput( const char* format /*!< printf-style format string for the remaining variable number of function arguments.*/, ... ); }; //@} } // namespace cmtk //@} #endif // #ifndef __cmtkTypedstream_h_included_ cmtk-3.3.1/libs/IO/cmtkTypedStreamInput.cxx000066400000000000000000000465261276303427400206250ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2013 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 5047 $ // // $LastChangedDate: 2013-11-27 14:59:44 -0800 (Wed, 27 Nov 2013) $ // // $LastChangedBy: torsten_at_home $ // */ #include "cmtkTypedStreamInput.h" #include #include #include #include #include #ifdef HAVE_MALLOC_H # include #endif namespace cmtk { TypedStreamInput::TypedStreamInput ( const std::string& filename ) { this->Open( filename ); } TypedStreamInput::TypedStreamInput ( const std::string& dir, const std::string& archive ) { this->Open( dir, archive ); } TypedStreamInput ::~TypedStreamInput() { this->Close(); } void TypedStreamInput ::Open ( const std::string& dir, const std::string& archive ) { static char fname[PATH_MAX]; // If "dir" parameter is empty, use current directory instead. if ( dir != "" ) { if ( static_cast( snprintf( fname, sizeof( fname ), "%s%c%s", dir.c_str(), CMTK_PATH_SEPARATOR, archive.c_str() ) ) >= sizeof( fname ) ) { StdErr << "WARNING: length of path exceeds system PATH_MAX in TypedStreamInput::Open and will be truncated.\n"; } } else { if ( static_cast( snprintf( fname, sizeof( fname ), "%s", archive.c_str() ) ) >= sizeof( fname ) ) { StdErr << "WARNING: length of path exceeds system PATH_MAX in TypedStreamInput::Open and will be truncated.\n"; } } this->Open( fname ); } void TypedStreamInput ::Open ( const std::string& filename ) { this->m_Status = Self::ERROR_NONE; this->Close(); #ifdef _MSC_VER const char *modestr = "rb"; #else const char *modestr = "r"; #endif if ( ! ( File = fopen( filename.c_str(), modestr ) ) ) { const std::string gzName = filename + ".gz"; GzFile = gzopen( gzName.c_str(), modestr ); if ( ! GzFile ) { StdErr << "ERROR: could not open file \"" << filename << "\" with mode \"" << modestr << "\"\n"; this->m_Status = Self::ERROR_SYSTEM; return; } } if ( GzFile ) { if (! gzgets( GzFile, Buffer, sizeof( this->Buffer ) ) ) { this->m_Status = Self::ERROR_FORMAT; gzclose( GzFile ); return; } } else if (! fgets( Buffer, sizeof( this->Buffer) , File ) ) { this->m_Status = Self::ERROR_FORMAT; fclose( File ); File = NULL; return; } if (Buffer[0] != '!' && Buffer[0] != '#') { this->m_Status = Self::ERROR_FORMAT; if ( GzFile ) { gzclose( GzFile ); GzFile = NULL; } if ( File ) { fclose( File ); File = NULL; } return; } if (2 != sscanf( Buffer+1, " TYPEDSTREAM %4d.%4d", &this->m_ReleaseMajor, &this->m_ReleaseMinor)) { this->m_Status = Self::ERROR_FORMAT; if ( GzFile ) { gzclose( GzFile ); GzFile = NULL; } if ( File ) { fclose( File ); File = NULL; } } else { if ( (this->m_ReleaseMajor > CMTK_VERSION_MAJOR) || ( (this->m_ReleaseMajor == CMTK_VERSION_MAJOR) && (this->m_ReleaseMinor > CMTK_VERSION_MINOR)) ) { StdErr << "WARNING: input archive was written by newer version of CMTK (" << this->m_ReleaseMajor << "." << this->m_ReleaseMinor << " or higher) - proceed with caution.\n"; } } } void TypedStreamInput ::Close() { if ( File || GzFile ) { while ( ! LevelStack.empty() ) { LevelStack.pop(); } if ( GzFile ) { gzclose( GzFile ); GzFile = NULL; } if ( File ) { fclose( File ); File = NULL; } } this->m_Status = Self::ERROR_NONE; SplitPosition = NULL; } TypedStreamInput::Condition TypedStreamInput ::Begin() { if ( !File && !GzFile) { this->m_Status = Self::ERROR_INVALID; return Self::CONDITION_ERROR; } if ( GzFile ) { if ( -1 == gzseek( GzFile, LevelStack.top(), SEEK_SET ) ) { this->m_Status = Self::ERROR_SYSTEM; return Self::CONDITION_ERROR; } } else { if ( fseek( File, LevelStack.top(), SEEK_SET ) ) { this->m_Status = Self::ERROR_SYSTEM; return Self::CONDITION_ERROR; } } return Self::CONDITION_OK; } TypedStreamInput::Condition TypedStreamInput ::End() { if ( ! File && ! GzFile ) { this->m_Status = Self::ERROR_INVALID; return Self::CONDITION_ERROR; } if ( LevelStack.empty() ) { // end without begin this->m_Status = Self::ERROR_LEVEL; return Self::CONDITION_ERROR; } Self::Token token; int currentLevel = 1; while ( currentLevel && (Self::TOKEN_EOF != ( token = this->ReadLineToken() ) ) ) { if ( token == Self::TOKEN_BEGIN ) { this->DebugOutput( "Skipping section %s at level %d.", BufferKey, currentLevel ); ++currentLevel; } else if ( token == Self::TOKEN_END ) { this->DebugOutput( "Leaving section %d.", currentLevel ); --currentLevel; } } LevelStack.pop(); return Self::CONDITION_OK; } TypedStreamInput::Condition TypedStreamInput ::Seek ( const char* section, const bool forward ) { if ( ! File && ! GzFile ) { this->m_Status = Self::ERROR_INVALID; return Self::CONDITION_ERROR; } if ( ! section ) { this->m_Status = Self::ERROR_ARG; return Self::CONDITION_ERROR; } unsigned initialLevel = LevelStack.size(); if ( ! forward ) { if ( GzFile ) { if ( initialLevel ) { if ( -1 == gzseek( GzFile, LevelStack.top(), SEEK_SET ) ) { this->m_Status = Self::ERROR_SYSTEM; return Self::CONDITION_ERROR; } } else { if ( -1 == gzseek( GzFile, 0, SEEK_SET) ) { this->m_Status = Self::ERROR_SYSTEM; return Self::CONDITION_ERROR; } } } else if ( initialLevel ) { if ( fseek( File, LevelStack.top(), SEEK_SET ) ) { this->m_Status = Self::ERROR_SYSTEM; return Self::CONDITION_ERROR; } } else { if ( fseek( File, 0, SEEK_SET) ) { this->m_Status = Self::ERROR_SYSTEM; return Self::CONDITION_ERROR; } } } unsigned currentLevel = initialLevel; this->DebugOutput( "Seeking section %s from level %d.", section, initialLevel ); Self::Token token; while ( Self::TOKEN_EOF != ( token = this->ReadLineToken() ) ) { if ( token == Self::TOKEN_BEGIN ) { this->DebugOutput( "Enter section %s at level %d.", BufferKey, currentLevel ); if ( this->StringCmp( BufferKey, section ) == 0 ) { if ( currentLevel == LevelStack.size() ) { if ( GzFile ) LevelStack.push( gztell( GzFile ) ); else LevelStack.push( ftell( File ) ); return Self::CONDITION_OK; } if ( currentLevel == LevelStack.size()-1 ) { LevelStack.pop(); if ( GzFile ) LevelStack.push( gztell( GzFile ) ); else LevelStack.push( ftell( File ) ); return Self::CONDITION_OK; } } ++ currentLevel; } if ( token == Self::TOKEN_END ) { this->DebugOutput( "Leaving section %d.", currentLevel ); if ( ! currentLevel ) { this->m_Status = Self::ERROR_LEVEL; return Self::CONDITION_ERROR; } if ( currentLevel < initialLevel ) { this->m_Status = Self::ERROR_NONE; return Self::CONDITION_ERROR; } -- currentLevel; } } this->DebugOutput( "Section %s not found.", section ); this->m_Status = Self::ERROR_NONE; return Self::CONDITION_ERROR; } TypedStreamInput::Condition TypedStreamInput ::Rewind() { if ( ! File && ! GzFile ) { this->m_Status = Self::ERROR_INVALID; return Self::CONDITION_ERROR; } if ( !LevelStack.empty() ) LevelStack.pop(); if ( LevelStack.empty() ) { if ( GzFile ) { if ( -1 == gzseek( GzFile, 0, SEEK_SET ) ) { this->m_Status = Self::ERROR_SYSTEM; return Self::CONDITION_ERROR; } } else if ( -1 == fseek( File, 0, SEEK_SET ) ) { this->m_Status = Self::ERROR_SYSTEM; return Self::CONDITION_ERROR; } } else { if ( GzFile ) { if (-1 == gzseek( GzFile, LevelStack.top(), SEEK_SET)) { this->m_Status = Self::ERROR_SYSTEM; return Self::CONDITION_ERROR; } } else if (-1 == fseek( File, LevelStack.top(), SEEK_SET)) { this->m_Status = Self::ERROR_SYSTEM; return Self::CONDITION_ERROR; } } return Self::CONDITION_OK; } bool TypedStreamInput ::ReadBool( const char* key, const bool defaultValue, const bool forward ) { int value; if ( this->GenericReadArray( key, Self::TYPE_BOOL, &value, 1, forward ) != Self::CONDITION_OK ) if ( this->GenericReadArray( key, Self::TYPE_INT, &value, 1, forward ) != Self::CONDITION_OK ) return defaultValue; return (value != 0); } TypedStreamInput::Condition TypedStreamInput ::ReadBoolArray( const char* key, byte *const array, const int size, const bool forward ) { return this->GenericReadArray( key, Self::TYPE_BINARYBOOL, array, size, forward ); } int TypedStreamInput ::ReadInt( const char* key, const int defaultValue, const bool forward ) { int value = defaultValue; if ( this->GenericReadArray( key, Self::TYPE_INT, &value, 1, forward ) != Self::CONDITION_OK ) return defaultValue; return value; } TypedStreamInput::Condition TypedStreamInput ::ReadIntArray( const char* key, int *const array, const int size, const bool forward ) { return this->GenericReadArray( key, Self::TYPE_INT, array, size, forward ); } float TypedStreamInput ::ReadFloat( const char* key, const float defaultValue, const bool forward ) { float value = defaultValue; if ( this->GenericReadArray( key, Self::TYPE_FLOAT, &value, 1, forward ) != Self::CONDITION_OK ) return defaultValue; return value; } TypedStreamInput::Condition TypedStreamInput ::ReadFloatArray( const char* key, float *const array, const int size, const bool forward ) { return this->GenericReadArray( key, Self::TYPE_FLOAT, array, size, forward ); } double TypedStreamInput ::ReadDouble( const char* key, const double defaultValue, const bool forward ) { double value = defaultValue; if ( this->GenericReadArray( key, Self::TYPE_DOUBLE, &value, 1, forward ) != Self::CONDITION_OK ) return defaultValue; return value; } TypedStreamInput::Condition TypedStreamInput ::ReadDoubleArray( const char* key, double *const array, const int size, const bool forward ) { return this->GenericReadArray( key, Self::TYPE_DOUBLE, array, size, forward ); } char* TypedStreamInput ::ReadString( const char* key, const char *defaultValue, const bool forward ) { char *value; if ( this->GenericReadArray( key, Self::TYPE_STRING, &value, 1, forward ) != Self::CONDITION_OK ) { if ( defaultValue ) return strdup( defaultValue ); else return NULL; } return value; } std::string TypedStreamInput ::ReadStdString( const char* key, const std::string& defaultValue, const bool forward ) { char *value; if ( this->GenericReadArray( key, Self::TYPE_STRING, &value, 1, forward ) != Self::CONDITION_OK ) { return std::string( defaultValue ); } return std::string( value ); } TypedStreamInput::Condition TypedStreamInput ::GenericReadArray( const char * key, const int type, void *const array, const int arraySize, const bool forward ) { if (!array || arraySize < 1) { this->m_Status = Self::ERROR_ARG; return Self::CONDITION_ERROR; } unsigned currentLevel = LevelStack.size(); if ( ! forward ) { if ( GzFile ) { if ( currentLevel ) { if ( -1 == gzseek( GzFile, LevelStack.top(), SEEK_SET ) ) { this->m_Status = Self::ERROR_SYSTEM; return Self::CONDITION_ERROR; } } else { if ( -1 == gzseek( GzFile, 0, SEEK_SET ) ) { this->m_Status = Self::ERROR_SYSTEM; return Self::CONDITION_ERROR; } } } else { if ( currentLevel ) { if ( fseek( File, LevelStack.top(), SEEK_SET ) ) { this->m_Status = Self::ERROR_SYSTEM; return Self::CONDITION_ERROR; } } else { if ( fseek( File, 0, SEEK_SET ) ) { this->m_Status = Self::ERROR_SYSTEM; return Self::CONDITION_ERROR; } } } } int line; char *buffer; while ( Self::TOKEN_EOF != ( line = this->ReadLineToken() ) ) { if ( line == Self::TOKEN_KEY) { if ( (currentLevel == LevelStack.size()) && (this->StringCmp( BufferKey, key )) == 0 ) { int i = 0; switch (type) { case Self::TYPE_INT: { int *arrayInt = static_cast( array ); do { buffer = strtok( BufferValue, "\t\n " ); while ( buffer ) { if ( *buffer == '\"' ) { this->m_Status = Self::ERROR_TYPE; return Self::CONDITION_ERROR; } if ( i >= arraySize ) return Self::CONDITION_OK; arrayInt[i++] = atoi(buffer); buffer = strtok( NULL, "\t\n " ); } } while ( i < arraySize && Self::TOKEN_VALUE == this->ReadLineToken() ); if ( i < arraySize) { this->m_Status = Self::ERROR_ARG; return Self::CONDITION_ERROR; } break; } case Self::TYPE_BOOL: { int *arrayInt = static_cast( array ); if (arraySize != 1) { this->m_Status = Self::ERROR_ARG; return Self::CONDITION_ERROR; } do { buffer = strtok( BufferValue, "\t\n " ); while ( buffer ) { if ( i >= arraySize ) return Self::CONDITION_OK; if ((buffer[0] == 'y' || buffer[0] == 'Y') && (buffer[1] == 'e' || buffer[1] == 'E') && (buffer[2] == 's' || buffer[2] == 'S')) { arrayInt[i++] = 1; } else { arrayInt[i++] = 0; } buffer = strtok( NULL, "\t\n " ); } } while ( i < arraySize && Self::TOKEN_VALUE == this->ReadLineToken() ); if ( i < arraySize ) { this->m_Status = Self::ERROR_ARG; return Self::CONDITION_ERROR; } break; } case Self::TYPE_BINARYBOOL: { byte *arrayInt = static_cast( array ); do { buffer = strtok( BufferValue, "\t\n " ); if ( buffer ) { int idx = 0; while ( (buffer[idx]=='0') || (buffer[idx]=='1') ) { if ( i >= arraySize ) return Self::CONDITION_OK; if ( buffer[idx] == '0' ) arrayInt[i/8] &= ~(1<<(i%8)); else arrayInt[i/8] |= (1<<(i%8)); ++i; ++idx; } } } while ( buffer && i < arraySize && Self::TOKEN_VALUE == this->ReadLineToken() ); if ( i < arraySize ) { this->m_Status = Self::ERROR_ARG; return Self::CONDITION_ERROR; } break; } case Self::TYPE_FLOAT: { float *arrayFloat = static_cast( array ); do { buffer = strtok( BufferValue, "\t\n " ); while ( buffer ) { if ( *buffer == '\"' ) { this->m_Status = Self::ERROR_TYPE; return Self::CONDITION_ERROR; } if ( i >= arraySize ) return Self::CONDITION_OK; arrayFloat[i++] = static_cast( atof( buffer ) ); buffer = strtok( NULL, "\t\n " ); } } while ( i < arraySize && Self::TOKEN_VALUE == this->ReadLineToken() ); if ( i < arraySize ) { this->m_Status = Self::ERROR_ARG; return Self::CONDITION_ERROR; } break; } case Self::TYPE_DOUBLE: { double *arrayDouble = static_cast( array ); do { buffer = strtok( BufferValue, "\t\n " ); while ( buffer ) { if ( *buffer == '\"' ) { this->m_Status = Self::ERROR_TYPE; return Self::CONDITION_ERROR; } if ( i >= arraySize ) return Self::CONDITION_OK; arrayDouble[i++] = atof( buffer ); buffer = strtok( NULL, "\t\n " ); } } while ( i < arraySize && Self::TOKEN_VALUE == this->ReadLineToken() ); if ( i < arraySize ) { this->m_Status = Self::ERROR_ARG; return Self::CONDITION_ERROR; } break; } case Self::TYPE_STRING: { char **arrayString = static_cast( array ); do { buffer = this->StringSplit( BufferValue ); while ( buffer ) { if ( i >= arraySize ) return Self::CONDITION_OK; if ( *buffer != '\"' ) { this->m_Status = Self::ERROR_TYPE; return Self::CONDITION_ERROR; } char *b; if (!(b = (char *)malloc(1+strlen(buffer)))) { for (--i; i >= 0; i--) free( arrayString[i] ); this->m_Status = Self::ERROR_SYSTEM; return Self::CONDITION_ERROR; } arrayString[i++] = b; buffer++; for (; *buffer && *buffer != '\n'; buffer++) { if (*buffer == '\"') continue; if (*buffer == '\\') { buffer++; if (*buffer == 'n') { *b++ = '\n'; continue; } if (*buffer == 't') { *b++ = '\t'; continue; } if (*buffer == '\0') break; } *b++ = *buffer; } *b = '\0'; buffer = this->StringSplit( NULL ); } } while ( i < arraySize && Self::TOKEN_VALUE == this->ReadLineToken() ); if ( i < arraySize ) { for (--i; i >= 0; i--) free( arrayString[i] ); this->m_Status = Self::ERROR_ARG; return Self::CONDITION_ERROR; } break; } } return Self::CONDITION_OK; } continue; } if ( line == Self::TOKEN_BEGIN ) { if ( GzFile ) LevelStack.push( gztell( GzFile ) ); else LevelStack.push( ftell( File ) ); continue; } if ( line == Self::TOKEN_END ) { if ( currentLevel == LevelStack.size() ) { this->m_Status = Self::ERROR_NONE; return Self::CONDITION_ERROR; } LevelStack.pop(); continue; } } return Self::CONDITION_ERROR; } TypedStreamInput::Token TypedStreamInput ::ReadLineToken() { if ( GzFile ) { if (! gzgets( GzFile, Buffer, sizeof( this->Buffer ) ) ) return Self::TOKEN_EOF; } else if (! fgets( Buffer, sizeof( this->Buffer ), File ) ) return Self::TOKEN_EOF; char* buffer; for ( buffer = Buffer; *buffer; buffer++) if (*buffer != ' ' && *buffer != '\t') break; if (*buffer == '\n' || *buffer == '!' || *buffer == '#') return Self::TOKEN_COMMENT; if (*buffer == '}') return Self::TOKEN_END; if (*buffer == '\"' || *buffer == '-' || *buffer == '.' || (*buffer >= '0' && *buffer <= '9')) { BufferValue = buffer; return Self::TOKEN_VALUE; } if (*buffer == '_' || (*buffer >= 'a' && *buffer <= 'z') || (*buffer >= 'A' && *buffer <= 'Z')) { BufferKey = buffer; for (; *buffer; buffer++) if (*buffer == ' ' || *buffer == '\t') break; for (; *buffer; buffer++) if (*buffer != ' ' && *buffer != '\t') break; BufferValue = buffer; if (*buffer == '{') return Self::TOKEN_BEGIN; return Self::TOKEN_KEY; } return Self::TOKEN_COMMENT; } } // namespace cmtk cmtk-3.3.1/libs/IO/cmtkTypedStreamInput.h000066400000000000000000000341631276303427400202440ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2013 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 5037 $ // // $LastChangedDate: 2013-11-25 16:52:56 -0800 (Mon, 25 Nov 2013) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkTypedStreamInput_h_included_ #define __cmtkTypedStreamInput_h_included_ #include #include #include #include #include #include #ifndef NULL #define NULL 0 #endif #include namespace cmtk { /** \addtogroup IO */ //@{ //@{ /** Class for reading "typedstream" archives. */ class TypedStreamInput : public TypedStream { public: /// This class. typedef TypedStreamInput Self; /// Base class. typedef TypedStream Superclass; /// Default constructor. TypedStreamInput() : TypedStream() {} /** Open constructor. *\param filename Name of the archive to open. *\param mode Access mode, ie. read-only, write-only, etc. */ TypedStreamInput( const std::string& filename ); /** Open constructor for separate path and archive names. *\param dir Directory to open archive in. *\param archive Name of the archive to open. */ TypedStreamInput( const std::string& dir, const std::string& archive ); /** Destructor. * Close() is called to close a possibly open archive. */ virtual ~TypedStreamInput(); /** Open another archive without constructing a new object. */ void Open( const std::string& filename ); /** Open another archive in explicit directory. */ void Open( const std::string& dir, const std::string& archive ); /** Close an open archive. */ void Close(); /** Move to a particular section in the open archive. * The named section is found if it is either inside the currently open * section or after it on the same level. * * This function may only be called for read-only archive, ie. for such that * were opened in MODE_READONLY mode. For writeable archive, it * will return an error. */ Self::Condition Seek( const char* section /*!< Name of the section whose beginning stream pointer is moved to. */, const bool forward = false /*!< Flag: read forward from current position in stream (if false, reset to current section start) */); /** Rewind archive. * This function resets filepointer of an open archive to the beginning of * the current section. */ Self::Condition Rewind(); /** Move to beginning of section. * This function will set the file read pointer to the beginning of the current section *\return Error condition. */ Self::Condition Begin(); /** Close current section. * In the open archive, this function will close the last section and decrease the nesting level by one. *\return Error condition. */ Self::Condition End(); /** Read boolean value from an open archive. * This function recognizes both yes/no and 0/1 entries in the archive. * First, "yes" and "no" is tried, if that doesn't work the function reads * an integer value from the same key. *\return If reading was succesful, the value from the archive is returned. * Otherwise the value given as the "defaultValue" parameter is returned. */ bool ReadBool( const char* key /*!< The name of the boolean entry in the archive.*/, const bool defaultValue = false /*!< Default value returned if no valid entry can be read. This parameter can be omitted and defaults to false.*/, const bool forward = false /*!< Flag: read forward from current position in stream (if false, reset to current section start) */); /** Read array of boole values from an open archive. * For a description of parameters and return value see ReadBool. */ Self::Condition ReadBoolArray( const char* key /*!< The name of the array in the archive.*/, byte *const array /*!< Pointer to allocated storage for the array to be read into.*/, const int size /*!< Size of the array.*/, const bool forward = false /*!< Flag: read forward from current position in stream (if false, reset to current section start) */); /** Read integer value from an open archive. * For a description of parameters and return value see ReadBool. */ int ReadInt( const char* key /*!< The name of the field in the archive.*/, const int defaultValue = 0 /*!< Default value returned if no valid entry can be read. This parameter can be omitted and defaults to zero.*/, const bool forward = false /*!< Flag: read forward from current position in stream (if false, reset to current section start) */); /** Read array of integer values from an open archive. * For a description of parameters and return value see ReadBool. */ Self::Condition ReadIntArray( const char* key /*!< The name of the array in the archive.*/, int *const array /*!< Pointer to allocated storage for the array to be read into.*/, const int size /*!< Size of the array.*/, const bool forward = false /*!< Flag: read forward from current position in stream (if false, reset to current section start) */); /** Read single-precision value from an open archive. * For a description of parameters and return value see ReadBool. */ float ReadFloat( const char* key /*!< The name of the field in the archive.*/, const float defaultValue = 0 /*!< Default value returned if no valid entry can be read. This parameter can be omitted and defaults to zero.*/, const bool forward = false /*!< Flag: read forward from current position in stream (if false, reset to current section start) */); /** Read array of single-precision values from an open archive. * For a description of parameters and return value see ReadBool. */ Self::Condition ReadFloatArray( const char* key /*!< The name of the array in the archive.*/, float *const array /*!< Pointer to allocated storage for the array to be read into.*/, const int size /*!< Size of the array.*/, const bool forward = false /*!< Flag: read forward from current position in stream (if false, reset to current section start) */); /** Read double-precision value from an open archive. * For a description of parameters and return value see ReadBool. */ double ReadDouble( const char* key /*!< The name of the field in the archive.*/, const double defaultValue = 0 /*!< Default value returned if the field is not found in the archive. */, const bool forward = false /*!< Flag: read forward from current position in stream (if false, reset to current section start) */); /** Read array of double-precision values from an open archive. * For a description of parameters and return value see ReadBool. */ Self::Condition ReadDoubleArray( const char* key /*!< The name of the array in the archive.*/, double *const array /*!< Pointer to allocated storage for the array to be read into.*/, const int size /*!< Size of the array.*/, const bool forward = false /*!< Flag: read forward from current position in stream (if false, reset to current section start) */); /** Read double- or single precision value from an open archive. * Whether double- or single-precision data is read depends on the definition * of the CMTK_COORDINATES_DOUBLE preprocessor symbol. This function is thus * guaranteed to always match the Types::Coordinate type. *\see CMTK_COORDINATES_DOUBLE *\see Types::Coordinate */ Types::Coordinate ReadCoordinate( const char* key /*!< The name of the field in the archive.*/, const Types::Coordinate defaultValue = 0 /*!< Default value if the field is not found.*/, const bool forward = false /*!< Flag: read forward from current position in stream (if false, reset to current section start) */) { #ifdef CMTK_COORDINATES_DOUBLE return this->ReadDouble( key, defaultValue, forward ); #else return this->ReadFloat( key, defaultValue, forward ); #endif } /** Read double- or single precision value from an open archive. * Whether double- or single-precision data is read depends on the definition * of the CMTK_DATA_DOUBLE preprocessor symbol. This function is thus * guaranteed to always match the Types::DataItem type. *\see CMTK_DATA_DOUBLE *\see Types::DataItem */ Types::DataItem ReadItem( const char* key /*!< The name of the field in the archive.*/, const Types::DataItem defaultValue = 0 /*!< Default value returned if the field is not found in the archive. */, const bool forward = false /*!< Flag: read forward from current position in stream (if false, reset to current section start) */) { #ifdef CMTK_DATA_DOUBLE return this->ReadDouble( key, defaultValue, forward ); #else return this->ReadFloat( key, defaultValue, forward ); #endif } /** Read array of double- or single precision values from an open archive. * Whether double- or single-precision data is read depends on the definition * of the CMTK_COORDINATES_DOUBLE preprocessor symbol. This function is thus * guaranteed to always match the Types::Coordinate type. *\see CMTK_COORDINATES_DOUBLE *\see Types::Coordinate */ Self::Condition ReadCoordinateArray( const char* key /*!< The name of the array in the archive.*/, Types::Coordinate *const array /*!< Pointer to allocated storage for the array to be read into.*/, const int size /*!< Size of the array.*/, const bool forward = false /*!< Flag: read forward from current position in stream (if false, reset to current section start) */) { #ifdef CMTK_COORDINATES_DOUBLE return this->ReadDoubleArray( key, array, size, forward ); #else return this->ReadFloatArray( key, array, size, forward ); #endif } /** Read array of double- or single precision values from an open archive. * Whether double- or single-precision data is read depends on the definition * of the CMTK_DATA_DOUBLE preprocessor symbol. This function is thus * guaranteed to always match the Types::DataItem type. *\see CMTK_DATA_DOUBLE *\see Types::DataItem */ Self::Condition ReadItemArray( const char* key /*!< The name of the array in the archive.*/, Types::DataItem *const array /*!< Pointer to allocated storage for the array to be read into.*/, const int size /*!< Size of the array.*/, const bool forward = false /*!< Flag: read forward from current position in stream (if false, reset to current section start) */) { #ifdef CMTK_DATA_DOUBLE return this->ReadDoubleArray( key, array, size, forward ); #else return this->ReadFloatArray( key, array, size, forward ); #endif } /** Read null-terminated string from an open archive. * The string returned is newly allocated by this function. So unless NULL * is returner, the string must later be freed by the caller in order to * avoid memory leaks. *\return A pointer to a newly allocated string is returned if reading was * succesful. If no valid entry could be read from the archive, a copy of * the string given as "defaultValue" parameter is returned. If that * parameter was NULL, the same value is also returned. */ char* ReadString( const char* key /*!< The name of the field in the archive.*/, const char* defaultValue = NULL /*!< Default value returned if the field is not found in the archive. */, const bool forward = false /*!< Flag: read forward from current position in stream (if false, reset to current section start) */); /** Read STL string from an open archive. *\return The string that was read. If no valid entry could be read from the archive, a copy of * the string given as "defaultValue" parameter is returned. */ std::string ReadStdString( const char* key /*!< The name of the field in the archive.*/, const std::string& defaultValue = "" /*!< Default value returned if the field is not found in the archive. */, const bool forward = false /*!< Flag: read forward from current position in stream (if false, reset to current section start) */); /// Get open stream major version. int GetReleaseMajor() const { if ( this->m_Status == Self::ERROR_NONE ) { return this->m_ReleaseMajor; } else { return -1; } } /// Get open stream minor version. int GetReleaseMinor() const { if ( this->m_Status == Self::ERROR_NONE ) { return this->m_ReleaseMinor; } else { return -1; } } private: /** Utility function: Read an array of arbitrary type. * This function is called by all reader functions. Internally, a "switch" * statement selects the correct code for the effective data type to be read. * Besides, common functions such as the skipping of inserted sections are * implemented as shared code for all data types. */ Self::Condition GenericReadArray( const char* key /*!< Field key (name)*/, const int type /*!< Array data type ID */, void *const array /*!< Target storage space for read data */, const int arraySize /*!< Number of array elements */, const bool forward = false /*!< Flag: read forward from current position in stream (if false, reset to current section start) */ ); /// Read the next archive line to the buffer. Self::Token ReadLineToken(); /// Release major version. int m_ReleaseMajor; /// Release minor version. int m_ReleaseMinor; }; //@} } // namespace cmtk //@} #endif // #ifndef __cmtkTypedStreamInput_h_included_ cmtk-3.3.1/libs/IO/cmtkTypedStreamOutput.cxx000066400000000000000000000407661276303427400210260ustar00rootroot00000000000000/* // // Copyright 2016 Google, Inc. // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2013 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 5398 $ // // $LastChangedDate: 2016-01-14 18:29:18 -0800 (Thu, 14 Jan 2016) $ // // $LastChangedBy: torstenrohlfing $ // */ #include "cmtkTypedStreamOutput.h" #include #include #include #include #include #ifdef HAVE_FCNTL_H # include #endif #ifdef HAVE_SYS_TIME_H # include #endif #ifdef HAVE_SYS_STAT_H # include #endif namespace cmtk { /** \addtogroup IO */ //@{ TypedStreamOutput::TypedStreamOutput ( const std::string& filename, const Self::Mode mode ) { this->Open( filename, mode ); } TypedStreamOutput::TypedStreamOutput ( const std::string& dir, const std::string& archive, const Self::Mode mode ) { this->Open( dir, archive, mode ); } TypedStreamOutput ::~TypedStreamOutput() { this->Close(); } void TypedStreamOutput ::Open ( const std::string& dir, const std::string& archive, const Self::Mode mode ) { static char fname[PATH_MAX]; // If "dir" parameter is empty, use current directory instead. if ( dir != "" ) { if ( static_cast( snprintf( fname, sizeof( fname ), "%s%c%s", dir.c_str(), CMTK_PATH_SEPARATOR, archive.c_str() ) ) >= sizeof( fname ) ) { StdErr << "WARNING: length of path exceeds system PATH_MAX in TypedStreamOutput::Open and will be truncated.\n"; } } else { if ( static_cast( snprintf( fname, sizeof( fname ), "%s", archive.c_str() ) ) >= sizeof( fname ) ) { StdErr << "WARNING: length of path exceeds system PATH_MAX in TypedStreamOutput::Open and will be truncated.\n"; } } #ifndef _MSC_VER // check if this is an existing directory; if it is, set access/modification time. // this is useful for dependency checking using file system timestamps. int fd = open( dir.c_str(), O_RDONLY ); if ( fd != -1 ) { struct stat buf; if ( !fstat( fd, &buf ) && S_ISDIR( buf.st_mode ) ) { futimes( fd, NULL ); } close( fd ); } #endif this->Open( fname, mode ); } void TypedStreamOutput ::Open ( const std::string& filename, const Self::Mode mode ) { this->m_Status = Self::ERROR_NONE; this->Close(); if ( mode != Self::MODE_WRITE && mode != Self::MODE_WRITE_ZLIB && mode != Self::MODE_APPEND ) { this->m_Status = Self::ERROR_ARG; return; } if ( mode == Self::MODE_WRITE || mode == Self::MODE_WRITE_ZLIB ) { if ( FileUtils::RecursiveMkPrefixDir( filename ) ) { StdErr << "ERROR: could not recursively create path for \"" << filename << "\"\n"; this->m_Status = Self::ERROR_SYSTEM; return; } } const char *modestr = ""; switch ( mode ) { #ifdef _MSC_VER case Self::MODE_WRITE: modestr = "wb"; break; case Self::MODE_WRITE_ZLIB: modestr = "wb"; break; case Self::MODE_APPEND: modestr = "ab"; break; #else case Self::MODE_WRITE: modestr = "w"; break; case Self::MODE_WRITE_ZLIB: modestr = "w"; break; case Self::MODE_APPEND: modestr = "a"; break; #endif default: modestr = ""; break; // cannot really get here due to earlier if's, but gcc doesn't understand that } if ( mode == Self::MODE_WRITE_ZLIB ) { const std::string gzName = filename + ".gz"; GzFile = gzopen( gzName.c_str(), modestr ); if ( ! GzFile ) { StdErr << "ERROR: could not open gz file \"" << gzName << "\" with mode \"" << modestr << "\"\n"; this->m_Status = Self::ERROR_SYSTEM; return; } } else { File = fopen( filename.c_str(), modestr ); if ( ! File ) { StdErr << "ERROR: could not open file \"" << filename << "\" with mode \"" << modestr << "\"\n"; this->m_Status = Self::ERROR_SYSTEM; return; } } this->m_Mode = mode; switch ( this->m_Mode ) { default: break; case Self::MODE_WRITE: case Self::MODE_WRITE_ZLIB: if ( GzFile ) gzprintf( GzFile, "%s\n", Self::GetTypedStreamIdent() ); else fprintf( File, "%s\n", Self::GetTypedStreamIdent() ); break; case Self::MODE_APPEND: if ( GzFile ) { if ( 0 == gztell( this->GzFile) ) gzprintf( GzFile, "%s\n", Self::GetTypedStreamIdent() ); } else if ( 0 == ftell( File) ) fprintf( File, "%s\n", Self::GetTypedStreamIdent() ); break; } } void TypedStreamOutput ::Close() { if ( File || GzFile ) { while ( ! LevelStack.empty() ) { LevelStack.pop(); int streamLevel = LevelStack.size(); if ( GzFile ) { for ( int level = 0; level < streamLevel; level++) gzputs( GzFile, "\t" ); gzputs( GzFile, "}\n" ); } else { for ( int level = 0; level < streamLevel; level++) fputs( "\t", File ); fputs( "}\n", File ); } } } if ( this->GzFile ) { gzclose( this->GzFile ); this->GzFile = NULL; } if ( this->File ) { fclose( this->File ); this->File = NULL; } this->m_Status = Self::ERROR_NONE; SplitPosition = NULL; } TypedStreamOutput::Condition TypedStreamOutput ::Begin ( const std::string& section ) { if ( !File && !GzFile) { this->m_Status = Self::ERROR_INVALID; return Self::CONDITION_ERROR; } int streamLevel = LevelStack.size(); if ( GzFile ) { for ( int level = 0; level < streamLevel; level++) gzputs( GzFile, "\t" ); gzprintf( GzFile, "%s {\n", section.c_str() ); } else { for ( int level = 0; level < streamLevel; level++) fputs( "\t", File ); fprintf( File, "%s {\n", section.c_str() ); } if ( GzFile ) LevelStack.push( gztell( GzFile ) ); else LevelStack.push( ftell( File ) ); return Self::CONDITION_OK; } TypedStreamOutput::Condition TypedStreamOutput ::End ( const bool flush ) { if ( ! File && ! GzFile ) { this->m_Status = Self::ERROR_INVALID; return Self::CONDITION_ERROR; } int streamLevel = LevelStack.size(); if ( streamLevel == 0 ) { // end without begin this->m_Status = Self::ERROR_LEVEL; return Self::CONDITION_ERROR; } LevelStack.pop(); if ( GzFile ) { for ( int level = 0; level < streamLevel-1; level++ ) gzputs( GzFile, "\t" ); gzputs( GzFile, "}\n" ); } else { for ( int level = 0; level < streamLevel-1; level++ ) fputs( "\t", File ); fputs( "}\n", File ); } if ( flush ) { fflush( File ); } return Self::CONDITION_OK; } TypedStreamOutput::Condition TypedStreamOutput ::WriteBool( const char* key, const bool value ) { int currentLevel = LevelStack.size(); if ( GzFile ) { for ( int level = 0; level < currentLevel; level++ ) gzputs( GzFile, "\t" ); gzprintf( GzFile, "%s %s\n", key, (value) ? "yes" : "no"); } else { for ( int level = 0; level < currentLevel; level++ ) fputs( "\t", File ); fprintf( File, "%s %s\n", key, (value) ? "yes" : "no"); } return Self::CONDITION_OK; } TypedStreamOutput::Condition TypedStreamOutput ::WriteInt( const char* key, const int value ) { int currentLevel = LevelStack.size(); if ( GzFile ) { for ( int level = 0; level < currentLevel; level++ ) gzputs( GzFile, "\t" ); gzprintf( GzFile, "%s %d\n", key, value ); } else { for ( int level = 0; level < currentLevel; level++ ) fputs( "\t", File ); fprintf( File, "%s %d\n", key, value ); } return Self::CONDITION_OK; } TypedStreamOutput::Condition TypedStreamOutput ::WriteFloat( const char* key, const float value ) { int currentLevel = LevelStack.size(); if ( GzFile ) { for ( int level = 0; level < currentLevel; level++ ) gzputs( GzFile, "\t" ); gzprintf( GzFile, "%s %.*f\n", key, PrecisionFloat, value ); } else { for ( int level = 0; level < currentLevel; level++ ) fputs( "\t", File ); fprintf( File, "%s %.*f\n", key, PrecisionFloat, value ); } return Self::CONDITION_OK; } TypedStreamOutput::Condition TypedStreamOutput ::WriteDouble( const char* key, const double value ) { int currentLevel = LevelStack.size(); if ( GzFile ) { for ( int level = 0; level < currentLevel; level++ ) gzputs( GzFile, "\t" ); gzprintf( this->GzFile, "%s %.*f\n", key, PrecisionDouble, value ); } else { for ( int level = 0; level < currentLevel; level++ ) fputs( "\t", File ); fprintf( File, "%s %.*f\n", key, PrecisionDouble, value ); } return Self::CONDITION_OK; } TypedStreamOutput::Condition TypedStreamOutput ::WriteString( const char* key, const std::string& value ) { return this->WriteString( key, value.c_str() ); } TypedStreamOutput::Condition TypedStreamOutput ::WriteString( const char* key, const char* value ) { char *buffer = Buffer; const char *strValue = (value) ? value : ""; while (*strValue) { if (*strValue == '\\') { *buffer++ = '\\'; *buffer++ = *strValue++; continue; } if (*strValue == '\"') { *buffer++ = '\\'; *buffer++ = *strValue++; continue; } if (*strValue == '\n') { *buffer++ = '\\'; *buffer++ = 'n'; strValue++; continue; } *buffer++ = *strValue++; } *buffer++ = '\0'; int currentLevel = LevelStack.size(); if ( GzFile ) { for ( int level = 0; level < currentLevel; level++) gzputs( GzFile, "\t" ); gzprintf( GzFile, "%s \"%s\"\n", key, Buffer); } else { for ( int level = 0; level < currentLevel; level++) fputs( "\t", File ); fprintf( File, "%s \"%s\"\n", key, Buffer); } return Self::CONDITION_OK; } TypedStreamOutput::Condition TypedStreamOutput ::WriteIntArray( const char* key, const int* array, const int size, const int valuesPerLine ) { if ( !array || size < 1) { this->m_Status = Self::ERROR_ARG; return Self::CONDITION_ERROR; } int currentLevel = LevelStack.size(); if ( GzFile ) { for ( int level = 0; level < currentLevel; level++) gzputs( GzFile, "\t" ); gzprintf( this->GzFile, "%s ", key ); for ( int i = 0; i < size; i++) { if (i && (i % valuesPerLine) == 0) { gzprintf( GzFile, "\n\t"); for ( int level = 0; level < currentLevel; level++ ) gzputs( GzFile, "\t" ); } gzprintf( GzFile, "%d ", array[i] ); } gzputs( GzFile, "\n" ); } else { for ( int level = 0; level < currentLevel; level++) fputs( "\t", File ); fprintf( File, "%s ", key ); for ( int i = 0; i < size; i++) { if (i && (i % valuesPerLine) == 0) { fprintf( File, "\n\t"); for ( int level = 0; level < currentLevel; level++ ) fputs( "\t", File ); } fprintf( File, "%d ", array[i] ); } fputs( "\n", File); } return Self::CONDITION_OK; } TypedStreamOutput::Condition TypedStreamOutput ::WriteIntArray( const char* key, const long long int* array, const int size, const int valuesPerLine ) { if ( !array || size < 1) { this->m_Status = Self::ERROR_ARG; return Self::CONDITION_ERROR; } int currentLevel = LevelStack.size(); if ( GzFile ) { for ( int level = 0; level < currentLevel; level++) gzputs( GzFile, "\t" ); gzprintf( this->GzFile, "%s ", key ); for ( int i = 0; i < size; i++) { if (i && (i % valuesPerLine) == 0) { gzprintf( GzFile, "\n\t"); for ( int level = 0; level < currentLevel; level++ ) gzputs( GzFile, "\t" ); } gzprintf( GzFile, "%ld ", array[i] ); } gzputs( GzFile, "\n" ); } else { for ( int level = 0; level < currentLevel; level++) fputs( "\t", File ); fprintf( File, "%s ", key ); for ( int i = 0; i < size; i++) { if (i && (i % valuesPerLine) == 0) { fprintf( File, "\n\t"); for ( int level = 0; level < currentLevel; level++ ) fputs( "\t", File ); } fprintf( File, "%ld ", array[i] ); } fputs( "\n", File); } return Self::CONDITION_OK; } TypedStreamOutput::Condition TypedStreamOutput ::WriteBoolArray( const char* key, const byte* array, const int size, const int valuesPerLine ) { if ( !array || size < 1) { this->m_Status = Self::ERROR_ARG; return Self::CONDITION_ERROR; } int currentLevel = LevelStack.size(); if ( GzFile ) { for ( int level = 0; level < currentLevel; level++) gzputs( GzFile, "\t" ); gzprintf( GzFile, "%s ", key ); for ( int i = 0; i < size; i++) { if (i && (i % valuesPerLine) == 0) { gzprintf( GzFile, "\n\t"); for ( int level = 0; level < currentLevel; level++ ) gzputs( GzFile, "\t" ); } gzprintf( GzFile, "%d", (array[i/8]>>(i%8))&1 ); } gzputs( GzFile, "\n" ); } else { for ( int level = 0; level < currentLevel; level++) fputs( "\t", File ); fprintf( File, "%s ", key ); for ( int i = 0; i < size; i++) { if (i && (i % valuesPerLine) == 0) { fprintf( File, "\n\t"); for ( int level = 0; level < currentLevel; level++ ) fputs( "\t", File ); } fprintf( File, "%d", (array[i/8]>>(i%8))&1 ); } fputs( "\n", File ); } return Self::CONDITION_OK; } TypedStreamOutput::Condition TypedStreamOutput ::WriteFloatArray( const char* key, const float* array, const int size, const int valuesPerLine ) { if ( !array || size < 1) { this->m_Status = Self::ERROR_ARG; return Self::CONDITION_ERROR; } int currentLevel = LevelStack.size(); if ( GzFile ) { for ( int level = 0; level < currentLevel; level++) gzputs( GzFile, "\t" ); gzprintf( GzFile, "%s ", key ); for ( int i = 0; i < size; i++ ) { if (i && (i % valuesPerLine) == 0) { gzprintf( GzFile, "\n\t"); for ( int level = 0; level < currentLevel; level++ ) gzputs( GzFile, "\t" ); } gzprintf( GzFile, "%.*g ", PrecisionFloat, array[i] ); } gzprintf( GzFile, "\n" ); } else { for ( int level = 0; level < currentLevel; level++) fputs( "\t", File ); fprintf( File, "%s ", key ); for ( int i = 0; i < size; i++ ) { if (i && (i % valuesPerLine) == 0) { fprintf( File, "\n\t"); for ( int level = 0; level < currentLevel; level++ ) fputs( "\t", File ); } fprintf( File, "%.*g ", PrecisionFloat, array[i] ); } fprintf( File, "\n" ); } return Self::CONDITION_OK; } TypedStreamOutput::Condition TypedStreamOutput ::WriteDoubleArray( const char* key, const double* array, const int size, const int valuesPerLine ) { if ( !array || size < 1) { this->m_Status = Self::ERROR_ARG; return Self::CONDITION_ERROR; } int currentLevel = LevelStack.size(); if ( GzFile ) { for ( int level = 0; level < currentLevel; level++) gzputs( GzFile, "\t" ); gzprintf( GzFile, "%s ", key ); for ( int i = 0; i < size; i++ ) { if (i && (i % valuesPerLine) == 0) { gzprintf( GzFile, "\n\t"); for ( int level = 0; level < currentLevel; level++ ) gzputs( GzFile, "\t" ); } gzprintf( GzFile, "%.*g ", PrecisionDouble, array[i] ); } gzprintf( GzFile, "\n" ); } else { for ( int level = 0; level < currentLevel; level++) fputs( "\t", File ); fprintf( File, "%s ", key ); for ( int i = 0; i < size; i++ ) { if (i && (i % valuesPerLine) == 0) { fprintf( File, "\n\t"); for ( int level = 0; level < currentLevel; level++ ) fputs( "\t", File ); } fprintf( File, "%.*g ", PrecisionDouble, array[i] ); } fprintf( File, "\n" ); } return Self::CONDITION_OK; } } // namespace cmtk cmtk-3.3.1/libs/IO/cmtkTypedStreamOutput.h000066400000000000000000000254731276303427400204510ustar00rootroot00000000000000/* // // Copyright 2016 Google, Inc. // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2013 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 5398 $ // // $LastChangedDate: 2016-01-14 18:29:18 -0800 (Thu, 14 Jan 2016) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkTypedStreamOutput_h_included_ #define __cmtkTypedStreamOutput_h_included_ #include #include #include #include #include #include #ifndef NULL #define NULL 0 #endif #include namespace cmtk { /** \addtogroup IO */ //@{ /** Class for writing "typedstream" archives. */ class TypedStreamOutput : public TypedStream { public: /// This class. typedef TypedStreamOutput Self; /// Base class. typedef TypedStream Superclass; /// Access modes for archives. typedef enum { /// Currently unset. MODE_UNSET, /// Write-only access. MODE_WRITE, /// Write-only access piped through zlib/gzip compression. MODE_WRITE_ZLIB, /// Open existing archive and append to it. MODE_APPEND } Mode; /// Default constructor. TypedStreamOutput() : TypedStream(), m_Mode( MODE_UNSET ) {} /** Open constructor. *\param filename Name of the archive to open. *\param mode Access mode, ie. read-only, write-only, etc. */ TypedStreamOutput( const std::string& filename, const Self::Mode mode ); /** Open constructor for separate path and archive names. *\param dir Directory to open archive in. *\param archive Name of the archive to open. *\param mode Access mode, ie. read-only, write-only, etc. */ TypedStreamOutput( const std::string& dir, const std::string& archive, const Self::Mode mode ); /** Destructor. * Close() is called to close a possibly open archive. */ virtual ~TypedStreamOutput(); /** Open another archive without constructing a new object. */ void Open( const std::string& filename, const Self::Mode mode ); /** Open another archive in explicit directory. */ void Open( const std::string& dir, const std::string& archive, const Self::Mode mode ); /** Close an open archive. */ void Close(); /** Begin a section. * This function will start a new section and increase the indentation level by one. *\param section Name of the new section. *\return Error condition. */ Self::Condition Begin( const std::string& section ); /** End a section. * In the open archive, this function will close the last section and * decrease the nesting level by one. *\param flush If this flag is set, the output file buffer will be flushed * after closing the section. *\return Error condition. */ Self::Condition End( const bool flush = false ); /// Write a boolean value to an open archive. Self::Condition WriteBool( const char* key /*!< The name of the field under which to write this value in the archive.*/, const bool value /*!< Value to write to the archive under the given key. */ ); /// Write an integer value to an open archive. Self::Condition WriteInt( const char* key /*!< The name of the field under which to write this value in the archive.*/, const int value /*!< Value to write to the archive under the given key. */ ); /// Write a float value to an open archive. Self::Condition WriteFloat( const char* key /*!< The name of the field under which to write this value in the archive.*/, const float value /*!< Value to write to the archive under the given key. */ ); /// Write a double precision float value to an open archive. Self::Condition WriteDouble( const char* key /*!< The name of the field under which to write this value in the archive.*/, const double value /*!< Value to write to the archive under the given key. */ ); /// Write an Types::Coordinate value to an open archive. Self::Condition WriteCoordinate( const char* key /*!< The name of the field under which to write this value in the archive.*/, const Types::Coordinate value /*!< Value to write to the archive under the given key. */ ) { #ifdef CMTK_COORDINATES_FLOAT return this->WriteFloat( key, value ); #else return this->WriteDouble( key, value ); #endif } /// Write an Types::DataItem value to an open archive. Self::Condition WriteItem( const char* key /*!< The name of the field under which to write this value in the archive.*/, const Types::DataItem value /*!< Value to write to the archive under the given key. */ ) { #ifdef CMTK_DATA_FLOAT return this->WriteFloat( key, value ); #else return this->WriteDouble( key, value ); #endif } /// Write a string to an open archive. Self::Condition WriteString( const char* key /*!< The name of the field under which to write this string in the archive.*/, const char* value /*!< String to write to the archive under the given key. */ ); /// Write a string to an open archive. Self::Condition WriteString( const char* key /*!< The name of the field under which to write this string in the archive.*/, const std::string& value /*!< String to write to the archive under the given key. */ ); /** Write array of integer values to an open archive. */ Self::Condition WriteIntArray( const char* key /*!< The name of the field under which to write this array in the archive.*/, const int* array /*!< Pointer to the array to be written.*/, const int size /*!< Number of values in the array. This is the number of values written to the archive. */, const int valuesPerLine = 10 /*!< Optional number of values per line of text written to the archive. This improves readability of the resulting archive as a text. */ ); Self::Condition WriteIntArray( const char* key /*!< The name of the field under which to write this array in the archive.*/, const long long int* array /*!< Pointer to the array to be written.*/, const int size /*!< Number of values in the array. This is the number of values written to the archive. */, const int valuesPerLine = 10 /*!< Optional number of values per line of text written to the archive. This improves readability of the resulting archive as a text. */ ); /** Write array of binay encoded boole values to an open archive. */ Self::Condition WriteBoolArray( const char* key /*!< The name of the field under which to write this array in the archive.*/, const byte* array /*!< Pointer to the array to be written.*/, const int size /*!< Number of values in the array. This is the number of values written to the archive. */, const int valuesPerLine = 10 /*!< Optional number of values per line of text written to the archive. This improves readability of the resulting archive as a text. */ ); /** Write array of single-precision values to an open archive. */ Self::Condition WriteFloatArray( const char* key/*!< The name of the field under which to write this array in the archive.*/, const float* array /*!< Pointer to the array to be written.*/, const int size /*!< Number of values in the array. This is the number of values written to the archive. */, const int valuesPerLine = 10 /*!< Optional number of values per line of text written to the archive. This improves readability of the resulting archive as a text. */ ); /** Write array of double-precision values to an open archive. */ Self::Condition WriteDoubleArray( const char* key /*!< The name of the field under which to write this array in the archive.*/, const double* array /*!< Pointer to the array to be written.*/, const int size /*!< Number of values in the array. This is the number of values written to the archive. */, const int valuesPerLine = 10 /*!< Optional number of values per line of text written to the archive. This improves readability of the resulting archive as a text. */ ); /** Write array of double- or single precision values to an open archive. * Whether double- or single-precision data is written depends on the * definition of the CMTK_COORDINATES_DOUBLE preprocessor symbol. This function * is thus guaranteed to always match the Types::Coordinate type. *\see CMTK_COORDINATES_DOUBLE *\see Types::Coordinate */ Self::Condition WriteCoordinateArray( const char* key/*!< The name of the field under which to write this array in the archive.*/, const Types::Coordinate* array /*!< Pointer to the array to be written.*/, const int size /*!< Number of values in the array. This is the number of values written to the archive. */, const int valuesPerLine = 10 /*!< Optional number of values per line of text written to the archive. This improves readability of the resulting archive as a text. */ ) { #ifdef CMTK_COORDINATES_DOUBLE return this->WriteDoubleArray( key, array, size, valuesPerLine ); #else return this->WriteFloatArray( key, array, size, valuesPerLine ); #endif } /** Write array of double- or single precision values to an open archive. * Whether double- or single-precision data is written depends on the * definition of the CMTK_DATA_DOUBLE preprocessor symbol. This function * is thus guaranteed to always match the Types::DataItem type. *\see CMTK_DATA_DOUBLE *\see Types::DataItem */ Self::Condition WriteItemArray( const char* key /*!< The name of the field under which to write this array in the archive.*/, const Types::DataItem* array /*!< Pointer to the array to be written.*/, const int size /*!< Number of values in the array. This is the number of values written to the archive. */, const int valuesPerLine = 10 /*!< Optional number of values per line of text written to the archive. This improves readability of the resulting archive as a text. */ ) { #ifdef CMTK_DATA_DOUBLE return this->WriteDoubleArray( key, array, size, valuesPerLine ); #else return this->WriteFloatArray( key, array, size, valuesPerLine ); #endif } private: /// Mode the current archive was opened with. Self::Mode m_Mode; }; //@} } // namespace cmtk //@} #endif // #ifndef __cmtkTypedStreamOutput_h_included_ cmtk-3.3.1/libs/IO/cmtkTypedStreamStudylist.cxx000066400000000000000000000104241276303427400215160ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2012 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4344 $ // // $LastChangedDate: 2012-05-11 14:51:08 -0700 (Fri, 11 May 2012) $ // // $LastChangedBy: torstenrohlfing $ // */ #include "cmtkTypedStreamStudylist.h" #include #include #include #include #include #include #include #include #include namespace cmtk { /** \addtogroup IO */ //@{ TypedStreamStudylist::TypedStreamStudylist() { this->Clear(); } void TypedStreamStudylist::Clear() { StudyPath[0] = StudyPath[1] = NULL; ReferenceStudyIndex = 0; this->m_AffineXform = AffineXform::SmartPtr( NULL ); this->m_WarpXform = WarpXform::SmartPtr( NULL ); } TypedStreamStudylist::~TypedStreamStudylist() { if ( StudyPath[0] ) free( StudyPath[0] ); if ( StudyPath[1] ) free( StudyPath[1] ); } bool TypedStreamStudylist::Read( const std::string& studylistpath ) { char archive[PATH_MAX]; snprintf( archive, sizeof( archive ), "%s%cstudylist", MountPoints::Translate( studylistpath ).c_str(), (int)CMTK_PATH_SEPARATOR ); ClassStreamInput classStream( archive ); if ( ! classStream.IsValid() ) { StdErr.printf( "Could not open studylist archive %s.\n", archive ); return false; } if ( StudyPath[0] ) free( StudyPath[0] ); classStream.Seek ( "source" ); StudyPath[0] = classStream.ReadString( "studyname", "" ); if ( StudyPath[1] ) free( StudyPath[1] ); classStream.Seek ( "source" ); StudyPath[1] = classStream.ReadString( "studyname", "" ); classStream.Close(); snprintf( archive, sizeof( archive ), "%s%cregistration", MountPoints::Translate(studylistpath).c_str(), (int)CMTK_PATH_SEPARATOR ); classStream.Open( archive ); if ( ! classStream.IsValid() ) { StdErr.printf( "Could not open studylist archive %s.\n", archive ); return false; } classStream.Seek ( "registration" ); char *referenceStudy = classStream.ReadString( "reference_study" ); ReferenceStudyIndex = ( StrCmp( referenceStudy, StudyPath[0] ) ) ? 1 : 0; bool legacy = false; char *floatingStudy = classStream.ReadString( "floating_study" ); if ( !floatingStudy ) { classStream.Begin(); floatingStudy = classStream.ReadString( "model_study" ); if ( floatingStudy ) { legacy = true; } else { StdErr.printf( "WARNING: Studylist %s/registration apparently has neither new 'floating_study' nor old 'model_study' entry\n", archive ); } } classStream >> this->m_AffineXform; if ( referenceStudy ) { this->m_AffineXform->SetMetaInfo( META_XFORM_FIXED_IMAGE_PATH, referenceStudy ); } if ( floatingStudy ) { this->m_AffineXform->SetMetaInfo( META_XFORM_MOVING_IMAGE_PATH, floatingStudy ); } if ( legacy ) { this->m_AffineXform = AffineXform::SmartPtr( this->m_AffineXform->MakeInverse() ); } classStream.Get( this->m_WarpXform ); if ( this->m_WarpXform ) { if ( referenceStudy ) { this->m_WarpXform->SetMetaInfo( META_XFORM_FIXED_IMAGE_PATH, referenceStudy ); } if ( floatingStudy ) { this->m_WarpXform->SetMetaInfo( META_XFORM_MOVING_IMAGE_PATH, floatingStudy ); } } classStream.Close(); return true; } } // namespace cmtk-3.3.1/libs/IO/cmtkTypedStreamStudylist.h000066400000000000000000000065701276303427400211520ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2012 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4341 $ // // $LastChangedDate: 2012-05-11 11:33:49 -0700 (Fri, 11 May 2012) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkTypedStreamStudylist_h_included_ #define __cmtkTypedStreamStudylist_h_included_ #include #include #include #include #include namespace cmtk { /** \addtogroup IO */ //@{ /** Studylist with typedstream file system interface. * This class provides the necessary functions to read studylist objects * from typedstream archives. */ class TypedStreamStudylist { public: /// Smart pointer to TypedStreamStudylist typedef SmartPointer SmartPtr; /// Default conctructor. TypedStreamStudylist(); /// Destructor. ~TypedStreamStudylist(); /** Read constructor. */ TypedStreamStudylist ( const std::string& studylistpath /*!< The typedstream archive to read the object from. */) { this->Clear(); this->Read( studylistpath ); } /// Read object from disk. bool Read( const std::string& studylistpath ); /// Return affine transformation as stored in the studylist. AffineXform::SmartPtr& GetAffineXform() { return this->m_AffineXform; } /// Return local deformation as stored in the studylist. WarpXform::SmartPtr& GetWarpXform() { return this->m_WarpXform; } /// Return study path. const char* GetStudyPath( const int index ) const { return StudyPath[index]; } /// Return reference study path. const char* GetReferenceStudyPath() const { return StudyPath[ReferenceStudyIndex]; } /// Return floating study path. const char* GetFloatingStudyPath( const int floatingIndex = 0 ) const { if ( floatingIndex < ReferenceStudyIndex ) return StudyPath[floatingIndex]; else if ( floatingIndex < 1 ) return StudyPath[floatingIndex+1]; else return NULL; } private: /// The names of the two studies referenced in the studylist. char *StudyPath[2]; /// Index of the reference study among the studies in this list (from 0). int ReferenceStudyIndex; /// Pointer to the affine transformation of this studylist. AffineXform::SmartPtr m_AffineXform; /// Pointer to the local deformation of this studylist. WarpXform::SmartPtr m_WarpXform; /// Initialize all internal data structures. void Clear(); }; //@} } // namespace cmtk #endif // #ifndef __cmtkTypedStreamStudylist_h_included_ cmtk-3.3.1/libs/IO/cmtkVolumeFromFile.cxx000066400000000000000000000036041276303427400202250ustar00rootroot00000000000000/* // // Copyright 2004-2012 SRI International // // Copyright 1997-2009 Torsten Rohlfing // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4260 $ // // $LastChangedDate: 2012-04-25 16:34:20 -0700 (Wed, 25 Apr 2012) $ // // $LastChangedBy: torstenrohlfing $ // */ #include "cmtkVolumeFromFile.h" #include #include #include #include namespace cmtk { /** \addtogroup IO */ //@{ const UniformVolume::SmartPtr VolumeFromFile::Read( const std::string& path ) { FileFormatID id = FileFormat::Identify( path ); switch ( id ) { case FILEFORMAT_DICOM: return VolumeFromFile::ReadDICOM( path ); case FILEFORMAT_VANDERBILT: return VolumeFromFile::ReadVanderbilt( path ); case FILEFORMAT_ANALYZE_HDR: return VolumeFromFile::ReadAnalyzeHdr( path, false /* bigendian */ ); case FILEFORMAT_ANALYZE_HDR_BIGENDIAN: return VolumeFromFile::ReadAnalyzeHdr( path, true /* bigendian */ ); default: ; } return UniformVolume::SmartPtr( NULL ); } } // namespace cmtk cmtk-3.3.1/libs/IO/cmtkVolumeFromFile.h000066400000000000000000000066571276303427400176650ustar00rootroot00000000000000/* // // Copyright 2004-2012 SRI International // // Copyright 1997-2009 Torsten Rohlfing // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4260 $ // // $LastChangedDate: 2012-04-25 16:34:20 -0700 (Wed, 25 Apr 2012) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkVolumeFromFile_h_included_ #define __cmtkVolumeFromFile_h_included_ #include #include #include namespace cmtk { /** \addtogroup IO */ //@{ /** Class to read uniform volume from disk file(s). */ class VolumeFromFile { public: /// Read volume from file automatically. static const UniformVolume::SmartPtr Read( const std::string& filename ); #ifdef CMTK_USE_DCMTK /// Read volume in multi-slice DICOM format. static const UniformVolume::SmartPtr ReadDICOM( const std::string& filename ); #else static const UniformVolume::SmartPtr ReadDICOM( const std::string& ) { throw Exception( "Library was configured without DICOM support." ); } #endif /// Read volume in Vanderbilt format. static const UniformVolume::SmartPtr ReadVanderbilt( const std::string& filename ); /// Read BioRad PIC file (confocal microscopy image). static const UniformVolume::SmartPtr ReadBioRad( const std::string& path ); /// Read Analyze 7.5 file (separate header file). static const UniformVolume::SmartPtr ReadAnalyzeHdr( const std::string& pathHdr, const bool bigEndian = false, const bool readData = true ); /// Read Nifti file. static const UniformVolume::SmartPtr ReadNifti( const std::string& pathHdr, const bool detached, const bool readData = true ); /// Write Analyze 7.5 file (separate header file). static void WriteAnalyzeHdr( const std::string& pathHdr, const UniformVolume& volume ); /// Write Nifti file. static void WriteNifti( const std::string& pathImg, const UniformVolume& volume ); /// Write MetaImage file. static void WriteMetaImage( const std::string& pathHdr, const UniformVolume& volume ); #ifdef CMTK_BUILD_NRRD /// Read NRRD file. static const UniformVolume::SmartPtr ReadNRRD( const std::string& path ); /// Write NRRD file. static void WriteNRRD( const std::string& pathHdr, const UniformVolume& volume ); #else /// Read NRRD file. static const UniformVolume::SmartPtr ReadNRRD( const std::string& ) { throw Exception( "Library was configured without Nrrd support." ); } /// Write NRRD file. static void WriteNRRD( const std::string&, const UniformVolume&, const bool = false ) { throw Exception( "Library was configured without Nrrd support." ); } #endif }; //@} } // namespace cmtk #endif // #ifndef __cmtkVolumeFromFile_h_included_ cmtk-3.3.1/libs/IO/cmtkVolumeFromFileAnalyze.cxx000066400000000000000000000370721276303427400215570ustar00rootroot00000000000000/* // // Copyright 1997-2010 Torsten Rohlfing // // Copyright 2004-2013 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4886 $ // // $LastChangedDate: 2013-09-27 21:08:52 -0700 (Fri, 27 Sep 2013) $ // // $LastChangedBy: torsten_at_home $ // */ #include "cmtkVolumeFromFile.h" #include #include #include #include #include #include #include #include #include #include #include #ifdef HAVE_ZLIB # include #endif #ifdef HAVE_SYS_STAT_H # include #endif namespace cmtk { /// Environment variable that turns on legacy Analyze I/O with incorrect anatomical orientations. const char* const CMTK_LEGACY_ANALYZE_IO = "CMTK_LEGACY_ANALYZE_IO"; /// Legacy environment variable that turns on legacy Analyze I/O with incorrect anatomical orientations. const char* const IGS_LEGACY_ANALYZE_IO = "IGS_LEGACY_ANALYZE_IO"; /** \addtogroup IO */ //@{ const UniformVolume::SmartPtr VolumeFromFile::ReadAnalyzeHdr( const std::string& pathHdr, const bool bigEndian, const bool readData ) { #ifdef _MSC_VER FILE *hdrFile = fopen( pathHdr.c_str(), "rb" ); #else FILE *hdrFile = fopen( pathHdr.c_str(), "r" ); #endif if ( !hdrFile ) { StdErr << "ERROR: could not open Analyze header file " << pathHdr << "\n"; return UniformVolume::SmartPtr( NULL ); } char buffer[348]; if ( 348 != fread( buffer, 1, 348, hdrFile ) ) { StdErr << "ERROR: could not read 348 bytes from header file " << pathHdr << "\n"; fclose( hdrFile ); return UniformVolume::SmartPtr( NULL ); } fclose( hdrFile ); FileHeader header( buffer, bigEndian ); short ndims = header.GetField( 40 ); if ( ndims < 3 ) { StdErr.printf( "ERROR: image dimension %d is smaller than 3 in file %s\n", ndims, pathHdr.c_str() ); return UniformVolume::SmartPtr( NULL ); } DataGrid::IndexType dims; dims[0] = header.GetField( 42 ); dims[1] = header.GetField( 44 ); dims[2] = header.GetField( 46 ); const int dims3 = header.GetField( 48 ); if ( (ndims > 3) && (dims3 > 1) ) { StdErr.printf( "WARNING: dimension %d is greater than 3 in file %s\n", ndims, pathHdr.c_str() ); } float pixelDim[3]; header.GetArray( pixelDim, 80, 3 ); UniformVolume::SmartPtr volume( new UniformVolume( dims, fabs( pixelDim[0] ), fabs( pixelDim[1] ), fabs( pixelDim[2] ) ) ); const byte orient = header.GetField( 252 ); std::string orientString = ""; const bool legacyMode = !header.CompareFieldStringN( 344, "SRI1", 4 ); if ( legacyMode ) { // // Legacy mode: read Analyze images with incorrect orientations to preserve backward compatibility // We do this by setting "apparent" anatomical axis orientation codes so that the new reorientation // function in DataGrid will perform the same reordering as its obsolete SetData() member used to. // switch ( orient ) { default: StdErr.printf( "WARNING: unsupported slice orientation %d in Analyze file %s\n", orient, pathHdr.c_str() ); break; case cmtk::ANALYZE_AXIAL: orientString = "RAS"; // INCORRECT LEGACY ORIENTATION break; case cmtk::ANALYZE_AXIAL_FLIP: orientString = "RAI"; // INCORRECT LEGACY ORIENTATION break; case cmtk::ANALYZE_CORONAL: orientString = "RIP"; // INCORRECT LEGACY ORIENTATION break; case cmtk::ANALYZE_CORONAL_FLIP: orientString = "RSA"; // INCORRECT LEGACY ORIENTATION break; case cmtk::ANALYZE_SAGITTAL: orientString = "AIR"; // INCORRECT LEGACY ORIENTATION break; case cmtk::ANALYZE_SAGITTAL_FLIP: orientString = "AIL"; // INCORRECT LEGACY ORIENTATION break; } StdErr << "INFO: reading Analyze hdr/img in legacy orientation mode, assuming " << orientString << " axes\n"; } else { switch ( orient ) { default: StdErr.printf( "WARNING: unsupported slice orientation %d in Analyze file %s\n", orient, pathHdr.c_str() ); break; case cmtk::ANALYZE_AXIAL: orientString = "LAS"; break; case cmtk::ANALYZE_AXIAL_FLIP: orientString = "LPS"; break; case cmtk::ANALYZE_CORONAL: orientString = "LSA"; break; case cmtk::ANALYZE_CORONAL_FLIP: orientString = "LIA"; break; case cmtk::ANALYZE_SAGITTAL: orientString = "ASL"; break; case cmtk::ANALYZE_SAGITTAL_FLIP: orientString = "AIL"; break; } } if ( !orientString.empty() ) { volume->SetMetaInfo( META_IMAGE_ORIENTATION, orientString ); volume->SetMetaInfo( META_IMAGE_ORIENTATION_ORIGINAL, orientString ); // Analyze is medical data, which we always treat in RAS space. volume->SetMetaInfo( META_SPACE, orientString ); volume->SetMetaInfo( META_SPACE_ORIGINAL, orientString ); volume->ChangeCoordinateSpace( AnatomicalOrientation::ORIENTATION_STANDARD ); } // fill "description" header field if ( header.GetField( 148 ) ) { char desc[81]; desc[80] = 0; volume->SetMetaInfo( META_IMAGE_DESCRIPTION, std::string( header.GetFieldString( 148, desc, 80 ) ) ); } // don't read data, we're done here. if ( ! readData ) return volume; ScalarDataType dtype; switch ( header.GetField( 70 ) ) { case cmtk::ANALYZE_TYPE_NONE: case cmtk::ANALYZE_TYPE_BINARY: case cmtk::ANALYZE_TYPE_COMPLEX: case cmtk::ANALYZE_TYPE_RGB: case cmtk::ANALYZE_TYPE_ALL: default: StdErr.printf( "ERROR: unsupported data type %d in Analyze file %s\n", header.GetField( 70 ), pathHdr.c_str() ); return volume; case cmtk::ANALYZE_TYPE_UNSIGNED_CHAR: dtype = TYPE_BYTE; break; case cmtk::ANALYZE_TYPE_SIGNED_SHORT: dtype = TYPE_SHORT; break; case cmtk::ANALYZE_TYPE_SIGNED_INT: dtype = TYPE_INT; break; case cmtk::ANALYZE_TYPE_FLOAT: dtype = TYPE_FLOAT; break; case cmtk::ANALYZE_TYPE_DOUBLE: dtype = TYPE_DOUBLE; break; case cmtk::ANALYZE_TYPE_USHORT: dtype = TYPE_USHORT; break; case cmtk::ANALYZE_TYPE_UINT: dtype = TYPE_UINT; break; } size_t offset = static_cast( header.GetField( 108 ) ); std::string pathImg = pathHdr; const size_t period = pathImg.rfind( ".hdr" ); if ( period != std::string::npos ) pathImg.replace( period, 4, ".img" ); CompressedStream stream( pathImg ); if ( stream.IsValid() ) { stream.Seek( offset, SEEK_CUR ); TypedArray::SmartPtr data( TypedArray::Create( dtype, volume->GetNumberOfPixels() ) ); if ( data->GetDataSize() == stream.Read( data->GetDataPtr(), data->GetItemSize(), data->GetDataSize() ) ) { #ifdef WORDS_BIGENDIAN if ( ! bigEndian ) data->ChangeEndianness(); #else if ( bigEndian ) data->ChangeEndianness(); #endif volume->SetData( data ); } else { StdErr << "ERROR: could not read " << data->GetDataSize() << " pixels from Analyze image file " << pathImg << "\n"; } } else { StdErr << "ERROR: could not open Analyze image file " << pathImg << "\n"; } return volume; } void VolumeFromFile::WriteAnalyzeHdr ( const std::string& pathHdr, const UniformVolume& volume ) { UniformVolume::SmartPtr writeVolume( volume.Clone() ); if ( writeVolume->MetaKeyExists( META_SPACE_ORIGINAL ) ) writeVolume->ChangeCoordinateSpace( writeVolume->GetMetaInfo( META_SPACE_ORIGINAL ) ); std::string currentOrientation = writeVolume->GetMetaInfo( META_IMAGE_ORIENTATION ); if ( currentOrientation == "" ) { currentOrientation = "LAS"; // default: write as is, axial tag, no reorientation. } std::string originalOrientation = writeVolume->GetMetaInfo( META_IMAGE_ORIENTATION_ORIGINAL ); if ( originalOrientation == "" ) { originalOrientation = currentOrientation; } // try to write something as close as possible to original orientation const char *const supportedOrientations[] = { "LAS", "LSA", "ASL", NULL }; const char* writeOrientation = AnatomicalOrientation::GetClosestOrientation( originalOrientation.c_str(), supportedOrientations ); if ( getenv( CMTK_LEGACY_ANALYZE_IO ) || getenv( IGS_LEGACY_ANALYZE_IO ) ) { const char *const supportedOrientationsLegacy[] = { "RAS", "RIP", "AIR", NULL }; writeOrientation = AnatomicalOrientation::GetClosestOrientation( originalOrientation.c_str(), supportedOrientationsLegacy ); } UniformVolume::SmartPtr reorientedVolume; if ( strcmp( writeOrientation, currentOrientation.c_str() ) ) { DebugOutput( 2 ) << "INFO: WriteAnalyzeHdr will reorient output volume from '" << currentOrientation << "' to '" << writeOrientation << "'\n"; reorientedVolume = UniformVolume::SmartPtr( volume.GetReoriented( writeOrientation ) ); writeVolume = reorientedVolume; } const TypedArray* data = writeVolume->GetData(); if ( ! data ) return; char buffer[348]; memset( buffer, 0, sizeof( buffer ) ); #ifdef WORDS_BIGENDIAN FileHeader header( buffer, true /*bigEndian*/ ); #else FileHeader header( buffer, false /*bigEndian*/ ); #endif header.StoreField(0, 348 ); // header size header.StoreField( 32, 16384 ); // extents header.StoreField( 36, 0 ); // session error header.StoreField( 38, 'r' ); // regular // ndims header.StoreField( 40, 4 ); // dimensions header.StoreField( 42, writeVolume->GetDims()[AXIS_X] ); header.StoreField( 44, writeVolume->GetDims()[AXIS_Y] ); header.StoreField( 46, writeVolume->GetDims()[AXIS_Z] ); header.StoreField( 48, 1 ); // write dims 3-7 header.StoreField( 50, 0 ); // just for safety header.StoreField( 52, 0 ); // just for safety header.StoreField( 54, 0 ); // just for safety header.StoreField( 56, 0 ); // just for safety header.StoreField( 68, 0.0 ); // vox_offset switch ( data->GetType() ) { default: header.StoreField( 70, cmtk::ANALYZE_TYPE_NONE ); header.StoreField( 72, 0 ); break; case TYPE_BYTE: header.StoreField( 70, cmtk::ANALYZE_TYPE_UNSIGNED_CHAR ); header.StoreField( 72, 8 * sizeof( byte ) ); break; case TYPE_SHORT: header.StoreField( 70, cmtk::ANALYZE_TYPE_SIGNED_SHORT ); header.StoreField( 72, 8 * sizeof( short ) ); break; case TYPE_USHORT: header.StoreField( 70, cmtk::ANALYZE_TYPE_USHORT ); header.StoreField( 72, 8 * sizeof( unsigned short ) ); break; case TYPE_INT: header.StoreField( 70, cmtk::ANALYZE_TYPE_SIGNED_INT ); header.StoreField( 72, 8 * sizeof( signed int ) ); break; case TYPE_UINT: header.StoreField( 70, cmtk::ANALYZE_TYPE_UINT ); header.StoreField( 72, 8 * sizeof( unsigned int ) ); break; case TYPE_FLOAT: header.StoreField( 70, cmtk::ANALYZE_TYPE_FLOAT ); header.StoreField( 72, 8 * sizeof( float ) ); break; case TYPE_DOUBLE: header.StoreField( 70, cmtk::ANALYZE_TYPE_DOUBLE ); header.StoreField( 72, 8 * sizeof( double ) ); break; } header.StoreField( 80, (float)writeVolume->m_Delta[AXIS_X] ); header.StoreField( 84, (float)writeVolume->m_Delta[AXIS_Y] ); header.StoreField( 88, (float)writeVolume->m_Delta[AXIS_Z] ); header.StoreField( 92, 1.0f ); // write sizes in dims 3 and header.StoreField( 96, 1.0f ); // 4 just to be safe // set zero offset for binary file. header.StoreField( 108, 0.0f ); // determine data range; const Types::DataItemRange dataRange = data->GetRange(); header.StoreField( 124, static_cast( dataRange.m_UpperBound ) ); // cal_max header.StoreField( 128, static_cast( dataRange.m_LowerBound ) ); // cal_min header.StoreField( 140, static_cast( dataRange.m_UpperBound ) ); header.StoreField( 144, static_cast( dataRange.m_LowerBound ) ); if ( volume.MetaKeyExists( META_IMAGE_DESCRIPTION ) ) header.StoreFieldString( 148, volume.GetMetaInfo( META_IMAGE_DESCRIPTION ).c_str(), 80 ); if ( getenv( CMTK_LEGACY_ANALYZE_IO ) || getenv( IGS_LEGACY_ANALYZE_IO ) ) { // slice orientation always axial from caudal to cranial header.StoreField( 252, cmtk::ANALYZE_AXIAL ); header.StoreField( 254, 0 ); //set Nifti sform code to 0. } else { if ( !strcmp( writeOrientation, "LAS" ) ) header.StoreField( 252, cmtk::ANALYZE_AXIAL ); else if ( !strcmp( writeOrientation, "LSA" ) ) header.StoreField( 252, cmtk::ANALYZE_CORONAL ); else if ( !strcmp( writeOrientation, "ASL" ) ) header.StoreField( 252, cmtk::ANALYZE_SAGITTAL ); header.StoreField( 254, 0 ); //set Nifti sform code to 0. // mark this as "new" SRI Analyze image. header.StoreFieldString( 344, "SRI1", 4 ); } // write binary data std::string pathImg = pathHdr; const size_t period = pathImg.rfind( ".hdr" ); if ( period != std::string::npos ) pathImg.replace( period, 4, ".img" ); if ( VolumeIO::GetWriteCompressed() ) { struct stat buf; if ( ! stat( pathImg.c_str(), &buf ) ) { StdErr << "WARNING: Analyze img file '" << pathImg << "' will be written compressed, but uncompressed file exists!\n"; } #ifdef _MSC_VER const char *const modestr = "w9b"; #else const char *const modestr = "w9"; #endif gzFile imgFile = gzopen( (pathImg + ".gz").c_str(), modestr ); if ( imgFile ) { const size_t dataSize = data->GetItemSize() * data->GetDataSize(); if ( dataSize != CompressedStream::Zlib::StaticSafeWrite( imgFile, data->GetDataPtr(), dataSize ) ) { StdErr << "WARNING: gzwrite() returned error when writing to " << pathImg << "\n"; } gzclose( imgFile ); } } else { #ifdef _MSC_VER const char *const modestr = "wb"; #else const char *const modestr = "w"; #endif FILE *imgFile = fopen( pathImg.c_str(), modestr ); if ( imgFile ) { fwrite( data->GetDataPtr(), data->GetItemSize(), data->GetDataSize(), imgFile ); fclose( imgFile ); } else { StdErr << "ERROR: could not open file '" << pathImg << "' for writing\n"; } } // write header info #ifdef _MSC_VER FILE *hdrFile = fopen( pathHdr.c_str(), "wb" ); #else FILE *hdrFile = fopen( pathHdr.c_str(), "w" ); #endif if ( hdrFile ) { if ( 348 != fwrite( buffer, 1, 348, hdrFile ) ) { StdErr << "ERROR: could not write 348 bytes to header file " << pathHdr << "\n"; } fclose( hdrFile ); } else { StdErr << "ERROR: could not open file '" << pathHdr << "' for writing\n"; } } } // namespace cmtk cmtk-3.3.1/libs/IO/cmtkVolumeFromFileBioRad.cxx000066400000000000000000000143341276303427400213100ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2012 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4447 $ // // $LastChangedDate: 2012-07-06 13:51:04 -0700 (Fri, 06 Jul 2012) $ // // $LastChangedBy: torstenrohlfing $ // */ #include "cmtkVolumeFromFile.h" #include #include #include #include namespace cmtk { /** \addtogroup IO */ //@{ /// BioRad microscopy image file header typedef struct { unsigned short nx, ny; // 0 2*2 image width and height in pixels short npic; // 4 2 number of images in file short ramp1_min; // 6 2*2 LUT1 ramp min. and max. short ramp1_max; int notes; // 10 4 no notes=0; has notes=non zero short byte_format; // 14 2 bytes=TRUE(1); words=FALSE(0) unsigned short n; // 16 2 image number within file char name[32]; // 18 32 file name short merged; // 50 2 merged format unsigned short color1; // 52 2 LUT1 color status unsigned short file_id; // 54 2 valid .PIC file=12345 short ramp2_min; // 56 2*2 LUT2 ramp min. and max. short ramp2_max; unsigned short color2; // 60 2 LUT2 color status short edited; // 62 2 image has been edited=TRUE(1) short lens; // 64 2 Integer part of lens magnification float mag_factor; // 66 4 4 byte real mag. factor (old ver.) unsigned short dummy[3]; // 70 6 NOT USED (old ver.=real lens mag.) } FileHeaderBioRad; const UniformVolume::SmartPtr VolumeFromFile::ReadBioRad( const std::string& path ) { CompressedStream stream( path ); // Biorad header is 76 bytes char buffer[76]; if ( 1 != stream.Read( &buffer, sizeof(buffer), 1 ) ) { StdErr << "ERROR: cannot read header from BioRad file " << path << ". Bailing out.\n"; return UniformVolume::SmartPtr( NULL ); } FileHeaderBioRad header; memcpy( &header.nx, buffer+0, sizeof( header.nx ) ); memcpy( &header.ny, buffer+2, sizeof( header.ny ) ); memcpy( &header.npic, buffer+4, sizeof( header.npic ) ); memcpy( &header.notes, buffer+10, sizeof( header.notes ) ); memcpy( &header.byte_format, buffer+14, sizeof( header.byte_format ) ); memcpy( &header.file_id, buffer+54, sizeof( header.file_id ) ); // check MagicNumber #ifdef WORDS_BIGENDIAN if ( Memory::ByteSwap( header.file_id ) != 12345 ) { StdErr << "ERROR: BioRad file " << path << " has invalid magic number. Bailing out.\n"; return UniformVolume::SmartPtr( NULL ); } #else if ( header.file_id != 12345 ) { StdErr << "ERROR: BioRad file " << path << " has invalid magic number. Bailing out.\n"; return UniformVolume::SmartPtr( NULL ); } #endif #ifdef WORDS_BIGENDIAN int dims[3] = { Memory::ByteSwap( header.nx ), Memory::ByteSwap( header.ny ), Memory::ByteSwap( header.npic ) }; #else int dims[3] = { header.nx, header.ny, header.npic }; #endif int numPixels = dims[0] * dims[1] * dims[2]; TypedArray::SmartPtr dataArray; if ( header.byte_format ) { dataArray = TypedArray::SmartPtr( TypedArray::Create( TYPE_BYTE, numPixels ) ); } else { dataArray = TypedArray::SmartPtr( TypedArray::Create( TYPE_USHORT, numPixels ) ); } stream.Read( dataArray->GetDataPtr(), dataArray->GetItemSize(), dataArray->GetDataSize() ); double pixelsizeX = 1, pixelsizeY = 1, pixelsizeZ = 1; bool flipX = false, flipY = false, flipZ = false;; while ( ! stream.Feof() ) { char lineheader[16], line[80]; stream.Read( lineheader, sizeof( lineheader ), 1 ); stream.Read( line, sizeof( line ), 1 ); // StdErr.printf( "%s\n->%s\n", lineheader, line ); double d1, d2, d3; if ( 3 == sscanf( line, "AXIS_2 %20lf %20lf %20lf", &d1, &d2, &d3 ) ) { pixelsizeX = fabs( d3 ); flipX = (d3 < 0 ); } if ( 3 == sscanf( line, "AXIS_3 %20lf %20lf %20lf", &d1, &d2, &d3 ) ) { pixelsizeY = fabs( d3 ); flipY = (d3 < 0 ); } if ( 3 == sscanf( line, "AXIS_4 %20lf %20lf %20lf", &d1, &d2, &d3 ) ) { pixelsizeZ = fabs( d3 ); flipZ = (d3 < 0 ); } } #ifdef WORDS_BIGENDIAN Types::Coordinate lensScale = 1; //Memory::ByteSwap( header.lens ); #else Types::Coordinate lensScale = 1; //header.lens; #endif // GJ I think this was backwards - want to swap if we ARE on big endian // since Biorad is always little endian //#ifndef WORDS_BIGENDIAN #ifdef WORDS_BIGENDIAN // change endianness from Sun to whatever we're currently on. dataArray->ChangeEndianness(); #endif UniformVolume::SmartPtr volume( new UniformVolume( DataGrid::IndexType::FromPointer( dims ), lensScale * pixelsizeX, lensScale * pixelsizeY, lensScale * pixelsizeZ, dataArray ) ); if ( flipX ) { StdErr << "WARNING: x pixel spacing is negative. Resulting volume will be mirrored accordingly.\n"; volume->ApplyMirrorPlane( AXIS_X ); } if ( flipY ) { StdErr << "WARNING: y pixel spacing is negative. Resulting volume will be mirrored accordingly.\n"; volume->ApplyMirrorPlane( AXIS_Y ); } if ( flipZ ) { StdErr << "WARNING: z pixel spacing is negative. Resulting volume will be mirrored accordingly.\n"; volume->ApplyMirrorPlane( AXIS_Z ); } return volume; } } // namespace cmtk cmtk-3.3.1/libs/IO/cmtkVolumeFromFileDICOM.cxx000066400000000000000000000072541276303427400210060ustar00rootroot00000000000000/* // // Copyright 2004-2013 SRI International // // Copyright 1997-2010 Torsten Rohlfing // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4947 $ // // $LastChangedDate: 2013-10-08 11:38:28 -0700 (Tue, 08 Oct 2013) $ // // $LastChangedBy: torstenrohlfing $ // */ #include "cmtkVolumeFromFile.h" #include #include namespace cmtk { const UniformVolume::SmartPtr VolumeFromFile::ReadDICOM( const std::string& path ) { UniformVolume::SmartPtr volume; try { DICOM dicom( path ); FixedVector<3,int> dims = dicom.GetDims(); FixedVector<3,double> pixelSize = dicom.GetPixelSize(); const unsigned long totalImageSizePixels = dims[0] * dims[1] * dims[2]; TypedArray::SmartPtr pixelDataArray = dicom.GetPixelDataArray( totalImageSizePixels ); UniformVolume::CoordinateVectorType imageOrigin = dicom.GetImageOrigin(); FixedArray< 2, FixedVector<3,double> > imageOrientation = dicom.GetImageOrientation(); // without further information, we "guess" the image normal vector UniformVolume::CoordinateVectorType sliceNormal = dicom.DemosaicAndGetNormal( imageOrientation, pixelSize, dims, pixelDataArray, imageOrigin ); // Construct volume and set the DICOM coordinates volume = UniformVolume::SmartPtr( new UniformVolume( UniformVolume::IndexType( dims ), pixelSize[0], pixelSize[1], pixelSize[2], pixelDataArray ) ); volume->SetMetaInfo( META_SPACE, "LPS" ); volume->SetMetaInfo( META_SPACE_ORIGINAL, "LPS" ); imageOrientation[0] *= pixelSize[0] / imageOrientation[0].RootSumOfSquares(); imageOrientation[1] *= pixelSize[1] / imageOrientation[1].RootSumOfSquares(); sliceNormal *= pixelSize[2] / sliceNormal.RootSumOfSquares(); const Types::Coordinate directions[3][3] = { { imageOrientation[0][0], imageOrientation[0][1], imageOrientation[0][2] }, { imageOrientation[1][0], imageOrientation[1][1], imageOrientation[1][2] }, { sliceNormal[0], sliceNormal[1], sliceNormal[2] } }; const Matrix3x3 m3( directions ); Matrix4x4 m4( m3 ); for ( int i = 0; i < 3; ++i ) m4[3][i] = imageOrigin[i]; volume->m_IndexToPhysicalMatrix = m4; // const std::string orientationString0 = volume->GetOrientationFromDirections(); volume->ChangeCoordinateSpace( AnatomicalOrientation::ORIENTATION_STANDARD ); const std::string orientationString = volume->GetOrientationFromDirections(); volume->SetMetaInfo( META_SPACE_UNITS_STRING, "mm" ); // seems to be implied in DICOM volume->SetMetaInfo( META_IMAGE_ORIENTATION, orientationString ); volume->SetMetaInfo( META_IMAGE_ORIENTATION_ORIGINAL, orientationString ); } catch ( const cmtk::Exception& ex ) { StdErr << "ERROR: " << ex.what() << "\n"; return UniformVolume::SmartPtr( NULL ); } return volume; } } // namespace cmtk cmtk-3.3.1/libs/IO/cmtkVolumeFromFileMetaFile.cxx000066400000000000000000000076071276303427400216430ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2012 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4260 $ // // $LastChangedDate: 2012-04-25 16:34:20 -0700 (Wed, 25 Apr 2012) $ // // $LastChangedBy: torstenrohlfing $ // */ #include "cmtkVolumeFromFile.h" #include #include #include #include #include #include namespace cmtk { /** \addtogroup IO */ //@{ void VolumeFromFile::WriteMetaImage ( const std::string& pathHdr, const UniformVolume& volume ) { const TypedArray* data = volume.GetData(); if ( ! data ) return; #ifdef _MSC_VER FILE *outfile = fopen( pathHdr.c_str(), "wb" ); #else FILE *outfile = fopen( pathHdr.c_str(), "w" ); #endif if ( ! outfile ) { StdErr << "Could not open file " << pathHdr << " for writing.\n"; return; } fprintf( outfile, "ObjectType = Image\n" ); fprintf( outfile, "NDims = 3\n" ); fprintf( outfile, "BinaryData = True\n" ); #ifndef WORDS_BIGENDIAN fprintf( outfile, "BinaryDataByteOrderMSB = False\n" ); fprintf( outfile, "ElementByteOrderMSB = False\n" ); #else fprintf( outfile, "BinaryDataByteOrderMSB = True\n" ); fprintf( outfile, "ElementByteOrderMSB = True\n" ); #endif const AffineXform::MatrixType matrix = volume.GetImageToPhysicalMatrix(); fprintf( outfile, "TransformMatrix = %lf %lf %lf %lf %lf %lf %lf %lf %lf\n", (double)matrix[0][0], (double)matrix[0][1], (double)matrix[0][2], (double)matrix[1][0], (double)matrix[1][1], (double)matrix[1][2], (double)matrix[2][0], (double)matrix[2][1], (double)matrix[2][2] ); fprintf( outfile, "Offset = %lf %lf %lf\n", (double)matrix[3][0], (double)matrix[3][1], (double)matrix[3][2] ); fprintf( outfile, "CenterOfRotation = 0 0 0\n" ); fprintf( outfile, "ElementSpacing = %f %f %f\n", volume.m_Delta[AXIS_X], volume.m_Delta[AXIS_Y], volume.m_Delta[AXIS_Z] ); fprintf( outfile, "DimSize = %d %d %d\n", volume.m_Dims[AXIS_X], volume.m_Dims[AXIS_Y], volume.m_Dims[AXIS_Z] ); fprintf( outfile, "AnatomicalOrientation = %s\n", volume.GetMetaInfo( META_SPACE ).c_str() ); fprintf( outfile, "ElementNumberOfChannels = 1\n" ); fputs( "ElementType = ", outfile ) ; switch ( data->GetType() ) { case TYPE_BYTE: fputs( "MET_UCHAR\n", outfile ); break; case TYPE_CHAR: fputs( "MET_CHAR\n", outfile ); break; case TYPE_SHORT: fputs( "MET_SHORT\n", outfile ); break; case TYPE_USHORT: fputs( "MET_USHORT\n", outfile ); break; case TYPE_INT: fputs( "MET_INT\n", outfile ); break; case TYPE_UINT: fputs( "MET_UINT\n", outfile ); break; case TYPE_FLOAT: fputs( "MET_FLOAT\n", outfile ); break; case TYPE_DOUBLE: fputs( "MET_DOUBLE\n", outfile ); break; default: fputs( "MET_UNKNOWN\n", outfile ); break; } fprintf( outfile, "ElementDataFile = LOCAL\n" ); fwrite( data->GetDataPtr(), data->GetItemSize(), data->GetDataSize(), outfile ); fclose( outfile ); } } // namespace cmtk cmtk-3.3.1/libs/IO/cmtkVolumeFromFileNRRD.cxx000066400000000000000000000251551276303427400207200ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2012 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4456 $ // // $LastChangedDate: 2012-07-18 11:15:45 -0700 (Wed, 18 Jul 2012) $ // // $LastChangedBy: torstenrohlfing $ // */ #include #ifdef CMTK_BUILD_NRRD #include "cmtkVolumeFromFile.h" #include #include #include #include #include #ifdef CMTK_BUILD_NRRD_TEEM # include #else # include #endif namespace cmtk { /** \addtogroup IO */ //@{ const UniformVolume::SmartPtr VolumeFromFile::ReadNRRD( const std::string& pathHdr ) { UniformVolume::SmartPtr volume( NULL ); try { Nrrd *nrrd = nrrdNew(); if ( nrrdLoad( nrrd, pathHdr.c_str(), NULL ) ) throw biffGetDone(NRRD); if ( nrrd->dim > 3 ) { StdErr << "ERROR: for now, nrrd input can only handle data with dimension 3 or less.\n"; return UniformVolume::SmartPtr( NULL ); } const int dims[3] = { static_cast( (nrrd->dim > 0) ? nrrd->axis[0].size : 1 ), static_cast( (nrrd->dim > 1) ? nrrd->axis[1].size : 1 ), static_cast( (nrrd->dim > 2) ? nrrd->axis[2].size : 1 ) }; // for each axis, if spacing is NaN, use direction vector to compute spacing. double spacing[3] = { 1, 1, 1 }; for ( size_t ax = 0; ax < nrrd->dim; ++ax ) { switch ( nrrdSpacingCalculate( nrrd, ax, spacing+ax, nrrd->axis[ax].spaceDirection ) ) { case nrrdSpacingStatusScalarNoSpace: break; case nrrdSpacingStatusDirection: break; case nrrdSpacingStatusScalarWithSpace: StdErr << "WARNING: nrrdSpacingCalculate returned nrrdSpacingStatusScalarWithSpace\n"; spacing[ax] = nrrd->axis[ax].spacing; break; case nrrdSpacingStatusNone: default: StdErr << "WARNING: no pixel spacings in Nrrd for axis " << ax << "; setting to 1.0\n"; spacing[ax] = 1.0; break; } } volume = UniformVolume::SmartPtr( new UniformVolume( DataGrid::IndexType::FromPointer( dims ), spacing[0], spacing[1], spacing[2] ) ); ScalarDataType type = TYPE_NONE; switch ( nrrd->type ) { case nrrdTypeUChar: type = TYPE_BYTE; break; case nrrdTypeChar: type = TYPE_CHAR; break; case nrrdTypeUShort: type = TYPE_USHORT; break; case nrrdTypeShort: type = TYPE_SHORT; break; case nrrdTypeInt: type = TYPE_INT; break; case nrrdTypeUInt: type = TYPE_UINT; break; case nrrdTypeFloat: type = TYPE_FLOAT; break; case nrrdTypeDouble: type = TYPE_DOUBLE; break; default: break; } if ( type != TYPE_NONE ) { TypedArray::SmartPtr data( TypedArray::Create( type, nrrd->data, volume->GetNumberOfPixels() ) ); volume->SetData( data ); } else { StdErr << "ERROR: unsupported data type in nrrd file.\n"; return volume; } const char* orientationSpaceAnatomical = NULL; switch ( nrrd->space ) { case nrrdSpaceRightAnteriorSuperior: case nrrdSpaceRightAnteriorSuperiorTime: orientationSpaceAnatomical = "RAS"; volume->SetMetaInfo( META_SPACE, orientationSpaceAnatomical ); break; case nrrdSpaceLeftAnteriorSuperior: case nrrdSpaceLeftAnteriorSuperiorTime: orientationSpaceAnatomical = "LAS"; volume->SetMetaInfo( META_SPACE, orientationSpaceAnatomical ); break; case nrrdSpaceLeftPosteriorSuperior: case nrrdSpaceLeftPosteriorSuperiorTime: orientationSpaceAnatomical = "LPS"; volume->SetMetaInfo( META_SPACE, orientationSpaceAnatomical ); break; case nrrdSpace3DRightHanded: volume->SetMetaInfo( META_SPACE, "3DRH" ); break; case nrrdSpace3DLeftHanded: volume->SetMetaInfo( META_SPACE, "3DLH" ); break; case nrrdSpace3DRightHandedTime: volume->SetMetaInfo( META_SPACE, "3DRHT" ); break; case nrrdSpace3DLeftHandedTime: volume->SetMetaInfo( META_SPACE, "3DLHT" ); break; default: break; } volume->SetMetaInfo( META_SPACE_ORIGINAL, volume->GetMetaInfo( META_SPACE ) ); const Types::Coordinate directions[3][3] = { { nrrd->axis[0].spaceDirection[0] * spacing[0], nrrd->axis[0].spaceDirection[1] * spacing[0], nrrd->axis[0].spaceDirection[2] * spacing[0] }, { nrrd->axis[1].spaceDirection[0] * spacing[1], nrrd->axis[1].spaceDirection[1] * spacing[1], nrrd->axis[1].spaceDirection[2] * spacing[1] }, { nrrd->axis[2].spaceDirection[0] * spacing[2], nrrd->axis[2].spaceDirection[1] * spacing[2], nrrd->axis[2].spaceDirection[2] * spacing[2] } }; const Matrix3x3 m3( directions ); Matrix4x4 m4( m3 ); for ( int i = 0; i < 3; ++i ) m4[3][i] = ( MathUtil::IsFinite( nrrd->spaceOrigin[i] ) ? nrrd->spaceOrigin[i] : 0 ); volume->m_IndexToPhysicalMatrix = m4; if ( orientationSpaceAnatomical ) { char orientationImage[4]; AnatomicalOrientation::GetOrientationFromDirections( orientationImage, m4, orientationSpaceAnatomical ); volume->SetMetaInfo( META_IMAGE_ORIENTATION, orientationImage ); volume->SetMetaInfo( META_IMAGE_ORIENTATION_ORIGINAL, orientationImage ); volume->ChangeCoordinateSpace( AnatomicalOrientation::ORIENTATION_STANDARD ); } else { } if ( nrrd->spaceUnits[0] ) volume->SetMetaInfo( META_SPACE_UNITS_STRING, nrrd->spaceUnits[0] ); char* desc = nrrdKeyValueGet( nrrd, "description" ); if ( desc ) { volume->SetMetaInfo( META_IMAGE_DESCRIPTION, desc ); free( desc ); } nrrdNix( nrrd ); } catch ( char* err ) { StdErr << "ERROR: nrrd library returned error '" << err << "'\n"; free( err ); } return volume; } void VolumeFromFile::WriteNRRD ( const std::string& pathHdr, const UniformVolume& volume ) { UniformVolume::SmartPtr writeVolume( volume.Clone() ); if ( writeVolume->MetaKeyExists( META_SPACE_ORIGINAL ) ) writeVolume->ChangeCoordinateSpace( writeVolume->GetMetaInfo( META_SPACE_ORIGINAL ) ); void* val = const_cast( writeVolume->GetData()->GetDataPtr() ); int type = nrrdTypeUnknown; switch ( writeVolume->GetData()->GetType() ) { case TYPE_BYTE: type = nrrdTypeUChar; break; case TYPE_CHAR: type = nrrdTypeChar; break; case TYPE_USHORT: type = nrrdTypeUShort; break; case TYPE_SHORT: type = nrrdTypeShort; break; case TYPE_INT: type = nrrdTypeInt; break; case TYPE_UINT: type = nrrdTypeUInt; break; case TYPE_FLOAT: type = nrrdTypeFloat; break; case TYPE_DOUBLE: type = nrrdTypeDouble; break; default: break; } Nrrd *nval = nrrdNew(); NrrdIoState *nios = nrrdIoStateNew(); if ( VolumeIO::GetWriteCompressed() && nrrdEncodingGzip->available() ) { nrrdIoStateEncodingSet( nios, nrrdEncodingGzip ); nrrdIoStateSet( nios, nrrdIoStateZlibLevel, 9 ); } else { StdErr << "WARNING: Nrrd library does not support Gzip compression encoding.\n" << " Please add -DTEEM_ZLIB to compiler options when building Nrrd library.\n"; } try { if ( nrrdWrap_va( nval, val, type, (size_t)3, (size_t)writeVolume->m_Dims[0], (size_t)writeVolume->m_Dims[1], (size_t)writeVolume->m_Dims[2] ) ) { throw( biffGetDone(NRRD) ); } nrrdSpaceDimensionSet( nval, 3 ); if ( writeVolume->MetaKeyExists(META_SPACE_UNITS_STRING) ) { nval->spaceUnits[0] = strdup( writeVolume->GetMetaInfo( META_SPACE_UNITS_STRING ).c_str() ); nval->spaceUnits[1] = strdup( writeVolume->GetMetaInfo( META_SPACE_UNITS_STRING ).c_str() ); nval->spaceUnits[2] = strdup( writeVolume->GetMetaInfo( META_SPACE_UNITS_STRING ).c_str() ); } if ( writeVolume->MetaKeyExists( META_IMAGE_DESCRIPTION ) ) { nrrdKeyValueAdd( nval, "description", volume.GetMetaInfo( META_IMAGE_DESCRIPTION ).c_str() ); } int kind[NRRD_DIM_MAX] = { nrrdKindDomain, nrrdKindDomain, nrrdKindDomain }; nrrdAxisInfoSet_nva( nval, nrrdAxisInfoKind, kind ); const std::string space = writeVolume->GetMetaInfo( META_SPACE ); // if the volume has a direction table, write it to the Nrrd if ( space == "RAS" ) { nval->space = nrrdSpaceRightAnteriorSuperior; } else if ( space == "LAS" ) { nval->space = nrrdSpaceLeftAnteriorSuperior; } else if ( space == "LPS" ) { nval->space = nrrdSpaceLeftPosteriorSuperior; } else if ( space == "3DRH" ) { nval->space = nrrdSpace3DRightHanded; } else if ( space == "3DLH" ) { nval->space = nrrdSpace3DLeftHanded; } else if ( space == "3DRHT" ) { nval->space = nrrdSpace3DRightHandedTime; } else if ( space == "3DLHT" ) { nval->space = nrrdSpace3DLeftHandedTime; } else { if ( space.length() == 3 ) { writeVolume->ChangeCoordinateSpace( "RAS" ); nval->space = nrrdSpaceRightAnteriorSuperior; } } const AffineXform::MatrixType& matrix = writeVolume->m_IndexToPhysicalMatrix; double spaceDir[NRRD_DIM_MAX][NRRD_SPACE_DIM_MAX]; for ( int i = 0; i < 3; ++i ) { for ( int j = 0; j < 3; ++j ) spaceDir[i][j] = matrix[i][j]; } nrrdAxisInfoSet_nva( nval, nrrdAxisInfoSpaceDirection, spaceDir ); double origin[NRRD_DIM_MAX] = { matrix[3][0], matrix[3][1], matrix[3][2] }; if ( nrrdSpaceOriginSet( nval, origin ) ) { throw( biffGetDone(NRRD) ); } nrrdAxisInfoSet_va( nval, nrrdAxisInfoLabel, "x", "y", "z" ); if ( nrrdSave( pathHdr.c_str(), nval, nios ) ) { throw( biffGetDone(NRRD) ); } } catch ( char* err ) { StdErr << "ERROR: NrrdIO library returned error '" << err << "'\n"; free( err ); } nrrdIoStateNix( nios ); nrrdNix(nval); } //@} } // namespace cmtk #endif // #ifdef CMTK_BUILD_NRRD cmtk-3.3.1/libs/IO/cmtkVolumeFromFileNifti.cxx000066400000000000000000000474221276303427400212250ustar00rootroot00000000000000/* // // Copyright 1997-2011 Torsten Rohlfing // // Copyright 2004-2012, 2014 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 5319 $ // // $LastChangedDate: 2014-04-11 14:03:19 -0700 (Fri, 11 Apr 2014) $ // // $LastChangedBy: torstenrohlfing $ // */ #include "cmtkVolumeFromFile.h" #include #include #include #include #include #include #include #include "nifti1.h" #include "nifti1_io_math.h" #include #include #include #ifdef HAVE_ZLIB # include #endif #ifdef HAVE_SYS_STAT_H # include #endif namespace cmtk { /** \addtogroup IO */ //@{ /// Helper function - make qform in NIFTI header from affine matrix void __matrixToNiftiQform( nifti_1_header& header, const AffineXform::MatrixType matrix ) { // transpose and put into nifti_io's mat44 struct mat44 R; for ( int j = 0; j < 4; ++j ) { for ( int i = 0; i < 4; ++i ) { R.m[i][j] = matrix[j][i]; } } // compute quaternion representation float qb, qc, qd, qx, qy, qz, dx, dy, dz, qfac; nifti_mat44_to_quatern( R, &qb, &qc, &qd, &qx, &qy, &qz, &dx, &dy, &dz, &qfac ) ; // set header fields header.pixdim[0] = qfac; header.quatern_b = qb; header.quatern_c = qc; header.quatern_d = qd; header.qoffset_x = qx; header.qoffset_y = qy; header.qoffset_z = qz; } /**\note If the imported NIFTI file contains a qform and/or sform, and the resulting image object is later written back to a new NIFTI file (via VolumeFromFile::WriteNifti), then these transformations will be stored in the output file's qform and sform fields, * respectively. * \note If the header contains only a qform, then the qform will be used to initialize cmtk::UniformVolume::m_IndexToPhysicalMatrix, which determines the anatomy-based gross alignment of the image, regardless of qform_code * \note If the header contains only an sform, then the sform will be used instead, regardless of sform_code. * \note If the header contains both sform and qform, then the transformation is used whose code is NIFTI_XFORM_SCANNER_ANAT. If both sform_code and qform_code are NIFTI_XFORM_SCANNER_ANAT, then qform is used. * \note If the header contains neither a qform or an sform, then it is assumed that the image is oriented "RAS", i.e., fastest-moving array index from Left to Right, second fastest from Posterior to Anterior, and third fastest from Inferior to Superior. *\warning In keeping with the nifti1.h documentation, it is assumed that qform_code and sform_code will always be non-negative. If this assumption is violated, qform and sform will be switched in the output file (as written by VolumeFromFile::WriteNifti), or * one of them may be missing. */ const UniformVolume::SmartPtr VolumeFromFile::ReadNifti( const std::string& pathHdr, const bool detached, const bool readData ) { CompressedStream hdrStream( pathHdr ); if ( !hdrStream.IsValid() ) { StdErr << "ERROR: could not open Nifti header file " << pathHdr << "\n"; return UniformVolume::SmartPtr( NULL ); } nifti_1_header buffer; if ( sizeof(buffer) != hdrStream.Read( &buffer, 1, sizeof(buffer) ) ) { StdErr << "ERROR: could not read " << sizeof( buffer ) << " bytes from header file " << pathHdr << "\n"; return UniformVolume::SmartPtr( NULL ); } hdrStream.Close(); // determine if we need to byte-swap const int dim0 = buffer.dim[0]; const bool byteSwap = ((dim0>0) && (dim0<8)) ? false : true; #ifdef WORDS_BIGENDIAN FileHeader header( &buffer, !byteSwap ); #else FileHeader header( &buffer, byteSwap ); #endif short ndims = header.GetField( 40 ); if ( ndims < 3 ) { StdErr << "ERROR: image dimension " << ndims << " is smaller than 3 in file " << pathHdr << "\n"; return UniformVolume::SmartPtr( NULL ); } const DataGrid::IndexType::ValueType dims[3] = { header.GetField( 42 ), header.GetField( 44 ), header.GetField( 46 ) }; const int dims3 = header.GetField( 48 ); if ( (ndims > 3) && (dims3 > 1) ) { StdErr << "WARNING: dimension " << ndims << " is greater than 3 in file " << pathHdr << "\n"; } float pixelDim[3]; header.GetArray( pixelDim, 80, 3 ); UniformVolume::SmartPtr volume( new UniformVolume( DataGrid::IndexType::FromPointer( dims ), fabs( pixelDim[0] ), fabs( pixelDim[1] ), fabs( pixelDim[2] ) ) ); // Nifti is in RAS space. const char *const niftiSpace = "RAS"; volume->SetMetaInfo( META_SPACE, niftiSpace ); volume->SetMetaInfo( META_SPACE_ORIGINAL, niftiSpace ); const char xyzt_units = header.GetField( offsetof( struct nifti_1_header, xyzt_units ) ); if ( xyzt_units & NIFTI_UNITS_MM ) volume->SetMetaInfo( META_SPACE_UNITS_STRING, "mm" ); else if ( xyzt_units & NIFTI_UNITS_MICRON ) volume->SetMetaInfo( META_SPACE_UNITS_STRING, "micron" ); else if ( xyzt_units & NIFTI_UNITS_METER ) volume->SetMetaInfo( META_SPACE_UNITS_STRING, "m" ); const short qform_code = header.GetField( offsetof(nifti_1_header,qform_code) ); const short sform_code = header.GetField( offsetof(nifti_1_header,sform_code) ); if ( sform_code > NIFTI_XFORM_UNKNOWN ) { float srow_x[4], srow_y[4], srow_z[4]; header.GetArray( srow_x, offsetof(nifti_1_header,srow_x), 4 ); header.GetArray( srow_y, offsetof(nifti_1_header,srow_y), 4 ); header.GetArray( srow_z, offsetof(nifti_1_header,srow_z), 4 ); const Types::Coordinate directions[4][4] = { { srow_x[0], srow_y[0], srow_z[0], 0 }, { srow_x[1], srow_y[1], srow_z[1], 0 }, { srow_x[2], srow_y[2], srow_z[2], 0 }, { srow_x[3], srow_y[3], srow_z[3], 1 } }; Matrix4x4 m4( directions ); if ( (sform_code == NIFTI_XFORM_SCANNER_ANAT) || (qform_code == NIFTI_XFORM_UNKNOWN) ) volume->m_IndexToPhysicalMatrix = m4; // NIFTI says, sform_code must be > 0, so should be safe to use "abs" and make negative to distinguish from qform (below) volume->m_AlternativeIndexToPhysicalMatrices[-abs(sform_code)] = m4; } if ( qform_code > NIFTI_XFORM_UNKNOWN ) { const float qb = header.GetField( offsetof(nifti_1_header,quatern_b) ); const float qc = header.GetField( offsetof(nifti_1_header,quatern_c) ); const float qd = header.GetField( offsetof(nifti_1_header,quatern_d) ); const float qx = header.GetField( offsetof(nifti_1_header,qoffset_x) ); const float qy = header.GetField( offsetof(nifti_1_header,qoffset_y) ); const float qz = header.GetField( offsetof(nifti_1_header,qoffset_z) ); const double qfac = (header.GetField( offsetof(nifti_1_header,pixdim) ) >= 0) ? 1.0f : -1.0f; const mat44 m44 = nifti_quatern_to_mat44( qb, qc, qd, qx, qy, qz, pixelDim[0], pixelDim[1], pixelDim[2], qfac ); Matrix4x4 m4; for ( int j = 0; j < 4; ++j ) { for ( int i = 0; i < 4; ++i ) { m4[i][j] = m44.m[j][i]; } } // qform overrides sform if both exist if ( (qform_code == NIFTI_XFORM_SCANNER_ANAT) || (sform_code != NIFTI_XFORM_SCANNER_ANAT) ) volume->m_IndexToPhysicalMatrix = m4; // NIFTI says, qform_code must be > 0, so should be safe to use "abs" volume->m_AlternativeIndexToPhysicalMatrices[abs(qform_code)] = m4; } char orientationImage[4]; AnatomicalOrientation::GetOrientationFromDirections( orientationImage, volume->m_IndexToPhysicalMatrix, niftiSpace ); volume->SetMetaInfo( META_IMAGE_ORIENTATION, orientationImage ); volume->SetMetaInfo( META_IMAGE_ORIENTATION_ORIGINAL, orientationImage ); if ( header.GetField( 148 ) ) { char desc[81]; desc[80] = 0; volume->SetMetaInfo( META_IMAGE_DESCRIPTION, std::string( header.GetFieldString( 148, desc, 80 ) ) ); } // don't read data, we're done here. if ( ! readData ) return volume; ScalarDataType dtype; switch ( header.GetField( 70 ) ) { case DT_NONE: case DT_BINARY: case DT_COMPLEX: case DT_COMPLEX128: case DT_COMPLEX256: case DT_INT64: case DT_UINT64: case DT_FLOAT128: case DT_RGB: case DT_ALL: default: StdErr << "ERROR: unsupported data type " << header.GetField( 70 ) << " in Nifti file " << pathHdr << "\n"; return volume; case DT_UNSIGNED_CHAR: dtype = TYPE_BYTE; break; case DT_INT8: dtype = TYPE_CHAR; break; case DT_SIGNED_SHORT: dtype = TYPE_SHORT; break; case DT_SIGNED_INT: dtype = TYPE_INT; break; case DT_FLOAT: dtype = TYPE_FLOAT; break; case DT_DOUBLE: dtype = TYPE_DOUBLE; break; case DT_UINT16: dtype = TYPE_USHORT; break; case DT_UINT32: dtype = TYPE_UINT; break; } size_t offset = static_cast( header.GetField( 108 ) ); std::string pathImg = pathHdr; if ( detached ) { const size_t period = pathImg.rfind( ".hdr" ); if ( period != std::string::npos ) pathImg.replace( period, 4, ".img" ); offset = 0; } CompressedStream stream( pathImg ); if ( stream.IsValid() ) { stream.Seek( offset, SEEK_CUR ); TypedArray::SmartPtr data( TypedArray::Create( dtype, volume->GetNumberOfPixels() ) ); if ( data->GetDataSize() == stream.Read( data->GetDataPtr(), data->GetItemSize(), data->GetDataSize() ) ) { if ( byteSwap ) data->ChangeEndianness(); volume->SetData( data ); } else { StdErr << "ERROR: could not read " << data->GetDataSize() << " pixels from Nifti image file " << pathImg << "\n"; } } else { StdErr << "ERROR: could not open Nifti image file " << pathImg << "\n"; } // if we read volume data (as opposed to grid geometry only) then check intent_code and set DataClass accordingly. if ( volume->GetData() ) { const short intent_code = header.GetField( 68 ); switch ( intent_code ) { case NIFTI_INTENT_LABEL: case NIFTI_INTENT_NEURONAME: volume->GetData()->SetDataClass( DATACLASS_LABEL ); break; default: volume->GetData()->SetDataClass( DATACLASS_GREY ); break; } } return volume; } void VolumeFromFile::WriteNifti ( const std::string& path, const UniformVolume& volume ) { bool detachedHeader = false; bool forceCompressed = false; std::string pathImg( path ); // first, look for .gz size_t suffixPosGz = pathImg.rfind( std::string( ".gz" ) ); if ( suffixPosGz != std::string::npos ) { // found: set force compression flag and remove .gz from path forceCompressed = true; pathImg = pathImg.substr( 0, suffixPosGz ); } std::string pathHdr( pathImg ); size_t suffixPos = pathHdr.rfind( ".img" ); if ( suffixPos != std::string::npos ) { detachedHeader = true; pathHdr.replace( suffixPos, 4, ".hdr" ); } UniformVolume::SmartPtr writeVolume( volume.Clone() ); writeVolume->ChangeCoordinateSpace( "RAS" ); const TypedArray* data = writeVolume->GetData(); if ( ! data ) return; nifti_1_header header; memset( &header, 0, sizeof( header ) ); header.sizeof_hdr = 348; // header size header.dim_info = 0; // ndims header.dim[0] = 3; // dimensions header.dim[1] = writeVolume->GetDims()[AXIS_X]; header.dim[2] = writeVolume->GetDims()[AXIS_Y]; header.dim[3] = writeVolume->GetDims()[AXIS_Z]; header.dim[4] = 1; header.dim[5] = 1; header.dim[6] = 1; header.dim[7] = 1; header.pixdim[0] = 1.0; header.pixdim[1] = static_cast( writeVolume->m_Delta[AXIS_X] ); header.pixdim[2] = static_cast( writeVolume->m_Delta[AXIS_Y] ); header.pixdim[3] = static_cast( writeVolume->m_Delta[AXIS_Z] ); header.pixdim[4] = 0.0; header.pixdim[5] = 0.0; const std::string spaceUnits = writeVolume->GetMetaInfo( META_SPACE_UNITS_STRING ); if ( spaceUnits == "mm" ) header.xyzt_units = NIFTI_UNITS_MM; else if ( (spaceUnits == "micron") || (spaceUnits == "um") ) header.xyzt_units = NIFTI_UNITS_MICRON; else if ( (spaceUnits == "m") || (spaceUnits == "meter") ) header.xyzt_units = NIFTI_UNITS_METER; for ( std::map::const_iterator it = volume.m_AlternativeIndexToPhysicalMatrices.begin(); it != volume.m_AlternativeIndexToPhysicalMatrices.end(); ++it ) { const AffineXform::MatrixType m4 = it->second; // this came from a qform if ( it->first > 0 ) { header.qform_code = it->first; __matrixToNiftiQform( header, it->second ); } // this came from an sform if ( it->first < 0 ) { header.sform_code = abs( it->first ); for ( int i = 0; i < 4; ++i ) { header.srow_x[i] = static_cast( m4[i][0] ); header.srow_y[i] = static_cast( m4[i][1] ); header.srow_z[i] = static_cast( m4[i][2] ); } } } // fallback - we want at least a generic qform to be set to the volume's index-to-physical matrix if ( ! (header.qform_code || header.sform_code) ) { #ifdef IGNORE // This piece of code, when replacing the next two lines (qform stuff) would make CMTK entirely backward-compatible (release 2.3 and earlier), but non-NIFTI-compliant. const AffineXform::MatrixType m4 = volume.m_IndexToPhysicalMatrix; header.sform_code = NIFTI_XFORM_SCANNER_ANAT; for ( int i = 0; i < 4; ++i ) { header.srow_x[i] = static_cast( m4[i][0] ); header.srow_y[i] = static_cast( m4[i][1] ); header.srow_z[i] = static_cast( m4[i][2] ); } #else header.qform_code = NIFTI_XFORM_SCANNER_ANAT; __matrixToNiftiQform( header, volume.m_IndexToPhysicalMatrix ); #endif } switch ( data->GetType() ) { default: header.datatype = DT_UNKNOWN; header.bitpix = 0; break; case TYPE_BYTE: header.datatype = DT_UNSIGNED_CHAR; header.bitpix = 8 * sizeof(byte); break; case TYPE_CHAR: header.datatype = DT_INT8; header.bitpix = 8 * sizeof(char); break; case TYPE_SHORT: header.datatype = DT_INT16; header.bitpix = 8 * sizeof(short); break; case TYPE_USHORT: header.datatype = DT_UINT16; header.bitpix = 8 * sizeof(unsigned short); break; case TYPE_INT: header.datatype = DT_INT32; header.bitpix = 8 * sizeof(int); break; case TYPE_UINT: header.datatype = DT_UINT32; header.bitpix = 8 * sizeof(unsigned int); break; case TYPE_FLOAT: header.datatype = DT_FLOAT; header.bitpix = 8 * sizeof(float); break; case TYPE_DOUBLE: header.datatype = DT_DOUBLE; header.bitpix = 8 * sizeof(double); break; } if ( data->GetDataClass() == DATACLASS_LABEL ) header.intent_code = NIFTI_INTENT_LABEL; else header.intent_code = 0; // determine data range; const Types::DataItemRange dataRange = data->GetRange(); header.cal_max = static_cast( dataRange.m_UpperBound ); header.cal_min = static_cast( dataRange.m_LowerBound ); if ( volume.MetaKeyExists( META_IMAGE_DESCRIPTION ) ) { memset( header.descrip, 0, sizeof( header.descrip ) ); strncpy( header.descrip, volume.GetMetaInfo( META_IMAGE_DESCRIPTION ).c_str(), sizeof( header.descrip )-1 ); } // see if we have the Phase Encode Direction from DICOM - set axis information in header if we do if ( volume.MetaKeyExists( META_IMAGE_SLICE_PEDIRECTION ) ) { const std::string peDirection = volume.GetMetaInfo( META_IMAGE_SLICE_PEDIRECTION ); if ( peDirection == "COL" ) header.dim_info = FPS_INTO_DIM_INFO( 1, 2, 3 ); else header.dim_info = FPS_INTO_DIM_INFO( 2, 1, 3 ); } if ( volume.MetaKeyExists( META_IMAGE_SLICEORDER ) ) { // here we assume that this is a volume straight out of the DICOM stacker, which keeps slices along dim[3] if ( header.dim_info == 0 ) // see if we previously set dim_info based on PE direction header.dim_info = FPS_INTO_DIM_INFO( 0, 0, 3 ); // if not, we need to set at least the slice direction const std::string sliceOrder = volume.GetMetaInfo( META_IMAGE_SLICEORDER ); if ( sliceOrder == META_IMAGE_SLICEORDER_SI ) header.slice_code = NIFTI_SLICE_SEQ_INC; else if ( sliceOrder == META_IMAGE_SLICEORDER_SD ) header.slice_code = NIFTI_SLICE_SEQ_DEC; else if ( sliceOrder == META_IMAGE_SLICEORDER_AI ) header.slice_code = NIFTI_SLICE_ALT_INC; else if ( sliceOrder == META_IMAGE_SLICEORDER_AD ) header.slice_code = NIFTI_SLICE_ALT_DEC; else if ( sliceOrder == META_IMAGE_SLICEORDER_AI2 ) header.slice_code = NIFTI_SLICE_ALT_INC2; else if ( sliceOrder == META_IMAGE_SLICEORDER_AD2 ) header.slice_code = NIFTI_SLICE_ALT_DEC2; header.slice_start = 0; header.slice_end = header.dim[3]-1; header.slice_duration = atof( volume.GetMetaInfo( META_IMAGE_SLICEDURATION ).c_str() ) / 1000; // convert from msec to s for AFNI header.xyzt_units |= NIFTI_UNITS_SEC; } #ifdef _MSC_VER const char *const modestr = "wb"; #else const char *const modestr = "w"; #endif if ( detachedHeader ) { memcpy( &header.magic, "ni1\x00", 4 ); header.vox_offset = 0; FILE *hdrFile = fopen( pathHdr.c_str(), modestr ); if ( hdrFile ) { fwrite( &header, 1, sizeof( header ), hdrFile ); const int extension = 0; fwrite( &extension, 1, 4, hdrFile ); fclose( hdrFile ); } else { StdErr << "ERROR: NIFTI header file '" << pathHdr << "' could not be opened for writing!\n"; } } else { memcpy( &header.magic, "n+1\x00", 4 ); header.vox_offset = 352; } if ( VolumeIO::GetWriteCompressed() || forceCompressed ) { struct stat buf; if ( ! stat( pathImg.c_str(), &buf ) ) { StdErr << "WARNING: NIFTI file '" << path << "' will be written compressed, but uncompressed file exists!\n"; } gzFile imgFile = gzopen( (pathImg+".gz").c_str(), modestr ); if ( imgFile ) { if ( ! detachedHeader ) { gzwrite( imgFile, &header, sizeof( header ) ); const int extension = 0; gzwrite( imgFile, &extension, 4 ); } const size_t dataSize = data->GetItemSize() * data->GetDataSize(); if ( dataSize != CompressedStream::Zlib::StaticSafeWrite( imgFile, data->GetDataPtr(), dataSize ) ) { StdErr << "WARNING: gzwrite() returned error when writing to " << pathImg << "\n"; } gzclose( imgFile ); } else { StdErr << "ERROR: could not open file '" << pathImg << ".gz' for writing\n"; } } else { FILE *imgFile = fopen( pathImg.c_str(), modestr ); if ( imgFile ) { if ( ! detachedHeader ) { fwrite( &header, 1, sizeof( header ), imgFile ); const int extension = 0; fwrite( &extension, 1, 4, imgFile ); } fwrite( data->GetDataPtr(), data->GetItemSize(), data->GetDataSize(), imgFile ); fclose( imgFile ); } else { StdErr << "ERROR: could not open file '" << pathImg << "' for writing\n"; } } } } // namespace cmtk cmtk-3.3.1/libs/IO/cmtkVolumeFromFileVanderbilt.cxx000066400000000000000000000076031276303427400222430ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2013 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4908 $ // // $LastChangedDate: 2013-10-01 13:06:26 -0700 (Tue, 01 Oct 2013) $ // // $LastChangedBy: torstenrohlfing $ // */ #include "cmtkVolumeFromFile.h" #include #include namespace cmtk { const UniformVolume::SmartPtr VolumeFromFile::ReadVanderbilt( const std::string& path ) { FILE *fp = fopen( path.c_str(), "r" ); if ( !fp ) return UniformVolume::SmartPtr( NULL ); int dims[3] = { 1, 1, 1 }; double calib[3] = { 0, 0, 0 }; char orientation[] = "RAS"; // parse header file for image dimensios etc. char line[96], key[32], value[64]; while ( !feof( fp ) ) { fgets( line, 96, fp ); if ( 2 == sscanf( line, "%32[a-zA-Z ]:= %64[0-9.: ]", key, value ) ) { if ( ! strcmp( key, "Columns " ) ) { dims[0] = atoi( value ); } else if ( ! strcmp( key, "Rows " ) ) { dims[1] = atoi( value ); } else if ( ! strcmp( key, "Slices " ) ) { dims[2] = atoi( value ); } else if ( ! strcmp( key, "Pixel size " ) ) { if ( 2 != sscanf( value, "%20lf : %20lf", calib, calib+1 ) ) { StdErr << "WARNING: could not determine pixel size from line '" << line << "'\n"; calib[0] = calib[1] = 1.0; } } else if ( ! strcmp( key, "Slice thickness " ) ) { calib[2] = static_cast( atof( value ) ); } } else { char axes[3]; if ( 3 == sscanf( line, "Patient orientation := %c : %c : %c", &axes[0], &axes[1], &axes[2] ) ) { const char *const translation = "PbcdeSgIijkRmnoAqLstuvwxyz"; for ( int i = 0; i < 3; ++i ) { orientation[i] = translation[axes[i]-'A']; } } } } fclose( fp ); // create volume, for the time being with empty data array. UniformVolume::SmartPtr volume( new UniformVolume( DataGrid::IndexType::FromPointer( dims ), calib[0], calib[1], calib[2] ) ); volume->SetMetaInfo( META_IMAGE_ORIENTATION, orientation ); volume->SetMetaInfo( META_IMAGE_ORIENTATION_ORIGINAL, orientation ); // generate image filename from header file path. std::string imagePath = path; const size_t lastSlashPos = path.rfind( '/' ); if ( lastSlashPos == std::string::npos ) imagePath = "image.bin"; else imagePath = path.substr( 0, lastSlashPos+1 ) + "image.bin"; // open image file and read data. CompressedStream imageStream( imagePath ); if ( imageStream.IsValid() ) { TypedArray::SmartPtr data = TypedArray::Create( TYPE_SHORT, dims[0] * dims[1] * dims[2] ); imageStream.Read( data->GetDataPtr(), data->GetItemSize(), data->GetDataSize() ); #ifndef WORDS_BIGENDIAN // change endianness from Sun to whatever we're currently on. data->ChangeEndianness(); #endif // set volume data array with what we just read. volume->SetData( TypedArray::SmartPtr( data ) ); } else { StdErr << "ERROR: cannot open image file " << imagePath << "\n"; throw ExitException( 1 ); } return volume; } } // namespace cmtk cmtk-3.3.1/libs/IO/cmtkVolumeFromSlices.cxx000066400000000000000000000243101276303427400205650ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2011, 2013 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4873 $ // // $LastChangedDate: 2013-09-24 12:53:12 -0700 (Tue, 24 Sep 2013) $ // // $LastChangedBy: torstenrohlfing $ // */ #include "cmtkVolumeFromSlices.h" #include #include #include #include #include #include #include namespace cmtk { /** \addtogroup IO */ //@{ void VolumeFromSlices::InitSequence ( const ScalarImage* image, const unsigned int numberOfSlices ) { Padding = false; Spacing[0] = image->GetPixelSize( AXIS_X ); Spacing[1] = image->GetPixelSize( AXIS_Y ); ImagePosition = image->GetImageOrigin(); Dims[0] = image->GetDims()[AXIS_X]; Dims[1] = image->GetDims()[AXIS_Y]; Dims[2] = numberOfSlices; BytesPerPixel = image->GetPixelData()->GetItemSize(); DataType = image->GetPixelData()->GetType(); DataSize = Dims[0] * Dims[1] * Dims[2]; VolumeDataArray = TypedArray::Create( image->GetPixelData()->GetType(), DataSize ); // Allocate array for axis sample points for ( unsigned int idx = 0; idx<3; ++idx ) Points[idx] = Memory::ArrayC::Allocate( Dims[idx] ); // Set sample points for uniform original x- and y-axis for ( unsigned int dim=0; dim<2; ++dim ) { for ( int idx=0; idx < Dims[dim]; ++idx ) { Points[dim][idx] = idx * Spacing[dim]; } // Set size in axis direction. Size[dim] = (Dims[dim]-1) * Spacing[dim]; } } char* VolumeFromSlices::AllocDataArray ( const int bytesPerPixel, const int dataSize ) const { return Memory::ArrayC::Allocate( bytesPerPixel * dataSize ); } TypedArray::SmartPtr VolumeFromSlices::EncapDataArray ( const ScalarDataType dtype, void *const data, const int data_size ) const { return TypedArray::Create( dtype, data, data_size, Padding, &PaddingValue, Memory::ArrayC::Delete ); } const char* VolumeFromSlices::FillPlane ( unsigned int& plane, const ScalarImage* image ) { char* rawDataPtr = static_cast( VolumeDataArray->GetDataPtr() ); const size_t bytesPerBlock = BytesPerPixel * Dims[0] * Dims[1]; for ( int planeIdx = 0; planeIdx < image->GetNumberOfFrames(); ++planeIdx, ++plane ) { const char *check = this->CheckImage( plane, image, planeIdx ); if ( check ) return check; memcpy( rawDataPtr + bytesPerBlock * plane, image->GetPixelData()->GetDataPtr(), bytesPerBlock ); // set world coordinate of the plane just read Types::Coordinate slicePosition = (ImagePosition - FirstImagePosition).RootSumOfSquares(); slicePosition = 1e-6 * ( MathUtil::Round( 1e+6 * slicePosition) ); Points[2][plane] = slicePosition; } return NULL; } UniformVolume::SmartPtr VolumeFromSlices::FinishVolume ( Types::Coordinate& sliceOffset, int& sliceDirection ) { Types::Coordinate *next_point = Points[2]; sliceOffset = next_point[0]; sliceDirection = MathUtil::Sign(next_point[1]-sliceOffset); Types::Coordinate previous_plane = sliceOffset; // normalize z-coordinates so that they start with zero and increase with // growing z-index. *next_point = 0; int idx; for ( idx=1, ++next_point; idx < Dims[2]; ++idx, ++next_point ) { Types::Coordinate next_plane = *next_point; (*next_point) = *(next_point-1)+fabs(next_plane-previous_plane); previous_plane = next_plane; } Size[2] = *(next_point-1); // Encapsulate raw volume data. if ( !VolumeDataArray ) VolumeDataArray = TypedArray::SmartPtr( this->EncapDataArray( SelectDataTypeInteger( BytesPerPixel, SignBit ), RawData, DataSize ) ); const Types::Coordinate* aux[] = { Points[0], Points[1], Points[2] }; UniformVolume::SmartPtr Result = this->ConstructVolume( Dims, Size, aux, VolumeDataArray ); // if something went wrong assembling the volume, then return NULL pointer if ( ! Result ) return Result; // clear reference, since now linked by volume. VolumeDataArray = TypedArray::SmartPtr::Null(); for ( idx = 0; idx<3; ++idx ) Memory::ArrayC::Delete( Points[idx] ); Result->SetMetaInfo( META_SPACE, "LPS" ); Result->SetMetaInfo( META_SPACE_ORIGINAL, "LPS" ); // actual image directions const Types::Coordinate spacing[3] = { (Size[0] / (Dims[0]-1)), (Size[1] / (Dims[1]-1)), (Size[2] / (Dims[2]-1)) }; this->ImageOrientation[0] *= spacing[0] / this->ImageOrientation[0].RootSumOfSquares(); this->ImageOrientation[1] *= spacing[1] / this->ImageOrientation[1].RootSumOfSquares(); this->IncrementVector *= spacing[2] / this->IncrementVector.RootSumOfSquares(); const Types::Coordinate directions[3][3] = { { this->ImageOrientation[0][0], this->ImageOrientation[0][1], this->ImageOrientation[0][2] }, { this->ImageOrientation[1][0], this->ImageOrientation[1][1], this->ImageOrientation[1][2] }, { this->IncrementVector[0], this->IncrementVector[1], this->IncrementVector[2] } }; const Matrix3x3 m3( directions ); Matrix4x4 m4( m3 ); for ( int i = 0; i < 3; ++i ) m4[3][i] = this->FirstImagePosition[i]; Result->m_IndexToPhysicalMatrix = m4; // const std::string orientationString0 = Result->GetOrientationFromDirections(); Result->ChangeCoordinateSpace( AnatomicalOrientation::ORIENTATION_STANDARD ); const std::string orientationString = Result->GetOrientationFromDirections(); Result->SetMetaInfo( META_SPACE_UNITS_STRING, "mm" ); // seems to be implied in DICOM Result->SetMetaInfo( META_IMAGE_ORIENTATION, orientationString ); Result->SetMetaInfo( META_IMAGE_ORIENTATION_ORIGINAL, orientationString ); return Result; } const char* VolumeFromSlices::CheckImage ( const int plane, const ScalarImage* image, const unsigned int frame ) { if ( ( this->Dims[0] != image->GetDims()[AXIS_X] ) || ( this->Dims[1] != image->GetDims()[AXIS_Y] ) ) return "Image size mismatch"; if ( ( fabs( image->GetPixelSize( AXIS_X ) - Spacing[0] ) > CMTK_MAX_CALIB_ERROR ) || ( fabs( image->GetPixelSize( AXIS_Y ) - Spacing[1] ) > CMTK_MAX_CALIB_ERROR ) ) return "Calibration mismatch"; // not too many things can go wrong for the very first slice. if ( plane == 0 ) { FirstImagePosition = ImagePosition = image->GetImageOrigin( frame ); ImageOrientation[0] = image->GetImageDirectionX(); ImageOrientation[1] = image->GetImageDirectionY(); return NULL; } // check whether this slice is parallel to the previous one for ( unsigned int dim = 0; dim<3; ++dim ) { if ( ( fabs( ImageOrientation[0][dim] - image->GetImageDirectionX()[dim] ) > CMTK_MAX_CALIB_ERROR ) || ( fabs( ImageOrientation[1][dim] - image->GetImageDirectionY()[dim] ) > CMTK_MAX_CALIB_ERROR ) ) return "Non-parallel image planes"; } // Second++ slice: Compute slice-to-slice vector ScalarImage::SpaceVectorType imageToImage = image->GetImageOrigin( frame ) - ImagePosition; if ( imageToImage.MaxAbsValue() < CMTK_MAX_LOCALIZE_ERROR ) { StdErr.printf( "Two slices at position (%f,%f,%f)\n", (float)ImagePosition[0], (float)ImagePosition[1], (float)ImagePosition[2] ); return "Encountered two slices in identical location."; } else imageToImage /= imageToImage.MaxAbsValue(); // Check whether slice-to-slice direction is orthogonal to image // axes. const Types::Coordinate scalarX = fabs( imageToImage * ImageOrientation[0] ); const Types::Coordinate scalarY = fabs( imageToImage * ImageOrientation[1] ); if ( (scalarX > CMTK_MAX_ANGLE_ERROR) || (scalarY > CMTK_MAX_ANGLE_ERROR) ) { fprintf( stderr, "errX = %f, errY = %f, thresh = %f\n", scalarX, scalarY, CMTK_MAX_ANGLE_ERROR ); return "Data grid must be orthogonal."; } // if this is the second slice, save increment vector for further tests. if ( plane == 1 ) IncrementVector = imageToImage; // otherwise, perform these tests else { // Are we still going in the same direction? if ( (imageToImage - IncrementVector).MaxAbsValue() > CMTK_MAX_LOCALIZE_ERROR ) { // Nope, but why? Let's give user some more hints if ( ( (imageToImage * IncrementVector) > 0 ) ) // Basically same direction, so FOV has changed return "Field-of-view mismatch"; else // Completely different direction: We're going backwards return "Encountered altering slice direction."; } } // Finally, save essential information about current image. ImagePosition = image->GetImageOrigin( frame ); return NULL; } UniformVolume::SmartPtr VolumeFromSlices::ConstructVolume ( const DataGrid::IndexType& dims, const UniformVolume::CoordinateVectorType& size, const Types::Coordinate *points[3], TypedArray::SmartPtr& data ) const { bool isUniform = true; Types::Coordinate error = 0; for ( unsigned int dim=0; (dim<3) && isUniform; ++dim ) { Types::Coordinate delta = points[dim][1] - points[dim][0]; for ( int idx=2; (idx ( this->m_Tolerance * delta ) ) isUniform = false; error = fabs( delta - (points[dim][idx] - points[dim][idx-1]) ); } } if ( !isUniform ) { StdErr << "ERROR: not a uniform volume (error = " << error << ")\n"; return UniformVolume::SmartPtr( NULL ); } return UniformVolume::SmartPtr( new UniformVolume( dims, size, data ) ); } } // namespace cmtk cmtk-3.3.1/libs/IO/cmtkVolumeFromSlices.h000066400000000000000000000227061276303427400202210ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2011, 2013 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4895 $ // // $LastChangedDate: 2013-09-30 11:36:32 -0700 (Mon, 30 Sep 2013) $ // // $LastChangedBy: torstenrohlfing $ // */ /**\name Build Volume from slice images. *\author Torsten Rohlfing */ #ifndef __cmtkVolumeFromSlices_h_included_ #define __cmtkVolumeFromSlices_h_included_ #include #include #include #include #include #include /**\name Error bounds for various floating point situations. */ //@{ /// Maximum calibration error in mm. Provides tolerance for fp rounding. #define CMTK_MAX_CALIB_ERROR 1e-5 /** Maximum angular error. * This is the maximum difference of grid angle cosines from 90 degrees. * Must be more tolerant than MAX_CALIB_ERROR as the imaging devices have * a ROTTEN floating-point accuracy. */ #define CMTK_MAX_ANGLE_ERROR 1e-3 /** Maximum error allowed for image localization. * Must be more tolerant than MAX_CALIB_ERROR again as the imaging devices have * an even WORSE than ROTTEN floating-point accuracy. */ #define CMTK_MAX_LOCALIZE_ERROR 1e-2 //@} namespace cmtk { /** \addtogroup IO */ //@{ /// Class for building 3D fields from slice image data. class VolumeFromSlices { public: /// This class. typedef VolumeFromSlices Self; /// Default constructor. VolumeFromSlices( const Types::Coordinate tolerance = 0 /*!< Tolerance for floating point comparisons, e.g., when testing for uniform pixel/slice spacings.*/ ) : m_Tolerance( tolerance ), DataSize( 0 ), RawData( NULL ), VolumeDataArray( NULL ), BytesPerPixel( 0 ), SignBit( false ), DataType( TYPE_NONE ), IncX( 0 ), IncY( 0 ), BlockSize( 0 ), Padding( false ) { Points[0] = Points[1] = Points[2] = NULL; } /// Virtual dummy destructor. virtual ~VolumeFromSlices() {} protected: /// Stored floating point tolerance. Types::Coordinate m_Tolerance; /** Start creation of new volume. */ void InitSequence( const ScalarImage* image, const unsigned int numberOfSlices ); /** Allocate memory for the 3D image data. */ virtual char* AllocDataArray( const int bytesperpixel, const int data_size ) const; /** Put image data into a custom data structure. * By default, the image data is encapsulated into a newly created * TypedArray object. */ virtual TypedArray::SmartPtr EncapDataArray( const ScalarDataType dtype, void *const data, const int data_size ) const; /** Copy one slice of data into field. * This function rearranges the bytes in the given 2D image so that after * all slices have been copied to the 3D array, the xy-plane is always axial * with respect to the patient. */ const char* FillPlane ( unsigned int& plane, const ScalarImage* image ); /** Finish volume creation and free temporary storage. *\param sliceOffset This reference is set to the absolute slice coordinate * of the original image that became the first plane in the resulting volume. * This can be used to write images with precisely the same absolute * positions later. *\param sliceDirection This reference is set to a flag indicating whether * in the original images the slice positions increased (+1) or decreased * (-1) with increasing slice index. *\return The newly created volume object as returned by ConstructVolume(). */ UniformVolume::SmartPtr FinishVolume( Types::Coordinate& sliceOffset, int& sliceDirection ); /** Finish volume creation without additional information. * If the additional information returned by the previous FinishVolume(...) * function is not reuqired, this function may be called instead. */ UniformVolume::SmartPtr FinishVolume () { Types::Coordinate dummy_c; int dummy_i; return FinishVolume( dummy_c, dummy_i ); } /** Construct Volume object. * This function takes the geometry and data as read from the slice images. * Its purpose is to compose an Volume object out of these components. * By default, an instance of UniformVolume will be created for * uniformly-spaced data, while an instance of igsRectilinearVolume is * created for non-uniformly spaced images. Derived classes my override this * function to create specialized volume classes derived from the * aforementioned base classes. *\param Dims Dimensions of the 3D data, ie. number of voxels in x-, y-, and * z-direction. *\param Size Extents of data in [mm] in x-, y-, and z-direction. *\param Points Positions of the grid points (voxels) with respect to the * three spatial coordinates. In case the points are uniformly spaced in all * three dimensions, an instance of UniformVolume is created with grid * spacing as defined by the uniform spacings in this array. Otherwise, an * instance of igsRectilinearVolume is created with precisely this array as * its "Points" field. *\param Data Pixel data array for the new volume. *\see igsRectilinearVolume#Points *\return The newly created instance of a class derived from Volume. *\see Volume */ virtual UniformVolume::SmartPtr ConstructVolume( const DataGrid::IndexType& Dims, const UniformVolume::CoordinateVectorType& Size, const Types::Coordinate *Points[3], TypedArray::SmartPtr& Data ) const; /** Check image consistency. * This function is used to verify that all images share the same matrix * size, identical pixel calibrations, and the same primitive data type. * Also, slices with zero distance and changing directions of the table * position are detected and reported. *\param plane Index of this image in the sequence. *\param image A reference to a structure describing the next image. *\param frame Index of frame within a multi-frame image. *\return A pointer to an error message, of NULL if image was okay. */ const char* CheckImage ( const int plane, const ScalarImage* image, const unsigned int frame = 0 ); /** Handle an error condition. * Basically, this function is intended to notify the user of errors * occurring during the volume building process, such as inconsistent images. * By default, all errors are simply printed to the standard error output. * Derived classes may override this function to provide * environment-specific interaction. *\param message A textual description of the error condition. */ virtual void HandleError ( const char* message ) const { fputs ( message, stderr ); } private: /** Dimensions of the 3D data. * This array is filled with the number of voxels in x-, y-, and z-direction. */ DataGrid::IndexType Dims; /** Size of the 3D data. * This array holds the extents of the 3D data in x-, y-, and z-direction. * All values are in real-world coordinates, ie. [mm]. */ UniformVolume::CoordinateVectorType Size; /** Axes points of the constructed volume. * During assembly of the 3D data, this array is filled with the positions * of the grid points in all three dimensions. */ Types::Coordinate* Points[3]; /// Number of voxels. unsigned int DataSize; /// Pointer to the volume data. char *RawData; /// Volume data array. TypedArray::SmartPtr VolumeDataArray; /// Number of allocated bytes per voxel. int BytesPerPixel; /// Is the data signed? bool SignBit; /// Primitive image data type. ScalarDataType DataType; /// Pixel calibration of the slice images. Types::Coordinate Spacing[2]; /// X-coordinate of image origin. ScalarImage::SpaceVectorType FirstImagePosition; /// X-coordinate of image origin. ScalarImage::SpaceVectorType ImagePosition; /// X-coordinate of image origin. ScalarImage::SpaceVectorType ImageOrientation[2]; /// Coordinate increment in x-direction for every block copy operation. int IncX; /// Coordinate increment in y-direction for every block copy operation. int IncY; /// Number of continuous bytes that can be copied. int BlockSize; /** Vector between the origins of subsequent images. * Once two images have been read, the difference of their origins in 3D * space is copied to this field. The origins of subsequent slices must then * be in the very same direction in order to make up a rectangular 3D * volume. */ ScalarImage::SpaceVectorType IncrementVector; /** Flag for pixel padding. * If this flag is set, PaddingValue defines a non-data value for padded * pixels. */ bool Padding; /** Padding value. */ union { unsigned char int8; unsigned short int16; unsigned int int32; } PaddingValue; }; //@} } // namespace cmtk #endif // #ifndef __cmtkVolumeFromSlices_h_included_ cmtk-3.3.1/libs/IO/cmtkVolumeFromStudy.cxx000066400000000000000000000102051276303427400204510ustar00rootroot00000000000000/* // // Copyright 2004-2012 SRI International // // Copyright 1997-2009 Torsten Rohlfing // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4799 $ // // $LastChangedDate: 2013-07-25 13:15:56 -0700 (Thu, 25 Jul 2013) $ // // $LastChangedBy: torstenrohlfing $ // */ #include "cmtkVolumeFromStudy.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef DEBUG # include #endif #include namespace cmtk { /** \addtogroup IO */ //@{ const UniformVolume::SmartPtr VolumeFromStudy::Read ( const Study* study, const Types::Coordinate tolerance ) { if ( !study ) return UniformVolume::SmartPtr( NULL ); const StudyImageSet* studyImageSet = dynamic_cast( study ); if ( studyImageSet ) { UniformVolume::SmartPtr volume = VolumeFromStudy( tolerance ).AssembleVolume( studyImageSet ); if ( ! volume ) StdErr << "ERROR: volume assembly failed in directory " << studyImageSet->GetImageDirectory() << "\n"; return volume; } else return VolumeIO::Read( study->GetFileSystemPath() ); } const UniformVolume::SmartPtr VolumeFromStudy::AssembleVolume( const StudyImageSet* study ) { UniformVolume::SmartPtr Result( NULL ); const std::string imageDir = MountPoints::Translate( study->GetImageDirectory() ); try { DebugOutput( 2 ) << "Reading images from path " << imageDir << "\n"; Progress::Begin( 0, study->size(), 1, "Volume image assembly" ); unsigned int nextPlane = 0; StudyImageSet::const_iterator it = study->begin(); while ( it != study->end() ) { DebugOutput( 2 ) << "\r" << *it; char fullpath[PATH_MAX]; snprintf( fullpath, sizeof( fullpath ), "%s%c%s", imageDir.c_str(), (int)CMTK_PATH_SEPARATOR, it->c_str() ); ScalarImage::SmartPtr image = ScalarImage::SmartPtr( DICOM::Read( fullpath ) ); // TODO: when returning NULL here, we also should tell // VolumeFromSlices that we give up, so it can free its // temporary storage. if ( !image ) return UniformVolume::SmartPtr( NULL ); if ( ! nextPlane ) { // special treatment for first image in sequence. if ( study->GetMultiFile() ) InitSequence( image, study->size() ); else InitSequence( image, study->m_Dims[AXIS_Z] ); } const char *error = FillPlane( nextPlane, image ); Progress::SetProgress( nextPlane ); if ( error ) { StdErr.printf( "ERROR: %s: %s\n", fullpath, error ); return UniformVolume::SmartPtr( NULL ); } ++it; } Progress::Done(); Result = this->FinishVolume(); // If seomthing went wrong constructing the volume, return the NULL pointer that we just got if ( ! Result ) return Result; TypedArray::SmartPtr data = Result->GetData(); if ( data ) { if ( study->GetPadding() && ! data->GetPaddingFlag() ) { data->SetPaddingValue( study->GetPaddingValue() ); } } } catch (...) { } return Result; } } // namespace cmtk cmtk-3.3.1/libs/IO/cmtkVolumeFromStudy.h000066400000000000000000000043221276303427400201010ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2011, 2013 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4797 $ // // $LastChangedDate: 2013-07-25 13:08:09 -0700 (Thu, 25 Jul 2013) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkVolumeFromStudy_h_included_ #define __cmtkVolumeFromStudy_h_included_ #include #include #include #include #include namespace cmtk { /** \addtogroup IO */ //@{ /// Class for building 3D volumes from an Study object. class VolumeFromStudy : private VolumeFromSlices { public: /// This class. typedef VolumeFromStudy Self; /// Constructor. VolumeFromStudy( const Types::Coordinate tolerance = 0 /*!< Tolerance for floating point comparisons, e.g., when testing for uniform pixel/slice spacings.*/ ) : VolumeFromSlices( tolerance ) {} /** Build volume from slice images. *\see VolumeFromSlices#AssembleVolume */ const UniformVolume::SmartPtr AssembleVolume ( const StudyImageSet* study ); /// Read from generic Study object. static const UniformVolume::SmartPtr Read( const Study* study, const Types::Coordinate tolerance = 0 /*!< Tolerance for floating point comparisons, e.g., when testing for uniform pixel/slice spacings.*/ ); }; //@} } // namespace cmtk #endif // #ifndef __cmtkVolumeFromStudy_h_included_ cmtk-3.3.1/libs/IO/cmtkVolumeIO.cxx000066400000000000000000000275441276303427400170420ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2013 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 5129 $ // // $LastChangedDate: 2014-01-09 13:48:15 -0800 (Thu, 09 Jan 2014) $ // // $LastChangedBy: torstenrohlfing $ // */ #include "cmtkVolumeIO.h" #include #include #include #include #include #include #include #include #include #include #include #ifdef HAVE_UNISTD_H # include #endif #ifdef HAVE_LIBGEN_H # include #endif #include #include #include #include namespace cmtk { /** \addtogroup IO */ //@{ /// Environment variable that turns off writing output images in the array order of the input images. const char* const CMTK_LEGACY_WRITE_IMAGES_RAS = "CMTK_LEGACY_WRITE_IMAGES_RAS"; /// Static global flag: when true, files are written compressed whenever possible. bool VolumeIO::WriteCompressedOn = true; UniformVolume::SmartPtr VolumeIO::Read( const std::string& path ) { UniformVolume::SmartPtr volume( NULL ); const std::string translatedPath = MountPoints::Translate( path ); const FileFormatID formatID = FileFormat::Identify( translatedPath ); switch ( formatID ) { case FILEFORMAT_DICOM: // (hopefully) multi-slice DICOM volume = VolumeFromFile::ReadDICOM( translatedPath ); break; case FILEFORMAT_VANDERBILT: volume = VolumeFromFile::ReadVanderbilt( translatedPath ); break; case FILEFORMAT_BIORAD: volume = VolumeFromFile::ReadBioRad( translatedPath ); break; case FILEFORMAT_ANALYZE_HDR: volume = VolumeFromFile::ReadAnalyzeHdr( translatedPath, false /*bigendian*/, true /*readData*/ ); break; case FILEFORMAT_ANALYZE_HDR_BIGENDIAN: volume = VolumeFromFile::ReadAnalyzeHdr( translatedPath, true /*bigendian*/, true /*readData*/ ); break; case FILEFORMAT_NIFTI_SINGLEFILE: volume = VolumeFromFile::ReadNifti( translatedPath, false /*detached*/, true /*readData*/ ); break; case FILEFORMAT_NIFTI_DETACHED: volume = VolumeFromFile::ReadNifti( translatedPath, true /*detached*/, true /*readData*/ ); break; case FILEFORMAT_NRRD: volume = VolumeFromFile::ReadNRRD( translatedPath ); break; case FILEFORMAT_NEXIST: StdErr << "ERROR: could not find file " << path << "\n"; throw ExitException( 1 ); default: StdErr << "ERROR: unidentified format of file " << path << "\n"; throw ExitException( 1 ); break; } if ( ! volume ) { StdErr << "ERROR: could not read image geometry from " << path << "\n"; throw ExitException( 1 ); } volume->SetMetaInfo( META_FS_PATH, path ); volume->SetMetaInfo( META_FILEFORMAT_ORIGINAL, FileFormat::Describe( formatID ) ); DebugOutput( 3 ).GetStream().printf( "%s\nRead %d x %d x %d voxels [%f x %f x %f mm total size].\n", path.c_str(), volume->GetDims()[0], volume->GetDims()[1], volume->GetDims()[2], volume->m_Size[0], volume->m_Size[1], volume->m_Size[2] ); const TypedArray* dataArray = volume->GetData(); if ( ! dataArray ) { StdErr << "ERROR: could not read image data from " << path << "\n"; throw ExitException( 1 ); } const Types::DataItemRange range = dataArray->GetRange(); DebugOutput( 3 ).GetStream().printf( "Data type %s, range [%f .. %f]\n", DataTypeName[ dataArray->GetType() ], static_cast( range.m_LowerBound ), static_cast( range.m_UpperBound ) ); return volume; } UniformVolume::SmartPtr VolumeIO::ReadGrid( const std::string& path ) { UniformVolume::SmartPtr volume( NULL ); const std::string translatedPath = MountPoints::Translate( path ); const FileFormatID formatID = FileFormat::Identify( translatedPath ); try { switch ( formatID ) { case FILEFORMAT_ANALYZE_HDR: volume = VolumeFromFile::ReadAnalyzeHdr( translatedPath, false /*bigendian*/, false /*readData*/ ); break; case FILEFORMAT_ANALYZE_HDR_BIGENDIAN: volume = VolumeFromFile::ReadAnalyzeHdr( translatedPath, true /*bigendian*/, false /*readData*/ ); break; case FILEFORMAT_NIFTI_SINGLEFILE: volume = VolumeFromFile::ReadNifti( translatedPath, false /*detached*/, false /*readData*/ ); break; case FILEFORMAT_NIFTI_DETACHED: volume = VolumeFromFile::ReadNifti( translatedPath, true /*detached*/, false /*readData*/ ); break; default: { // For now, default to full reader for other image file formats volume = VolumeIO::Read( path ); } } } catch (...) {} if ( ! volume ) { StdErr << "ERROR: could not read image from " << path << "\n"; throw ExitException( 1 ); } DebugOutput( 3 ).GetStream().printf( "%s\nRead %d x %d x %d voxels [%f x %f x %f mm total size].\n", path.c_str(), volume->GetDims()[0], volume->GetDims()[1], volume->GetDims()[2], volume->m_Size[0], volume->m_Size[1], volume->m_Size[2] ); volume->SetMetaInfo( META_FS_PATH, path ); volume->SetMetaInfo( META_FILEFORMAT_ORIGINAL, FileFormat::Describe( formatID ) ); return volume; } UniformVolume::SmartPtr VolumeIO ::ReadGridOriented( const std::string& path, const char* orientation ) { UniformVolume::SmartPtr volume( Self::ReadGrid( path ) ); const std::string orientationOriginal = volume->GetMetaInfo( META_IMAGE_ORIENTATION ); if ( orientationOriginal == "" ) { StdErr << "WARNING: image does not have valid orientation meta information; cannot reorient.\n"; return volume; } else { if ( orientationOriginal != orientation ) { DebugOutput( 3 ) << "Reorienting image from '" << orientationOriginal << "' to '" << orientation << "'\n"; return volume->GetReoriented( orientation ); } } return volume; } UniformVolume::SmartPtr VolumeIO ::ReadOriented( const std::string& path, const char* orientation ) { UniformVolume::SmartPtr volume( Self::Read( path ) ); const std::string orientationOriginal = volume->GetMetaInfo( META_IMAGE_ORIENTATION ); if ( orientationOriginal == "" ) { StdErr << "WARNING: image does not have valid orientation meta information; cannot reorient.\n"; return volume; } else { if ( orientationOriginal != orientation ) { DebugOutput( 3 ) << "INFO: reorienting image from '" << orientationOriginal << "' to '" << orientation << "'\n"; return volume->GetReoriented( orientation ); } } return volume; } void VolumeIO::Write ( const UniformVolume& volume, const std::string& pathAndFormat ) { std::string actualPath = pathAndFormat; FileFormatID fileFormat = FILEFORMAT_UNKNOWN; const size_t period = pathAndFormat.rfind( '.' ); if ( period != std::string::npos ) { std::string suffix = pathAndFormat.substr( period ); // check whether we have a compression-related suffix if ( suffix == ".gz" ) { // include actual suffix const size_t period2 = pathAndFormat.rfind( '.', period-1 ); suffix = pathAndFormat.substr( period2, period-period2 ); } if ( suffix == ".hdr" ) { fileFormat = FILEFORMAT_ANALYZE_HDR; } else { if ( suffix == ".img" ) { fileFormat = FILEFORMAT_NIFTI_DETACHED; } else { if ( suffix == ".nii" ) { fileFormat = FILEFORMAT_NIFTI_SINGLEFILE; } else { if ( suffix == ".mha" ) { fileFormat = FILEFORMAT_METAIMAGE; } else { if ( ( suffix == ".nrrd") || (suffix == ".nhdr") ) { fileFormat = FILEFORMAT_NRRD; } } } } } } #ifndef _MSC_VER const size_t colon = pathAndFormat.find( ':' ); if ( colon != std::string::npos ) { actualPath = pathAndFormat.substr( colon+1 ); const std::string format = pathAndFormat.substr( colon-1 ); if ( format == "ANALYZE" ) { fileFormat = FILEFORMAT_ANALYZE_HDR; } else if ( format == "NIFTI" ) { fileFormat = FILEFORMAT_NIFTI_SINGLEFILE; } else if ( format == "NRRD" ) { fileFormat = FILEFORMAT_NRRD; } else if ( format == "METAIMAGE" ) { fileFormat = FILEFORMAT_METAIMAGE; } } #endif if ( fileFormat == FILEFORMAT_UNKNOWN ) { StdErr << "Fileformat not recognized; writing single-file NIFTI instead.\n"; fileFormat = FILEFORMAT_NIFTI_SINGLEFILE; } const std::string absolutePath = FileUtils::GetAbsolutePath( actualPath ); Write( volume, fileFormat, absolutePath ); } void VolumeIO::Write ( const UniformVolume& volume, const FileFormatID format, const std::string& path ) { if ( ! volume.GetData() ) { StdErr << "ERROR: cannot write volume that does not contain any data.\n"; return; } DebugOutput( 3 ).GetStream().printf( "%s\nWriting %d x %d x %d voxels [%f x %f x %f mm total size].\n", path.c_str(), volume.GetDims()[0], volume.GetDims()[1], volume.GetDims()[2], volume.m_Size[0], volume.m_Size[1], volume.m_Size[2] ); const TypedArray *data = volume.GetData(); if ( data == NULL ) return; FileUtils::RecursiveMkPrefixDir( path ); const UniformVolume* actualVolume = &volume; // if volume was reoriented from its original array order, temporarily reorient back and set actual volume to temporary volume. cmtk::UniformVolume::SmartConstPtr reorientedVolume; if ( getenv( CMTK_LEGACY_WRITE_IMAGES_RAS ) ) { DebugOutput( 1 ) << "INFO: forcing legacy RAS image writing due to set environment variable\n"; } else { if ( volume.MetaKeyExists( cmtk::META_IMAGE_ORIENTATION_ORIGINAL ) && (volume.GetMetaInfo( cmtk::META_IMAGE_ORIENTATION ) != volume.GetMetaInfo( cmtk::META_IMAGE_ORIENTATION_ORIGINAL ) ) ) { try { reorientedVolume = cmtk::UniformVolume::SmartConstPtr( volume.GetReoriented( volume.GetMetaInfo( cmtk::META_IMAGE_ORIENTATION_ORIGINAL ).c_str() ) ); actualVolume = reorientedVolume; } catch ( const AffineXform::MatrixType::SingularMatrixException& ) { StdErr << "ERROR: singular matrix in cmtk::UniformVolume::GetReoriented(). Writing volume in current orientation."; } } } switch ( format ) { case FILEFORMAT_ANALYZE_HDR: { VolumeFromFile::WriteAnalyzeHdr( path, *actualVolume ); break; } case FILEFORMAT_NIFTI_DETACHED: case FILEFORMAT_NIFTI_SINGLEFILE: { VolumeFromFile::WriteNifti( path, *actualVolume ); break; } case FILEFORMAT_METAIMAGE: { VolumeFromFile::WriteMetaImage( path, *actualVolume ); break; } case FILEFORMAT_NRRD: { VolumeFromFile::WriteNRRD( path, *actualVolume ); break; } break; default: break; } // volume.SetMetaInfo( META_FS_PATH, path ); } VolumeIO::Initializer::Initializer() { if ( getenv( "IGS_WRITE_UNCOMPRESSED" ) || getenv( "CMTK_WRITE_UNCOMPRESSED" ) ) VolumeIO::SetWriteCompressedOff(); } VolumeIO::Initializer VolumeIO::Initializer::Instance; } // namespace cmtk cmtk-3.3.1/libs/IO/cmtkVolumeIO.h000066400000000000000000000135151276303427400164600ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2013 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4834 $ // // $LastChangedDate: 2013-09-12 15:15:51 -0700 (Thu, 12 Sep 2013) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkVolumeIO_h_included_ #define __cmtkVolumeIO_h_included_ #include #include #include #include #include namespace cmtk { /** \addtogroup IO */ //@{ /** Class for input/output of 3-D image data. * This class is an easy-to-use wrapper around all low-level image readers and * writers. * * An image can be read, for example from a Nifti/Analyze file pair, simply as follows: * \code * cmtk::UniformVolume::SmartPtr volume( cmtk::VolumeIO::Read( "image.hdr" ) ); * \endcode * * Note that we typically want images to be oriented consistently, so the preferred reader call would be: * \code * cmtk::UniformVolume::SmartPtr volume( cmtk::VolumeIO::ReadOriented( "image.hdr" ); * \endcode * * Similarly, we can write an image simply by calling * \code * cmtk::VolumeIO::Write( volume, "image.hdr" ); * \endcode * * The output format is determined automatically from the file name suffix. See * \see VolumeIO::Write * for more details. */ class VolumeIO { public: /// This class. typedef VolumeIO Self; /// Read volume data from filesystem. static UniformVolume::SmartPtr Read( const std::string& path ); /// Read grid only from filesystem. static UniformVolume::SmartPtr ReadGrid( const std::string& path ); /// Read grid only from filesystem and bring into standard "RAS" orientation. static UniformVolume::SmartPtr ReadGridOriented( const std::string& path, const char* orientation ); /// Read grid only from filesystem and bring into standard "RAS" orientation. static UniformVolume::SmartPtr ReadGridOriented( const std::string& path ) { return Self::ReadGridOriented( path, AnatomicalOrientation::ORIENTATION_STANDARD ); } /** Read image from filesystem and reorient to align anatomy with coordinate axes. *\param path Filesystem path of the image to read. *\param orientation Three-character orientation code. The image will be brought into the orientation * specified by this string. Default is "RAS", i.e., the returned image will be oriented so that the * positive x axis is aligned with the anatomical L/R (left/right) direction, the y axis is aligned * with the P/A (posterior/anterior) direction, and the y axis is aligned with the I/S (inferior/superior) * direction. */ static UniformVolume::SmartPtr ReadOriented( const std::string& path, const char* orientation ); /** Read image from filesystem and reorient to align anatomy with coordinate axes of standard coordinate system ("RAS"). *\param path Filesystem path of the image to read. */ static UniformVolume::SmartPtr ReadOriented( const std::string& path ) { return Self::ReadOriented( path, AnatomicalOrientation::ORIENTATION_STANDARD ); } /** Write volume data to filesystem. *\param volume Image object that is written to disk. *\param format Selector for output file format. *\param path Filesystem path of the image to write. */ static void Write( const UniformVolume& volume, const FileFormatID format, const std::string& path ); /** Write volume data to filesystem with automatic format parsing. * The output file format is determined automatically from the output name suffix. * \note Note that using ".hdr" will write a deprecated Analyze 7.5 hdr/img format pair * with private extensions and questionable assumptions regarding the anatomical * orientation of the image. To write a NIFTI hdr/img pair that avoids these problems, * use the filename suffix ".img" (or write a single-file NIFTI using the ".nii" suffix). *\param volume Image object that is written to disk. *\param pathAndFormat Filesystem path of the image to write. */ static void Write( const UniformVolume& volume, const std::string& pathAndFormat ); /// Set flag for writing compressed images. static void SetWriteCompressedOn() { Self::WriteCompressedOn = true; } /// Clear flag for writing compressed images. static void SetWriteCompressedOff() { Self::WriteCompressedOn = false; } /// Get flag for writing compressed images. static bool GetWriteCompressed() { return Self::WriteCompressedOn; } private: /// Global setting: write compressed images. static bool WriteCompressedOn; /** Initializer class. * An object of this class is automatically instantiated when a program is run. * Its constructor takes care of initializing VolumeIO, e.g., by evaluating the * IGS_WRITE_UNCOMPRESSED environment variable. */ class Initializer { private: /// Default constructor: initialize VolumeIO settings. Initializer(); /// Instance of the initializer class. static Initializer Instance; }; }; //@} } // namespace cmtk #endif // #ifndef __cmtkVolumeIO_h_included_ cmtk-3.3.1/libs/IO/cmtkXformIO.cxx000066400000000000000000000131311276303427400166510ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2014 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 5363 $ // // $LastChangedDate: 2014-06-26 13:59:07 -0700 (Thu, 26 Jun 2014) $ // // $LastChangedBy: torstenrohlfing $ // */ #include "cmtkXformIO.h" #include #include #include #include #include #include #include #include #include #include #include #include #include namespace cmtk { /** \addtogroup IO */ //@{ Xform::SmartPtr XformIO::Read( const std::string& path ) { const std::string realPath = MountPoints::Translate( path ); switch ( FileFormat::Identify( realPath ) ) { case FILEFORMAT_NRRD: #ifdef CMTK_BUILD_NRRD return Self::ReadNrrd( realPath ); #else StdErr << "ERROR: " << realPath << " is a Nrrd file, but Nrrd support is not enabled.\n" << " Please re-configure software using either '--with-nrrd' or '--with-nrrd-teem' switch.\n"; return Xform::SmartPtr( NULL ); #endif case FILEFORMAT_ITK_TFM: return AffineXformITKIO::Read( path ); case FILEFORMAT_STUDYLIST: DebugOutput( 1 ) << "Reading transformation from studylist " << realPath << "\n"; { TypedStreamStudylist studylist( realPath ); if ( studylist.GetWarpXform() ) return studylist.GetWarpXform(); else return studylist.GetAffineXform(); } case FILEFORMAT_TYPEDSTREAM: DebugOutput( 1 ) << "Reading transformation from typedstream file " << realPath << "\n"; { ClassStreamInput stream( realPath ); WarpXform* warpXform; stream >> warpXform; if ( warpXform ) return Xform::SmartPtr( warpXform ); stream.Open( realPath ); PolynomialXform polyXform; try { stream >> polyXform; return Xform::SmartPtr( new PolynomialXform( polyXform ) ); } catch ( const cmtk::Exception& ) { } stream.Open( realPath ); AffineXform affineXform; try { stream >> affineXform; } catch ( const cmtk::Exception& ex ) { StdErr << "ERROR: " << ex.what() << "\n"; return Xform::SmartPtr( NULL ); } return Xform::SmartPtr( new AffineXform( affineXform ) ); } case FILEFORMAT_NEXIST: StdErr << "The file/directory " << realPath << " does not exist or cannot be read\n"; throw ExitException( 1 ); default: StdErr << "The file/directory " << realPath << " does not seem to be in a supported transformation format\n"; throw ExitException( 1 ); } return Xform::SmartPtr( NULL ); } void XformIO::Write ( const Xform* xform, const std::string& path ) { FileFormatID fileFormat = FILEFORMAT_TYPEDSTREAM; const size_t period = path.rfind( '.' ); if ( period != std::string::npos ) { const std::string suffix = path.substr( period ); if ( (suffix == ".nrrd") || (suffix == ".nhdr") ) { fileFormat = FILEFORMAT_NRRD; } else if ( suffix == ".nii") { fileFormat = FILEFORMAT_NIFTI_SINGLEFILE; } else if ( suffix == ".img" ) { fileFormat = FILEFORMAT_NIFTI_DETACHED; } else { if ( (suffix == ".tfm") || (suffix == ".txt") ) { fileFormat = FILEFORMAT_ITK_TFM; } } } const std::string absolutePath = FileUtils::GetAbsolutePath( path ); switch ( fileFormat ) { case FILEFORMAT_NRRD: #ifdef CMTK_BUILD_NRRD WriteNrrd( xform, absolutePath ); #else StdErr << "ERROR: XformIO::Write -- Nrrd support not configured.\n"; #endif break; case FILEFORMAT_NIFTI_DETACHED: case FILEFORMAT_NIFTI_SINGLEFILE: Self::WriteNIFTI( xform, absolutePath ); break; case FILEFORMAT_ITK_TFM: { const AffineXform* affineXform = dynamic_cast( xform ); if ( affineXform ) AffineXformITKIO::Write( path, *affineXform ); break; } case FILEFORMAT_TYPEDSTREAM: { ClassStreamOutput stream( absolutePath, ClassStreamOutput::MODE_WRITE ); const AffineXform* affineXform = dynamic_cast( xform ); if ( affineXform ) stream << *affineXform; const PolynomialXform* polyXform = dynamic_cast( xform ); if ( polyXform ) stream << *polyXform; const SplineWarpXform* splineWarpXform = dynamic_cast( xform ); if ( splineWarpXform ) stream << *splineWarpXform; } break; default: // cannot really get here, but gcc doesn't know that break; } } } // namespace cmtk cmtk-3.3.1/libs/IO/cmtkXformIO.h000066400000000000000000000047641276303427400163120ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2012, 2014 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 5224 $ // // $LastChangedDate: 2014-03-11 16:38:30 -0700 (Tue, 11 Mar 2014) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkXformIO_h_included__ #define __cmtkXformIO_h_included__ #include #include namespace cmtk { /** \addtogroup IO */ //@{ /** Utility class for one-stop transformation import. * When reading a transformation file using the Read() function, the file and transformation type * are automatically detected based on each file format's "magic number". * * When writing a transformation using the Write() function, the path or file name suffix determines * the output file format. Supported formats are: ITK Transformation file (".txt"; ".tfm"), Nrrd deformation * fields (".nrrd"; ".nhdr"), and legacy TypedStream (all other suffixes). */ class XformIO { public: /// This class. typedef XformIO Self; /// Read transformation from filesystem. static Xform::SmartPtr Read( const std::string& path ); /// Write transformation to filesystem. static void Write( const Xform* xform, const std::string& path ); protected: #ifdef CMTK_BUILD_NRRD /// Read deformation field from Nrrd image file. static Xform::SmartPtr ReadNrrd( const std::string& path ); /// Write transformation to filesystem. static void WriteNrrd( const Xform* xform, const std::string& path ); #endif // #ifdef CMTK_BUILD_NRRD /// Write transformation to filesystem. static void WriteNIFTI( const Xform* xform, const std::string& path ); }; //@} } // namespace cmtk #endif // #ifndef __cmtkXformIO_h_included__ cmtk-3.3.1/libs/IO/cmtkXformIONifti.cxx000066400000000000000000000136751276303427400176600ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2014 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 5129 $ // // $LastChangedDate: 2014-01-09 13:48:15 -0800 (Thu, 09 Jan 2014) $ // // $LastChangedBy: torstenrohlfing $ // */ #include "cmtkXformIO.h" #include #include #include #include "nifti1.h" #include "nifti1_io_math.h" #include #include #include #ifdef HAVE_ZLIB # include #endif #ifdef HAVE_SYS_STAT_H # include #endif namespace cmtk { /** \addtogroup IO */ //@{ void XformIO::WriteNIFTI ( const Xform* xform, const std::string& path ) { const DeformationField* dfield = dynamic_cast( xform ); if ( ! dfield ) { StdErr << "ERROR: XformIO::WriteNIFTI can only write DeformationField objects so far.\n" << " No data was written.\n"; return; } const size_t dfieldRegionSize = dfield->m_Dims.Product(); TypedArray::SmartPtr data = TypedArray::Create( TYPE_COORDINATE, 3 * dfieldRegionSize ); for ( size_t ofs = 0; ofs < dfieldRegionSize; ++ofs ) { for ( int dim = 0; dim < 3; ++dim ) { data->Set( dfield->m_Parameters[3*ofs+dim], ofs + dim * dfieldRegionSize ); } } bool detachedHeader = false; bool forceCompressed = false; std::string pathImg( path ); // first, look for .gz size_t suffixPosGz = pathImg.rfind( std::string( ".gz" ) ); if ( suffixPosGz != std::string::npos ) { // found: set force compression flag and remove .gz from path forceCompressed = true; pathImg = pathImg.substr( 0, suffixPosGz ); } std::string pathHdr( pathImg ); size_t suffixPos = pathHdr.rfind( ".img" ); if ( suffixPos != std::string::npos ) { detachedHeader = true; pathHdr.replace( suffixPos, 4, ".hdr" ); } nifti_1_header header; memset( &header, 0, sizeof( header ) ); header.sizeof_hdr = 348; // header size header.dim_info = 0; // ndims header.dim[0] = 5; // dimensions header.dim[1] = dfield->m_Dims[AXIS_X]; header.dim[2] = dfield->m_Dims[AXIS_Y]; header.dim[3] = dfield->m_Dims[AXIS_Z]; header.dim[4] = 1; header.dim[5] = 3; header.dim[6] = 0; header.dim[7] = 0; header.pixdim[0] = 1.0; header.pixdim[1] = static_cast( dfield->m_Spacing[AXIS_X] ); header.pixdim[2] = static_cast( dfield->m_Spacing[AXIS_Y] ); header.pixdim[3] = static_cast( dfield->m_Spacing[AXIS_Z] ); header.pixdim[4] = 0.0; header.pixdim[5] = 1.0; header.intent_code = NIFTI_INTENT_DISPVECT; header.qform_code = header.sform_code = 0; header.bitpix = 8 * sizeof( Types::Coordinate ); if ( sizeof( Types::Coordinate ) == sizeof( float ) ) { header.datatype = DT_FLOAT; } else { header.datatype = DT_DOUBLE; } // determine data range; const Types::DataItemRange dataRange = data->GetRange(); header.cal_max = static_cast( dataRange.m_UpperBound ); header.cal_min = static_cast( dataRange.m_LowerBound ); #ifdef _MSC_VER const char *const modestr = "wb"; #else const char *const modestr = "w"; #endif if ( detachedHeader ) { memcpy( &header.magic, "ni1\x00", 4 ); header.vox_offset = 0; FILE *hdrFile = fopen( pathHdr.c_str(), modestr ); if ( hdrFile ) { fwrite( &header, 1, sizeof( header ), hdrFile ); const int extension = 0; fwrite( &extension, 1, 4, hdrFile ); fclose( hdrFile ); } else { StdErr << "ERROR: NIFTI header file '" << pathHdr << "' could not be opened for writing!\n"; } } else { memcpy( &header.magic, "n+1\x00", 4 ); header.vox_offset = 352; } if ( VolumeIO::GetWriteCompressed() || forceCompressed ) { struct stat buf; if ( ! stat( pathImg.c_str(), &buf ) ) { StdErr << "WARNING: NIFTI file '" << path << "' will be written compressed, but uncompressed file exists!\n"; } gzFile imgFile = gzopen( (pathImg+".gz").c_str(), modestr ); if ( imgFile ) { if ( ! detachedHeader ) { gzwrite( imgFile, &header, sizeof( header ) ); const int extension = 0; gzwrite( imgFile, &extension, 4 ); } const size_t dataSize = data->GetItemSize() * data->GetDataSize(); if ( dataSize != CompressedStream::Zlib::StaticSafeWrite( imgFile, data->GetDataPtr(), dataSize ) ) { StdErr << "WARNING: gzwrite() returned error when writing to " << pathImg << "\n"; } gzclose( imgFile ); } else { StdErr << "ERROR: could not open file '" << pathImg << ".gz' for writing\n"; } } else { FILE *imgFile = fopen( pathImg.c_str(), modestr ); if ( imgFile ) { if ( ! detachedHeader ) { fwrite( &header, 1, sizeof( header ), imgFile ); const int extension = 0; fwrite( &extension, 1, 4, imgFile ); } fwrite( data->GetDataPtr(), data->GetItemSize(), data->GetDataSize(), imgFile ); fclose( imgFile ); } else { StdErr << "ERROR: could not open file '" << pathImg << "' for writing\n"; } } } //@} } // namespace cmtk cmtk-3.3.1/libs/IO/cmtkXformIONrrd.cxx000066400000000000000000000214241276303427400175030ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2013 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 5129 $ // // $LastChangedDate: 2014-01-09 13:48:15 -0800 (Thu, 09 Jan 2014) $ // // $LastChangedBy: torstenrohlfing $ // */ #include "cmtkXformIO.h" #include #ifdef CMTK_BUILD_NRRD_TEEM # include #else # ifdef CMTK_BUILD_NRRD # include # endif #endif #ifdef CMTK_BUILD_NRRD namespace cmtk { /** \addtogroup IO */ //@{ Xform::SmartPtr XformIO::ReadNrrd( const std::string& path ) { DeformationField::SmartPtr dfield( NULL ); try { Nrrd *nrrd = nrrdNew(); if ( nrrdLoad( nrrd, path.c_str(), NULL ) ) throw biffGetDone(NRRD); if ( nrrd->dim != 4 ) { StdErr << "ERROR: deformation field must be stored as 4-dimensional Nrrd.\n"; return dfield; } if ( nrrd->axis[0].kind != nrrdKindVector ) { StdErr << "ERROR: deformation field vectors in Nrrd must be stored together.\n"; return dfield; } if ( nrrd->axis[0].size != 3 ) { StdErr << "ERROR: deformation field vectors in Nrrd must be three dimensional.\n"; return dfield; } NrrdAxisInfo* nrrdSpaceAxes = nrrd->axis+1; const int dims[3] = { static_cast( nrrdSpaceAxes[0].size ), static_cast( nrrdSpaceAxes[1].size ), static_cast( nrrdSpaceAxes[2].size ) }; // for each axis, if spacing is NaN, use direction vector to compute spacing. double spacing[3] = { 1, 1, 1 }; for ( size_t ax = 0; ax < 3; ++ax ) { switch ( nrrdSpacingCalculate( nrrd, ax+1, spacing+ax, nrrd->axis[ax+1].spaceDirection ) ) { case nrrdSpacingStatusScalarNoSpace: break; case nrrdSpacingStatusDirection: break; case nrrdSpacingStatusScalarWithSpace: StdErr << "WARNING: nrrdSpacingCalculate returned nrrdSpacingStatusScalarWithSpace\n"; spacing[ax] = nrrdSpaceAxes[ax].spacing; break; case nrrdSpacingStatusNone: default: StdErr << "WARNING: no pixel spacings in Nrrd for axis " << ax << "; setting to 1.0\n"; spacing[ax] = 1.0; break; } } const Types::Coordinate size[3] = { (dims[0]-1) * spacing[0], (dims[1]-1) * spacing[1], (dims[2]-1) * spacing[2] }; const Types::Coordinate origin[3] = { nrrd->spaceOrigin[0], nrrd->spaceOrigin[1], nrrd->spaceOrigin[2] }; dfield = DeformationField::SmartPtr( new DeformationField( FixedVector<3,Types::Coordinate>::FromPointer( size ), DeformationField::ControlPointIndexType::FromPointer( dims ), origin ) ); ScalarDataType type = TYPE_NONE; switch ( nrrd->type ) { case nrrdTypeUChar: type = TYPE_BYTE; break; case nrrdTypeChar: type = TYPE_CHAR; break; case nrrdTypeUShort: type = TYPE_USHORT; break; case nrrdTypeShort: type = TYPE_SHORT; break; case nrrdTypeInt: type = TYPE_INT; break; case nrrdTypeFloat: type = TYPE_FLOAT; break; case nrrdTypeDouble: type = TYPE_DOUBLE; break; default: break; } if ( type != TYPE_NONE ) { TypedArray::SmartPtr data( TypedArray::Create( type, nrrd->data, 3 * dims[0] * dims[1] * dims[2] ) ); data->ConvertSubArray( dfield->m_Parameters, TYPE_COORDINATE, 0, data->GetDataSize() ); } else { StdErr << "ERROR: unsupported data type in nrrd file.\n"; return dfield; } const char* orientationSpace = NULL; switch ( nrrd->space ) { case nrrdSpaceRightAnteriorSuperior: case nrrdSpaceRightAnteriorSuperiorTime: orientationSpace = "RAS"; break; case nrrdSpaceLeftAnteriorSuperior: case nrrdSpaceLeftAnteriorSuperiorTime: orientationSpace = "LAS"; break; case nrrdSpaceLeftPosteriorSuperior: case nrrdSpaceLeftPosteriorSuperiorTime: orientationSpace = "LPS"; break; default: break; } if ( orientationSpace ) { dfield->SetMetaInfo( META_SPACE, orientationSpace ); dfield->SetMetaInfo( META_SPACE_ORIGINAL, orientationSpace ); const Types::Coordinate directions[3][3] = { { nrrdSpaceAxes[0].spaceDirection[0] * spacing[0], nrrdSpaceAxes[0].spaceDirection[1] * spacing[0], nrrdSpaceAxes[0].spaceDirection[2] * spacing[0] }, { nrrdSpaceAxes[1].spaceDirection[0] * spacing[1], nrrdSpaceAxes[1].spaceDirection[1] * spacing[1], nrrdSpaceAxes[1].spaceDirection[2] * spacing[1] }, { nrrdSpaceAxes[2].spaceDirection[0] * spacing[2], nrrdSpaceAxes[2].spaceDirection[1] * spacing[2], nrrdSpaceAxes[2].spaceDirection[2] * spacing[2] } }; const Matrix3x3 m3( directions ); Matrix4x4 m4( m3 ); for ( int i = 0; i < 3; ++i ) m4[3][i] = nrrd->spaceOrigin[i]; try { AffineXform::SmartPtr xform( new AffineXform( m4 ) ) ; dfield->SetInitialAffineXform( xform ); } catch ( const AffineXform::MatrixType::SingularMatrixException& ) { StdErr << "ERROR: space directions in Nrrd file cause singular matrix exception in XformIO::ReadNrrd(). Using identity matrix instead.\n"; } char orientationImage[4]; AnatomicalOrientation::GetOrientationFromDirections( orientationImage, m4, orientationSpace ); dfield->SetMetaInfo( META_IMAGE_ORIENTATION, orientationImage ); dfield->SetMetaInfo( META_IMAGE_ORIENTATION_ORIGINAL, orientationImage ); } nrrdNix( nrrd ); } catch ( char* err ) { StdErr << "ERROR: nrrd library returned error '" << err << "'\n"; free( err ); } return dfield; } void XformIO::WriteNrrd ( const Xform* xform, const std::string& path ) { const DeformationField* dfield = dynamic_cast( xform ); if ( ! dfield ) { StdErr << "ERROR: XformIO::WriteNrrd can only write DeformationField objects so far.\n" << " No data was written.\n"; return; } void* val = static_cast( dfield->m_Parameters ); const int type = (sizeof(Types::Coordinate) == sizeof(float)) ? nrrdTypeFloat : nrrdTypeDouble; Nrrd *nval = nrrdNew(); NrrdIoState *nios = nrrdIoStateNew(); if ( nrrdEncodingGzip->available() ) { nrrdIoStateEncodingSet( nios, nrrdEncodingGzip ); nrrdIoStateSet( nios, nrrdIoStateZlibLevel, 9 ); } else { StdErr << "WARNING: Nrrd library does not support Gzip compression encoding.\n" << " Please add -DTEEM_ZLIB to compiler options when building Nrrd library.\n"; } try { if ( nrrdWrap_va( nval, val, type, 4, (size_t)3, (size_t)dfield->m_Dims[0], (size_t)dfield->m_Dims[1], (size_t)dfield->m_Dims[2] ) ) { throw( biffGetDone(NRRD) ); } nrrdSpaceDimensionSet( nval, 3 ); if ( dfield->MetaKeyExists(META_SPACE_UNITS_STRING) ) { nval->spaceUnits[0] = strdup( dfield->GetMetaInfo( META_SPACE_UNITS_STRING ).c_str() ); } int kind[NRRD_DIM_MAX] = { nrrdKindVector, nrrdKindDomain, nrrdKindDomain, nrrdKindDomain }; nrrdAxisInfoSet_nva( nval, nrrdAxisInfoKind, kind ); nrrdAxisInfoSet_va( nval, nrrdAxisInfoLabel, "Vx;Vy;Vz", "x", "y", "z" ); double origin[NRRD_DIM_MAX] = { dfield->m_Offset[0], dfield->m_Offset[1], dfield->m_Offset[2] }; if ( nrrdSpaceOriginSet( nval, origin ) ) { throw( biffGetDone(NRRD) ); } nval->space = nrrdSpaceRightAnteriorSuperior; double spaceDir[NRRD_DIM_MAX][NRRD_SPACE_DIM_MAX]; for ( int i = 0; i < 4; ++i ) { for ( int j = 0; j < 3; ++j ) { if ( i ) { if ( i-1 == j ) spaceDir[i][j] = dfield->m_Spacing[ i-1 ]; else spaceDir[i][j] = 0.0; } else { spaceDir[i][j] = AIR_NAN; } } } nrrdAxisInfoSet_nva( nval, nrrdAxisInfoSpaceDirection, spaceDir ); if ( nrrdSave( path.c_str(), nval, nios ) ) { throw( biffGetDone(NRRD) ); } } catch ( char* err ) { StdErr << "ERROR: NrrdIO library returned error '" << err << "'\n"; free( err ); } nrrdIoStateNix( nios ); nrrdNix(nval); } //@} } // namespace cmtk #endif // #ifdef CMTK_BUILD_NRRD cmtk-3.3.1/libs/IO/cmtkXformListIO.cxx000066400000000000000000000046641276303427400175200ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2010, 2013-2014 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 5166 $ // // $LastChangedDate: 2014-01-13 14:26:33 -0800 (Mon, 13 Jan 2014) $ // // $LastChangedBy: torstenrohlfing $ // */ #include "cmtkXformListIO.h" #include #include #include cmtk::XformList cmtk::XformListIO::MakeFromStringList( const std::vector& stringList ) { XformList xformList; for ( std::vector::const_iterator it = stringList.begin(); it != stringList.end(); ++it ) { const bool inverse = (*it == "-i" ) || (*it == "--inverse" ); if ( inverse ) { ++it; if ( it == stringList.end() ) { StdErr << "ERROR: '--inverse' / '-i' must be followed by at least one more transformation\n"; throw ExitException( 1 ); } } try { Xform::SmartPtr xform( XformIO::Read( it->c_str() ) ); if ( ! xform ) { StdErr << "ERROR: could not read target-to-reference transformation from " << *it << "\n"; throw ExitException( 1 ); } xformList.Add( xform, inverse ); } catch ( const AffineXform::MatrixType::SingularMatrixException& ) { StdErr << "ERROR: singular matrix encountered reading transformation from " << *it << "\n"; throw ExitException( 1 ); } catch ( const PolynomialHelper::DegreeUnsupported& ex ) { StdErr << "ERROR: polynomial degree unsupported in " << *it << "\n"; StdErr << ex.what() << "\n"; throw ExitException( 1 ); } } return xformList; } cmtk-3.3.1/libs/IO/cmtkXformListIO.h000066400000000000000000000036441276303427400171420ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2010 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 2261 $ // // $LastChangedDate: 2010-08-19 11:55:24 -0700 (Thu, 19 Aug 2010) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkXformListIO_h_included__ #define __cmtkXformListIO_h_included__ #include #include #include #include namespace cmtk { /** \addtogroup IO */ //@{ /** Utility class to generate a list of concatenated transformation objects. */ class XformListIO { public: /// This class. typedef XformListIO Self; /** Create transformation list from string list. *\return An XformList object with concatenated Xform (and derived) objects, each of which * may be optionally inverted. */ static XformList MakeFromStringList( const std::vector& stringList /*!< List of transformation paths. If an entry is "--inverse" or "-i", then the next following transformation is marked to be applied inverse.*/ ); }; //@} } // namespace cmtk #endif // #ifndef __cmtkXformListIO_h_included__ cmtk-3.3.1/libs/IO/doxygen.h000066400000000000000000000023221276303427400155510ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2011 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 2734 $ // // $LastChangedDate: 2011-01-14 10:23:45 -0800 (Fri, 14 Jan 2011) $ // // $LastChangedBy: torstenrohlfing $ // */ /** \defgroup IO cmtkIO Library * This library provides classes for input and output of images, transformations, * and other types of objects. */ cmtk-3.3.1/libs/IO/nifti1.h000066400000000000000000002064211276303427400152740ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // Copyright 2004-2009 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 578 $ // // $LastChangedDate: 2009-10-12 20:24:24 -0700 (Mon, 12 Oct 2009) $ // // $LastChangedBy: torstenrohlfing $ // */ /** \file nifti1.h \brief Official definition of the nifti1 header. Written by Bob Cox, SSCC, NIMH. */ #ifndef _NIFTI_HEADER_ #define _NIFTI_HEADER_ /***************************************************************************** ** This file defines the "NIFTI-1" header format. ** ** It is derived from 2 meetings at the NIH (31 Mar 2003 and ** ** 02 Sep 2003) of the Data Format Working Group (DFWG), ** ** chartered by the NIfTI (Neuroimaging Informatics Technology ** ** Initiative) at the National Institutes of Health (NIH). ** **--------------------------------------------------------------** ** Neither the National Institutes of Health (NIH), the DFWG, ** ** nor any of the members or employees of these institutions ** ** imply any warranty of usefulness of this material for any ** ** purpose, and do not assume any liability for damages, ** ** incidental or otherwise, caused by any use of this document. ** ** If these conditions are not acceptable, do not use this! ** **--------------------------------------------------------------** ** Author: Robert W Cox (NIMH, Bethesda) ** ** Advisors: John Ashburner (FIL, London), ** ** Stephen Smith (FMRIB, Oxford), ** ** Mark Jenkinson (FMRIB, Oxford) ** ******************************************************************************/ /*---------------------------------------------------------------------------*/ /* Note that the ANALYZE 7.5 file header (dbh.h) is (c) Copyright 1986-1995 Biomedical Imaging Resource Mayo Foundation Incorporation of components of dbh.h are by permission of the Mayo Foundation. Changes from the ANALYZE 7.5 file header in this file are released to the public domain, including the functional comments and any amusing asides. -----------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/ /*! INTRODUCTION TO NIFTI-1: ------------------------ The twin (and somewhat conflicting) goals of this modified ANALYZE 7.5 format are: (a) To add information to the header that will be useful for functional neuroimaging data analysis and display. These additions include: - More basic data types. - Two affine transformations to specify voxel coordinates. - "Intent" codes and parameters to describe the meaning of the data. - Affine scaling of the stored data values to their "true" values. - Optional storage of the header and image data in one file (.nii). (b) To maintain compatibility with non-NIFTI-aware ANALYZE 7.5 compatible software (i.e., such a program should be able to do something useful with a NIFTI-1 dataset -- at least, with one stored in a traditional .img/.hdr file pair). Most of the unused fields in the ANALYZE 7.5 header have been taken, and some of the lesser-used fields have been co-opted for other purposes. Notably, most of the data_history substructure has been co-opted for other purposes, since the ANALYZE 7.5 format describes this substructure as "not required". NIFTI-1 FLAG (MAGIC STRINGS): ---------------------------- To flag such a struct as being conformant to the NIFTI-1 spec, the last 4 bytes of the header must be either the C String "ni1" or "n+1"; in hexadecimal, the 4 bytes 6E 69 31 00 or 6E 2B 31 00 (in any future version of this format, the '1' will be upgraded to '2', etc.). Normally, such a "magic number" or flag goes at the start of the file, but trying to avoid clobbering widely-used ANALYZE 7.5 fields led to putting this marker last. However, recall that "the last shall be first" (Matthew 20:16). If a NIFTI-aware program reads a header file that is NOT marked with a NIFTI magic string, then it should treat the header as an ANALYZE 7.5 structure. NIFTI-1 FILE STORAGE: -------------------- "ni1" means that the image data is stored in the ".img" file corresponding to the header file (starting at file offset 0). "n+1" means that the image data is stored in the same file as the header information. We recommend that the combined header+data filename suffix be ".nii". When the dataset is stored in one file, the first byte of image data is stored at byte location (int)vox_offset in this combined file. The minimum allowed value of vox_offset is 352; for compatibility with some software, vox_offset should be an integral multiple of 16. GRACE UNDER FIRE: ---------------- Most NIFTI-aware programs will only be able to handle a subset of the full range of datasets possible with this format. All NIFTI-aware programs should take care to check if an input dataset conforms to the program's needs and expectations (e.g., check datatype, intent_code, etc.). If the input dataset can't be handled by the program, the program should fail gracefully (e.g., print a useful warning; not crash). SAMPLE CODES: ------------ The associated files nifti1_io.h and nifti1_io.c provide a sample implementation in C of a set of functions to read, write, and manipulate NIFTI-1 files. The file nifti1_test.c is a sample program that uses the nifti1_io.c functions. -----------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/ /* HEADER STRUCT DECLARATION: ------------------------- In the comments below for each field, only NIFTI-1 specific requirements or changes from the ANALYZE 7.5 format are described. For convenience, the 348 byte header is described as a single struct, rather than as the ANALYZE 7.5 group of 3 substructs. Further comments about the interpretation of various elements of this header are after the data type definition itself. Fields that are marked as ++UNUSED++ have no particular interpretation in this standard. (Also see the UNUSED FIELDS comment section, far below.) The presumption below is that the various C types have particular sizes: sizeof(int) = sizeof(float) = 4 ; sizeof(short) = 2 -----------------------------------------------------------------------------*/ /*=================*/ #ifdef __cplusplus extern "C" { #endif /*=================*/ /*! \struct nifti_1_header \brief Data structure defining the fields in the nifti1 header. This binary header should be found at the beginning of a valid NIFTI-1 header file. */ struct #ifdef __GNUC__ __attribute__((packed)) #endif /*************************/ /************************/ nifti_1_header { /* NIFTI-1 usage */ /* ANALYZE 7.5 field(s) */ /*************************/ /************************/ /*--- was header_key substruct ---*/ int sizeof_hdr; /*!< MUST be 348 */ /* int sizeof_hdr; */ char data_type[10]; /*!< ++UNUSED++ */ /* char data_type[10]; */ char db_name[18]; /*!< ++UNUSED++ */ /* char db_name[18]; */ int extents; /*!< ++UNUSED++ */ /* int extents; */ short session_error; /*!< ++UNUSED++ */ /* short session_error; */ char regular; /*!< ++UNUSED++ */ /* char regular; */ char dim_info; /*!< MRI slice ordering. */ /* char hkey_un0; */ /*--- was image_dimension substruct ---*/ short dim[8]; /*!< Data array dimensions.*/ /* short dim[8]; */ float intent_p1 ; /*!< 1st intent parameter. */ /* short unused8; */ /* short unused9; */ float intent_p2 ; /*!< 2nd intent parameter. */ /* short unused10; */ /* short unused11; */ float intent_p3 ; /*!< 3rd intent parameter. */ /* short unused12; */ /* short unused13; */ short intent_code ; /*!< NIFTI_INTENT_* code. */ /* short unused14; */ short datatype; /*!< Defines data type! */ /* short datatype; */ short bitpix; /*!< Number bits/voxel. */ /* short bitpix; */ short slice_start; /*!< First slice index. */ /* short dim_un0; */ float pixdim[8]; /*!< Grid spacings. */ /* float pixdim[8]; */ float vox_offset; /*!< Offset into .nii file */ /* float vox_offset; */ float scl_slope ; /*!< Data scaling: slope. */ /* float funused1; */ float scl_inter ; /*!< Data scaling: offset. */ /* float funused2; */ short slice_end; /*!< Last slice index. */ /* float funused3; */ char slice_code ; /*!< Slice timing order. */ char xyzt_units ; /*!< Units of pixdim[1..4] */ float cal_max; /*!< Max display intensity */ /* float cal_max; */ float cal_min; /*!< Min display intensity */ /* float cal_min; */ float slice_duration;/*!< Time for 1 slice. */ /* float compressed; */ float toffset; /*!< Time axis shift. */ /* float verified; */ int glmax; /*!< ++UNUSED++ */ /* int glmax; */ int glmin; /*!< ++UNUSED++ */ /* int glmin; */ /*--- was data_history substruct ---*/ char descrip[80]; /*!< any text you like. */ /* char descrip[80]; */ char aux_file[24]; /*!< auxiliary filename. */ /* char aux_file[24]; */ short qform_code ; /*!< NIFTI_XFORM_* code. */ /*-- all ANALYZE 7.5 ---*/ short sform_code ; /*!< NIFTI_XFORM_* code. */ /* fields below here */ /* are replaced */ float quatern_b ; /*!< Quaternion b param. */ float quatern_c ; /*!< Quaternion c param. */ float quatern_d ; /*!< Quaternion d param. */ float qoffset_x ; /*!< Quaternion x shift. */ float qoffset_y ; /*!< Quaternion y shift. */ float qoffset_z ; /*!< Quaternion z shift. */ float srow_x[4] ; /*!< 1st row affine transform. */ float srow_y[4] ; /*!< 2nd row affine transform. */ float srow_z[4] ; /*!< 3rd row affine transform. */ char intent_name[16];/*!< 'name' or meaning of data. */ char magic[4] ; /*!< MUST be "ni1\0" or "n+1\0". */ } ; /**** 348 bytes total ****/ typedef struct nifti_1_header nifti_1_header ; /*---------------------------------------------------------------------------*/ /* HEADER EXTENSIONS: ----------------- After the end of the 348 byte header (e.g., after the magic field), the next 4 bytes are a char array field named "extension". By default, all 4 bytes of this array should be set to zero. In a .nii file, these 4 bytes will always be present, since the earliest start point for the image data is byte #352. In a separate .hdr file, these bytes may or may not be present. If not present (i.e., if the length of the .hdr file is 348 bytes), then a NIfTI-1 compliant program should use the default value of extension={0,0,0,0}. The first byte (extension[0]) is the only value of this array that is specified at present. The other 3 bytes are reserved for future use. If extension[0] is nonzero, it indicates that extended header information is present in the bytes following the extension array. In a .nii file, this extended header data is before the image data (and vox_offset must be set correctly to allow for this). In a .hdr file, this extended data follows extension and proceeds (potentially) to the end of the file. The format of extended header data is weakly specified. Each extension must be an integer multiple of 16 bytes long. The first 8 bytes of each extension comprise 2 integers: int esize , ecode ; These values may need to be byte-swapped, as indicated by dim[0] for the rest of the header. * esize is the number of bytes that form the extended header data + esize must be a positive integral multiple of 16 + this length includes the 8 bytes of esize and ecode themselves * ecode is a non-negative integer that indicates the format of the extended header data that follows + different ecode values are assigned to different developer groups + at present, the "registered" values for code are = 0 = unknown private format (not recommended!) = 2 = DICOM format (i.e., attribute tags and values) = 4 = AFNI group (i.e., ASCII XML-ish elements) In the interests of interoperability (a primary rationale for NIfTI), groups developing software that uses this extension mechanism are encouraged to document and publicize the format of their extensions. To this end, the NIfTI DFWG will assign even numbered codes upon request to groups submitting at least rudimentary documentation for the format of their extension; at present, the contact is mailto:rwcox@nih.gov. The assigned codes and documentation will be posted on the NIfTI website. All odd values of ecode (and 0) will remain unassigned; at least, until the even ones are used up, when we get to 2,147,483,646. Note that the other contents of the extended header data section are totally unspecified by the NIfTI-1 standard. In particular, if binary data is stored in such a section, its byte order is not necessarily the same as that given by examining dim[0]; it is incumbent on the programs dealing with such data to determine the byte order of binary extended header data. Multiple extended header sections are allowed, each starting with an esize,ecode value pair. The first esize value, as described above, is at bytes #352-355 in the .hdr or .nii file (files start at byte #0). If this value is positive, then the second (esize2) will be found starting at byte #352+esize1 , the third (esize3) at byte #352+esize1+esize2, et cetera. Of course, in a .nii file, the value of vox_offset must be compatible with these extensions. If a malformed file indicates that an extended header data section would run past vox_offset, then the entire extended header section should be ignored. In a .hdr file, if an extended header data section would run past the end-of-file, that extended header data should also be ignored. With the above scheme, a program can successively examine the esize and ecode values, and skip over each extended header section if the program doesn't know how to interpret the data within. Of course, any program can simply ignore all extended header sections simply by jumping straight to the image data using vox_offset. -----------------------------------------------------------------------------*/ /*! \struct nifti1_extender \brief This structure represents a 4-byte string that should follow the binary nifti_1_header data in a NIFTI-1 header file. If the char values are {1,0,0,0}, the file is expected to contain extensions, values of {0,0,0,0} imply the file does not contain extensions. Other sequences of values are not currently defined. */ struct nifti1_extender { char extension[4] ; } ; typedef struct nifti1_extender nifti1_extender ; /*! \struct nifti1_extension \brief Data structure defining the fields of a header extension. */ struct nifti1_extension { int esize ; /*!< size of extension, in bytes (must be multiple of 16) */ int ecode ; /*!< extension code, one of the NIFTI_ECODE_ values */ char * edata ; /*!< raw data, with no byte swapping (length is esize-8) */ } ; typedef struct nifti1_extension nifti1_extension ; /*---------------------------------------------------------------------------*/ /* DATA DIMENSIONALITY (as in ANALYZE 7.5): --------------------------------------- dim[0] = number of dimensions; - if dim[0] is outside range 1..7, then the header information needs to be byte swapped appropriately - ANALYZE supports dim[0] up to 7, but NIFTI-1 reserves dimensions 1,2,3 for space (x,y,z), 4 for time (t), and 5,6,7 for anything else needed. dim[i] = length of dimension #i, for i=1..dim[0] (must be positive) - also see the discussion of intent_code, far below pixdim[i] = voxel width along dimension #i, i=1..dim[0] (positive) - cf. ORIENTATION section below for use of pixdim[0] - the units of pixdim can be specified with the xyzt_units field (also described far below). Number of bits per voxel value is in bitpix, which MUST correspond with the datatype field. The total number of bytes in the image data is dim[1] * ... * dim[dim[0]] * bitpix / 8 In NIFTI-1 files, dimensions 1,2,3 are for space, dimension 4 is for time, and dimension 5 is for storing multiple values at each spatiotemporal voxel. Some examples: - A typical whole-brain FMRI experiment's time series: - dim[0] = 4 - dim[1] = 64 pixdim[1] = 3.75 xyzt_units = NIFTI_UNITS_MM - dim[2] = 64 pixdim[2] = 3.75 | NIFTI_UNITS_SEC - dim[3] = 20 pixdim[3] = 5.0 - dim[4] = 120 pixdim[4] = 2.0 - A typical T1-weighted anatomical volume: - dim[0] = 3 - dim[1] = 256 pixdim[1] = 1.0 xyzt_units = NIFTI_UNITS_MM - dim[2] = 256 pixdim[2] = 1.0 - dim[3] = 128 pixdim[3] = 1.1 - A single slice EPI time series: - dim[0] = 4 - dim[1] = 64 pixdim[1] = 3.75 xyzt_units = NIFTI_UNITS_MM - dim[2] = 64 pixdim[2] = 3.75 | NIFTI_UNITS_SEC - dim[3] = 1 pixdim[3] = 5.0 - dim[4] = 1200 pixdim[4] = 0.2 - A 3-vector stored at each point in a 3D volume: - dim[0] = 5 - dim[1] = 256 pixdim[1] = 1.0 xyzt_units = NIFTI_UNITS_MM - dim[2] = 256 pixdim[2] = 1.0 - dim[3] = 128 pixdim[3] = 1.1 - dim[4] = 1 pixdim[4] = 0.0 - dim[5] = 3 intent_code = NIFTI_INTENT_VECTOR - A single time series with a 3x3 matrix at each point: - dim[0] = 5 - dim[1] = 1 xyzt_units = NIFTI_UNITS_SEC - dim[2] = 1 - dim[3] = 1 - dim[4] = 1200 pixdim[4] = 0.2 - dim[5] = 9 intent_code = NIFTI_INTENT_GENMATRIX - intent_p1 = intent_p2 = 3.0 (indicates matrix dimensions) -----------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/ /* DATA STORAGE: ------------ If the magic field is "n+1", then the voxel data is stored in the same file as the header. In this case, the voxel data starts at offset (int)vox_offset into the header file. Thus, vox_offset=352.0 means that the data starts immediately after the NIFTI-1 header. If vox_offset is greater than 352, the NIFTI-1 format does not say much about the contents of the dataset file between the end of the header and the start of the data. FILES: ----- If the magic field is "ni1", then the voxel data is stored in the associated ".img" file, starting at offset 0 (i.e., vox_offset is not used in this case, and should be set to 0.0). When storing NIFTI-1 datasets in pairs of files, it is customary to name the files in the pattern "name.hdr" and "name.img", as in ANALYZE 7.5. When storing in a single file ("n+1"), the file name should be in the form "name.nii" (the ".nft" and ".nif" suffixes are already taken; cf. http://www.icdatamaster.com/n.html ). BYTE ORDERING: ------------- The byte order of the data arrays is presumed to be the same as the byte order of the header (which is determined by examining dim[0]). Floating point types are presumed to be stored in IEEE-754 format. -----------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/ /* DETAILS ABOUT vox_offset: ------------------------ In a .nii file, the vox_offset field value is interpreted as the start location of the image data bytes in that file. In a .hdr/.img file pair, the vox_offset field value is the start location of the image data bytes in the .img file. * If vox_offset is less than 352 in a .nii file, it is equivalent to 352 (i.e., image data never starts before byte #352 in a .nii file). * The default value for vox_offset in a .nii file is 352. * In a .hdr file, the default value for vox_offset is 0. * vox_offset should be an integer multiple of 16; otherwise, some programs may not work properly (e.g., SPM). This is to allow memory-mapped input to be properly byte-aligned. Note that since vox_offset is an IEEE-754 32 bit float (for compatibility with the ANALYZE-7.5 format), it effectively has a 24 bit mantissa. All integers from 0 to 2^24 can be represented exactly in this format, but not all larger integers are exactly storable as IEEE-754 32 bit floats. However, unless you plan to have vox_offset be potentially larger than 16 MB, this should not be an issue. (Actually, any integral multiple of 16 up to 2^27 can be represented exactly in this format, which allows for up to 128 MB of random information before the image data. If that isn't enough, then perhaps this format isn't right for you.) In a .img file (i.e., image data stored separately from the NIfTI-1 header), data bytes between #0 and #vox_offset-1 (inclusive) are completely undefined and unregulated by the NIfTI-1 standard. One potential use of having vox_offset > 0 in the .hdr/.img file pair storage method is to make the .img file be a copy of (or link to) a pre-existing image file in some other format, such as DICOM; then vox_offset would be set to the offset of the image data in this file. (It may not be possible to follow the "multiple-of-16 rule" with an arbitrary external file; using the NIfTI-1 format in such a case may lead to a file that is incompatible with software that relies on vox_offset being a multiple of 16.) In a .nii file, data bytes between #348 and #vox_offset-1 (inclusive) may be used to store user-defined extra information; similarly, in a .hdr file, any data bytes after byte #347 are available for user-defined extra information. The (very weak) regulation of this extra header data is described elsewhere. -----------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/ /* DATA SCALING: ------------ If the scl_slope field is nonzero, then each voxel value in the dataset should be scaled as y = scl_slope * x + scl_inter where x = voxel value stored y = "true" voxel value Normally, we would expect this scaling to be used to store "true" floating values in a smaller integer datatype, but that is not required. That is, it is legal to use scaling even if the datatype is a float type (crazy, perhaps, but legal). - However, the scaling is to be ignored if datatype is DT_RGB24. - If datatype is a complex type, then the scaling is to be applied to both the real and imaginary parts. The cal_min and cal_max fields (if nonzero) are used for mapping (possibly scaled) dataset values to display colors: - Minimum display intensity (black) corresponds to dataset value cal_min. - Maximum display intensity (white) corresponds to dataset value cal_max. - Dataset values below cal_min should display as black also, and values above cal_max as white. - Colors "black" and "white", of course, may refer to any scalar display scheme (e.g., a color lookup table specified via aux_file). - cal_min and cal_max only make sense when applied to scalar-valued datasets (i.e., dim[0] < 5 or dim[5] = 1). -----------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/ /* TYPE OF DATA (acceptable values for datatype field): --------------------------------------------------- Values of datatype smaller than 256 are ANALYZE 7.5 compatible. Larger values are NIFTI-1 additions. These are all multiples of 256, so that no bits below position 8 are set in datatype. But there is no need to use only powers-of-2, as the original ANALYZE 7.5 datatype codes do. The additional codes are intended to include a complete list of basic scalar types, including signed and unsigned integers from 8 to 64 bits, floats from 32 to 128 bits, and complex (float pairs) from 64 to 256 bits. Note that most programs will support only a few of these datatypes! A NIFTI-1 program should fail gracefully (e.g., print a warning message) when it encounters a dataset with a type it doesn't like. -----------------------------------------------------------------------------*/ #undef DT_UNKNOWN /* defined in dirent.h on some Unix systems */ /*! \defgroup NIFTI1_DATATYPES \brief nifti1 datatype codes @{ */ /*--- the original ANALYZE 7.5 type codes ---*/ #define DT_NONE 0 #define DT_UNKNOWN 0 /* what it says, dude */ #define DT_BINARY 1 /* binary (1 bit/voxel) */ #define DT_UNSIGNED_CHAR 2 /* unsigned char (8 bits/voxel) */ #define DT_SIGNED_SHORT 4 /* signed short (16 bits/voxel) */ #define DT_SIGNED_INT 8 /* signed int (32 bits/voxel) */ #define DT_FLOAT 16 /* float (32 bits/voxel) */ #define DT_COMPLEX 32 /* complex (64 bits/voxel) */ #define DT_DOUBLE 64 /* double (64 bits/voxel) */ #define DT_RGB 128 /* RGB triple (24 bits/voxel) */ #define DT_ALL 255 /* not very useful (?) */ /*----- another set of names for the same ---*/ #define DT_UINT8 2 #define DT_INT16 4 #define DT_INT32 8 #define DT_FLOAT32 16 #define DT_COMPLEX64 32 #define DT_FLOAT64 64 #define DT_RGB24 128 /*------------------- new codes for NIFTI ---*/ #define DT_INT8 256 /* signed char (8 bits) */ #define DT_UINT16 512 /* unsigned short (16 bits) */ #define DT_UINT32 768 /* unsigned int (32 bits) */ #define DT_INT64 1024 /* long long (64 bits) */ #define DT_UINT64 1280 /* unsigned long long (64 bits) */ #define DT_FLOAT128 1536 /* long double (128 bits) */ #define DT_COMPLEX128 1792 /* double pair (128 bits) */ #define DT_COMPLEX256 2048 /* long double pair (256 bits) */ /* @} */ /*------- aliases for all the above codes ---*/ /*! \defgroup NIFTI1_DATATYPE_ALIASES \brief aliases for the nifti1 datatype codes @{ */ /*! unsigned char. */ #define NIFTI_TYPE_UINT8 2 /*! signed short. */ #define NIFTI_TYPE_INT16 4 /*! signed int. */ #define NIFTI_TYPE_INT32 8 /*! 32 bit float. */ #define NIFTI_TYPE_FLOAT32 16 /*! 64 bit complex = 2 32 bit floats. */ #define NIFTI_TYPE_COMPLEX64 32 /*! 64 bit float = double. */ #define NIFTI_TYPE_FLOAT64 64 /*! 3 8 bit bytes. */ #define NIFTI_TYPE_RGB24 128 /*! signed char. */ #define NIFTI_TYPE_INT8 256 /*! unsigned short. */ #define NIFTI_TYPE_UINT16 512 /*! unsigned int. */ #define NIFTI_TYPE_UINT32 768 /*! signed long long. */ #define NIFTI_TYPE_INT64 1024 /*! unsigned long long. */ #define NIFTI_TYPE_UINT64 1280 /*! 128 bit float = long double. */ #define NIFTI_TYPE_FLOAT128 1536 /*! 128 bit complex = 2 64 bit floats. */ #define NIFTI_TYPE_COMPLEX128 1792 /*! 256 bit complex = 2 128 bit floats */ #define NIFTI_TYPE_COMPLEX256 2048 /* @} */ /*-------- sample typedefs for complicated types ---*/ #if 0 typedef struct { float r,i; } complex_float ; typedef struct { double r,i; } complex_double ; typedef struct { long double r,i; } complex_longdouble ; typedef struct { unsigned char r,g,b; } rgb_byte ; #endif /*---------------------------------------------------------------------------*/ /* INTERPRETATION OF VOXEL DATA: ---------------------------- The intent_code field can be used to indicate that the voxel data has some particular meaning. In particular, a large number of codes is given to indicate that the the voxel data should be interpreted as being drawn from a given probability distribution. VECTOR-VALUED DATASETS: ---------------------- The 5th dimension of the dataset, if present (i.e., dim[0]=5 and dim[5] > 1), contains multiple values (e.g., a vector) to be stored at each spatiotemporal location. For example, the header values - dim[0] = 5 - dim[1] = 64 - dim[2] = 64 - dim[3] = 20 - dim[4] = 1 (indicates no time axis) - dim[5] = 3 - datatype = DT_FLOAT - intent_code = NIFTI_INTENT_VECTOR mean that this dataset should be interpreted as a 3D volume (64x64x20), with a 3-vector of floats defined at each point in the 3D grid. A program reading a dataset with a 5th dimension may want to reformat the image data to store each voxels' set of values together in a struct or array. This programming detail, however, is beyond the scope of the NIFTI-1 file specification! Uses of dimensions 6 and 7 are also not specified here. STATISTICAL PARAMETRIC DATASETS (i.e., SPMs): -------------------------------------------- Values of intent_code from NIFTI_FIRST_STATCODE to NIFTI_LAST_STATCODE (inclusive) indicate that the numbers in the dataset should be interpreted as being drawn from a given distribution. Most such distributions have auxiliary parameters (e.g., NIFTI_INTENT_TTEST has 1 DOF parameter). If the dataset DOES NOT have a 5th dimension, then the auxiliary parameters are the same for each voxel, and are given in header fields intent_p1, intent_p2, and intent_p3. If the dataset DOES have a 5th dimension, then the auxiliary parameters are different for each voxel. For example, the header values - dim[0] = 5 - dim[1] = 128 - dim[2] = 128 - dim[3] = 1 (indicates a single slice) - dim[4] = 1 (indicates no time axis) - dim[5] = 2 - datatype = DT_FLOAT - intent_code = NIFTI_INTENT_TTEST mean that this is a 2D dataset (128x128) of t-statistics, with the t-statistic being in the first "plane" of data and the degrees-of-freedom parameter being in the second "plane" of data. If the dataset 5th dimension is used to store the voxel-wise statistical parameters, then dim[5] must be 1 plus the number of parameters required by that distribution (e.g., intent_code=NIFTI_INTENT_TTEST implies dim[5] must be 2, as in the example just above). Note: intent_code values 2..10 are compatible with AFNI 1.5x (which is why there is no code with value=1, which is obsolescent in AFNI). OTHER INTENTIONS: ---------------- The purpose of the intent_* fields is to help interpret the values stored in the dataset. Some non-statistical values for intent_code and conventions are provided for storing other complex data types. The intent_name field provides space for a 15 character (plus 0 byte) 'name' string for the type of data stored. Examples: - intent_code = NIFTI_INTENT_ESTIMATE; intent_name = "T1"; could be used to signify that the voxel values are estimates of the NMR parameter T1. - intent_code = NIFTI_INTENT_TTEST; intent_name = "House"; could be used to signify that the voxel values are t-statistics for the significance of 'activation' response to a House stimulus. - intent_code = NIFTI_INTENT_DISPVECT; intent_name = "ToMNI152"; could be used to signify that the voxel values are a displacement vector that transforms each voxel (x,y,z) location to the corresponding location in the MNI152 standard brain. - intent_code = NIFTI_INTENT_SYMMATRIX; intent_name = "DTI"; could be used to signify that the voxel values comprise a diffusion tensor image. If no data name is implied or needed, intent_name[0] should be set to 0. -----------------------------------------------------------------------------*/ /*! default: no intention is indicated in the header. */ #define NIFTI_INTENT_NONE 0 /*-------- These codes are for probability distributions ---------------*/ /* Most distributions have a number of parameters, below denoted by p1, p2, and p3, and stored in - intent_p1, intent_p2, intent_p3 if dataset doesn't have 5th dimension - image data array if dataset does have 5th dimension Functions to compute with many of the distributions below can be found in the CDF library from U Texas. Formulas for and discussions of these distributions can be found in the following books: [U] Univariate Discrete Distributions, NL Johnson, S Kotz, AW Kemp. [C1] Continuous Univariate Distributions, vol. 1, NL Johnson, S Kotz, N Balakrishnan. [C2] Continuous Univariate Distributions, vol. 2, NL Johnson, S Kotz, N Balakrishnan. */ /*----------------------------------------------------------------------*/ /*! [C2, chap 32] Correlation coefficient R (1 param): p1 = degrees of freedom R/sqrt(1-R*R) is t-distributed with p1 DOF. */ /*! \defgroup NIFTI1_INTENT_CODES \brief nifti1 intent codes, to describe intended meaning of dataset contents @{ */ #define NIFTI_INTENT_CORREL 2 /*! [C2, chap 28] Student t statistic (1 param): p1 = DOF. */ #define NIFTI_INTENT_TTEST 3 /*! [C2, chap 27] Fisher F statistic (2 params): p1 = numerator DOF, p2 = denominator DOF. */ #define NIFTI_INTENT_FTEST 4 /*! [C1, chap 13] Standard normal (0 params): Density = N(0,1). */ #define NIFTI_INTENT_ZSCORE 5 /*! [C1, chap 18] Chi-squared (1 param): p1 = DOF. Density(x) proportional to exp(-x/2) * x^(p1/2-1). */ #define NIFTI_INTENT_CHISQ 6 /*! [C2, chap 25] Beta distribution (2 params): p1=a, p2=b. Density(x) proportional to x^(a-1) * (1-x)^(b-1). */ #define NIFTI_INTENT_BETA 7 /*! [U, chap 3] Binomial distribution (2 params): p1 = number of trials, p2 = probability per trial. Prob(x) = (p1 choose x) * p2^x * (1-p2)^(p1-x), for x=0,1,...,p1. */ #define NIFTI_INTENT_BINOM 8 /*! [C1, chap 17] Gamma distribution (2 params): p1 = shape, p2 = scale. Density(x) proportional to x^(p1-1) * exp(-p2*x). */ #define NIFTI_INTENT_GAMMA 9 /*! [U, chap 4] Poisson distribution (1 param): p1 = mean. Prob(x) = exp(-p1) * p1^x / x! , for x=0,1,2,.... */ #define NIFTI_INTENT_POISSON 10 /*! [C1, chap 13] Normal distribution (2 params): p1 = mean, p2 = standard deviation. */ #define NIFTI_INTENT_NORMAL 11 /*! [C2, chap 30] Noncentral F statistic (3 params): p1 = numerator DOF, p2 = denominator DOF, p3 = numerator noncentrality parameter. */ #define NIFTI_INTENT_FTEST_NONC 12 /*! [C2, chap 29] Noncentral chi-squared statistic (2 params): p1 = DOF, p2 = noncentrality parameter. */ #define NIFTI_INTENT_CHISQ_NONC 13 /*! [C2, chap 23] Logistic distribution (2 params): p1 = location, p2 = scale. Density(x) proportional to sech^2((x-p1)/(2*p2)). */ #define NIFTI_INTENT_LOGISTIC 14 /*! [C2, chap 24] Laplace distribution (2 params): p1 = location, p2 = scale. Density(x) proportional to exp(-abs(x-p1)/p2). */ #define NIFTI_INTENT_LAPLACE 15 /*! [C2, chap 26] Uniform distribution: p1 = lower end, p2 = upper end. */ #define NIFTI_INTENT_UNIFORM 16 /*! [C2, chap 31] Noncentral t statistic (2 params): p1 = DOF, p2 = noncentrality parameter. */ #define NIFTI_INTENT_TTEST_NONC 17 /*! [C1, chap 21] Weibull distribution (3 params): p1 = location, p2 = scale, p3 = power. Density(x) proportional to ((x-p1)/p2)^(p3-1) * exp(-((x-p1)/p2)^p3) for x > p1. */ #define NIFTI_INTENT_WEIBULL 18 /*! [C1, chap 18] Chi distribution (1 param): p1 = DOF. Density(x) proportional to x^(p1-1) * exp(-x^2/2) for x > 0. p1 = 1 = 'half normal' distribution p1 = 2 = Rayleigh distribution p1 = 3 = Maxwell-Boltzmann distribution. */ #define NIFTI_INTENT_CHI 19 /*! [C1, chap 15] Inverse Gaussian (2 params): p1 = mu, p2 = lambda Density(x) proportional to exp(-p2*(x-p1)^2/(2*p1^2*x)) / x^3 for x > 0. */ #define NIFTI_INTENT_INVGAUSS 20 /*! [C2, chap 22] Extreme value type I (2 params): p1 = location, p2 = scale cdf(x) = exp(-exp(-(x-p1)/p2)). */ #define NIFTI_INTENT_EXTVAL 21 /*! Data is a 'p-value' (no params). */ #define NIFTI_INTENT_PVAL 22 /*! Data is ln(p-value) (no params). To be safe, a program should compute p = exp(-abs(this_value)). The nifti_stats.c library returns this_value as positive, so that this_value = -log(p). */ #define NIFTI_INTENT_LOGPVAL 23 /*! Data is log10(p-value) (no params). To be safe, a program should compute p = pow(10.,-abs(this_value)). The nifti_stats.c library returns this_value as positive, so that this_value = -log10(p). */ #define NIFTI_INTENT_LOG10PVAL 24 /*! Smallest intent_code that indicates a statistic. */ #define NIFTI_FIRST_STATCODE 2 /*! Largest intent_code that indicates a statistic. */ #define NIFTI_LAST_STATCODE 24 /*---------- these values for intent_code aren't for statistics ----------*/ /*! To signify that the value at each voxel is an estimate of some parameter, set intent_code = NIFTI_INTENT_ESTIMATE. The name of the parameter may be stored in intent_name. */ #define NIFTI_INTENT_ESTIMATE 1001 /*! To signify that the value at each voxel is an index into some set of labels, set intent_code = NIFTI_INTENT_LABEL. The filename with the labels may stored in aux_file. */ #define NIFTI_INTENT_LABEL 1002 /*! To signify that the value at each voxel is an index into the NeuroNames labels set, set intent_code = NIFTI_INTENT_NEURONAME. */ #define NIFTI_INTENT_NEURONAME 1003 /*! To store an M x N matrix at each voxel: - dataset must have a 5th dimension (dim[0]=5 and dim[5]>1) - intent_code must be NIFTI_INTENT_GENMATRIX - dim[5] must be M*N - intent_p1 must be M (in float format) - intent_p2 must be N (ditto) - the matrix values A[i][[j] are stored in row-order: - A[0][0] A[0][1] ... A[0][N-1] - A[1][0] A[1][1] ... A[1][N-1] - etc., until - A[M-1][0] A[M-1][1] ... A[M-1][N-1] */ #define NIFTI_INTENT_GENMATRIX 1004 /*! To store an NxN symmetric matrix at each voxel: - dataset must have a 5th dimension - intent_code must be NIFTI_INTENT_SYMMATRIX - dim[5] must be N*(N+1)/2 - intent_p1 must be N (in float format) - the matrix values A[i][[j] are stored in row-order: - A[0][0] - A[1][0] A[1][1] - A[2][0] A[2][1] A[2][2] - etc.: row-by-row */ #define NIFTI_INTENT_SYMMATRIX 1005 /*! To signify that the vector value at each voxel is to be taken as a displacement field or vector: - dataset must have a 5th dimension - intent_code must be NIFTI_INTENT_DISPVECT - dim[5] must be the dimensionality of the displacment vector (e.g., 3 for spatial displacement, 2 for in-plane) */ #define NIFTI_INTENT_DISPVECT 1006 /* specifically for displacements */ #define NIFTI_INTENT_VECTOR 1007 /* for any other type of vector */ /*! To signify that the vector value at each voxel is really a spatial coordinate (e.g., the vertices or nodes of a surface mesh): - dataset must have a 5th dimension - intent_code must be NIFTI_INTENT_POINTSET - dim[0] = 5 - dim[1] = number of points - dim[2] = dim[3] = dim[4] = 1 - dim[5] must be the dimensionality of space (e.g., 3 => 3D space). - intent_name may describe the object these points come from (e.g., "pial", "gray/white" , "EEG", "MEG"). */ #define NIFTI_INTENT_POINTSET 1008 /*! To signify that the vector value at each voxel is really a triple of indexes (e.g., forming a triangle) from a pointset dataset: - dataset must have a 5th dimension - intent_code must be NIFTI_INTENT_TRIANGLE - dim[0] = 5 - dim[1] = number of triangles - dim[2] = dim[3] = dim[4] = 1 - dim[5] = 3 - datatype should be an integer type (preferably DT_INT32) - the data values are indexes (0,1,...) into a pointset dataset. */ #define NIFTI_INTENT_TRIANGLE 1009 /*! To signify that the vector value at each voxel is a quaternion: - dataset must have a 5th dimension - intent_code must be NIFTI_INTENT_QUATERNION - dim[0] = 5 - dim[5] = 4 - datatype should be a floating point type */ #define NIFTI_INTENT_QUATERNION 1010 /*! Dimensionless value - no params - although, as in _ESTIMATE the name of the parameter may be stored in intent_name. */ #define NIFTI_INTENT_DIMLESS 1011 /* @} */ /*---------------------------------------------------------------------------*/ /* 3D IMAGE (VOLUME) ORIENTATION AND LOCATION IN SPACE: --------------------------------------------------- There are 3 different methods by which continuous coordinates can attached to voxels. The discussion below emphasizes 3D volumes, and the continuous coordinates are referred to as (x,y,z). The voxel index coordinates (i.e., the array indexes) are referred to as (i,j,k), with valid ranges: i = 0 .. dim[1]-1 j = 0 .. dim[2]-1 (if dim[0] >= 2) k = 0 .. dim[3]-1 (if dim[0] >= 3) The (x,y,z) coordinates refer to the CENTER of a voxel. In methods 2 and 3, the (x,y,z) axes refer to a subject-based coordinate system, with +x = Right +y = Anterior +z = Superior. This is a right-handed coordinate system. However, the exact direction these axes point with respect to the subject depends on qform_code (Method 2) and sform_code (Method 3). N.B.: The i index varies most rapidly, j index next, k index slowest. Thus, voxel (i,j,k) is stored starting at location (i + j*dim[1] + k*dim[1]*dim[2]) * (bitpix/8) into the dataset array. N.B.: The ANALYZE 7.5 coordinate system is +x = Left +y = Anterior +z = Superior which is a left-handed coordinate system. This backwardness is too difficult to tolerate, so this NIFTI-1 standard specifies the coordinate order which is most common in functional neuroimaging. N.B.: The 3 methods below all give the locations of the voxel centers in the (x,y,z) coordinate system. In many cases, programs will wish to display image data on some other grid. In such a case, the program will need to convert its desired (x,y,z) values into (i,j,k) values in order to extract (or interpolate) the image data. This operation would be done with the inverse transformation to those described below. N.B.: Method 2 uses a factor 'qfac' which is either -1 or 1; qfac is stored in the otherwise unused pixdim[0]. If pixdim[0]=0.0 (which should not occur), we take qfac=1. Of course, pixdim[0] is only used when reading a NIFTI-1 header, not when reading an ANALYZE 7.5 header. N.B.: The units of (x,y,z) can be specified using the xyzt_units field. METHOD 1 (the "old" way, used only when qform_code = 0): ------------------------------------------------------- The coordinate mapping from (i,j,k) to (x,y,z) is the ANALYZE 7.5 way. This is a simple scaling relationship: x = pixdim[1] * i y = pixdim[2] * j z = pixdim[3] * k No particular spatial orientation is attached to these (x,y,z) coordinates. (NIFTI-1 does not have the ANALYZE 7.5 orient field, which is not general and is often not set properly.) This method is not recommended, and is present mainly for compatibility with ANALYZE 7.5 files. METHOD 2 (used when qform_code > 0, which should be the "normal" case): --------------------------------------------------------------------- The (x,y,z) coordinates are given by the pixdim[] scales, a rotation matrix, and a shift. This method is intended to represent "scanner-anatomical" coordinates, which are often embedded in the image header (e.g., DICOM fields (0020,0032), (0020,0037), (0028,0030), and (0018,0050)), and represent the nominal orientation and location of the data. This method can also be used to represent "aligned" coordinates, which would typically result from some post-acquisition alignment of the volume to a standard orientation (e.g., the same subject on another day, or a rigid rotation to true anatomical orientation from the tilted position of the subject in the scanner). The formula for (x,y,z) in terms of header parameters and (i,j,k) is: [ x ] [ R11 R12 R13 ] [ pixdim[1] * i ] [ qoffset_x ] [ y ] = [ R21 R22 R23 ] [ pixdim[2] * j ] + [ qoffset_y ] [ z ] [ R31 R32 R33 ] [ qfac * pixdim[3] * k ] [ qoffset_z ] The qoffset_* shifts are in the NIFTI-1 header. Note that the center of the (i,j,k)=(0,0,0) voxel (first value in the dataset array) is just (x,y,z)=(qoffset_x,qoffset_y,qoffset_z). The rotation matrix R is calculated from the quatern_* parameters. This calculation is described below. The scaling factor qfac is either 1 or -1. The rotation matrix R defined by the quaternion parameters is "proper" (has determinant 1). This may not fit the needs of the data; for example, if the image grid is i increases from Left-to-Right j increases from Anterior-to-Posterior k increases from Inferior-to-Superior Then (i,j,k) is a left-handed triple. In this example, if qfac=1, the R matrix would have to be [ 1 0 0 ] [ 0 -1 0 ] which is "improper" (determinant = -1). [ 0 0 1 ] If we set qfac=-1, then the R matrix would be [ 1 0 0 ] [ 0 -1 0 ] which is proper. [ 0 0 -1 ] This R matrix is represented by quaternion [a,b,c,d] = [0,1,0,0] (which encodes a 180 degree rotation about the x-axis). METHOD 3 (used when sform_code > 0): ----------------------------------- The (x,y,z) coordinates are given by a general affine transformation of the (i,j,k) indexes: x = srow_x[0] * i + srow_x[1] * j + srow_x[2] * k + srow_x[3] y = srow_y[0] * i + srow_y[1] * j + srow_y[2] * k + srow_y[3] z = srow_z[0] * i + srow_z[1] * j + srow_z[2] * k + srow_z[3] The srow_* vectors are in the NIFTI_1 header. Note that no use is made of pixdim[] in this method. WHY 3 METHODS? -------------- Method 1 is provided only for backwards compatibility. The intention is that Method 2 (qform_code > 0) represents the nominal voxel locations as reported by the scanner, or as rotated to some fiducial orientation and location. Method 3, if present (sform_code > 0), is to be used to give the location of the voxels in some standard space. The sform_code indicates which standard space is present. Both methods 2 and 3 can be present, and be useful in different contexts (method 2 for displaying the data on its original grid; method 3 for displaying it on a standard grid). In this scheme, a dataset would originally be set up so that the Method 2 coordinates represent what the scanner reported. Later, a registration to some standard space can be computed and inserted in the header. Image display software can use either transform, depending on its purposes and needs. In Method 2, the origin of coordinates would generally be whatever the scanner origin is; for example, in MRI, (0,0,0) is the center of the gradient coil. In Method 3, the origin of coordinates would depend on the value of sform_code; for example, for the Talairach coordinate system, (0,0,0) corresponds to the Anterior Commissure. QUATERNION REPRESENTATION OF ROTATION MATRIX (METHOD 2) ------------------------------------------------------- The orientation of the (x,y,z) axes relative to the (i,j,k) axes in 3D space is specified using a unit quaternion [a,b,c,d], where a*a+b*b+c*c+d*d=1. The (b,c,d) values are all that is needed, since we require that a = sqrt(1.0-(b*b+c*c+d*d)) be nonnegative. The (b,c,d) values are stored in the (quatern_b,quatern_c,quatern_d) fields. The quaternion representation is chosen for its compactness in representing rotations. The (proper) 3x3 rotation matrix that corresponds to [a,b,c,d] is [ a*a+b*b-c*c-d*d 2*b*c-2*a*d 2*b*d+2*a*c ] R = [ 2*b*c+2*a*d a*a+c*c-b*b-d*d 2*c*d-2*a*b ] [ 2*b*d-2*a*c 2*c*d+2*a*b a*a+d*d-c*c-b*b ] [ R11 R12 R13 ] = [ R21 R22 R23 ] [ R31 R32 R33 ] If (p,q,r) is a unit 3-vector, then rotation of angle h about that direction is represented by the quaternion [a,b,c,d] = [cos(h/2), p*sin(h/2), q*sin(h/2), r*sin(h/2)]. Requiring a >= 0 is equivalent to requiring -Pi <= h <= Pi. (Note that [-a,-b,-c,-d] represents the same rotation as [a,b,c,d]; there are 2 quaternions that can be used to represent a given rotation matrix R.) To rotate a 3-vector (x,y,z) using quaternions, we compute the quaternion product [0,x',y',z'] = [a,b,c,d] * [0,x,y,z] * [a,-b,-c,-d] which is equivalent to the matrix-vector multiply [ x' ] [ x ] [ y' ] = R [ y ] (equivalence depends on a*a+b*b+c*c+d*d=1) [ z' ] [ z ] Multiplication of 2 quaternions is defined by the following: [a,b,c,d] = a*1 + b*I + c*J + d*K where I*I = J*J = K*K = -1 (I,J,K are square roots of -1) I*J = K J*K = I K*I = J J*I = -K K*J = -I I*K = -J (not commutative!) For example [a,b,0,0] * [0,0,0,1] = [0,0,-b,a] since this expands to (a+b*I)*(K) = (a*K+b*I*K) = (a*K-b*J). The above formula shows how to go from quaternion (b,c,d) to rotation matrix and direction cosines. Conversely, given R, we can compute the fields for the NIFTI-1 header by a = 0.5 * sqrt(1+R11+R22+R33) (not stored) b = 0.25 * (R32-R23) / a => quatern_b c = 0.25 * (R13-R31) / a => quatern_c d = 0.25 * (R21-R12) / a => quatern_d If a=0 (a 180 degree rotation), alternative formulas are needed. See the nifti1_io.c function mat44_to_quatern() for an implementation of the various cases in converting R to [a,b,c,d]. Note that R-transpose (= R-inverse) would lead to the quaternion [a,-b,-c,-d]. The choice to specify the qoffset_x (etc.) values in the final coordinate system is partly to make it easy to convert DICOM images to this format. The DICOM attribute "Image Position (Patient)" (0020,0032) stores the (Xd,Yd,Zd) coordinates of the center of the first voxel. Here, (Xd,Yd,Zd) refer to DICOM coordinates, and Xd=-x, Yd=-y, Zd=z, where (x,y,z) refers to the NIFTI coordinate system discussed above. (i.e., DICOM +Xd is Left, +Yd is Posterior, +Zd is Superior, whereas +x is Right, +y is Anterior , +z is Superior. ) Thus, if the (0020,0032) DICOM attribute is extracted into (px,py,pz), then qoffset_x = -px qoffset_y = -py qoffset_z = pz is a reasonable setting when qform_code=NIFTI_XFORM_SCANNER_ANAT. That is, DICOM's coordinate system is 180 degrees rotated about the z-axis from the neuroscience/NIFTI coordinate system. To transform between DICOM and NIFTI, you just have to negate the x- and y-coordinates. The DICOM attribute (0020,0037) "Image Orientation (Patient)" gives the orientation of the x- and y-axes of the image data in terms of 2 3-vectors. The first vector is a unit vector along the x-axis, and the second is along the y-axis. If the (0020,0037) attribute is extracted into the value (xa,xb,xc,ya,yb,yc), then the first two columns of the R matrix would be [ -xa -ya ] [ -xb -yb ] [ xc yc ] The negations are because DICOM's x- and y-axes are reversed relative to NIFTI's. The third column of the R matrix gives the direction of displacement (relative to the subject) along the slice-wise direction. This orientation is not encoded in the DICOM standard in a simple way; DICOM is mostly concerned with 2D images. The third column of R will be either the cross-product of the first 2 columns or its negative. It is possible to infer the sign of the 3rd column by examining the coordinates in DICOM attribute (0020,0032) "Image Position (Patient)" for successive slices. However, this method occasionally fails for reasons that I (RW Cox) do not understand. -----------------------------------------------------------------------------*/ /* [qs]form_code value: */ /* x,y,z coordinate system refers to: */ /*-----------------------*/ /*---------------------------------------*/ /*! \defgroup NIFTI1_XFORM_CODES \brief nifti1 xform codes to describe the "standard" coordinate system @{ */ /*! Arbitrary coordinates (Method 1). */ #define NIFTI_XFORM_UNKNOWN 0 /*! Scanner-based anatomical coordinates */ #define NIFTI_XFORM_SCANNER_ANAT 1 /*! Coordinates aligned to another file's, or to anatomical "truth". */ #define NIFTI_XFORM_ALIGNED_ANAT 2 /*! Coordinates aligned to Talairach- Tournoux Atlas; (0,0,0)=AC, etc. */ #define NIFTI_XFORM_TALAIRACH 3 /*! MNI 152 normalized coordinates. */ #define NIFTI_XFORM_MNI_152 4 /* @} */ /*---------------------------------------------------------------------------*/ /* UNITS OF SPATIAL AND TEMPORAL DIMENSIONS: ---------------------------------------- The codes below can be used in xyzt_units to indicate the units of pixdim. As noted earlier, dimensions 1,2,3 are for x,y,z; dimension 4 is for time (t). - If dim[4]=1 or dim[0] < 4, there is no time axis. - A single time series (no space) would be specified with - dim[0] = 4 (for scalar data) or dim[0] = 5 (for vector data) - dim[1] = dim[2] = dim[3] = 1 - dim[4] = number of time points - pixdim[4] = time step - xyzt_units indicates units of pixdim[4] - dim[5] = number of values stored at each time point Bits 0..2 of xyzt_units specify the units of pixdim[1..3] (e.g., spatial units are values 1..7). Bits 3..5 of xyzt_units specify the units of pixdim[4] (e.g., temporal units are multiples of 8). This compression of 2 distinct concepts into 1 byte is due to the limited space available in the 348 byte ANALYZE 7.5 header. The macros XYZT_TO_SPACE and XYZT_TO_TIME can be used to mask off the undesired bits from the xyzt_units fields, leaving "pure" space and time codes. Inversely, the macro SPACE_TIME_TO_XYZT can be used to assemble a space code (0,1,2,...,7) with a time code (0,8,16,32,...,56) into the combined value for xyzt_units. Note that codes are provided to indicate the "time" axis units are actually frequency in Hertz (_HZ), in part-per-million (_PPM) or in radians-per-second (_RADS). The toffset field can be used to indicate a nonzero start point for the time axis. That is, time point #m is at t=toffset+m*pixdim[4] for m=0..dim[4]-1. -----------------------------------------------------------------------------*/ /*! \defgroup NIFTI1_UNITS \brief nifti1 units codes to describe the unit of measurement for each dimension of the dataset @{ */ /*! NIFTI code for unspecified units. */ #define NIFTI_UNITS_UNKNOWN 0 /** Space codes are multiples of 1. **/ /*! NIFTI code for meters. */ #define NIFTI_UNITS_METER 1 /*! NIFTI code for millimeters. */ #define NIFTI_UNITS_MM 2 /*! NIFTI code for micrometers. */ #define NIFTI_UNITS_MICRON 3 /** Time codes are multiples of 8. **/ /*! NIFTI code for seconds. */ #define NIFTI_UNITS_SEC 8 /*! NIFTI code for milliseconds. */ #define NIFTI_UNITS_MSEC 16 /*! NIFTI code for microseconds. */ #define NIFTI_UNITS_USEC 24 /*** These units are for spectral data: ***/ /*! NIFTI code for Hertz. */ #define NIFTI_UNITS_HZ 32 /*! NIFTI code for ppm. */ #define NIFTI_UNITS_PPM 40 /*! NIFTI code for radians per second. */ #define NIFTI_UNITS_RADS 48 /* @} */ #undef XYZT_TO_SPACE #undef XYZT_TO_TIME #define XYZT_TO_SPACE(xyzt) ( (xyzt) & 0x07 ) #define XYZT_TO_TIME(xyzt) ( (xyzt) & 0x38 ) #undef SPACE_TIME_TO_XYZT #define SPACE_TIME_TO_XYZT(ss,tt) ( (((char)(ss)) & 0x07) \ | (((char)(tt)) & 0x38) ) /*---------------------------------------------------------------------------*/ /* MRI-SPECIFIC SPATIAL AND TEMPORAL INFORMATION: --------------------------------------------- A few fields are provided to store some extra information that is sometimes important when storing the image data from an FMRI time series experiment. (After processing such data into statistical images, these fields are not likely to be useful.) { freq_dim } = These fields encode which spatial dimension (1,2, or 3) { phase_dim } = corresponds to which acquisition dimension for MRI data. { slice_dim } = Examples: Rectangular scan multi-slice EPI: freq_dim = 1 phase_dim = 2 slice_dim = 3 (or some permutation) Spiral scan multi-slice EPI: freq_dim = phase_dim = 0 slice_dim = 3 since the concepts of frequency- and phase-encoding directions don't apply to spiral scan slice_duration = If this is positive, AND if slice_dim is nonzero, indicates the amount of time used to acquire 1 slice. slice_duration*dim[slice_dim] can be less than pixdim[4] with a clustered acquisition method, for example. slice_code = If this is nonzero, AND if slice_dim is nonzero, AND if slice_duration is positive, indicates the timing pattern of the slice acquisition. The following codes are defined: NIFTI_SLICE_SEQ_INC == sequential increasing NIFTI_SLICE_SEQ_DEC == sequential decreasing NIFTI_SLICE_ALT_INC == alternating increasing NIFTI_SLICE_ALT_DEC == alternating decreasing NIFTI_SLICE_ALT_INC2 == alternating increasing #2 NIFTI_SLICE_ALT_DEC2 == alternating decreasing #2 { slice_start } = Indicates the start and end of the slice acquisition { slice_end } = pattern, when slice_code is nonzero. These values are present to allow for the possible addition of "padded" slices at either end of the volume, which don't fit into the slice timing pattern. If there are no padding slices, then slice_start=0 and slice_end=dim[slice_dim]-1 are the correct values. For these values to be meaningful, slice_start must be non-negative and slice_end must be greater than slice_start. Otherwise, they should be ignored. The following table indicates the slice timing pattern, relative to time=0 for the first slice acquired, for some sample cases. Here, dim[slice_dim]=7 (there are 7 slices, labeled 0..6), slice_duration=0.1, and slice_start=1, slice_end=5 (1 padded slice on each end). slice index SEQ_INC SEQ_DEC ALT_INC ALT_DEC ALT_INC2 ALT_DEC2 6 : n/a n/a n/a n/a n/a n/a n/a = not applicable 5 : 0.4 0.0 0.2 0.0 0.4 0.2 (slice time offset 4 : 0.3 0.1 0.4 0.3 0.1 0.0 doesn't apply to 3 : 0.2 0.2 0.1 0.1 0.3 0.3 slices outside 2 : 0.1 0.3 0.3 0.4 0.0 0.1 the range 1 : 0.0 0.4 0.0 0.2 0.2 0.4 slice_start .. 0 : n/a n/a n/a n/a n/a n/a slice_end) The SEQ slice_codes are sequential ordering (uncommon but not unknown), either increasing in slice number or decreasing (INC or DEC), as illustrated above. The ALT slice codes are alternating ordering. The 'standard' way for these to operate (without the '2' on the end) is for the slice timing to start at the edge of the slice_start .. slice_end group (at slice_start for INC and at slice_end for DEC). For the 'ALT_*2' slice_codes, the slice timing instead starts at the first slice in from the edge (at slice_start+1 for INC2 and at slice_end-1 for DEC2). This latter acquisition scheme is found on some Siemens scanners. The fields freq_dim, phase_dim, slice_dim are all squished into the single byte field dim_info (2 bits each, since the values for each field are limited to the range 0..3). This unpleasantness is due to lack of space in the 348 byte allowance. The macros DIM_INFO_TO_FREQ_DIM, DIM_INFO_TO_PHASE_DIM, and DIM_INFO_TO_SLICE_DIM can be used to extract these values from the dim_info byte. The macro FPS_INTO_DIM_INFO can be used to put these 3 values into the dim_info byte. -----------------------------------------------------------------------------*/ #undef DIM_INFO_TO_FREQ_DIM #undef DIM_INFO_TO_PHASE_DIM #undef DIM_INFO_TO_SLICE_DIM #define DIM_INFO_TO_FREQ_DIM(di) ( ((di) ) & 0x03 ) #define DIM_INFO_TO_PHASE_DIM(di) ( ((di) >> 2) & 0x03 ) #define DIM_INFO_TO_SLICE_DIM(di) ( ((di) >> 4) & 0x03 ) #undef FPS_INTO_DIM_INFO #define FPS_INTO_DIM_INFO(fd,pd,sd) ( ( ( ((char)(fd)) & 0x03) ) | \ ( ( ((char)(pd)) & 0x03) << 2 ) | \ ( ( ((char)(sd)) & 0x03) << 4 ) ) /*! \defgroup NIFTI1_SLICE_ORDER \brief nifti1 slice order codes, describing the acquisition order of the slices @{ */ #define NIFTI_SLICE_UNKNOWN 0 #define NIFTI_SLICE_SEQ_INC 1 #define NIFTI_SLICE_SEQ_DEC 2 #define NIFTI_SLICE_ALT_INC 3 #define NIFTI_SLICE_ALT_DEC 4 #define NIFTI_SLICE_ALT_INC2 5 /* 05 May 2005: RWCox */ #define NIFTI_SLICE_ALT_DEC2 6 /* 05 May 2005: RWCox */ /* @} */ /*---------------------------------------------------------------------------*/ /* UNUSED FIELDS: ------------- Some of the ANALYZE 7.5 fields marked as ++UNUSED++ may need to be set to particular values for compatibility with other programs. The issue of interoperability of ANALYZE 7.5 files is a murky one -- not all programs require exactly the same set of fields. (Unobscuring this murkiness is a principal motivation behind NIFTI-1.) Some of the fields that may need to be set for other (non-NIFTI aware) software to be happy are: extents dbh.h says this should be 16384 regular dbh.h says this should be the character 'r' glmin, } dbh.h says these values should be the min and max voxel glmax } values for the entire dataset It is best to initialize ALL fields in the NIFTI-1 header to 0 (e.g., with calloc()), then fill in what is needed. -----------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/ /* MISCELLANEOUS C MACROS -----------------------------------------------------------------------------*/ /*.................*/ /*! Given a nifti_1_header struct, check if it has a good magic number. Returns NIFTI version number (1..9) if magic is good, 0 if it is not. */ #define NIFTI_VERSION(h) \ ( ( (h).magic[0]=='n' && (h).magic[3]=='\0' && \ ( (h).magic[1]=='i' || (h).magic[1]=='+' ) && \ ( (h).magic[2]>='1' && (h).magic[2]<='9' ) ) \ ? (h).magic[2]-'0' : 0 ) /*.................*/ /*! Check if a nifti_1_header struct says if the data is stored in the same file or in a separate file. Returns 1 if the data is in the same file as the header, 0 if it is not. */ #define NIFTI_ONEFILE(h) ( (h).magic[1] == '+' ) /*.................*/ /*! Check if a nifti_1_header struct needs to be byte swapped. Returns 1 if it needs to be swapped, 0 if it does not. */ #define NIFTI_NEEDS_SWAP(h) ( (h).dim[0] < 0 || (h).dim[0] > 7 ) /*.................*/ /*! Check if a nifti_1_header struct contains a 5th (vector) dimension. Returns size of 5th dimension if > 1, returns 0 otherwise. */ #define NIFTI_5TH_DIM(h) ( ((h).dim[0]>4 && (h).dim[5]>1) ? (h).dim[5] : 0 ) /*****************************************************************************/ /*=================*/ #ifdef __cplusplus } #endif /*=================*/ #endif /* _NIFTI_HEADER_ */ cmtk-3.3.1/libs/IO/nifti1_io_math.c000066400000000000000000001123741276303427400167720ustar00rootroot00000000000000#include "nifti1_io_math.h" /* typedefs, prototypes, macros, etc. */ /*****===================================================================*****/ /***** Sample functions to deal with NIFTI-1 and ANALYZE files *****/ /*****...................................................................*****/ /***** This code is released to the public domain. *****/ /*****...................................................................*****/ /***** Author: Robert W Cox, SSCC/DIRP/NIMH/NIH/DHHS/USA/EARTH *****/ /***** Date: August 2003 *****/ /*****...................................................................*****/ /***** Neither the National Institutes of Health (NIH), nor any of its *****/ /***** employees imply any warranty of usefulness of this software for *****/ /***** any purpose, and do not assume any liability for damages, *****/ /***** incidental or otherwise, caused by any use of this document. *****/ /*****===================================================================*****/ /** \file nifti1_io.c \brief main collection of nifti1 i/o routines - written by Bob Cox, SSCC NIMH - revised by Mark Jenkinson, FMRIB - revised by Rick Reynolds, SSCC, NIMH - revised by Kate Fissell, University of Pittsburgh The library history can be viewed via "nifti_tool -nifti_hist".
      The library version can be viewed via "nifti_tool -nifti_ver". */ /*! global history and version strings, for printing */ static char const * const gni_history[] = { "----------------------------------------------------------------------\n" "history (of nifti library changes):\n" "\n", "0.0 August, 2003 [rwcox]\n" " (Robert W Cox of the National Institutes of Health, SSCC/DIRP/NIMH)\n" " - initial version\n" "\n", "0.1 July/August, 2004 [Mark Jenkinson]\n" " (FMRIB Centre, University of Oxford, UK)\n" " - Mainly adding low-level IO and changing things to allow gzipped\n" " files to be read and written\n" " - Full backwards compatability should have been maintained\n" "\n", "0.2 16 Nov 2004 [rickr]\n" " (Rick Reynolds of the National Institutes of Health, SSCC/DIRP/NIMH)\n" " - included Mark's changes in the AFNI distribution (including znzlib/)\n" " (HAVE_ZLIB is commented out for the standard distribution)\n" " - modified nifti_validfilename() and nifti_makebasename()\n" " - added nifti_find_file_extension()\n" "\n", "0.3 3 Dec 2004 [rickr]\n" " - note: header extensions are not yet checked for\n" " - added formatted history as global string, for printing\n" " - added nifti_disp_lib_hist(), to display the nifti library history\n" " - added nifti_disp_lib_version(), to display the nifti library history\n", " - re-wrote nifti_findhdrname()\n" " o used nifti_find_file_extension()\n" " o changed order of file tests (default is .nii, depends on input)\n" " o free hdrname on failure\n" " - made similar changes to nifti_findimgname()\n" " - check for NULL return from nifti_findhdrname() calls\n", " - removed most of ERREX() macros\n" " - modified nifti_image_read()\n" " o added debug info and error checking (on gni_debug > 0, only)\n" " o fail if workingname is NULL\n" " o check for failure to open header file\n" " o free workingname on failure\n" " o check for failure of nifti_image_load()\n" " o check for failure of nifti_convert_nhdr2nim()\n", " - changed nifti_image_load() to int, and check nifti_read_buffer return\n" " - changed nifti_read_buffer() to fail on short read, and to count float\n" " fixes (to print on debug)\n" " - changed nifti_image_infodump to print to stderr\n" " - updated function header comments, or moved comments above header\n" " - removed const keyword\n" " - added LNI_FERR() macro for error reporting on input files\n" "\n", "0.4 10 Dec 2004 [rickr] - added header extensions\n" " - in nifti1_io.h:\n" " o added num_ext and ext_list to the definition of nifti_image\n" " o made many functions static (more to follow)\n" " o added LNI_MAX_NIA_EXT_LEN, for max nifti_type 3 extension length\n", " - added __DATE__ to version output in nifti_disp_lib_version()\n" " - added nifti_disp_matrix_orient() to print orientation information\n" " - added '.nia' as a valid file extension in nifti_find_file_extension()\n" " - added much more debug output\n" " - in nifti_image_read(), in the case of an ASCII header, check for\n" " extensions after the end of the header\n", " - added nifti_read_extensions() function\n" " - added nifti_read_next_extension() function\n" " - added nifti_add_exten_to_list() function\n" " - added nifti_check_extension() function\n" " - added nifti_write_extensions() function\n" " - added nifti_extension_size() function\n" " - in nifti_set_iname_offest():\n" " o adjust offset by the extension size and the extender size\n", " o fixed the 'ceiling modulo 16' computation\n" " - in nifti_image_write_hdr_img2(): \n" " o added extension writing\n" " o check for NULL return from nifti_findimgname()\n" " - include number of extensions in nifti_image_to_ascii() output\n" " - in nifti_image_from_ascii():\n" " o return bytes_read as a parameter, computed from the final spos\n" " o extract num_ext from ASCII header\n" "\n", "0.5 14 Dec 2004 [rickr] - added sub-brick reading functions\n" " - added nifti_brick_list type to nifti1_io.h, along with new prototypes\n" " - added main nifti_image_read_bricks() function, with description\n" " - added nifti_image_load_bricks() - library function (requires nim)\n" " - added valid_nifti_brick_list() - library function\n" " - added free_NBL() - library function\n", " - added update_nifti_image_for_brick_list() for dimension update\n" " - added nifti_load_NBL_bricks(), nifti_alloc_NBL_mem(),\n" " nifti_copynsort() and force_positive() (static functions)\n" " - in nifti_image_read(), check for failed load only if read_data is set\n" " - broke most of nifti_image_load() into nifti_image_load_prep()\n" "\n", "0.6 15 Dec 2004 [rickr] - added sub-brick writing functionality\n" " - in nifti1_io.h, removed znzlib directory from include - all nifti\n" " library files are now under the nifti directory\n" " - nifti_read_extensions(): print no offset warning for nifti_type 3\n" " - nifti_write_all_data():\n" " o pass nifti_brick_list * NBL, for optional writing\n" " o if NBL, write each sub-brick, sequentially\n", " - nifti_set_iname_offset(): case 1 must have sizeof() cast to int\n" " - pass NBL to nifti_image_write_hdr_img2(), and allow NBL or data\n" " - added nifti_image_write_bricks() wrapper for ...write_hdr_img2()\n" " - included compression abilities\n" "\n", "0.7 16 Dec 2004 [rickr] - minor changes to extension reading\n" "\n", "0.8 21 Dec 2004 [rickr] - restrict extension reading, and minor changes\n" " - in nifti_image_read(), compute bytes for extensions (see remaining)\n" " - in nifti_read_extensions(), pass 'remain' as space for extensions,\n" " pass it to nifti_read_next_ext(), and update for each one read \n" " - in nifti_check_extension(), require (size <= remain)\n", " - in update_nifti_image_brick_list(), update nvox\n" " - in nifti_image_load_bricks(), make explicit check for nbricks <= 0\n" " - in int_force_positive(), check for (!list)\n" " - in swap_nifti_header(), swap sizeof_hdr, and reorder to struct order\n" " - change get_filesize functions to signed ( < 0 is no file or error )\n", " - in nifti_validfilename(), lose redundant (len < 0) check\n" " - make print_hex_vals() static\n" " - in disp_nifti_1_header, restrict string field widths\n" "\n", "0.9 23 Dec 2004 [rickr] - minor changes\n" " - broke ASCII header reading out of nifti_image_read(), into new\n" " functions has_ascii_header() and read_ascii_image()\n", " - check image_read failure and znzseek failure\n" " - altered some debug output\n" " - nifti_write_all_data() now returns an int\n" "\n", "0.10 29 Dec 2004 [rickr]\n" " - renamed nifti_valid_extension() to nifti_check_extension()\n" " - added functions nifti_makehdrname() and nifti_makeimgname()\n" " - added function valid_nifti_extensions()\n" " - in nifti_write_extensions(), check for validity before writing\n", " - rewrote nifti_image_write_hdr_img2():\n" " o set write_data and leave_open flags from write_opts\n" " o add debug print statements\n" " o use nifti_write_ascii_image() for the ascii case\n" " o rewrote the logic of all cases to be easier to follow\n", " - broke out code as nifti_write_ascii_image() function\n" " - added debug to top-level write functions, and free the znzFile\n" " - removed unused internal function nifti_image_open()\n" "\n", "0.11 30 Dec 2004 [rickr] - small mods\n" " - moved static function prototypes from header to C file\n" " - free extensions in nifti_image_free()\n" "\n", "1.0 07 Jan 2005 [rickr] - INITIAL RELEASE VERSION\n" " - added function nifti_set_filenames()\n" " - added function nifti_read_header()\n" " - added static function nhdr_looks_good()\n" " - added static function need_nhdr_swap()\n" " - exported nifti_add_exten_to_list symbol\n", " - fixed #bytes written in nifti_write_extensions()\n" " - only modify offset if it is too small (nifti_set_iname_offset)\n" " - added nifti_type 3 to nifti_makehdrname and nifti_makeimgname\n" " - added function nifti_set_filenames()\n" "\n", "1.1 07 Jan 2005 [rickr]\n" " - in nifti_read_header(), swap if needed\n" "\n", "1.2 07 Feb 2005 [kate fissell c/o rickr] \n" " - nifti1.h: added doxygen comments for main struct and #define groups\n" " - nifti1_io.h: added doxygen comments for file and nifti_image struct\n" " - nifti1_io.h: added doxygen comments for file and some functions\n" " - nifti1_io.c: changed nifti_copy_nim_info to use memcpy\n" "\n", "1.3 09 Feb 2005 [rickr]\n" " - nifti1.h: added doxygen comments for extension structs\n" " - nifti1_io.h: put most #defines in #ifdef _NIFTI1_IO_C_ block\n" " - added a doxygen-style description to every exported function\n" " - added doxygen-style comments within some functions\n" " - re-exported many znzFile functions that I had made static\n" " - re-added nifti_image_open (sorry, Mark)\n" " - every exported function now has 'nifti' in the name (19 functions)\n", " - made sure every alloc() has a failure test\n" " - added nifti_copy_extensions function, for use in nifti_copy_nim_info\n" " - nifti_is_gzfile: added initial strlen test\n" " - nifti_set_filenames: added set_byte_order parameter option\n" " (it seems appropriate to set the BO when new files are associated)\n" " - disp_nifti_1_header: prints to stdout (a.o.t. stderr), with fflush\n" "\n", "1.4 23 Feb 2005 [rickr] - sourceforge merge\n" " - merged into the nifti_io CVS directory structure at sourceforge.net\n" " - merged in 4 changes by Mark, and re-added his const keywords\n" " - cast some pointers to (void *) for -pedantic compile option\n" " - added nifti_free_extensions()\n" "\n", "1.5 02 Mar 2005 [rickr] - started nifti global options\n" " - gni_debug is now g_opts.debug\n" " - added validity check parameter to nifti_read_header\n" " - need_nhdr_swap no longer does test swaps on the stack\n" "\n", "1.6 05 April 2005 [rickr] - validation and collapsed_image_read\n" " - added nifti_read_collapsed_image(), an interface for reading partial\n" " datasets, specifying a subset of array indices\n" " - for read_collapsed_image, added static functions: rci_read_data(),\n" " rci_alloc_mem(), and make_pivot_list()\n", " - added nifti_nim_is_valid() to check for consistency (more to do)\n" " - added nifti_nim_has_valid_dims() to do many dimensions tests\n" "\n", "1.7 08 April 2005 [rickr]\n" " - added nifti_update_dims_from_array() - to update dimensions\n" " - modified nifti_makehdrname() and nifti_makeimgname():\n" " if prefix has a valid extension, use it (else make one up)\n" " - added nifti_get_intlist - for making an array of ints\n" " - fixed init of NBL->bsize in nifti_alloc_NBL_mem() {thanks, Bob}\n" "\n", "1.8 14 April 2005 [rickr]\n" " - added nifti_set_type_from_names(), for nifti_set_filenames()\n" " (only updates type if number of files does not match it)\n" " - added is_valid_nifti_type(), just to be sure\n" " - updated description of nifti_read_collapsed_image() for *data change\n" " (if *data is already set, assume memory exists for results)\n" " - modified rci_alloc_mem() to allocate only if *data is NULL\n" "\n", "1.9 19 April 2005 [rickr]\n" " - added extension codes NIFTI_ECODE_COMMENT and NIFTI_ECODE_XCEDE\n" " - added nifti_type codes NIFTI_MAX_ECODE and NIFTI_MAX_FTYPE\n" " - added nifti_add_extension() {exported}\n" " - added nifti_fill_extension() as a static function\n" " - added nifti_is_valid_ecode() {exported}\n", " - nifti_type values are now NIFTI_FTYPE_* file codes\n" " - in nifti_read_extensions(), decrement 'remain' by extender size, 4\n" " - in nifti_set_iname_offset(), case 1, update if offset differs\n" " - only output '-d writing nifti file' if debug > 1\n" "\n", "1.10 10 May 2005 [rickr]\n" " - files are read using ZLIB only if they end in '.gz'\n" "\n", "1.11 12 August 2005 [kate fissell]\n" " - Kate's 0.2 release packaging, for sourceforge\n" "\n", "1.12 17 August 2005 [rickr] - comment (doxygen) updates\n" " - updated comments for most functions (2 updates from Cinly Ooi)\n" " - added nifti_type_and_names_match()\n" "\n", "1.12a 24 August 2005 [rickr] - remove all tabs from Clibs/*/*.[ch]\n", "1.12b 25 August 2005 [rickr] - changes by Hans Johnson\n", "1.13 25 August 2005 [rickr]\n", " - finished changes by Hans for Insight\n" " - added const in all appropraite parameter locations (30-40)\n" " (any pointer referencing data that will not change)\n" " - shortened all string constants below 509 character limit\n" "1.14 28 October 2005 [HJohnson]\n", " - use nifti_set_filenames() in nifti_convert_nhdr2nim()\n" "1.15 02 November 2005 [rickr]\n", " - added skip_blank_ext to nifti_global_options\n" " - added nifti_set_skip_blank_ext(), to set option\n" " - if skip_blank_ext and no extensions, do not read/write extender\n" "1.16 18 November 2005 [rickr]\n", " - removed any test or access of dim[i], i>dim[0]\n" " - do not set pixdim for collapsed dims to 1.0, leave them as they are\n" " - added magic and dim[i] tests in nifti_hdr_looks_good()\n" " - added 2 size_t casts\n" "1.17 22 November 2005 [rickr]\n", " - in hdr->nim, for i > dim[0], pass 0 or 1, else set to 1\n" "1.18 02 March 2006 [rickr]\n", " - in nifti_alloc_NBL_mem(), fixed nt=0 case from 1.17 change\n" "1.19 23 May 2006 [HJohnson,rickr]\n", " - nifti_write_ascii_image(): free(hstr)\n" " - nifti_copy_extensions(): clear num_ext and ext_list\n" "1.20 27 Jun 2006 [rickr]\n", " - nifti_findhdrname(): fixed assign of efirst to match stated logic\n" " (problem found by Atle Bjørnerud)\n" "1.21 05 Sep 2006 [rickr] update for nifticlib-0.4 release\n", " - was reminded to actually add nifti_set_skip_blank_ext()\n" " - init g_opts.skip_blank_ext to 0\n" "1.22 01 Jun 2007 nifticlib-0.5 release\n", "1.23 05 Jun 2007 nifti_add_exten_to_list: revert on failure, free old list\n" "1.24 07 Jun 2007 nifti_copy_extensions: use esize-8 for data size\n" "1.25 12 Jun 2007 [rickr] EMPTY_IMAGE creation\n", " - added nifti_make_new_header() - to create from dims/dtype\n" " - added nifti_make_new_nim() - to create from dims/dtype/fill\n" " - added nifti_is_valid_datatype(), and more debug info\n", "1.26 27 Jul 2007 [rickr] handle single volumes > 2^31 bytes (but < 2^32)\n", "1.27 28 Jul 2007 [rickr] nim->nvox, NBL-bsize are now type size_t\n" "1.28 30 Jul 2007 [rickr] size_t updates\n", "1.29 08 Aug 2007 [rickr] for list, valid_nifti_brick_list requires 3 dims\n" "1.30 08 Nov 2007 [Yaroslav/rickr]\n" " - fix ARM struct alignment problem in byte-swapping routines\n", "1.31 29 Nov 2007 [rickr] for nifticlib-1.0.0\n" " - added nifti_datatype_to/from_string routines\n" " - added DT_RGBA32/NIFTI_TYPE_RGBA32 datatype macros (2304)\n" " - added NIFTI_ECODE_FREESURFER (14)\n", "1.32 08 Dec 2007 [rickr]\n" " - nifti_hdr_looks_good() allows ANALYZE headers (req. by V. Luccio)\n" " - added nifti_datatype_is_valid()\n", "1.33 05 Feb 2008 [hansj,rickr] - block nia.gz use\n" "1.34 13 Jun 2008 [rickr] - added nifti_compiled_with_zlib()\n" "1.35 03 Aug 2008 [rickr]\n", " - deal with swapping, so that CPU type does not affect output\n" " (motivated by C Burns)\n" " - added nifti_analyze75 structure and nifti_swap_as_analyze()\n" " - previous swap_nifti_header is saved as old_swap_nifti_header\n" " - also swap UNUSED fields in nifti_1_header struct\n", "1.36 07 Oct 2008 [rickr]\n", " - added nifti_NBL_matches_nim() check for write_bricks()\n" "1.37 10 Mar 2009 [rickr]\n", " - H Johnson cast updates (06 Feb)\n" " - added NIFTI_ECODE_PYPICKLE for PyNIfTI (06 Feb)\n" " - added NIFTI_ECODEs 18-28 for the LONI MiND group\n" "1.38 28 Apr 2009 [rickr]\n", " - uppercase extensions are now valid (requested by M. Coursolle)\n" " - nifti_set_allow_upper_fext controls this option (req by C. Ooi)\n" "1.39 23 Jun 2009 [rickr]: added 4 checks of alloc() returns\n", "1.40 16 Mar 2010 [rickr]: added NIFTI_ECODE_VOXBO for D. Kimberg\n", "1.41 28 Apr 2010 [rickr]: added NIFTI_ECODE_CARET for J. Harwell\n", "1.42 06 Jul 2010 [rickr]: trouble with large (gz) files\n", " - noted/investigated by M Hanke and Y Halchenko\n" " - fixed znzread/write, noting example by M Adler\n" " - changed nifti_swap_* routines/calls to take size_t (6)\n" "1.43 07 Jul 2010 [rickr]: fixed znzR/W to again return nmembers\n", "1.44 19 Jul 2013 [rickr]: ITK compatibility updates from H Johnson\n", "----------------------------------------------------------------------\n" }; static const char gni_version[] = "nifti library version 1.44 (19 July, 2013)"; /*---------------------------------------------------------------------------*/ /*! Given the quaternion parameters (etc.), compute a transformation matrix. See comments in nifti1.h for details. - qb,qc,qd = quaternion parameters - qx,qy,qz = offset parameters - dx,dy,dz = grid stepsizes (non-negative inputs are set to 1.0) - qfac = sign of dz step (< 0 is negative; >= 0 is positive)
         If qx=qy=qz=0, dx=dy=dz=1, then the output is a rotation matrix.
         For qfac >= 0, the rotation is proper.
         For qfac <  0, the rotation is improper.
         
      \see "QUATERNION REPRESENTATION OF ROTATION MATRIX" in nifti1.h \see nifti_mat44_to_quatern, nifti_make_orthog_mat44, nifti_mat44_to_orientation *//*-------------------------------------------------------------------------*/ mat44 nifti_quatern_to_mat44( const float qb, const float qc, const float qd, const float qx, const float qy, const float qz, const float dx, const float dy, const float dz, const float qfac ) { mat44 R ; double a,b=qb,c=qc,d=qd , xd,yd,zd ; /* last row is always [ 0 0 0 1 ] */ R.m[3][0]=R.m[3][1]=R.m[3][2] = 0.0f ; R.m[3][3]= 1.0f ; /* compute a parameter from b,c,d */ a = 1.0l - (b*b + c*c + d*d) ; if( a < 1.e-7l ){ /* special case */ a = 1.0l / sqrt(b*b+c*c+d*d) ; b *= a ; c *= a ; d *= a ; /* normalize (b,c,d) vector */ a = 0.0l ; /* a = 0 ==> 180 degree rotation */ } else{ a = sqrt(a) ; /* angle = 2*arccos(a) */ } /* load rotation matrix, including scaling factors for voxel sizes */ xd = (dx > 0.0) ? dx : 1.0l ; /* make sure are positive */ yd = (dy > 0.0) ? dy : 1.0l ; zd = (dz > 0.0) ? dz : 1.0l ; if( qfac < 0.0 ) zd = -zd ; /* left handedness? */ R.m[0][0] = ( (a*a+b*b-c*c-d*d) * xd) ; R.m[0][1] = 2.0l * (b*c-a*d ) * yd ; R.m[0][2] = 2.0l * (b*d+a*c ) * zd ; R.m[1][0] = 2.0l * (b*c+a*d ) * xd ; R.m[1][1] = ( (a*a+c*c-b*b-d*d) * yd) ; R.m[1][2] = 2.0l * (c*d-a*b ) * zd ; R.m[2][0] = 2.0l * (b*d-a*c ) * xd ; R.m[2][1] = 2.0l * (c*d+a*b ) * yd ; R.m[2][2] = ( (a*a+d*d-c*c-b*b) * zd) ; /* load offsets */ R.m[0][3] = qx ; R.m[1][3] = qy ; R.m[2][3] = qz ; return R ; } /*---------------------------------------------------------------------------*/ /*! Given the 3x4 upper corner of the matrix R, compute the quaternion parameters that fit it. - Any NULL pointer on input won't get assigned (e.g., if you don't want dx,dy,dz, just pass NULL in for those pointers). - If the 3 input matrix columns are NOT orthogonal, they will be orthogonalized prior to calculating the parameters, using the polar decomposition to find the orthogonal matrix closest to the column-normalized input matrix. - However, if the 3 input matrix columns are NOT orthogonal, then the matrix produced by nifti_quatern_to_mat44 WILL have orthogonal columns, so it won't be the same as the matrix input here. This "feature" is because the NIFTI 'qform' transform is deliberately not fully general -- it is intended to model a volume with perpendicular axes. - If the 3 input matrix columns are not even linearly independent, you'll just have to take your luck, won't you? \see "QUATERNION REPRESENTATION OF ROTATION MATRIX" in nifti1.h \see nifti_quatern_to_mat44, nifti_make_orthog_mat44, nifti_mat44_to_orientation *//*-------------------------------------------------------------------------*/ void nifti_mat44_to_quatern( mat44 R , float *qb, float *qc, float *qd, float *qx, float *qy, float *qz, float *dx, float *dy, float *dz, float *qfac ) { double r11,r12,r13 , r21,r22,r23 , r31,r32,r33 ; double xd,yd,zd , a,b,c,d ; mat33 P,Q ; /* offset outputs are read write out of input matrix */ ASSIF(qx,R.m[0][3]) ; ASSIF(qy,R.m[1][3]) ; ASSIF(qz,R.m[2][3]) ; /* load 3x3 matrix into local variables */ r11 = R.m[0][0] ; r12 = R.m[0][1] ; r13 = R.m[0][2] ; r21 = R.m[1][0] ; r22 = R.m[1][1] ; r23 = R.m[1][2] ; r31 = R.m[2][0] ; r32 = R.m[2][1] ; r33 = R.m[2][2] ; /* compute lengths of each column; these determine grid spacings */ xd = sqrt( r11*r11 + r21*r21 + r31*r31 ) ; yd = sqrt( r12*r12 + r22*r22 + r32*r32 ) ; zd = sqrt( r13*r13 + r23*r23 + r33*r33 ) ; /* if a column length is zero, patch the trouble */ if( xd == 0.0l ){ r11 = 1.0l ; r21 = r31 = 0.0l ; xd = 1.0l ; } if( yd == 0.0l ){ r22 = 1.0l ; r12 = r32 = 0.0l ; yd = 1.0l ; } if( zd == 0.0l ){ r33 = 1.0l ; r13 = r23 = 0.0l ; zd = 1.0l ; } /* assign the output lengths */ ASSIF(dx,(float)xd) ; ASSIF(dy,(float)yd) ; ASSIF(dz,(float)zd) ; /* normalize the columns */ r11 /= xd ; r21 /= xd ; r31 /= xd ; r12 /= yd ; r22 /= yd ; r32 /= yd ; r13 /= zd ; r23 /= zd ; r33 /= zd ; /* At this point, the matrix has normal columns, but we have to allow for the fact that the hideous user may not have given us a matrix with orthogonal columns. So, now find the orthogonal matrix closest to the current matrix. One reason for using the polar decomposition to get this orthogonal matrix, rather than just directly orthogonalizing the columns, is so that inputting the inverse matrix to R will result in the inverse orthogonal matrix at this point. If we just orthogonalized the columns, this wouldn't necessarily hold. */ Q.m[0][0] = r11 ; Q.m[0][1] = r12 ; Q.m[0][2] = r13 ; /* load Q */ Q.m[1][0] = r21 ; Q.m[1][1] = r22 ; Q.m[1][2] = r23 ; Q.m[2][0] = r31 ; Q.m[2][1] = r32 ; Q.m[2][2] = r33 ; P = nifti_mat33_polar(Q) ; /* P is orthog matrix closest to Q */ r11 = P.m[0][0] ; r12 = P.m[0][1] ; r13 = P.m[0][2] ; /* unload */ r21 = P.m[1][0] ; r22 = P.m[1][1] ; r23 = P.m[1][2] ; r31 = P.m[2][0] ; r32 = P.m[2][1] ; r33 = P.m[2][2] ; /* [ r11 r12 r13 ] */ /* at this point, the matrix [ r21 r22 r23 ] is orthogonal */ /* [ r31 r32 r33 ] */ /* compute the determinant to determine if it is proper */ zd = r11*r22*r33-r11*r32*r23-r21*r12*r33 +r21*r32*r13+r31*r12*r23-r31*r22*r13 ; /* should be -1 or 1 */ if( zd > 0 ){ /* proper */ ASSIF(qfac,1.0f) ; } else { /* improper ==> flip 3rd column */ ASSIF(qfac,-1.0f) ; r13 = -r13 ; r23 = -r23 ; r33 = -r33 ; } /* now, compute quaternion parameters */ a = r11 + r22 + r33 + 1.0l ; if( a > 0.5l ){ /* simplest case */ a = 0.5l * sqrt(a) ; b = 0.25l * (r32-r23) / a ; c = 0.25l * (r13-r31) / a ; d = 0.25l * (r21-r12) / a ; } else { /* trickier case */ xd = 1.0 + r11 - (r22+r33) ; /* 4*b*b */ yd = 1.0 + r22 - (r11+r33) ; /* 4*c*c */ zd = 1.0 + r33 - (r11+r22) ; /* 4*d*d */ if( xd > 1.0 ){ b = 0.5l * sqrt(xd) ; c = 0.25l* (r12+r21) / b ; d = 0.25l* (r13+r31) / b ; a = 0.25l* (r32-r23) / b ; } else if( yd > 1.0 ){ c = 0.5l * sqrt(yd) ; b = 0.25l* (r12+r21) / c ; d = 0.25l* (r23+r32) / c ; a = 0.25l* (r13-r31) / c ; } else { d = 0.5l * sqrt(zd) ; b = 0.25l* (r13+r31) / d ; c = 0.25l* (r23+r32) / d ; a = 0.25l* (r21-r12) / d ; } if( a < 0.0l ){ b=-b ; c=-c ; d=-d; a=-a; } } ASSIF(qb,(float)b) ; ASSIF(qc,(float)c) ; ASSIF(qd,(float)d) ; return ; } /*---------------------------------------------------------------------------*/ /*! Compute the inverse of a bordered 4x4 matrix.
         - Some numerical code fragments were generated by Maple 8.
         - If a singular matrix is input, the output matrix will be all zero.
         - You can check for this by examining the [3][3] element, which will
           be 1.0 for the normal case and 0.0 for the bad case.
      
           The input matrix should have the form:
              [ r11 r12 r13 v1 ]
              [ r21 r22 r23 v2 ]
              [ r31 r32 r33 v3 ]
              [  0   0   0   1 ]
           
      *//*-------------------------------------------------------------------------*/ mat44 nifti_mat44_inverse( mat44 R ) { double r11,r12,r13,r21,r22,r23,r31,r32,r33,v1,v2,v3 , deti ; mat44 Q ; /* INPUT MATRIX IS: */ r11 = R.m[0][0]; r12 = R.m[0][1]; r13 = R.m[0][2]; /* [ r11 r12 r13 v1 ] */ r21 = R.m[1][0]; r22 = R.m[1][1]; r23 = R.m[1][2]; /* [ r21 r22 r23 v2 ] */ r31 = R.m[2][0]; r32 = R.m[2][1]; r33 = R.m[2][2]; /* [ r31 r32 r33 v3 ] */ v1 = R.m[0][3]; v2 = R.m[1][3]; v3 = R.m[2][3]; /* [ 0 0 0 1 ] */ deti = r11*r22*r33-r11*r32*r23-r21*r12*r33 +r21*r32*r13+r31*r12*r23-r31*r22*r13 ; if( deti != 0.0l ) deti = 1.0l / deti ; Q.m[0][0] = ( deti*( r22*r33-r32*r23) ) ; Q.m[0][1] = ( deti*(-r12*r33+r32*r13) ) ; Q.m[0][2] = ( deti*( r12*r23-r22*r13) ) ; Q.m[0][3] = ( deti*(-r12*r23*v3+r12*v2*r33+r22*r13*v3 -r22*v1*r33-r32*r13*v2+r32*v1*r23) ) ; Q.m[1][0] = ( deti*(-r21*r33+r31*r23) ) ; Q.m[1][1] = ( deti*( r11*r33-r31*r13) ) ; Q.m[1][2] = ( deti*(-r11*r23+r21*r13) ) ; Q.m[1][3] = ( deti*( r11*r23*v3-r11*v2*r33-r21*r13*v3 +r21*v1*r33+r31*r13*v2-r31*v1*r23) ) ; Q.m[2][0] = ( deti*( r21*r32-r31*r22) ) ; Q.m[2][1] = ( deti*(-r11*r32+r31*r12) ) ; Q.m[2][2] = ( deti*( r11*r22-r21*r12) ) ; Q.m[2][3] = ( deti*(-r11*r22*v3+r11*r32*v2+r21*r12*v3 -r21*r32*v1-r31*r12*v2+r31*r22*v1) ) ; Q.m[3][0] = Q.m[3][1] = Q.m[3][2] = 0.0l ; Q.m[3][3] = (deti == 0.0l) ? 0.0l : 1.0l ; /* failure flag if deti == 0 */ return Q ; } /*---------------------------------------------------------------------------*/ /*! Input 9 floats and make an orthgonal mat44 out of them. Each row is normalized, then nifti_mat33_polar() is used to orthogonalize them. If row #3 (r31,r32,r33) is input as zero, then it will be taken to be the cross product of rows #1 and #2. This function can be used to create a rotation matrix for transforming an oblique volume to anatomical coordinates. For this application: - row #1 (r11,r12,r13) is the direction vector along the image i-axis - row #2 (r21,r22,r23) is the direction vector along the image j-axis - row #3 (r31,r32,r33) is the direction vector along the slice direction (if available; otherwise enter it as 0's) The first 2 rows can be taken from the DICOM attribute (0020,0037) "Image Orientation (Patient)". After forming the rotation matrix, the complete affine transformation from (i,j,k) grid indexes to (x,y,z) spatial coordinates can be computed by multiplying each column by the appropriate grid spacing: - column #1 (R.m[0][0],R.m[1][0],R.m[2][0]) by delta-x - column #2 (R.m[0][1],R.m[1][1],R.m[2][1]) by delta-y - column #3 (R.m[0][2],R.m[1][2],R.m[2][2]) by delta-z and by then placing the center (x,y,z) coordinates of voxel (0,0,0) into the column #4 (R.m[0][3],R.m[1][3],R.m[2][3]). \sa nifti_quatern_to_mat44, nifti_mat44_to_quatern, nifti_mat44_to_orientation *//*-------------------------------------------------------------------------*/ mat44 nifti_make_orthog_mat44( const double r11, const double r12, const double r13 , const double r21, const double r22, const double r23 , const double r31, const double r32, const double r33 ) { mat44 R ; mat33 Q , P ; double val ; R.m[3][0] = R.m[3][1] = R.m[3][2] = 0.0l ; R.m[3][3] = 1.0l ; Q.m[0][0] = r11 ; Q.m[0][1] = r12 ; Q.m[0][2] = r13 ; /* load Q */ Q.m[1][0] = r21 ; Q.m[1][1] = r22 ; Q.m[1][2] = r23 ; Q.m[2][0] = r31 ; Q.m[2][1] = r32 ; Q.m[2][2] = r33 ; /* normalize row 1 */ val = Q.m[0][0]*Q.m[0][0] + Q.m[0][1]*Q.m[0][1] + Q.m[0][2]*Q.m[0][2] ; if( val > 0.0l ){ val = 1.0l / sqrt(val) ; Q.m[0][0] *= val ; Q.m[0][1] *= val ; Q.m[0][2] *= val ; } else { Q.m[0][0] = 1.0l ; Q.m[0][1] = 0.0l ; Q.m[0][2] = 0.0l ; } /* normalize row 2 */ val = Q.m[1][0]*Q.m[1][0] + Q.m[1][1]*Q.m[1][1] + Q.m[1][2]*Q.m[1][2] ; if( val > 0.0l ){ val = 1.0l / sqrt(val) ; Q.m[1][0] *= val ; Q.m[1][1] *= val ; Q.m[1][2] *= val ; } else { Q.m[1][0] = 0.0l ; Q.m[1][1] = 1.0l ; Q.m[1][2] = 0.0l ; } /* normalize row 3 */ val = Q.m[2][0]*Q.m[2][0] + Q.m[2][1]*Q.m[2][1] + Q.m[2][2]*Q.m[2][2] ; if( val > 0.0l ){ val = 1.0l / sqrt(val) ; Q.m[2][0] *= val ; Q.m[2][1] *= val ; Q.m[2][2] *= val ; } else { Q.m[2][0] = Q.m[0][1]*Q.m[1][2] - Q.m[0][2]*Q.m[1][1] ; /* cross */ Q.m[2][1] = Q.m[0][2]*Q.m[1][0] - Q.m[0][0]*Q.m[1][2] ; /* product */ Q.m[2][2] = Q.m[0][0]*Q.m[1][1] - Q.m[0][1]*Q.m[1][0] ; } P = nifti_mat33_polar(Q) ; /* P is orthog matrix closest to Q */ R.m[0][0] = P.m[0][0] ; R.m[0][1] = P.m[0][1] ; R.m[0][2] = P.m[0][2] ; R.m[1][0] = P.m[1][0] ; R.m[1][1] = P.m[1][1] ; R.m[1][2] = P.m[1][2] ; R.m[2][0] = P.m[2][0] ; R.m[2][1] = P.m[2][1] ; R.m[2][2] = P.m[2][2] ; R.m[0][3] = R.m[1][3] = R.m[2][3] = 0.0f ; return R ; } /*----------------------------------------------------------------------*/ /*! compute the inverse of a 3x3 matrix *//*--------------------------------------------------------------------*/ mat33 nifti_mat33_inverse( mat33 R ) /* inverse of 3x3 matrix */ { double r11,r12,r13,r21,r22,r23,r31,r32,r33 , deti ; mat33 Q ; /* INPUT MATRIX: */ r11 = R.m[0][0]; r12 = R.m[0][1]; r13 = R.m[0][2]; /* [ r11 r12 r13 ] */ r21 = R.m[1][0]; r22 = R.m[1][1]; r23 = R.m[1][2]; /* [ r21 r22 r23 ] */ r31 = R.m[2][0]; r32 = R.m[2][1]; r33 = R.m[2][2]; /* [ r31 r32 r33 ] */ deti = r11*r22*r33-r11*r32*r23-r21*r12*r33 +r21*r32*r13+r31*r12*r23-r31*r22*r13 ; if( deti != 0.0l ) deti = 1.0l / deti ; Q.m[0][0] = ( deti*( r22*r33-r32*r23) ) ; Q.m[0][1] = ( deti*(-r12*r33+r32*r13) ) ; Q.m[0][2] = ( deti*( r12*r23-r22*r13) ) ; Q.m[1][0] = ( deti*(-r21*r33+r31*r23) ) ; Q.m[1][1] = ( deti*( r11*r33-r31*r13) ) ; Q.m[1][2] = ( deti*(-r11*r23+r21*r13) ) ; Q.m[2][0] = ( deti*( r21*r32-r31*r22) ) ; Q.m[2][1] = ( deti*(-r11*r32+r31*r12) ) ; Q.m[2][2] = ( deti*( r11*r22-r21*r12) ) ; return Q ; } /*----------------------------------------------------------------------*/ /*! compute the determinant of a 3x3 matrix *//*--------------------------------------------------------------------*/ double nifti_mat33_determ( mat33 R ) /* determinant of 3x3 matrix */ { double r11,r12,r13,r21,r22,r23,r31,r32,r33 ; /* INPUT MATRIX: */ r11 = R.m[0][0]; r12 = R.m[0][1]; r13 = R.m[0][2]; /* [ r11 r12 r13 ] */ r21 = R.m[1][0]; r22 = R.m[1][1]; r23 = R.m[1][2]; /* [ r21 r22 r23 ] */ r31 = R.m[2][0]; r32 = R.m[2][1]; r33 = R.m[2][2]; /* [ r31 r32 r33 ] */ return (r11*r22*r33-r11*r32*r23-r21*r12*r33 +r21*r32*r13+r31*r12*r23-r31*r22*r13) ; } /*----------------------------------------------------------------------*/ /*! compute the max row norm of a 3x3 matrix *//*--------------------------------------------------------------------*/ double nifti_mat33_rownorm( mat33 A ) /* max row norm of 3x3 matrix */ { double r1,r2,r3 ; r1 = ( fabs(A.m[0][0])+fabs(A.m[0][1])+fabs(A.m[0][2]) ) ; r2 = ( fabs(A.m[1][0])+fabs(A.m[1][1])+fabs(A.m[1][2]) ) ; r3 = ( fabs(A.m[2][0])+fabs(A.m[2][1])+fabs(A.m[2][2]) ) ; if( r1 < r2 ) r1 = r2 ; if( r1 < r3 ) r1 = r3 ; return r1 ; } /*----------------------------------------------------------------------*/ /*! compute the max column norm of a 3x3 matrix *//*--------------------------------------------------------------------*/ double nifti_mat33_colnorm( mat33 A ) /* max column norm of 3x3 matrix */ { double r1,r2,r3 ; r1 = ( fabs(A.m[0][0])+fabs(A.m[1][0])+fabs(A.m[2][0]) ) ; r2 = ( fabs(A.m[0][1])+fabs(A.m[1][1])+fabs(A.m[2][1]) ) ; r3 = ( fabs(A.m[0][2])+fabs(A.m[1][2])+fabs(A.m[2][2]) ) ; if( r1 < r2 ) r1 = r2 ; if( r1 < r3 ) r1 = r3 ; return r1 ; } /*----------------------------------------------------------------------*/ /*! multiply 2 3x3 matrices *//*--------------------------------------------------------------------*/ mat33 nifti_mat33_mul( mat33 A , mat33 B ) /* multiply 2 3x3 matrices */ { mat33 C ; int i,j ; for( i=0 ; i < 3 ; i++ ) for( j=0 ; j < 3 ; j++ ) C.m[i][j] = A.m[i][0] * B.m[0][j] + A.m[i][1] * B.m[1][j] + A.m[i][2] * B.m[2][j] ; return C ; } /*---------------------------------------------------------------------------*/ /*! polar decomposition of a 3x3 matrix This finds the closest orthogonal matrix to input A (in both the Frobenius and L2 norms). Algorithm is that from NJ Higham, SIAM J Sci Stat Comput, 7:1160-1174. *//*-------------------------------------------------------------------------*/ mat33 nifti_mat33_polar( mat33 A ) { mat33 X , Y , Z ; double alp,bet,gam,gmi , dif=1.0f ; int k=0 ; X = A ; /* force matrix to be nonsingular */ gam = nifti_mat33_determ(X) ; while( gam == 0.0 ){ /* perturb matrix */ gam = ( 0.00001 * ( 0.001 + nifti_mat33_rownorm(X) ) ) ; X.m[0][0] += gam ; X.m[1][1] += gam ; X.m[2][2] += gam ; gam = nifti_mat33_determ(X) ; } while(1){ Y = nifti_mat33_inverse(X) ; if( dif > 0.3 ){ /* far from convergence */ alp = ( sqrt( nifti_mat33_rownorm(X) * nifti_mat33_colnorm(X) ) ) ; bet = ( sqrt( nifti_mat33_rownorm(Y) * nifti_mat33_colnorm(Y) ) ) ; gam = ( sqrt( bet / alp ) ) ; gmi = ( 1.0 / gam ) ; } else { gam = gmi = 1.0f ; /* close to convergence */ } Z.m[0][0] = ( 0.5 * ( gam*X.m[0][0] + gmi*Y.m[0][0] ) ) ; Z.m[0][1] = ( 0.5 * ( gam*X.m[0][1] + gmi*Y.m[1][0] ) ) ; Z.m[0][2] = ( 0.5 * ( gam*X.m[0][2] + gmi*Y.m[2][0] ) ) ; Z.m[1][0] = ( 0.5 * ( gam*X.m[1][0] + gmi*Y.m[0][1] ) ) ; Z.m[1][1] = ( 0.5 * ( gam*X.m[1][1] + gmi*Y.m[1][1] ) ) ; Z.m[1][2] = ( 0.5 * ( gam*X.m[1][2] + gmi*Y.m[2][1] ) ) ; Z.m[2][0] = ( 0.5 * ( gam*X.m[2][0] + gmi*Y.m[0][2] ) ) ; Z.m[2][1] = ( 0.5 * ( gam*X.m[2][1] + gmi*Y.m[1][2] ) ) ; Z.m[2][2] = ( 0.5 * ( gam*X.m[2][2] + gmi*Y.m[2][2] ) ) ; dif = ( fabs(Z.m[0][0]-X.m[0][0])+fabs(Z.m[0][1]-X.m[0][1]) +fabs(Z.m[0][2]-X.m[0][2])+fabs(Z.m[1][0]-X.m[1][0]) +fabs(Z.m[1][1]-X.m[1][1])+fabs(Z.m[1][2]-X.m[1][2]) +fabs(Z.m[2][0]-X.m[2][0])+fabs(Z.m[2][1]-X.m[2][1]) +fabs(Z.m[2][2]-X.m[2][2]) ); k = k+1 ; if( k > 100 || dif < 3.e-6 ) break ; /* convergence or exhaustion */ X = Z ; } return Z ; } cmtk-3.3.1/libs/IO/nifti1_io_math.h000066400000000000000000000067721276303427400170030ustar00rootroot00000000000000/** \file nifti1_io.h \brief Data structures for using nifti1_io API. - Written by Bob Cox, SSCC NIMH - Revisions by Rick Reynolds, SSCC NIMH */ #ifndef _NIFTI_IO_HEADER_ #define _NIFTI_IO_HEADER_ #include #include #include #include #include /*=================*/ #ifdef __cplusplus extern "C" { #endif /*=================*/ /*****===================================================================*****/ /***** File nifti1_io.h == Declarations for nifti1_io.c *****/ /*****...................................................................*****/ /***** This code is released to the public domain. *****/ /*****...................................................................*****/ /***** Author: Robert W Cox, SSCC/DIRP/NIMH/NIH/DHHS/USA/EARTH *****/ /***** Date: August 2003 *****/ /*****...................................................................*****/ /***** Neither the National Institutes of Health (NIH), nor any of its *****/ /***** employees imply any warranty of usefulness of this software for *****/ /***** any purpose, and do not assume any liability for damages, *****/ /***** incidental or otherwise, caused by any use of this document. *****/ /*****===================================================================*****/ /* Modified by: Mark Jenkinson (FMRIB Centre, University of Oxford, UK) Date: July/August 2004 Mainly adding low-level IO and changing things to allow gzipped files to be read and written Full backwards compatability should have been maintained Modified by: Rick Reynolds (SSCC/DIRP/NIMH, National Institutes of Health) Date: December 2004 Modified and added many routines for I/O. */ #undef ASSIF /* assign v to *p, if possible */ #define ASSIF(p,v) if( (p)!=NULL ) *(p) = (v) /********************** Some sample data structures **************************/ typedef struct { /** 4x4 matrix struct **/ double m[4][4] ; } mat44 ; typedef struct { /** 3x3 matrix struct **/ double m[3][3] ; } mat33 ; /*****************************************************************************/ /*--------------- Prototypes of functions defined in this file --------------*/ mat44 nifti_mat44_inverse( mat44 R ) ; mat33 nifti_mat33_inverse( mat33 R ) ; mat33 nifti_mat33_polar ( mat33 A ) ; double nifti_mat33_rownorm( mat33 A ) ; double nifti_mat33_colnorm( mat33 A ) ; double nifti_mat33_determ ( mat33 R ) ; mat33 nifti_mat33_mul ( mat33 A , mat33 B ) ; void nifti_mat44_to_quatern( mat44 R , float *qb, float *qc, float *qd, float *qx, float *qy, float *qz, float *dx, float *dy, float *dz, float *qfac ) ; mat44 nifti_quatern_to_mat44( const float qb, const float qc, const float qd, const float qx, const float qy, const float qz, const float dx, const float dy, const float dz, const float qfac ); mat44 nifti_make_orthog_mat44( const double r11, const double r12, const double r13 , const double r21, const double r22, const double r23 , const double r31, const double r32, const double r33 ) ; /*=================*/ #ifdef __cplusplus } #endif /*=================*/ #endif /* _NIFTI_IO_HEADER_ */ cmtk-3.3.1/libs/Numerics/000077500000000000000000000000001276303427400152025ustar00rootroot00000000000000cmtk-3.3.1/libs/Numerics/CMakeLists.txt000066400000000000000000000041241276303427400177430ustar00rootroot00000000000000## ## Copyright 1997-2009 Torsten Rohlfing ## ## Copyright 2004-2011 SRI International ## ## This file is part of the Computational Morphometry Toolkit. ## ## http://www.nitrc.org/projects/cmtk/ ## ## The Computational Morphometry Toolkit is free software: you can ## redistribute it and/or modify it under the terms of the GNU General Public ## License as published by the Free Software Foundation, either version 3 of ## the License, or (at your option) any later version. ## ## The Computational Morphometry Toolkit is distributed in the hope that it ## will be useful, but WITHOUT ANY WARRANTY; without even the implied ## warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License along ## with the Computational Morphometry Toolkit. If not, see ## . ## ## $Revision: 3161 $ ## ## $LastChangedDate: 2011-04-18 14:37:45 -0700 (Mon, 18 Apr 2011) $ ## ## $LastChangedBy: torstenrohlfing $ ## # Sources of non-templated classes. SET(cmtkNumerics_SRCS ap.cxx bdsvd.cxx bidiagonal.cxx blas.cxx cholesky.cxx det.cxx gammaf.cxx hessenberg.cxx hsschur.cxx ibetaf.cxx lbfgsb.cxx lq.cxx lu.cxx normaldistr.cxx nsevd.cxx qr.cxx reflections.cxx rotations.cxx sblas.cxx sevd.cxx spddet.cxx studenttdistr.cxx studentttests.cxx svd.cxx tdevd.cxx tridiagonal.cxx mersenne.cxx ) ADD_LIBRARY(cmtkNumerics ${cmtkNumerics_SRCS}) IF(CMTK_LIBRARY_PROPERTIES) SET_TARGET_PROPERTIES(cmtkNumerics PROPERTIES ${CMTK_LIBRARY_PROPERTIES}) ENDIF(CMTK_LIBRARY_PROPERTIES) INSTALL(TARGETS cmtkNumerics RUNTIME DESTINATION ${CMTK_INSTALL_BIN_DIR} COMPONENT runtime LIBRARY DESTINATION ${CMTK_INSTALL_LIB_DIR} COMPONENT runtime ARCHIVE DESTINATION ${CMTK_INSTALL_LIB_DIR} COMPONENT libraries) FILE(GLOB files_h "${CMAKE_CURRENT_SOURCE_DIR}/*.h") FILE(GLOB files_txx "${CMAKE_CURRENT_SOURCE_DIR}/*.txx") INSTALL(FILES ${files_h} ${files_txx} DESTINATION ${CMTK_INSTALL_INCLUDE_DIR}/Numerics COMPONENT headers) cmtk-3.3.1/libs/Numerics/ap.cxx000066400000000000000000000340211276303427400163260ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2009, 2013 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4861 $ // // $LastChangedDate: 2013-09-23 16:41:17 -0700 (Mon, 23 Sep 2013) $ // // $LastChangedBy: torstenrohlfing $ // */ /******************************************************************** AP Library version 1.2 Copyright (c) 2003-2007, Sergey Bochkanov (ALGLIB project). See www.alglib.net or alglib.sources.ru for details. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer listed in this license in the documentation and/or other materials provided with the distribution. - Neither the name of the copyright holders nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ********************************************************************/ #include "ap.h" /******************************************************************** Optimized ABLAS interface ********************************************************************/ #ifdef AP_WIN32 #include extern "C" { typedef ap::real_value_type (*_ddot1)(const ap::real_value_type*, const ap::real_value_type*, long); typedef void (*_dmove1)(const ap::real_value_type*, const ap::real_value_type*, long); typedef void (*_dmoves1)(const ap::real_value_type*, const ap::real_value_type*, long, ap::real_value_type); typedef void (*_dmoveneg1)(const ap::real_value_type*, const ap::real_value_type*, long); typedef void (*_dadd1)(const ap::real_value_type*, const ap::real_value_type*, long); typedef void (*_dadds1)(const ap::real_value_type*, const ap::real_value_type*, long, ap::real_value_type); typedef void (*_dsub1)(const ap::real_value_type*, const ap::real_value_type*, long); typedef void (*_dmuls1)(const ap::real_value_type*, long, ap::real_value_type); } HINSTANCE ABLAS = LoadLibrary("ablas.dll"); static _ddot1 ddot1 = ABLAS==NULL ? NULL : (_ddot1) GetProcAddress(ABLAS, "ASMDotProduct1"); static _dmove1 dmove1 = ABLAS==NULL ? NULL : (_dmove1) GetProcAddress(ABLAS, "ASMMove1"); static _dmoves1 dmoves1 = ABLAS==NULL ? NULL : (_dmoves1) GetProcAddress(ABLAS, "ASMMoveS1"); static _dmoveneg1 dmoveneg1 = ABLAS==NULL ? NULL : (_dmoveneg1) GetProcAddress(ABLAS, "ASMMoveNeg1"); static _dadd1 dadd1 = ABLAS==NULL ? NULL : (_dadd1) GetProcAddress(ABLAS, "ASMAdd1"); static _dadds1 dadds1 = ABLAS==NULL ? NULL : (_dadds1) GetProcAddress(ABLAS, "ASMAddS1"); static _dsub1 dsub1 = ABLAS==NULL ? NULL : (_dsub1) GetProcAddress(ABLAS, "ASMSub1"); static _dmuls1 dmuls1 = ABLAS==NULL ? NULL : (_dmuls1) GetProcAddress(ABLAS, "ASMMulS1"); #endif const ap::real_value_type ap::machineepsilon = 5E-16; const ap::real_value_type ap::maxrealnumber = 1E300; const ap::real_value_type ap::minrealnumber = 1E-300; /******************************************************************** ap::complex operations ********************************************************************/ bool ap::operator==(const ap::complex& lhs, const ap::complex& rhs) { return lhs.x==rhs.x && lhs.y==rhs.y; } bool ap::operator!=(const ap::complex& lhs, const ap::complex& rhs) { return lhs.x!=rhs.x || lhs.y!=rhs.y; } const ap::complex ap::operator+(const ap::complex& lhs) { return lhs; } const ap::complex ap::operator-(const ap::complex& lhs) { return ap::complex(-lhs.x, -lhs.y); } const ap::complex ap::operator+(const ap::complex& lhs, const ap::complex& rhs) { ap::complex r = lhs; r += rhs; return r; } const ap::complex ap::operator+(const ap::complex& lhs, const ap::real_value_type& rhs) { ap::complex r = lhs; r += rhs; return r; } const ap::complex ap::operator+(const ap::real_value_type& lhs, const ap::complex& rhs) { ap::complex r = rhs; r += lhs; return r; } const ap::complex ap::operator-(const ap::complex& lhs, const ap::complex& rhs) { ap::complex r = lhs; r -= rhs; return r; } const ap::complex ap::operator-(const ap::complex& lhs, const ap::real_value_type& rhs) { ap::complex r = lhs; r -= rhs; return r; } const ap::complex ap::operator-(const ap::real_value_type& lhs, const ap::complex& rhs) { ap::complex r = lhs; r -= rhs; return r; } const ap::complex ap::operator*(const ap::complex& lhs, const ap::complex& rhs) { return ap::complex(lhs.x*rhs.x - lhs.y*rhs.y, lhs.x*rhs.y + lhs.y*rhs.x); } const ap::complex ap::operator*(const ap::complex& lhs, const ap::real_value_type& rhs) { return ap::complex(lhs.x*rhs, lhs.y*rhs); } const ap::complex ap::operator*(const ap::real_value_type& lhs, const ap::complex& rhs) { return ap::complex(lhs*rhs.x, lhs*rhs.y); } const ap::complex ap::operator/(const ap::complex& lhs, const ap::complex& rhs) { ap::complex result; ap::real_value_type e; ap::real_value_type f; if( fabs(rhs.y)(v1, v2, N); } ap::complex ap::vdotproduct(const ap::complex *v1, const ap::complex *v2, int N) { return ap::_vdotproduct(v1, v2, N); } void ap::vmove(double *vdst, const double* vsrc, int N) { #ifdef AP_WIN32 if( dmove1!=NULL ) { dmove1(vdst, vsrc, N); return; } #endif ap::_vmove(vdst, vsrc, N); } void ap::vmove(float *vdst, const float* vsrc, int N) { //#ifdef AP_WIN32 // if( dmove1!=NULL ) // { // dmove1(vdst, vsrc, N); // return; // } //#endif ap::_vmove(vdst, vsrc, N); } void ap::vmove(ap::complex *vdst, const ap::complex* vsrc, int N) { ap::_vmove(vdst, vsrc, N); } void ap::vmove(double *vdst, const double *vsrc, int N, double alpha) { #ifdef AP_WIN32 if( dmoves1!=NULL ) { dmoves1(vdst, vsrc, N, alpha); return; } #endif ap::_vmove(vdst, vsrc, N, alpha); } void ap::vmove(float *vdst, const float *vsrc, int N, float alpha) { //#ifdef AP_WIN32 // if( dmoves1!=NULL ) // { // dmoves1(vdst, vsrc, N, alpha); // return; // } //#endif ap::_vmove(vdst, vsrc, N, alpha); } void ap::vmove(ap::complex *vdst, const ap::complex *vsrc, int N, ap::real_value_type alpha) { ap::_vmove(vdst, vsrc, N, alpha); } void ap::vmove(ap::complex *vdst, const ap::complex *vsrc, int N, ap::complex alpha) { ap::_vmove(vdst, vsrc, N, alpha); } void ap::vadd(ap::real_value_type *vdst, const ap::real_value_type *vsrc, int N) { #ifdef AP_WIN32 if( dadd1!=NULL ) { dadd1(vdst, vsrc, N); return; } #endif ap::_vadd(vdst, vsrc, N); } void ap::vadd(ap::complex *vdst, const ap::complex *vsrc, int N) { ap::_vadd(vdst, vsrc, N); } void ap::vadd(ap::real_value_type *vdst, const ap::real_value_type *vsrc, int N, ap::real_value_type alpha) { #ifdef AP_WIN32 if( dadds1!=NULL ) { dadds1(vdst, vsrc, N, alpha); return; } #endif ap::_vadd(vdst, vsrc, N, alpha); } void ap::vadd(ap::complex *vdst, const ap::complex *vsrc, int N, ap::real_value_type alpha) { ap::_vadd(vdst, vsrc, N, alpha); } void ap::vadd(ap::complex *vdst, const ap::complex *vsrc, int N, ap::complex alpha) { ap::_vadd(vdst, vsrc, N, alpha); } void ap::vsub(ap::real_value_type *vdst, const ap::real_value_type *vsrc, int N) { #ifdef AP_WIN32 if( dsub1!=NULL ) { dsub1(vdst, vsrc, N); return; } #endif ap::_vsub(vdst, vsrc, N); } void ap::vsub(ap::complex *vdst, const ap::complex *vsrc, int N) { ap::_vsub(vdst, vsrc, N); } void ap::vsub(ap::real_value_type *vdst, const ap::real_value_type *vsrc, int N, ap::real_value_type alpha) { #ifdef AP_WIN32 if( dadds1!=NULL ) { dadds1(vdst, vsrc, N, -alpha); return; } #endif ap::_vsub(vdst, vsrc, N, alpha); } void ap::vsub(ap::complex *vdst, const ap::complex *vsrc, int N, ap::real_value_type alpha) { ap::_vsub(vdst, vsrc, N, alpha); } void ap::vsub(ap::complex *vdst, const ap::complex *vsrc, int N, ap::complex alpha) { ap::_vsub(vdst, vsrc, N, alpha); } void ap::vmul(ap::real_value_type *vdst, int N, ap::real_value_type alpha) { #ifdef AP_WIN32 if( dmuls1!=NULL ) { dmuls1(vdst, N, alpha); return; } #endif ap::_vmul(vdst, N, alpha); } void ap::vmul(ap::complex *vdst, int N, ap::real_value_type alpha) { ap::_vmul(vdst, N, alpha); } void ap::vmul(ap::complex *vdst, int N, ap::complex alpha) { ap::_vmul(vdst, N, alpha); } /******************************************************************** standard functions ********************************************************************/ int ap::sign(ap::real_value_type x) { if( x>0 ) return 1; if( x<0 ) return -1; return 0; } int ap::round(ap::real_value_type x) { return int(floor(x+0.5)); } int ap::trunc(ap::real_value_type x) { return int(x>0 ? floor(x) : ceil(x)); } int ap::ifloor(ap::real_value_type x) { return int(floor(x)); } ap::real_value_type ap::pi() { return 3.14159265358979323846; } ap::real_value_type ap::sqr(ap::real_value_type x) { return x*x; } int ap::maxint(int m1, int m2) { return m1>m2 ? m1 : m2; } int ap::minint(int m1, int m2) { return m1>m2 ? m2 : m1; } ap::real_value_type ap::maxreal(ap::real_value_type m1, ap::real_value_type m2) { return m1>m2 ? m1 : m2; } ap::real_value_type ap::minreal(ap::real_value_type m1, ap::real_value_type m2) { return m1>m2 ? m2 : m1; } /******************************************************************** Service routines: ********************************************************************/ void* ap::amalloc(size_t size, size_t alignment) { if( alignment<=1 ) { // // no alignment, just call malloc // void *block = malloc(sizeof(void*)+size); void **p = (void**)block; *p = block; return (void*)((char*)block+sizeof(void*)); } else { // // align. // void *block = malloc(alignment-1+sizeof(void*)+size); char *result = (char*)block+sizeof(void*); //if( ((unsigned int)(result))%alignment!=0 ) // result += alignment - ((unsigned int)(result))%alignment; if( (result-(char*)0)%alignment!=0 ) result += alignment - (result-(char*)0)%alignment; *((void**)(result-sizeof(void*))) = block; return result; } } void ap::afree(void *block) { void *p = *((void**)((char*)block-sizeof(void*))); free(p); } int ap::vlen(int n1, int n2) { return n2-n1+1; } cmtk-3.3.1/libs/Numerics/ap.h000066400000000000000000000474771276303427400157760ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2011, 2013 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4941 $ // // $LastChangedDate: 2013-10-04 12:49:06 -0700 (Fri, 04 Oct 2013) $ // // $LastChangedBy: torstenrohlfing $ // */ /******************************************************************** AP Library version 1.2.1 Copyright (c) 2003-2007, Sergey Bochkanov (ALGLIB project). See www.alglib.net or alglib.sources.ru for details. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer listed in this license in the documentation and/or other materials provided with the distribution. - Neither the name of the copyright holders nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ********************************************************************/ #ifndef AP_H #define AP_H #include #include #include #include /******************************************************************** Array bounds check ********************************************************************/ #ifndef NO_AP_ASSERT // #define AP_ASSERT // This code avoids definition of the #endif // both AP_ASSERT and NO_AP_ASSERT symbols #ifdef AP_ASSERT // #ifdef NO_AP_ASSERT // #undef AP_ASSERT // #endif // #endif // /******************************************************************** Current environment. ********************************************************************/ #ifndef AP_WIN32 #ifndef AP_UNKNOWN #define AP_UNKNOWN #endif #endif #ifdef AP_WIN32 #ifdef AP_UNKNOWN #error Multiple environments are declared! #endif #endif /******************************************************************** This symbol is used for debugging. Do not define it and do not remove comments. ********************************************************************/ //#define UNSAFE_MEM_COPY /******************************************************************** Namespace of a standard library AlgoPascal. ********************************************************************/ namespace ap { #ifdef CMTK_NUMERICS_DOUBLE typedef double real_value_type; #else typedef float real_value_type; #endif /******************************************************************** Service routines: amalloc - allocates an aligned block of size bytes afree - frees block allocated by amalloc vlen - just alias for n2-n1+1 ********************************************************************/ void* amalloc(size_t size, size_t alignment); void afree(void *block); int vlen(int n1, int n2); /******************************************************************** Exception class. ********************************************************************/ class ap_error { public: ap_error(){}; ap_error(const char *s){ msg = s; }; std::string msg; static void make_assertion(bool bClause) { if(!bClause) throw ap_error(); }; static void make_assertion(bool bClause, const char *msg) { if(!bClause) throw ap_error(msg); }; private: }; /******************************************************************** Class defining a complex number with ap::real_value_type precision. ********************************************************************/ class complex; class complex { public: complex():x(0.0),y(0.0){}; complex(const real_value_type &_x):x(_x),y(0.0){}; complex(const real_value_type &_x, const real_value_type &_y):x(_x),y(_y){}; complex(const complex &z):x(z.x),y(z.y){}; complex& operator= (const real_value_type& v){ x = v; y = 0.0; return *this; }; complex& operator+=(const real_value_type& v){ x += v; return *this; }; complex& operator-=(const real_value_type& v){ x -= v; return *this; }; complex& operator*=(const real_value_type& v){ x *= v; y *= v; return *this; }; complex& operator/=(const real_value_type& v){ x /= v; y /= v; return *this; }; complex& operator= (const complex& z){ x = z.x; y = z.y; return *this; }; complex& operator+=(const complex& z){ x += z.x; y += z.y; return *this; }; complex& operator-=(const complex& z){ x -= z.x; y -= z.y; return *this; }; complex& operator*=(const complex& z){ real_value_type t = x*z.x-y*z.y; y = x*z.y+y*z.x; x = t; return *this; }; complex& operator/=(const complex& z) { ap::complex result; real_value_type e; real_value_type f; if( fabs(z.y) class template_1d_array { public: template_1d_array() { m_Vec=0; m_iVecSize = 0; m_iLow = 0; m_iHigh = -1; }; ~template_1d_array() { if(m_Vec) { if( Aligned ) ap::afree(m_Vec); else delete[] m_Vec; } }; template_1d_array(const template_1d_array &rhs) { m_Vec=0; m_iVecSize = 0; m_iLow = 0; m_iHigh = -1; if( rhs.m_iVecSize!=0 ) setcontent(rhs.m_iLow, rhs.m_iHigh, rhs.getcontent()); }; const template_1d_array& operator=(const template_1d_array &rhs) { if( this==&rhs ) return *this; if( rhs.m_iVecSize!=0 ) setcontent(rhs.m_iLow, rhs.m_iHigh, rhs.getcontent()); else { m_Vec=0; m_iVecSize = 0; m_iLow = 0; m_iHigh = -1; } return *this; }; const T& operator()(int i) const { #ifndef NO_AP_ASSERT ap_error::make_assertion(i>=m_iLow && i<=m_iHigh); #endif return m_Vec[ i-m_iLow ]; }; T& operator()(int i) { #ifndef NO_AP_ASSERT ap_error::make_assertion(i>=m_iLow && i<=m_iHigh); #endif return m_Vec[ i-m_iLow ]; }; void setbounds( int iLow, int iHigh ) { if(m_Vec) { if( Aligned ) ap::afree(m_Vec); else delete[] m_Vec; } m_iLow = iLow; m_iHigh = iHigh; m_iVecSize = iHigh-iLow+1; if( Aligned ) m_Vec = (T*)ap::amalloc(m_iVecSize*sizeof(T), 16); else m_Vec = new T[m_iVecSize]; }; void setcontent( int iLow, int iHigh, const T *pContent ) { setbounds(iLow, iHigh); for(int i=0; i getvector(int iStart, int iEnd) { if( iStart>iEnd || wrongIdx(iStart) || wrongIdx(iEnd) ) return raw_vector(0, 0, 1); else return raw_vector(m_Vec+iStart-m_iLow, iEnd-iStart+1, 1); }; const_raw_vector getvector(int iStart, int iEnd) const { if( iStart>iEnd || wrongIdx(iStart) || wrongIdx(iEnd) ) return const_raw_vector(0, 0, 1); else return const_raw_vector(m_Vec+iStart-m_iLow, iEnd-iStart+1, 1); }; private: bool wrongIdx(int i) const { return im_iHigh; }; T *m_Vec; long m_iVecSize; long m_iLow, m_iHigh; }; /******************************************************************** Template of a dynamical two-dimensional array ********************************************************************/ template class template_2d_array { public: template_2d_array() { m_Vec=0; m_iVecSize=0; m_iLow1 = 0; m_iHigh1 = -1; m_iLow2 = 0; m_iHigh2 = -1; m_iConstOffset = 0; m_iLinearMember = 0; }; ~template_2d_array() { if(m_Vec) { if( Aligned ) ap::afree(m_Vec); else delete[] m_Vec; } }; template_2d_array(const template_2d_array &rhs) { m_Vec=0; m_iVecSize=0; m_iLow1 = 0; m_iHigh1 = -1; m_iLow2 = 0; m_iHigh2 = -1; m_iConstOffset = 0; m_iLinearMember = 0; if( rhs.m_iVecSize!=0 ) { setbounds(rhs.m_iLow1, rhs.m_iHigh1, rhs.m_iLow2, rhs.m_iHigh2); for(int i=m_iLow1; i<=m_iHigh1; i++) vmove(&(operator()(i,m_iLow2)), &(rhs(i,m_iLow2)), m_iHigh2-m_iLow2+1); } }; const template_2d_array& operator=(const template_2d_array &rhs) { if( this==&rhs ) return *this; if( rhs.m_iVecSize!=0 ) { setbounds(rhs.m_iLow1, rhs.m_iHigh1, rhs.m_iLow2, rhs.m_iHigh2); for(int i=m_iLow1; i<=m_iHigh1; i++) vmove(&(operator()(i,m_iLow2)), &(rhs(i,m_iLow2)), m_iHigh2-m_iLow2+1); } else { m_Vec=0; m_iVecSize=0; m_iLow1 = 0; m_iHigh1 = -1; m_iLow2 = 0; m_iHigh2 = -1; } return *this; }; const T& operator()(int i1, int i2) const { #ifndef NO_AP_ASSERT ap_error::make_assertion(i1>=m_iLow1 && i1<=m_iHigh1); ap_error::make_assertion(i2>=m_iLow2 && i2<=m_iHigh2); #endif return m_Vec[ m_iConstOffset + i2 +i1*m_iLinearMember]; }; T& operator()(int i1, int i2) { #ifndef NO_AP_ASSERT ap_error::make_assertion(i1>=m_iLow1 && i1<=m_iHigh1); ap_error::make_assertion(i2>=m_iLow2 && i2<=m_iHigh2); #endif return m_Vec[ m_iConstOffset + i2 +i1*m_iLinearMember]; }; void setbounds( int iLow1, int iHigh1, int iLow2, int iHigh2 ) { if(m_Vec) { if( Aligned ) ap::afree(m_Vec); else delete[] m_Vec; } int n1 = iHigh1-iLow1+1; int n2 = iHigh2-iLow2+1; m_iVecSize = n1*n2; if( Aligned ) { //if( n2%2!=0 ) while( (n2*sizeof(T))%16!=0 ) { n2++; m_iVecSize += n1; } m_Vec = (T*)ap::amalloc(m_iVecSize*sizeof(T), 16); } else m_Vec = new T[m_iVecSize]; m_iLow1 = iLow1; m_iHigh1 = iHigh1; m_iLow2 = iLow2; m_iHigh2 = iHigh2; m_iConstOffset = -m_iLow2-m_iLow1*n2; m_iLinearMember = n2; }; void setcontent( int iLow1, int iHigh1, int iLow2, int iHigh2, const T *pContent ) { setbounds(iLow1, iHigh1, iLow2, iHigh2); for(int i=m_iLow1; i<=m_iHigh1; i++, pContent += m_iHigh2-m_iLow2+1) vmove(&(operator()(i,m_iLow2)), pContent, m_iHigh2-m_iLow2+1); }; int getlowbound(int iBoundNum) const { return iBoundNum==1 ? m_iLow1 : m_iLow2; }; int gethighbound(int iBoundNum) const { return iBoundNum==1 ? m_iHigh1 : m_iHigh2; }; raw_vector getcolumn(int iColumn, int iRowStart, int iRowEnd) { if( (iRowStart>iRowEnd) || wrongColumn(iColumn) || wrongRow(iRowStart) ||wrongRow(iRowEnd) ) return raw_vector(0, 0, 1); else return raw_vector(&((*this)(iRowStart, iColumn)), iRowEnd-iRowStart+1, m_iLinearMember); }; raw_vector getrow(int iRow, int iColumnStart, int iColumnEnd) { if( (iColumnStart>iColumnEnd) || wrongRow(iRow) || wrongColumn(iColumnStart) || wrongColumn(iColumnEnd)) return raw_vector(0, 0, 1); else return raw_vector(&((*this)(iRow, iColumnStart)), iColumnEnd-iColumnStart+1, 1); }; const_raw_vector getcolumn(int iColumn, int iRowStart, int iRowEnd) const { if( (iRowStart>iRowEnd) || wrongColumn(iColumn) || wrongRow(iRowStart) ||wrongRow(iRowEnd) ) return const_raw_vector(0, 0, 1); else return const_raw_vector(&((*this)(iRowStart, iColumn)), iRowEnd-iRowStart+1, m_iLinearMember); }; const_raw_vector getrow(int iRow, int iColumnStart, int iColumnEnd) const { if( (iColumnStart>iColumnEnd) || wrongRow(iRow) || wrongColumn(iColumnStart) || wrongColumn(iColumnEnd)) return const_raw_vector(0, 0, 1); else return const_raw_vector(&((*this)(iRow, iColumnStart)), iColumnEnd-iColumnStart+1, 1); }; private: bool wrongRow(int i) const { return im_iHigh1; }; bool wrongColumn(int j) const { return jm_iHigh2; }; T *m_Vec; long m_iVecSize; long m_iLow1, m_iLow2, m_iHigh1, m_iHigh2; long m_iConstOffset, m_iLinearMember; }; typedef template_1d_array integer_1d_array; typedef template_1d_array real_1d_array; typedef template_1d_array complex_1d_array; typedef template_1d_array boolean_1d_array; typedef template_2d_array integer_2d_array; typedef template_2d_array real_2d_array; typedef template_2d_array complex_2d_array; typedef template_2d_array boolean_2d_array; /******************************************************************** Structs for use with the linear regression module (moved from linreg.h -mh 2009.05.31) ********************************************************************/ struct linearmodel { real_1d_array w; }; struct lrreport { real_2d_array c; real_value_type rmserror; real_value_type avgerror; real_value_type avgrelerror; real_value_type cvrmserror; real_value_type cvavgerror; real_value_type cvavgrelerror; int ncvdefects; integer_1d_array cvdefects; }; /******************************************************************** Constants and functions introduced for compatibility with AlgoPascal ********************************************************************/ extern const real_value_type machineepsilon; extern const real_value_type maxrealnumber; extern const real_value_type minrealnumber; int sign(real_value_type x); int round(real_value_type x); int trunc(real_value_type x); int ifloor(real_value_type x); real_value_type pi(); real_value_type sqr(real_value_type x); int maxint(int m1, int m2); int minint(int m1, int m2); real_value_type maxreal(real_value_type m1, real_value_type m2); real_value_type minreal(real_value_type m1, real_value_type m2); } //namespace ap #endif cmtk-3.3.1/libs/Numerics/apvt.h000066400000000000000000000532201276303427400163270ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2009, 2013 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4964 $ // // $LastChangedDate: 2013-10-11 09:20:01 -0700 (Fri, 11 Oct 2013) $ // // $LastChangedBy: torstenrohlfing $ // */ /******************************************************************** Templates for AP Library Copyright (c) 2003-2008, Sergey Bochkanov (ALGLIB project). See www.alglib.net or alglib.sources.ru for details. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer listed in this license in the documentation and/or other materials provided with the distribution. - Neither the name of the copyright holders nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ********************************************************************/ #ifndef APVT_H #define APVT_H /******************************************************************** Template defining vector in memory. It is used by the basic subroutines of linear algebra. Vector consists of Length elements of type T, starting from an element, which Data is pointed to. Interval between adjacent elements equals the value of Step. The class provides an access for reading only. ********************************************************************/ template class const_raw_vector { public: const_raw_vector(const T *Data, int Length, int Step): pData(const_cast(Data)),iLength(Length),iStep(Step){}; const T* GetData() const { return pData; }; int GetLength() const { return iLength; }; int GetStep() const { return iStep; }; protected: T *pData; int iLength, iStep; }; /******************************************************************** Template defining vector in memory, derived from const_raw_vector. It is used by the basic subroutines of linear algebra. Vector consists of Length elements of type T, starting from an element, which Data is pointed to. Interval between adjacent elements equals the value of Step. The class provides an access both for reading and writing. ********************************************************************/ template class raw_vector : public const_raw_vector { public: raw_vector(T *Data, int Length, int Step):const_raw_vector(Data, Length, Step){}; T* GetData() { return const_raw_vector::pData; }; }; /******************************************************************** Dot product ********************************************************************/ template T vdotproduct(const_raw_vector v1, const_raw_vector v2) { #ifndef NO_AP_ASSERT ap_error::make_assertion(v1.GetLength()==v2.GetLength()); #endif if( v1.GetStep()==1 && v2.GetStep()==1 ) { // // fast // T r = 0; const T *p1 = v1.GetData(); const T *p2 = v2.GetData(); int imax = v1.GetLength()/4; int i; for(i=imax; i!=0; i--) { r += (*p1)*(*p2) + p1[1]*p2[1] + p1[2]*p2[2] + p1[3]*p2[3]; p1+=4; p2+=4; } for(i=0; i T _vdotproduct(const T *v1, const T *v2, int N) { T r = 0; int imax = N/4; int i; for(i=imax; i!=0; i--) { r += (*v1)*(*v2) + v1[1]*v2[1] + v1[2]*v2[2] + v1[3]*v2[3]; v1+=4; v2+=4; } for(i=0; i void vmove(raw_vector vdst, const_raw_vector vsrc) { #ifndef NO_AP_ASSERT ap_error::make_assertion(vdst.GetLength()==vsrc.GetLength()); #endif if( vdst.GetStep()==1 && vsrc.GetStep()==1 ) { // // fast // T *p1 = vdst.GetData(); const T *p2 = vsrc.GetData(); int imax = vdst.GetLength()/2; int i; for(i=imax; i!=0; i--) { *p1 = *p2; p1[1] = p2[1]; p1 += 2; p2 += 2; } if(vdst.GetLength()%2 != 0) *p1 = *p2; return; } else { // // general // int offset11 = vdst.GetStep(), offset12 = 2*offset11, offset13 = 3*offset11, offset14 = 4*offset11; int offset21 = vsrc.GetStep(), offset22 = 2*offset21, offset23 = 3*offset21, offset24 = 4*offset21; T *p1 = vdst.GetData(); const T *p2 = vsrc.GetData(); int imax = vdst.GetLength()/4; int i; for(i=0; i void _vmove(T *vdst, const T* vsrc, int N) { int imax = N/2; int i; for(i=imax; i!=0; i--) { *vdst = *vsrc; vdst[1] = vsrc[1]; vdst += 2; vsrc += 2; } if(N%2!=0) *vdst = *vsrc; } /******************************************************************** Copy one vector multiplied by -1 into another. ********************************************************************/ template void vmoveneg(raw_vector vdst, const_raw_vector vsrc) { #ifndef NO_AP_ASSERT ap_error::make_assertion(vdst.GetLength()==vsrc.GetLength()); #endif if( vdst.GetStep()==1 && vsrc.GetStep()==1 ) { // // fast // T *p1 = vdst.GetData(); const T *p2 = vsrc.GetData(); int imax = vdst.GetLength()/2; int i; for(i=0; i void _vmoveneg(T *vdst, const T *vsrc, int N) { T *p1 = vdst; const T *p2 = vsrc; int imax = N/2; int i; for(i=0; i void vmove(raw_vector vdst, const_raw_vector vsrc, T2 alpha) { #ifndef NO_AP_ASSERT ap_error::make_assertion(vdst.GetLength()==vsrc.GetLength()); #endif if( vdst.GetStep()==1 && vsrc.GetStep()==1 ) { // // fast // T *p1 = vdst.GetData(); const T *p2 = vsrc.GetData(); int imax = vdst.GetLength()/4; int i; for(i=imax; i!=0; i--) { *p1 = alpha*(*p2); p1[1] = alpha*p2[1]; p1[2] = alpha*p2[2]; p1[3] = alpha*p2[3]; p1 += 4; p2 += 4; } for(i=0; i void _vmove(T *vdst, const T *vsrc, int N, T2 alpha) { T *p1 = vdst; const T *p2 = vsrc; int imax = N/4; int i; for(i=imax; i!=0; i--) { *p1 = alpha*(*p2); p1[1] = alpha*p2[1]; p1[2] = alpha*p2[2]; p1[3] = alpha*p2[3]; p1 += 4; p2 += 4; } for(i=0; i void vadd(raw_vector vdst, const_raw_vector vsrc) { #ifndef NO_AP_ASSERT ap_error::make_assertion(vdst.GetLength()==vsrc.GetLength()); #endif if( vdst.GetStep()==1 && vsrc.GetStep()==1 ) { // // fast // T *p1 = vdst.GetData(); const T *p2 = vsrc.GetData(); int imax = vdst.GetLength()/4; int i; for(i=imax; i!=0; i--) { *p1 += *p2; p1[1] += p2[1]; p1[2] += p2[2]; p1[3] += p2[3]; p1 += 4; p2 += 4; } for(i=0; i void _vadd(T *vdst, const T *vsrc, int N) { T *p1 = vdst; const T *p2 = vsrc; int imax = N/4; int i; for(i=imax; i!=0; i--) { *p1 += *p2; p1[1] += p2[1]; p1[2] += p2[2]; p1[3] += p2[3]; p1 += 4; p2 += 4; } for(i=0; i void vadd(raw_vector vdst, const_raw_vector vsrc, T2 alpha) { #ifndef NO_AP_ASSERT ap_error::make_assertion(vdst.GetLength()==vsrc.GetLength()); #endif if( vdst.GetStep()==1 && vsrc.GetStep()==1 ) { // // fast // T *p1 = vdst.GetData(); const T *p2 = vsrc.GetData(); int imax = vdst.GetLength()/4; int i; for(i=imax; i!=0; i--) { *p1 += alpha*(*p2); p1[1] += alpha*p2[1]; p1[2] += alpha*p2[2]; p1[3] += alpha*p2[3]; p1 += 4; p2 += 4; } for(i=0; i void _vadd(T *vdst, const T *vsrc, int N, T2 alpha) { T *p1 = vdst; const T *p2 = vsrc; int imax = N/4; int i; for(i=imax; i!=0; i--) { *p1 += alpha*(*p2); p1[1] += alpha*p2[1]; p1[2] += alpha*p2[2]; p1[3] += alpha*p2[3]; p1 += 4; p2 += 4; } for(i=0; i void vsub(raw_vector vdst, const_raw_vector vsrc) { #ifndef NO_AP_ASSERT ap_error::make_assertion(vdst.GetLength()==vsrc.GetLength()); #endif if( vdst.GetStep()==1 && vsrc.GetStep()==1 ) { // // fast // T *p1 = vdst.GetData(); const T *p2 = vsrc.GetData(); int imax = vdst.GetLength()/4; int i; for(i=imax; i!=0; i--) { *p1 -= *p2; p1[1] -= p2[1]; p1[2] -= p2[2]; p1[3] -= p2[3]; p1 += 4; p2 += 4; } for(i=0; i void _vsub(T *vdst, const T *vsrc, int N) { T *p1 = vdst; const T *p2 = vsrc; int imax = N/4; int i; for(i=imax; i!=0; i--) { *p1 -= *p2; p1[1] -= p2[1]; p1[2] -= p2[2]; p1[3] -= p2[3]; p1 += 4; p2 += 4; } for(i=0; i void vsub(raw_vector vdst, const_raw_vector vsrc, T2 alpha) { vadd(vdst, vsrc, -alpha); } /******************************************************************** vsub, another form ********************************************************************/ template void _vsub(T *vdst, const T *vsrc, int N, T2 alpha) { _vadd(vdst, vsrc, N, -alpha); } /******************************************************************** In-place vector multiplication ********************************************************************/ template void vmul(raw_vector vdst, T2 alpha) { if( vdst.GetStep()==1 ) { // // fast // T *p1 = vdst.GetData(); int imax = vdst.GetLength()/4; int i; for(i=imax; i!=0; i--) { *p1 *= alpha; p1[1] *= alpha; p1[2] *= alpha; p1[3] *= alpha; p1 += 4; } for(i=0; i void _vmul(T *vdst, int N, T2 alpha) { T *p1 = vdst; int imax = N/4; int i; for(i=imax; i!=0; i--) { *p1 *= alpha; p1[1] *= alpha; p1[2] *= alpha; p1[3] *= alpha; p1 += 4; } for(i=0; i. // // $Revision: 3193 $ // // $LastChangedDate: 2011-04-28 16:15:14 -0700 (Thu, 28 Apr 2011) $ // // $LastChangedBy: torstenrohlfing $ // */ /************************************************************************* Copyright (c) 1992-2007 The University of Tennessee. All rights reserved. Contributors: * Sergey Bochkanov (ALGLIB project). Translation from FORTRAN to pseudocode. See subroutines comments for additional copyrights. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer listed in this license in the documentation and/or other materials provided with the distribution. - Neither the name of the copyright holders nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *************************************************************************/ #include "bdsvd.h" bool bidiagonalsvddecompositioninternal(ap::real_1d_array& d, ap::real_1d_array e, int n, bool isupper, bool isfractionalaccuracyrequired, ap::real_2d_array& u, int ustart, int nru, ap::real_2d_array& c, int cstart, int ncc, ap::real_2d_array& vt, int vstart, int ncvt); ap::real_value_type extsignbdsqr(ap::real_value_type a, ap::real_value_type b); void svd2x2(ap::real_value_type f, ap::real_value_type g, ap::real_value_type h, ap::real_value_type& ssmin, ap::real_value_type& ssmax); void svdv2x2(ap::real_value_type f, ap::real_value_type g, ap::real_value_type h, ap::real_value_type& ssmin, ap::real_value_type& ssmax, ap::real_value_type& snr, ap::real_value_type& csr, ap::real_value_type& snl, ap::real_value_type& csl); /************************************************************************* Singular value decomposition of a bidiagonal matrix (extended algorithm) The algorithm performs the singular value decomposition of a bidiagonal matrix B (upper or lower) representing it as B = Q*S*P^T, where Q and P - orthogonal matrices, S - diagonal matrix with non-negative elements on the main diagonal, in descending order. The algorithm finds singular values. In addition, the algorithm can calculate matrices Q and P (more precisely, not the matrices, but their product with given matrices U and VT - U*Q and (P^T)*VT)). Of course, matrices U and VT can be of any type, including identity. Furthermore, the algorithm can calculate Q'*C (this product is calculated more effectively than U*Q, because this calculation operates with rows instead of matrix columns). The feature of the algorithm is its ability to find all singular values including those which are arbitrarily close to 0 with relative accuracy close to machine precision. If the parameter IsFractionalAccuracyRequired is set to True, all singular values will have high relative accuracy close to machine precision. If the parameter is set to False, only the biggest singular value will have relative accuracy close to machine precision. The absolute error of other singular values is equal to the absolute error of the biggest singular value. Input parameters: D - main diagonal of matrix B. Array whose index ranges within [0..N-1]. E - superdiagonal (or subdiagonal) of matrix B. Array whose index ranges within [0..N-2]. N - size of matrix B. IsUpper - True, if the matrix is upper bidiagonal. IsFractionalAccuracyRequired - accuracy to search singular values with. U - matrix to be multiplied by Q. Array whose indexes range within [0..NRU-1, 0..N-1]. The matrix can be bigger, in that case only the submatrix [0..NRU-1, 0..N-1] will be multiplied by Q. NRU - number of rows in matrix U. C - matrix to be multiplied by Q'. Array whose indexes range within [0..N-1, 0..NCC-1]. The matrix can be bigger, in that case only the submatrix [0..N-1, 0..NCC-1] will be multiplied by Q'. NCC - number of columns in matrix C. VT - matrix to be multiplied by P^T. Array whose indexes range within [0..N-1, 0..NCVT-1]. The matrix can be bigger, in that case only the submatrix [0..N-1, 0..NCVT-1] will be multiplied by P^T. NCVT - number of columns in matrix VT. Output parameters: D - singular values of matrix B in descending order. U - if NRU>0, contains matrix U*Q. VT - if NCVT>0, contains matrix (P^T)*VT. C - if NCC>0, contains matrix Q'*C. Result: True, if the algorithm has converged. False, if the algorithm hasn't converged (rare case). Additional information: The type of convergence is controlled by the internal parameter TOL. If the parameter is greater than 0, the singular values will have relative accuracy TOL. If TOL<0, the singular values will have absolute accuracy ABS(TOL)*norm(B). By default, |TOL| falls within the range of 10*Epsilon and 100*Epsilon, where Epsilon is the machine precision. It is not recommended to use TOL less than 10*Epsilon since this will considerably slow down the algorithm and may not lead to error decreasing. History: * 31 March, 2007. changed MAXITR from 6 to 12. -- LAPACK routine (version 3.0) -- Univ. of Tennessee, Univ. of California Berkeley, NAG Ltd., Courant Institute, Argonne National Lab, and Rice University October 31, 1999. *************************************************************************/ bool rmatrixbdsvd(ap::real_1d_array& d, ap::real_1d_array e, int n, bool isupper, bool isfractionalaccuracyrequired, ap::real_2d_array& u, int nru, ap::real_2d_array& c, int ncc, ap::real_2d_array& vt, int ncvt) { bool result; ap::real_1d_array d1; ap::real_1d_array e1; d1.setbounds(1, n); ap::vmove(&d1(1), &d(0), ap::vlen(1,n)); if( n>1 ) { e1.setbounds(1, n-1); ap::vmove(&e1(1), &e(0), ap::vlen(1,n-1)); } result = bidiagonalsvddecompositioninternal(d1, e1, n, isupper, isfractionalaccuracyrequired, u, 0, nru, c, 0, ncc, vt, 0, ncvt); ap::vmove(&d(0), &d1(1), ap::vlen(0,n-1)); return result; } /************************************************************************* Internal working subroutine for bidiagonal decomposition *************************************************************************/ bool bidiagonalsvddecompositioninternal(ap::real_1d_array& d, ap::real_1d_array e, int n, bool isupper, bool isfractionalaccuracyrequired, ap::real_2d_array& u, int ustart, int nru, ap::real_2d_array& c, int cstart, int ncc, ap::real_2d_array& vt, int vstart, int ncvt) { bool result; int i; int idir; int isub; int iter; int j; int ll; int lll; int m; int maxit; int oldll; int oldm; ap::real_value_type abse; ap::real_value_type abss; ap::real_value_type cosl; ap::real_value_type cosr; ap::real_value_type cs; ap::real_value_type eps; ap::real_value_type f; ap::real_value_type g; ap::real_value_type h; ap::real_value_type mu; ap::real_value_type oldcs; ap::real_value_type oldsn; ap::real_value_type r; ap::real_value_type shift; ap::real_value_type sigmn; ap::real_value_type sigmx; ap::real_value_type sinl; ap::real_value_type sinr; ap::real_value_type sll; ap::real_value_type smax; ap::real_value_type smin; ap::real_value_type sminl; ap::real_value_type sminoa; ap::real_value_type sn; ap::real_value_type thresh; ap::real_value_type tol; ap::real_value_type tolmul; ap::real_value_type unfl; ap::real_1d_array work0; ap::real_1d_array work1; ap::real_1d_array work2; ap::real_1d_array work3; int maxitr; bool matrixsplitflag; bool iterflag; ap::real_1d_array utemp; ap::real_1d_array vttemp; ap::real_1d_array ctemp; ap::real_1d_array etemp; bool fwddir; ap::real_value_type tmp; int mm1; int mm0; bool bchangedir; int uend; int cend; int vend; result = true; if( n==0 ) { return result; } if( n==1 ) { if( d(1)<0 ) { d(1) = -d(1); if( ncvt>0 ) { ap::vmul(&vt(vstart, vstart), ap::vlen(vstart,vstart+ncvt-1), -1); } } return result; } // // init // work0.setbounds(1, n-1); work1.setbounds(1, n-1); work2.setbounds(1, n-1); work3.setbounds(1, n-1); uend = ustart+ap::maxint(nru-1, 0); vend = vstart+ap::maxint(ncvt-1, 0); cend = cstart+ap::maxint(ncc-1, 0); utemp.setbounds(ustart, uend); vttemp.setbounds(vstart, vend); ctemp.setbounds(cstart, cend); maxitr = 12; fwddir = true; // // resize E from N-1 to N // etemp.setbounds(1, n); for(i = 1; i <= n-1; i++) { etemp(i) = e(i); } e.setbounds(1, n); for(i = 1; i <= n-1; i++) { e(i) = etemp(i); } e(n) = 0; idir = 0; // // Get machine constants // eps = ap::machineepsilon; unfl = ap::minrealnumber; // // If matrix lower bidiagonal, rotate to be upper bidiagonal // by applying Givens rotations on the left // if( !isupper ) { for(i = 1; i <= n-1; i++) { generaterotation(d(i), e(i), cs, sn, r); d(i) = r; e(i) = sn*d(i+1); d(i+1) = cs*d(i+1); work0(i) = cs; work1(i) = sn; } // // Update singular vectors if desired // if( nru>0 ) { applyrotationsfromtheright(fwddir, ustart, uend, 1+ustart-1, n+ustart-1, work0, work1, u, utemp); } if( ncc>0 ) { applyrotationsfromtheleft(fwddir, 1+cstart-1, n+cstart-1, cstart, cend, work0, work1, c, ctemp); } } // // Compute singular values to relative accuracy TOL // (By setting TOL to be negative, algorithm will compute // singular values to absolute accuracy ABS(TOL)*norm(input matrix)) // tolmul = ap::maxreal(ap::real_value_type(10), ap::minreal(ap::real_value_type(100), pow((ap::real_value_type)eps, (ap::real_value_type)-0.125))); tol = tolmul*eps; if( !isfractionalaccuracyrequired ) { tol = -tol; } // // Compute approximate maximum, minimum singular values // smax = 0; for(i = 1; i <= n; i++) { smax = ap::maxreal(smax, fabs(d(i))); } for(i = 1; i <= n-1; i++) { smax = ap::maxreal(smax, fabs(e(i))); } sminl = 0; if( tol>=0 ) { // // Relative accuracy desired // sminoa = fabs(d(1)); if( sminoa!=0 ) { mu = sminoa; for(i = 2; i <= n; i++) { mu = fabs(d(i))*(mu/(mu+fabs(e(i-1)))); sminoa = ap::minreal(sminoa, mu); if( sminoa==0 ) { break; } } } sminoa = sminoa/sqrt(ap::real_value_type(n)); thresh = ap::maxreal(tol*sminoa, maxitr*n*n*unfl); } else { // // Absolute accuracy desired // thresh = ap::maxreal(fabs(tol)*smax, maxitr*n*n*unfl); } // // Prepare for main iteration loop for the singular values // (MAXIT is the maximum number of passes through the inner // loop permitted before nonconvergence signalled.) // maxit = maxitr*n*n; iter = 0; oldll = -1; oldm = -1; // // M points to last element of unconverged part of matrix // m = n; // // Begin main iteration loop // while(true) { // // Check for convergence or exceeding iteration count // if( m<=1 ) { break; } if( iter>maxit ) { result = false; return result; } // // Find diagonal block of matrix to work on // if( tol<0&&fabs(d(m))<=thresh ) { d(m) = 0; } smax = fabs(d(m)); smin = smax; matrixsplitflag = false; for(lll = 1; lll <= m-1; lll++) { ll = m-lll; abss = fabs(d(ll)); abse = fabs(e(ll)); if( tol<0&&abss<=thresh ) { d(ll) = 0; } if( abse<=thresh ) { matrixsplitflag = true; break; } smin = ap::minreal(smin, abss); smax = ap::maxreal(smax, ap::maxreal(abss, abse)); } if( !matrixsplitflag ) { ll = 0; } else { // // Matrix splits since E(LL) = 0 // e(ll) = 0; if( ll==m-1 ) { // // Convergence of bottom singular value, return to top of loop // m = m-1; continue; } } ll = ll+1; // // E(LL) through E(M-1) are nonzero, E(LL-1) is zero // if( ll==m-1 ) { // // 2 by 2 block, handle separately // svdv2x2(d(m-1), e(m-1), d(m), sigmn, sigmx, sinr, cosr, sinl, cosl); d(m-1) = sigmx; e(m-1) = 0; d(m) = sigmn; // // Compute singular vectors, if desired // if( ncvt>0 ) { mm0 = m+(vstart-1); mm1 = m-1+(vstart-1); ap::vmove(&vttemp(vstart), &vt(mm1, vstart), ap::vlen(vstart,vend), cosr); ap::vadd(&vttemp(vstart), &vt(mm0, vstart), ap::vlen(vstart,vend), sinr); ap::vmul(&vt(mm0, vstart), ap::vlen(vstart,vend), cosr); ap::vsub(&vt(mm0, vstart), &vt(mm1, vstart), ap::vlen(vstart,vend), sinr); ap::vmove(&vt(mm1, vstart), &vttemp(vstart), ap::vlen(vstart,vend)); } if( nru>0 ) { mm0 = m+ustart-1; mm1 = m-1+ustart-1; ap::vmove(utemp.getvector(ustart, uend), u.getcolumn(mm1, ustart, uend), cosl); ap::vadd(utemp.getvector(ustart, uend), u.getcolumn(mm0, ustart, uend), sinl); ap::vmul(u.getcolumn(mm0, ustart, uend), cosl); ap::vsub(u.getcolumn(mm0, ustart, uend), u.getcolumn(mm1, ustart, uend), sinl); ap::vmove(u.getcolumn(mm1, ustart, uend), utemp.getvector(ustart, uend)); } if( ncc>0 ) { mm0 = m+cstart-1; mm1 = m-1+cstart-1; ap::vmove(&ctemp(cstart), &c(mm1, cstart), ap::vlen(cstart,cend), cosl); ap::vadd(&ctemp(cstart), &c(mm0, cstart), ap::vlen(cstart,cend), sinl); ap::vmul(&c(mm0, cstart), ap::vlen(cstart,cend), cosl); ap::vsub(&c(mm0, cstart), &c(mm1, cstart), ap::vlen(cstart,cend), sinl); ap::vmove(&c(mm1, cstart), &ctemp(cstart), ap::vlen(cstart,cend)); } m = m-2; continue; } // // If working on new submatrix, choose shift direction // (from larger end diagonal element towards smaller) // // Previously was // "if (LL>OLDM) or (M // Very strange that LAPACK still contains it. // bchangedir = false; if( idir==1&&fabs(d(ll))<1.0E-3*fabs(d(m)) ) { bchangedir = true; } if( idir==2&&fabs(d(m))<1.0E-3*fabs(d(ll)) ) { bchangedir = true; } if( ll!=oldll||m!=oldm||bchangedir ) { if( fabs(d(ll))>=fabs(d(m)) ) { // // Chase bulge from top (big end) to bottom (small end) // idir = 1; } else { // // Chase bulge from bottom (big end) to top (small end) // idir = 2; } } // // Apply convergence tests // if( idir==1 ) { // // Run convergence test in forward direction // First apply standard test to bottom of matrix // if( fabs(e(m-1))<=fabs(tol)*fabs(d(m))||(tol<0&&fabs(e(m-1))<=thresh) ) { e(m-1) = 0; continue; } if( tol>=0 ) { // // If relative accuracy desired, // apply convergence criterion forward // mu = fabs(d(ll)); sminl = mu; iterflag = false; for(lll = ll; lll <= m-1; lll++) { if( fabs(e(lll))<=tol*mu ) { e(lll) = 0; iterflag = true; break; } mu = fabs(d(lll+1))*(mu/(mu+fabs(e(lll)))); sminl = ap::minreal(sminl, mu); } if( iterflag ) { continue; } } } else { // // Run convergence test in backward direction // First apply standard test to top of matrix // if( fabs(e(ll))<=fabs(tol)*fabs(d(ll))||(tol<0&&fabs(e(ll))<=thresh) ) { e(ll) = 0; continue; } if( tol>=0 ) { // // If relative accuracy desired, // apply convergence criterion backward // mu = fabs(d(m)); sminl = mu; iterflag = false; for(lll = m-1; lll >= ll; lll--) { if( fabs(e(lll))<=tol*mu ) { e(lll) = 0; iterflag = true; break; } mu = fabs(d(lll))*(mu/(mu+fabs(e(lll)))); sminl = ap::minreal(sminl, mu); } if( iterflag ) { continue; } } } oldll = ll; oldm = m; // // Compute shift. First, test if shifting would ruin relative // accuracy, and if so set the shift to zero. // if( tol>=0&&n*tol*(sminl/smax)<=ap::maxreal(eps, 0.01*tol) ) { // // Use a zero shift to avoid loss of relative accuracy // shift = 0; } else { // // Compute the shift from 2-by-2 block at end of matrix // if( idir==1 ) { sll = fabs(d(ll)); svd2x2(d(m-1), e(m-1), d(m), shift, r); } else { sll = fabs(d(m)); svd2x2(d(ll), e(ll), d(ll+1), shift, r); } // // Test if shift negligible, and if so set to zero // if( sll>0 ) { if( ap::sqr(shift/sll)ll ) { e(i-1) = oldsn*r; } generaterotation(oldcs*r, d(i+1)*sn, oldcs, oldsn, tmp); d(i) = tmp; work0(i-ll+1) = cs; work1(i-ll+1) = sn; work2(i-ll+1) = oldcs; work3(i-ll+1) = oldsn; } h = d(m)*cs; d(m) = h*oldcs; e(m-1) = h*oldsn; // // Update singular vectors // if( ncvt>0 ) { applyrotationsfromtheleft(fwddir, ll+vstart-1, m+vstart-1, vstart, vend, work0, work1, vt, vttemp); } if( nru>0 ) { applyrotationsfromtheright(fwddir, ustart, uend, ll+ustart-1, m+ustart-1, work2, work3, u, utemp); } if( ncc>0 ) { applyrotationsfromtheleft(fwddir, ll+cstart-1, m+cstart-1, cstart, cend, work2, work3, c, ctemp); } // // Test convergence // if( fabs(e(m-1))<=thresh ) { e(m-1) = 0; } } else { // // Chase bulge from bottom to top // Save cosines and sines for later singular vector updates // cs = 1; oldcs = 1; for(i = m; i >= ll+1; i--) { generaterotation(d(i)*cs, e(i-1), cs, sn, r); if( i0 ) { applyrotationsfromtheleft(!fwddir, ll+vstart-1, m+vstart-1, vstart, vend, work2, work3, vt, vttemp); } if( nru>0 ) { applyrotationsfromtheright(!fwddir, ustart, uend, ll+ustart-1, m+ustart-1, work0, work1, u, utemp); } if( ncc>0 ) { applyrotationsfromtheleft(!fwddir, ll+cstart-1, m+cstart-1, cstart, cend, work0, work1, c, ctemp); } // // Test convergence // if( fabs(e(ll))<=thresh ) { e(ll) = 0; } } } else { // // Use nonzero shift // if( idir==1 ) { // // Chase bulge from top to bottom // Save cosines and sines for later singular vector updates // f = (fabs(d(ll))-shift)*(extsignbdsqr(ap::real_value_type(1), d(ll))+shift/d(ll)); g = e(ll); for(i = ll; i <= m-1; i++) { generaterotation(f, g, cosr, sinr, r); if( i>ll ) { e(i-1) = r; } f = cosr*d(i)+sinr*e(i); e(i) = cosr*e(i)-sinr*d(i); g = sinr*d(i+1); d(i+1) = cosr*d(i+1); generaterotation(f, g, cosl, sinl, r); d(i) = r; f = cosl*e(i)+sinl*d(i+1); d(i+1) = cosl*d(i+1)-sinl*e(i); if( i0 ) { applyrotationsfromtheleft(fwddir, ll+vstart-1, m+vstart-1, vstart, vend, work0, work1, vt, vttemp); } if( nru>0 ) { applyrotationsfromtheright(fwddir, ustart, uend, ll+ustart-1, m+ustart-1, work2, work3, u, utemp); } if( ncc>0 ) { applyrotationsfromtheleft(fwddir, ll+cstart-1, m+cstart-1, cstart, cend, work2, work3, c, ctemp); } // // Test convergence // if( fabs(e(m-1))<=thresh ) { e(m-1) = 0; } } else { // // Chase bulge from bottom to top // Save cosines and sines for later singular vector updates // f = (fabs(d(m))-shift)*(extsignbdsqr(ap::real_value_type(1), d(m))+shift/d(m)); g = e(m-1); for(i = m; i >= ll+1; i--) { generaterotation(f, g, cosr, sinr, r); if( ill+1 ) { g = sinl*e(i-2); e(i-2) = cosl*e(i-2); } work0(i-ll) = cosr; work1(i-ll) = -sinr; work2(i-ll) = cosl; work3(i-ll) = -sinl; } e(ll) = f; // // Test convergence // if( fabs(e(ll))<=thresh ) { e(ll) = 0; } // // Update singular vectors if desired // if( ncvt>0 ) { applyrotationsfromtheleft(!fwddir, ll+vstart-1, m+vstart-1, vstart, vend, work2, work3, vt, vttemp); } if( nru>0 ) { applyrotationsfromtheright(!fwddir, ustart, uend, ll+ustart-1, m+ustart-1, work0, work1, u, utemp); } if( ncc>0 ) { applyrotationsfromtheleft(!fwddir, ll+cstart-1, m+cstart-1, cstart, cend, work0, work1, c, ctemp); } } } // // QR iteration finished, go back and check convergence // continue; } // // All singular values converged, so make them positive // for(i = 1; i <= n; i++) { if( d(i)<0 ) { d(i) = -d(i); // // Change sign of singular vectors, if desired // if( ncvt>0 ) { ap::vmul(&vt(i+vstart-1, vstart), ap::vlen(vstart,vend), -1); } } } // // Sort the singular values into decreasing order (insertion sort on // singular values, but only one transposition per singular vector) // for(i = 1; i <= n-1; i++) { // // Scan for smallest D(I) // isub = 1; smin = d(1); for(j = 2; j <= n+1-i; j++) { if( d(j)<=smin ) { isub = j; smin = d(j); } } if( isub!=n+1-i ) { // // Swap singular values and vectors // d(isub) = d(n+1-i); d(n+1-i) = smin; if( ncvt>0 ) { j = n+1-i; ap::vmove(&vttemp(vstart), &vt(isub+vstart-1, vstart), ap::vlen(vstart,vend)); ap::vmove(&vt(isub+vstart-1, vstart), &vt(j+vstart-1, vstart), ap::vlen(vstart,vend)); ap::vmove(&vt(j+vstart-1, vstart), &vttemp(vstart), ap::vlen(vstart,vend)); } if( nru>0 ) { j = n+1-i; ap::vmove(utemp.getvector(ustart, uend), u.getcolumn(isub+ustart-1, ustart, uend)); ap::vmove(u.getcolumn(isub+ustart-1, ustart, uend), u.getcolumn(j+ustart-1, ustart, uend)); ap::vmove(u.getcolumn(j+ustart-1, ustart, uend), utemp.getvector(ustart, uend)); } if( ncc>0 ) { j = n+1-i; ap::vmove(&ctemp(cstart), &c(isub+cstart-1, cstart), ap::vlen(cstart,cend)); ap::vmove(&c(isub+cstart-1, cstart), &c(j+cstart-1, cstart), ap::vlen(cstart,cend)); ap::vmove(&c(j+cstart-1, cstart), &ctemp(cstart), ap::vlen(cstart,cend)); } } } return result; } ap::real_value_type extsignbdsqr(ap::real_value_type a, ap::real_value_type b) { ap::real_value_type result; if( b>=0 ) { result = fabs(a); } else { result = -fabs(a); } return result; } void svd2x2(ap::real_value_type f, ap::real_value_type g, ap::real_value_type h, ap::real_value_type& ssmin, ap::real_value_type& ssmax) { ap::real_value_type aas; ap::real_value_type at; ap::real_value_type au; ap::real_value_type c; ap::real_value_type fa; ap::real_value_type fhmn; ap::real_value_type fhmx; ap::real_value_type ga; ap::real_value_type ha; fa = fabs(f); ga = fabs(g); ha = fabs(h); fhmn = ap::minreal(fa, ha); fhmx = ap::maxreal(fa, ha); if( fhmn==0 ) { ssmin = 0; if( fhmx==0 ) { ssmax = ga; } else { ssmax = ap::maxreal(fhmx, ga)*sqrt(1+ap::sqr(ap::minreal(fhmx, ga)/ap::maxreal(fhmx, ga))); } } else { if( gafa; if( swp ) { // // Now FA .ge. HA // pmax = 3; temp = ft; ft = ht; ht = temp; temp = fa; fa = ha; ha = temp; } gt = g; ga = fabs(gt); if( ga==0 ) { // // Diagonal matrix // ssmin = ha; ssmax = fa; clt = 1; crt = 1; slt = 0; srt = 0; } else { gasmal = true; if( ga>fa ) { pmax = 2; if( fa/ga1 ) { v = ga/ha; ssmin = fa/v; } else { v = fa/ga; ssmin = v*ha; } clt = 1; slt = ht/gt; srt = 1; crt = ft/gt; } } if( gasmal ) { // // Normal case // d = fa-ha; if( d==fa ) { l = 1; } else { l = d/fa; } m = gt/ft; t = 2-l; mm = m*m; tt = t*t; s = sqrt(tt+mm); if( l==0 ) { r = fabs(m); } else { r = sqrt(l*l+mm); } a = 0.5*(s+r); ssmin = ha/a; ssmax = fa*a; if( mm==0 ) { // // Note that M is very tiny // if( l==0 ) { t = extsignbdsqr(ap::real_value_type(2), ft)*extsignbdsqr(ap::real_value_type(1), gt); } else { t = gt/extsignbdsqr(d, ft)+m/t; } } else { t = (m/(s+t)+m/(r+l))*(1+a); } l = sqrt(t*t+4); crt = 2/l; srt = t/l; clt = (crt+srt*m)/a; v = ht/ft; slt = v*srt/a; } } if( swp ) { csl = srt; snl = crt; csr = slt; snr = clt; } else { csl = clt; snl = slt; csr = crt; snr = srt; } // // Correct signs of SSMAX and SSMIN // if( pmax==1 ) { tsign = extsignbdsqr(ap::real_value_type(1), csr)*extsignbdsqr(ap::real_value_type(1), csl)*extsignbdsqr(ap::real_value_type(1), f); } if( pmax==2 ) { tsign = extsignbdsqr(ap::real_value_type(1), snr)*extsignbdsqr(ap::real_value_type(1), csl)*extsignbdsqr(ap::real_value_type(1), g); } if( pmax==3 ) { tsign = extsignbdsqr(ap::real_value_type(1), snr)*extsignbdsqr(ap::real_value_type(1), snl)*extsignbdsqr(ap::real_value_type(1), h); } ssmax = extsignbdsqr(ssmax, tsign); ssmin = extsignbdsqr(ssmin, tsign*extsignbdsqr(ap::real_value_type(1), f)*extsignbdsqr(ap::real_value_type(1), h)); } cmtk-3.3.1/libs/Numerics/bdsvd.h000066400000000000000000000161261276303427400164630ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2010 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 2376 $ // // $LastChangedDate: 2010-09-29 11:31:12 -0700 (Wed, 29 Sep 2010) $ // // $LastChangedBy: torstenrohlfing $ // */ /************************************************************************* Copyright (c) 1992-2007 The University of Tennessee. All rights reserved. Contributors: * Sergey Bochkanov (ALGLIB project). Translation from FORTRAN to pseudocode. See subroutines comments for additional copyrights. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer listed in this license in the documentation and/or other materials provided with the distribution. - Neither the name of the copyright holders nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *************************************************************************/ #ifndef _bdsvd_h #define _bdsvd_h #include "ap.h" #include "rotations.h" /************************************************************************* Singular value decomposition of a bidiagonal matrix (extended algorithm) The algorithm performs the singular value decomposition of a bidiagonal matrix B (upper or lower) representing it as B = Q*S*P^T, where Q and P - orthogonal matrices, S - diagonal matrix with non-negative elements on the main diagonal, in descending order. The algorithm finds singular values. In addition, the algorithm can calculate matrices Q and P (more precisely, not the matrices, but their product with given matrices U and VT - U*Q and (P^T)*VT)). Of course, matrices U and VT can be of any type, including identity. Furthermore, the algorithm can calculate Q'*C (this product is calculated more effectively than U*Q, because this calculation operates with rows instead of matrix columns). The feature of the algorithm is its ability to find all singular values including those which are arbitrarily close to 0 with relative accuracy close to machine precision. If the parameter IsFractionalAccuracyRequired is set to True, all singular values will have high relative accuracy close to machine precision. If the parameter is set to False, only the biggest singular value will have relative accuracy close to machine precision. The absolute error of other singular values is equal to the absolute error of the biggest singular value. Input parameters: D - main diagonal of matrix B. Array whose index ranges within [0..N-1]. E - superdiagonal (or subdiagonal) of matrix B. Array whose index ranges within [0..N-2]. N - size of matrix B. IsUpper - True, if the matrix is upper bidiagonal. IsFractionalAccuracyRequired - accuracy to search singular values with. U - matrix to be multiplied by Q. Array whose indexes range within [0..NRU-1, 0..N-1]. The matrix can be bigger, in that case only the submatrix [0..NRU-1, 0..N-1] will be multiplied by Q. NRU - number of rows in matrix U. C - matrix to be multiplied by Q'. Array whose indexes range within [0..N-1, 0..NCC-1]. The matrix can be bigger, in that case only the submatrix [0..N-1, 0..NCC-1] will be multiplied by Q'. NCC - number of columns in matrix C. VT - matrix to be multiplied by P^T. Array whose indexes range within [0..N-1, 0..NCVT-1]. The matrix can be bigger, in that case only the submatrix [0..N-1, 0..NCVT-1] will be multiplied by P^T. NCVT - number of columns in matrix VT. Output parameters: D - singular values of matrix B in descending order. U - if NRU>0, contains matrix U*Q. VT - if NCVT>0, contains matrix (P^T)*VT. C - if NCC>0, contains matrix Q'*C. Result: True, if the algorithm has converged. False, if the algorithm hasn't converged (rare case). Additional information: The type of convergence is controlled by the internal parameter TOL. If the parameter is greater than 0, the singular values will have relative accuracy TOL. If TOL<0, the singular values will have absolute accuracy ABS(TOL)*norm(B). By default, |TOL| falls within the range of 10*Epsilon and 100*Epsilon, where Epsilon is the machine precision. It is not recommended to use TOL less than 10*Epsilon since this will considerably slow down the algorithm and may not lead to error decreasing. History: * 31 March, 2007. changed MAXITR from 6 to 12. -- LAPACK routine (version 3.0) -- Univ. of Tennessee, Univ. of California Berkeley, NAG Ltd., Courant Institute, Argonne National Lab, and Rice University October 31, 1999. *************************************************************************/ bool rmatrixbdsvd(ap::real_1d_array& d, ap::real_1d_array e, int n, bool isupper, bool isfractionalaccuracyrequired, ap::real_2d_array& u, int nru, ap::real_2d_array& c, int ncc, ap::real_2d_array& vt, int ncvt); #endif cmtk-3.3.1/libs/Numerics/bidiagonal.cxx000066400000000000000000000530471276303427400200300ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2011, 2013 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4972 $ // // $LastChangedDate: 2013-10-11 13:26:22 -0700 (Fri, 11 Oct 2013) $ // // $LastChangedBy: torstenrohlfing $ // */ /************************************************************************* Copyright (c) 1992-2007 The University of Tennessee. All rights reserved. Contributors: * Sergey Bochkanov (ALGLIB project). Translation from FORTRAN to pseudocode. See subroutines comments for additional copyrights. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer listed in this license in the documentation and/or other materials provided with the distribution. - Neither the name of the copyright holders nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *************************************************************************/ #include "bidiagonal.h" /************************************************************************* Reduction of a rectangular matrix to bidiagonal form The algorithm reduces the rectangular matrix A to bidiagonal form by orthogonal transformations P and Q: A = Q*B*P. Input parameters: A - source matrix. array[0..M-1, 0..N-1] M - number of rows in matrix A. N - number of columns in matrix A. Output parameters: A - matrices Q, B, P in compact form (see below). TauQ - scalar factors which are used to form matrix Q. TauP - scalar factors which are used to form matrix P. The main diagonal and one of the secondary diagonals of matrix A are replaced with bidiagonal matrix B. Other elements contain elementary reflections which form MxM matrix Q and NxN matrix P, respectively. If M>=N, B is the upper bidiagonal MxN matrix and is stored in the corresponding elements of matrix A. Matrix Q is represented as a product of elementary reflections Q = H(0)*H(1)*...*H(n-1), where H(i) = 1-tau*v*v'. Here tau is a scalar which is stored in TauQ[i], and vector v has the following structure: v(0:i-1)=0, v(i)=1, v(i+1:m-1) is stored in elements A(i+1:m-1,i). Matrix P is as follows: P = G(0)*G(1)*...*G(n-2), where G(i) = 1 - tau*u*u'. Tau is stored in TauP[i], u(0:i)=0, u(i+1)=1, u(i+2:n-1) is stored in elements A(i,i+2:n-1). If M n): m=5, n=6 (m < n): ( d e u1 u1 u1 ) ( d u1 u1 u1 u1 u1 ) ( v1 d e u2 u2 ) ( e d u2 u2 u2 u2 ) ( v1 v2 d e u3 ) ( v1 e d u3 u3 u3 ) ( v1 v2 v3 d e ) ( v1 v2 e d u4 u4 ) ( v1 v2 v3 v4 d ) ( v1 v2 v3 e d u5 ) ( v1 v2 v3 v4 v5 ) Here vi and ui are vectors which form H(i) and G(i), and d and e - are the diagonal and off-diagonal elements of matrix B. *************************************************************************/ void rmatrixbd(ap::real_2d_array& a, int m, int n, ap::real_1d_array& tauq, ap::real_1d_array& taup) { ap::real_1d_array work; ap::real_1d_array t; int maxmn; int i; // int j; ap::real_value_type ltau; // // Prepare // if( n<=0||m<=0 ) { return; } maxmn = ap::maxint(m, n); work.setbounds(0, maxmn); t.setbounds(0, maxmn); if( m>=n ) { tauq.setbounds(0, n-1); taup.setbounds(0, n-1); } else { tauq.setbounds(0, m-1); taup.setbounds(0, m-1); } if( m>=n ) { // // Reduce to upper bidiagonal form // for(i = 0; i <= n-1; i++) { // // Generate elementary reflector H(i) to annihilate A(i+1:m-1,i) // ap::vmove(t.getvector(1, m-i), a.getcolumn(i, i, m-1)); generatereflection(t, m-i, ltau); tauq(i) = ltau; ap::vmove(a.getcolumn(i, i, m-1), t.getvector(1, m-i)); t(1) = 1; // // Apply H(i) to A(i:m-1,i+1:n-1) from the left // applyreflectionfromtheleft(a, ltau, t, i, m-1, i+1, n-1, work); if( i=QColumns>=0. Output parameters: Q - first QColumns columns of matrix Q. Array[0..M-1, 0..QColumns-1] If QColumns=0, the array is not modified. -- ALGLIB -- Copyright 2005 by Bochkanov Sergey *************************************************************************/ void rmatrixbdunpackq(const ap::real_2d_array& qp, int m, int n, const ap::real_1d_array& tauq, int qcolumns, ap::real_2d_array& q) { int i; int j; #ifndef NO_AP_ASSERT ap::ap_error::make_assertion(qcolumns<=m, "RMatrixBDUnpackQ: QColumns>M!"); ap::ap_error::make_assertion(qcolumns>=0, "RMatrixBDUnpackQ: QColumns<0!"); #endif if( m==0||n==0||qcolumns==0 ) { return; } // // prepare Q // q.setbounds(0, m-1, 0, qcolumns-1); for(i = 0; i <= m-1; i++) { for(j = 0; j <= qcolumns-1; j++) { if( i==j ) { q(i,j) = 1; } else { q(i,j) = 0; } } } // // Calculate // rmatrixbdmultiplybyq(qp, m, n, tauq, q, m, qcolumns, false, false); } /************************************************************************* Multiplication by matrix Q which reduces matrix A to bidiagonal form. The algorithm allows pre- or post-multiply by Q or Q'. Input parameters: QP - matrices Q and P in compact form. Output of ToBidiagonal subroutine. M - number of rows in matrix A. N - number of columns in matrix A. TAUQ - scalar factors which are used to form Q. Output of ToBidiagonal subroutine. Z - multiplied matrix. array[0..ZRows-1,0..ZColumns-1] ZRows - number of rows in matrix Z. If FromTheRight=False, ZRows=M, otherwise ZRows can be arbitrary. ZColumns - number of columns in matrix Z. If FromTheRight=True, ZColumns=M, otherwise ZColumns can be arbitrary. FromTheRight - pre- or post-multiply. DoTranspose - multiply by Q or Q'. Output parameters: Z - product of Z and Q. Array[0..ZRows-1,0..ZColumns-1] If ZRows=0 or ZColumns=0, the array is not modified. -- ALGLIB -- Copyright 2005 by Bochkanov Sergey *************************************************************************/ void rmatrixbdmultiplybyq(const ap::real_2d_array& qp, int m, int n, const ap::real_1d_array& tauq, ap::real_2d_array& z, int zrows, int zcolumns, bool fromtheright, bool dotranspose) { int i; int i1; int i2; int istep; ap::real_1d_array v; ap::real_1d_array work; int mx; if( m<=0||n<=0||zrows<=0||zcolumns<=0 ) { return; } #ifndef NO_AP_ASSERT ap::ap_error::make_assertion( (fromtheright&&zcolumns==m) || (!fromtheright&&zrows==m), "RMatrixBDMultiplyByQ: incorrect Z size!"); #endif // // init // mx = ap::maxint(m, n); mx = ap::maxint(mx, zrows); mx = ap::maxint(mx, zcolumns); v.setbounds(0, mx); work.setbounds(0, mx); if( m>=n ) { // // setup // if( fromtheright ) { i1 = 0; i2 = n-1; istep = +1; } else { i1 = n-1; i2 = 0; istep = -1; } if( dotranspose ) { i = i1; i1 = i2; i2 = i; istep = -istep; } // // Process // i = i1; do { ap::vmove(v.getvector(1, m-i), qp.getcolumn(i, i, m-1)); v(1) = 1; if( fromtheright ) { applyreflectionfromtheright(z, tauq(i), v, 0, zrows-1, i, m-1, work); } else { applyreflectionfromtheleft(z, tauq(i), v, i, m-1, 0, zcolumns-1, work); } i = i+istep; } while(i!=i2+istep); } else { // // setup // if( fromtheright ) { i1 = 0; i2 = m-2; istep = +1; } else { i1 = m-2; i2 = 0; istep = -1; } if( dotranspose ) { i = i1; i1 = i2; i2 = i; istep = -istep; } // // Process // if( m-1>0 ) { i = i1; do { ap::vmove(v.getvector(1, m-i-1), qp.getcolumn(i, i+1, m-1)); v(1) = 1; if( fromtheright ) { applyreflectionfromtheright(z, tauq(i), v, 0, zrows-1, i+1, m-1, work); } else { applyreflectionfromtheleft(z, tauq(i), v, i+1, m-1, 0, zcolumns-1, work); } i = i+istep; } while(i!=i2+istep); } } } /************************************************************************* Unpacking matrix P which reduces matrix A to bidiagonal form. The subroutine returns transposed matrix P. Input parameters: QP - matrices Q and P in compact form. Output of ToBidiagonal subroutine. M - number of rows in matrix A. N - number of columns in matrix A. TAUP - scalar factors which are used to form P. Output of ToBidiagonal subroutine. PTRows - required number of rows of matrix P^T. N >= PTRows >= 0. Output parameters: PT - first PTRows columns of matrix P^T Array[0..PTRows-1, 0..N-1] If PTRows=0, the array is not modified. -- ALGLIB -- Copyright 2005-2007 by Bochkanov Sergey *************************************************************************/ void rmatrixbdunpackpt(const ap::real_2d_array& qp, int m, int n, const ap::real_1d_array& taup, int ptrows, ap::real_2d_array& pt) { int i; int j; #ifndef NO_AP_ASSERT ap::ap_error::make_assertion(ptrows<=n, "RMatrixBDUnpackPT: PTRows>N!"); ap::ap_error::make_assertion(ptrows>=0, "RMatrixBDUnpackPT: PTRows<0!"); #endif if( m==0||n==0||ptrows==0 ) { return; } // // prepare PT // pt.setbounds(0, ptrows-1, 0, n-1); for(i = 0; i <= ptrows-1; i++) { for(j = 0; j <= n-1; j++) { if( i==j ) { pt(i,j) = 1; } else { pt(i,j) = 0; } } } // // Calculate // rmatrixbdmultiplybyp(qp, m, n, taup, pt, ptrows, n, true, true); } /************************************************************************* Multiplication by matrix P which reduces matrix A to bidiagonal form. The algorithm allows pre- or post-multiply by P or P'. Input parameters: QP - matrices Q and P in compact form. Output of RMatrixBD subroutine. M - number of rows in matrix A. N - number of columns in matrix A. TAUP - scalar factors which are used to form P. Output of RMatrixBD subroutine. Z - multiplied matrix. Array whose indexes range within [0..ZRows-1,0..ZColumns-1]. ZRows - number of rows in matrix Z. If FromTheRight=False, ZRows=N, otherwise ZRows can be arbitrary. ZColumns - number of columns in matrix Z. If FromTheRight=True, ZColumns=N, otherwise ZColumns can be arbitrary. FromTheRight - pre- or post-multiply. DoTranspose - multiply by P or P'. Output parameters: Z - product of Z and P. Array whose indexes range within [0..ZRows-1,0..ZColumns-1]. If ZRows=0 or ZColumns=0, the array is not modified. -- ALGLIB -- Copyright 2005-2007 by Bochkanov Sergey *************************************************************************/ void rmatrixbdmultiplybyp(const ap::real_2d_array& qp, int m, int n, const ap::real_1d_array& taup, ap::real_2d_array& z, int zrows, int zcolumns, bool fromtheright, bool dotranspose) { int i; ap::real_1d_array v; ap::real_1d_array work; int mx; int i1; int i2; int istep; if( m<=0||n<=0||zrows<=0||zcolumns<=0 ) { return; } #ifndef NO_AP_ASSERT ap::ap_error::make_assertion((fromtheright&&zcolumns==n)||(!fromtheright&&zrows==n), "RMatrixBDMultiplyByP: incorrect Z size!"); #endif // // init // mx = ap::maxint(m, n); mx = ap::maxint(mx, zrows); mx = ap::maxint(mx, zcolumns); v.setbounds(0, mx); work.setbounds(0, mx); v.setbounds(0, mx); work.setbounds(0, mx); if( m>=n ) { // // setup // if( fromtheright ) { i1 = n-2; i2 = 0; istep = -1; } else { i1 = 0; i2 = n-2; istep = +1; } if( !dotranspose ) { i = i1; i1 = i2; i2 = i; istep = -istep; } // // Process // if( n-1>0 ) { i = i1; do { ap::vmove(&v(1), &qp(i, i+1), ap::vlen(1,n-1-i)); v(1) = 1; if( fromtheright ) { applyreflectionfromtheright(z, taup(i), v, 0, zrows-1, i+1, n-1, work); } else { applyreflectionfromtheleft(z, taup(i), v, i+1, n-1, 0, zcolumns-1, work); } i = i+istep; } while(i!=i2+istep); } } else { // // setup // if( fromtheright ) { i1 = m-1; i2 = 0; istep = -1; } else { i1 = 0; i2 = m-1; istep = +1; } if( !dotranspose ) { i = i1; i1 = i2; i2 = i; istep = -istep; } // // Process // i = i1; do { ap::vmove(&v(1), &qp(i, i), ap::vlen(1,n-i)); v(1) = 1; if( fromtheright ) { applyreflectionfromtheright(z, taup(i), v, 0, zrows-1, i, n-1, work); } else { applyreflectionfromtheleft(z, taup(i), v, i, n-1, 0, zcolumns-1, work); } i = i+istep; } while(i!=i2+istep); } } /************************************************************************* Unpacking of the main and secondary diagonals of bidiagonal decomposition of matrix A. Input parameters: B - output of RMatrixBD subroutine. M - number of rows in matrix B. N - number of columns in matrix B. Output parameters: IsUpper - True, if the matrix is upper bidiagonal. otherwise IsUpper is False. D - the main diagonal. Array whose index ranges within [0..Min(M,N)-1]. E - the secondary diagonal (upper or lower, depending on the value of IsUpper). Array index ranges within [0..Min(M,N)-1], the last element is not used. -- ALGLIB -- Copyright 2005-2007 by Bochkanov Sergey *************************************************************************/ void rmatrixbdunpackdiagonals(const ap::real_2d_array& b, int m, int n, bool& isupper, ap::real_1d_array& d, ap::real_1d_array& e) { int i; isupper = m>=n; if( m<=0||n<=0 ) { return; } if( isupper ) { d.setbounds(0, n-1); e.setbounds(0, n-1); for(i = 0; i <= n-2; i++) { d(i) = b(i,i); e(i) = b(i,i+1); } d(n-1) = b(n-1,n-1); } else { d.setbounds(0, m-1); e.setbounds(0, m-1); for(i = 0; i <= m-2; i++) { d(i) = b(i,i); e(i) = b(i+1,i); } d(m-1) = b(m-1,m-1); } } cmtk-3.3.1/libs/Numerics/bidiagonal.h000066400000000000000000000270421276303427400174510ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2010 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 2373 $ // // $LastChangedDate: 2010-09-29 11:17:31 -0700 (Wed, 29 Sep 2010) $ // // $LastChangedBy: torstenrohlfing $ // */ /************************************************************************* Copyright (c) 1992-2007 The University of Tennessee. All rights reserved. Contributors: * Sergey Bochkanov (ALGLIB project). Translation from FORTRAN to pseudocode. See subroutines comments for additional copyrights. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer listed in this license in the documentation and/or other materials provided with the distribution. - Neither the name of the copyright holders nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *************************************************************************/ #ifndef _bidiagonal_h #define _bidiagonal_h #include "ap.h" #include "reflections.h" /************************************************************************* Reduction of a rectangular matrix to bidiagonal form The algorithm reduces the rectangular matrix A to bidiagonal form by orthogonal transformations P and Q: A = Q*B*P. Input parameters: A - source matrix. array[0..M-1, 0..N-1] M - number of rows in matrix A. N - number of columns in matrix A. Output parameters: A - matrices Q, B, P in compact form (see below). TauQ - scalar factors which are used to form matrix Q. TauP - scalar factors which are used to form matrix P. The main diagonal and one of the secondary diagonals of matrix A are replaced with bidiagonal matrix B. Other elements contain elementary reflections which form MxM matrix Q and NxN matrix P, respectively. If M>=N, B is the upper bidiagonal MxN matrix and is stored in the corresponding elements of matrix A. Matrix Q is represented as a product of elementary reflections Q = H(0)*H(1)*...*H(n-1), where H(i) = 1-tau*v*v'. Here tau is a scalar which is stored in TauQ[i], and vector v has the following structure: v(0:i-1)=0, v(i)=1, v(i+1:m-1) is stored in elements A(i+1:m-1,i). Matrix P is as follows: P = G(0)*G(1)*...*G(n-2), where G(i) = 1 - tau*u*u'. Tau is stored in TauP[i], u(0:i)=0, u(i+1)=1, u(i+2:n-1) is stored in elements A(i,i+2:n-1). If M n): m=5, n=6 (m < n): ( d e u1 u1 u1 ) ( d u1 u1 u1 u1 u1 ) ( v1 d e u2 u2 ) ( e d u2 u2 u2 u2 ) ( v1 v2 d e u3 ) ( v1 e d u3 u3 u3 ) ( v1 v2 v3 d e ) ( v1 v2 e d u4 u4 ) ( v1 v2 v3 v4 d ) ( v1 v2 v3 e d u5 ) ( v1 v2 v3 v4 v5 ) Here vi and ui are vectors which form H(i) and G(i), and d and e - are the diagonal and off-diagonal elements of matrix B. *************************************************************************/ void rmatrixbd(ap::real_2d_array& a, int m, int n, ap::real_1d_array& tauq, ap::real_1d_array& taup); /************************************************************************* Unpacking matrix Q which reduces a matrix to bidiagonal form. Input parameters: QP - matrices Q and P in compact form. Output of ToBidiagonal subroutine. M - number of rows in matrix A. N - number of columns in matrix A. TAUQ - scalar factors which are used to form Q. Output of ToBidiagonal subroutine. QColumns - required number of columns in matrix Q. M>=QColumns>=0. Output parameters: Q - first QColumns columns of matrix Q. Array[0..M-1, 0..QColumns-1] If QColumns=0, the array is not modified. -- ALGLIB -- Copyright 2005 by Bochkanov Sergey *************************************************************************/ void rmatrixbdunpackq(const ap::real_2d_array& qp, int m, int n, const ap::real_1d_array& tauq, int qcolumns, ap::real_2d_array& q); /************************************************************************* Multiplication by matrix Q which reduces matrix A to bidiagonal form. The algorithm allows pre- or post-multiply by Q or Q'. Input parameters: QP - matrices Q and P in compact form. Output of ToBidiagonal subroutine. M - number of rows in matrix A. N - number of columns in matrix A. TAUQ - scalar factors which are used to form Q. Output of ToBidiagonal subroutine. Z - multiplied matrix. array[0..ZRows-1,0..ZColumns-1] ZRows - number of rows in matrix Z. If FromTheRight=False, ZRows=M, otherwise ZRows can be arbitrary. ZColumns - number of columns in matrix Z. If FromTheRight=True, ZColumns=M, otherwise ZColumns can be arbitrary. FromTheRight - pre- or post-multiply. DoTranspose - multiply by Q or Q'. Output parameters: Z - product of Z and Q. Array[0..ZRows-1,0..ZColumns-1] If ZRows=0 or ZColumns=0, the array is not modified. -- ALGLIB -- Copyright 2005 by Bochkanov Sergey *************************************************************************/ void rmatrixbdmultiplybyq(const ap::real_2d_array& qp, int m, int n, const ap::real_1d_array& tauq, ap::real_2d_array& z, int zrows, int zcolumns, bool fromtheright, bool dotranspose); /************************************************************************* Unpacking matrix P which reduces matrix A to bidiagonal form. The subroutine returns transposed matrix P. Input parameters: QP - matrices Q and P in compact form. Output of ToBidiagonal subroutine. M - number of rows in matrix A. N - number of columns in matrix A. TAUP - scalar factors which are used to form P. Output of ToBidiagonal subroutine. PTRows - required number of rows of matrix P^T. N >= PTRows >= 0. Output parameters: PT - first PTRows columns of matrix P^T Array[0..PTRows-1, 0..N-1] If PTRows=0, the array is not modified. -- ALGLIB -- Copyright 2005-2007 by Bochkanov Sergey *************************************************************************/ void rmatrixbdunpackpt(const ap::real_2d_array& qp, int m, int n, const ap::real_1d_array& taup, int ptrows, ap::real_2d_array& pt); /************************************************************************* Multiplication by matrix P which reduces matrix A to bidiagonal form. The algorithm allows pre- or post-multiply by P or P'. Input parameters: QP - matrices Q and P in compact form. Output of RMatrixBD subroutine. M - number of rows in matrix A. N - number of columns in matrix A. TAUP - scalar factors which are used to form P. Output of RMatrixBD subroutine. Z - multiplied matrix. Array whose indexes range within [0..ZRows-1,0..ZColumns-1]. ZRows - number of rows in matrix Z. If FromTheRight=False, ZRows=N, otherwise ZRows can be arbitrary. ZColumns - number of columns in matrix Z. If FromTheRight=True, ZColumns=N, otherwise ZColumns can be arbitrary. FromTheRight - pre- or post-multiply. DoTranspose - multiply by P or P'. Output parameters: Z - product of Z and P. Array whose indexes range within [0..ZRows-1,0..ZColumns-1]. If ZRows=0 or ZColumns=0, the array is not modified. -- ALGLIB -- Copyright 2005-2007 by Bochkanov Sergey *************************************************************************/ void rmatrixbdmultiplybyp(const ap::real_2d_array& qp, int m, int n, const ap::real_1d_array& taup, ap::real_2d_array& z, int zrows, int zcolumns, bool fromtheright, bool dotranspose); /************************************************************************* Unpacking of the main and secondary diagonals of bidiagonal decomposition of matrix A. Input parameters: B - output of RMatrixBD subroutine. M - number of rows in matrix B. N - number of columns in matrix B. Output parameters: IsUpper - True, if the matrix is upper bidiagonal. otherwise IsUpper is False. D - the main diagonal. Array whose index ranges within [0..Min(M,N)-1]. E - the secondary diagonal (upper or lower, depending on the value of IsUpper). Array index ranges within [0..Min(M,N)-1], the last element is not used. -- ALGLIB -- Copyright 2005-2007 by Bochkanov Sergey *************************************************************************/ void rmatrixbdunpackdiagonals(const ap::real_2d_array& b, int m, int n, bool& isupper, ap::real_1d_array& d, ap::real_1d_array& e); #endif cmtk-3.3.1/libs/Numerics/blas.cxx000066400000000000000000000310041276303427400166450ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2011, 2013 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4973 $ // // $LastChangedDate: 2013-10-11 13:31:38 -0700 (Fri, 11 Oct 2013) $ // // $LastChangedBy: torstenrohlfing $ // */ /************************************************************************* Copyright (c) 2005-2007, Sergey Bochkanov (ALGLIB project). Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer listed in this license in the documentation and/or other materials provided with the distribution. - Neither the name of the copyright holders nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *************************************************************************/ #include "blas.h" int vectoridxabsmax(const ap::real_1d_array& x, int i1, int i2) { int result; int i; result = i1; for(i = i1+1; i <= i2; i++) { if( fabs(x(i))>fabs(x(result)) ) { result = i; } } return result; } int columnidxabsmax(const ap::real_2d_array& x, int i1, int i2, int j) { int result; int i; result = i1; for(i = i1+1; i <= i2; i++) { if( fabs(x(i,j))>fabs(x(result,j)) ) { result = i; } } return result; } ap::real_value_type upperhessenberg1norm(const ap::real_2d_array& a, int i1, int i2, int j1, int j2, ap::real_1d_array& work) { ap::real_value_type result; int i; int j; #ifndef NO_AP_ASSERT ap::ap_error::make_assertion(i2-i1==j2-j1, "UpperHessenberg1Norm: I2-I1<>J2-J1!"); #endif for(j = j1; j <= j2; j++) { work(j) = 0; } for(i = i1; i <= i2; i++) { for(j = ap::maxint(j1, j1+i-i1-1); j <= j2; j++) { work(j) = work(j)+fabs(a(i,j)); } } result = 0; for(j = j1; j <= j2; j++) { result = ap::maxreal(result, work(j)); } return result; } void copymatrix(const ap::real_2d_array& a, int is1, int is2, int js1, int js2, ap::real_2d_array& b, int id1, int id2, int jd1, int jd2) { int isrc; int idst; if( is1>is2||js1>js2 ) { return; } #ifndef NO_AP_ASSERT ap::ap_error::make_assertion(is2-is1==id2-id1, "CopyMatrix: different sizes!"); ap::ap_error::make_assertion(js2-js1==jd2-jd1, "CopyMatrix: different sizes!"); #endif for(isrc = is1; isrc <= is2; isrc++) { idst = isrc-is1+id1; ap::vmove(&b(idst, jd1), &a(isrc, js1), ap::vlen(jd1,jd2)); } } void inplacetranspose(ap::real_2d_array& a, int i1, int i2, int j1, int j2, ap::real_1d_array& work) { int i; int j; int ips; int jps; int l; if( i1>i2||j1>j2 ) { return; } #ifndef NO_AP_ASSERT ap::ap_error::make_assertion(i1-i2==j1-j2, "InplaceTranspose error: incorrect array size!"); #endif for(i = i1; i <= i2-1; i++) { j = j1+i-i1; ips = i+1; jps = j1+ips-i1; l = i2-i; ap::vmove(work.getvector(1, l), a.getcolumn(j, ips, i2)); ap::vmove(a.getcolumn(j, ips, i2), a.getrow(i, jps, j2)); ap::vmove(&a(i, jps), &work(1), ap::vlen(jps,j2)); } } void copyandtranspose(const ap::real_2d_array& a, int is1, int is2, int js1, int js2, ap::real_2d_array& b, int id1, int id2, int jd1, int jd2) { int isrc; int jdst; if( is1>is2||js1>js2 ) { return; } #ifndef NO_AP_ASSERT ap::ap_error::make_assertion(is2-is1==jd2-jd1, "CopyAndTranspose: different sizes!"); ap::ap_error::make_assertion(js2-js1==id2-id1, "CopyAndTranspose: different sizes!"); #endif for(isrc = is1; isrc <= is2; isrc++) { jdst = isrc-is1+jd1; ap::vmove(b.getcolumn(jdst, id1, id2), a.getrow(isrc, js1, js2)); } } void matrixvectormultiply(const ap::real_2d_array& a, int i1, int i2, int j1, int j2, bool trans, const ap::real_1d_array& x, int ix1, int ix2, ap::real_value_type alpha, ap::real_1d_array& y, int iy1, int iy2, ap::real_value_type beta) { int i; ap::real_value_type v; if( !trans ) { // // y := alpha*A*x + beta*y; // if( i1>i2||j1>j2 ) { return; } #ifndef NO_AP_ASSERT ap::ap_error::make_assertion(j2-j1==ix2-ix1, "MatrixVectorMultiply: A and X dont match!"); ap::ap_error::make_assertion(i2-i1==iy2-iy1, "MatrixVectorMultiply: A and Y dont match!"); #endif // // beta*y // if( beta==0 ) { for(i = iy1; i <= iy2; i++) { y(i) = 0; } } else { ap::vmul(&y(iy1), ap::vlen(iy1,iy2), beta); } // // alpha*A*x // for(i = i1; i <= i2; i++) { v = ap::vdotproduct(&a(i, j1), &x(ix1), ap::vlen(j1,j2)); y(iy1+i-i1) = y(iy1+i-i1)+alpha*v; } } else { // // y := alpha*A'*x + beta*y; // if( i1>i2||j1>j2 ) { return; } #ifndef NO_AP_ASSERT ap::ap_error::make_assertion(i2-i1==ix2-ix1, "MatrixVectorMultiply: A and X dont match!"); ap::ap_error::make_assertion(j2-j1==iy2-iy1, "MatrixVectorMultiply: A and Y dont match!"); #endif // // beta*y // if( beta==0 ) { for(i = iy1; i <= iy2; i++) { y(i) = 0; } } else { ap::vmul(&y(iy1), ap::vlen(iy1,iy2), beta); } // // alpha*A'*x // for(i = i1; i <= i2; i++) { v = alpha*x(ix1+i-i1); ap::vadd(&y(iy1), &a(i, j1), ap::vlen(iy1,iy2), v); } } } ap::real_value_type pythag2(ap::real_value_type x, ap::real_value_type y) { ap::real_value_type result; ap::real_value_type w; ap::real_value_type xabs; ap::real_value_type yabs; ap::real_value_type z; xabs = fabs(x); yabs = fabs(y); w = ap::maxreal(xabs, yabs); z = ap::minreal(xabs, yabs); if( z==0 ) { result = w; } else { result = w*sqrt(1+ap::sqr(z/w)); } return result; } void matrixmatrixmultiply(const ap::real_2d_array& a, int ai1, int ai2, int aj1, int aj2, bool transa, const ap::real_2d_array& b, int bi1, int bi2, int bj1, int bj2, bool transb, ap::real_value_type alpha, ap::real_2d_array& c, int ci1, int ci2, int cj1, int cj2, ap::real_value_type beta, ap::real_1d_array& work) { int arows; int acols; int brows; int bcols; int crows; int i; int j; int k = 0; int l; int r; ap::real_value_type v; // // Setup // if( !transa ) { arows = ai2-ai1+1; acols = aj2-aj1+1; } else { arows = aj2-aj1+1; acols = ai2-ai1+1; } if( !transb ) { brows = bi2-bi1+1; bcols = bj2-bj1+1; } else { brows = bj2-bj1+1; bcols = bi2-bi1+1; } #ifndef NO_AP_ASSERT ap::ap_error::make_assertion(acols==brows, "MatrixMatrixMultiply: incorrect matrix sizes!"); #endif if( arows<=0||acols<=0||brows<=0||bcols<=0 ) { return; } crows = arows; // // Test WORK // i = ap::maxint(arows, acols); i = ap::maxint(brows, i); i = ap::maxint(i, bcols); work(1) = 0; work(i) = 0; // // Prepare C // if( beta==0 ) { for(i = ci1; i <= ci2; i++) { for(j = cj1; j <= cj2; j++) { c(i,j) = 0; } } } else { for(i = ci1; i <= ci2; i++) { ap::vmul(&c(i, cj1), ap::vlen(cj1,cj2), beta); } } // // A*B // if( !transa&&!transb ) { for(l = ai1; l <= ai2; l++) { for(r = bi1; r <= bi2; r++) { v = alpha*a(l,aj1+r-bi1); k = ci1+l-ai1; ap::vadd(&c(k, cj1), &b(r, bj1), ap::vlen(cj1,cj2), v); } } return; } // // A*B' // if( !transa&&transb ) { if( arows*acols. // // $Revision: 4861 $ // // $LastChangedDate: 2013-09-23 16:41:17 -0700 (Mon, 23 Sep 2013) $ // // $LastChangedBy: torstenrohlfing $ // */ /************************************************************************* Copyright (c) 2005-2007, Sergey Bochkanov (ALGLIB project). Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer listed in this license in the documentation and/or other materials provided with the distribution. - Neither the name of the copyright holders nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *************************************************************************/ #ifndef _blas_h #define _blas_h #include "ap.h" int vectoridxabsmax(const ap::real_1d_array& x, int i1, int i2); int columnidxabsmax(const ap::real_2d_array& x, int i1, int i2, int j); ap::real_value_type upperhessenberg1norm(const ap::real_2d_array& a, int i1, int i2, int j1, int j2, ap::real_1d_array& work); void copymatrix(const ap::real_2d_array& a, int is1, int is2, int js1, int js2, ap::real_2d_array& b, int id1, int id2, int jd1, int jd2); void inplacetranspose(ap::real_2d_array& a, int i1, int i2, int j1, int j2, ap::real_1d_array& work); void copyandtranspose(const ap::real_2d_array& a, int is1, int is2, int js1, int js2, ap::real_2d_array& b, int id1, int id2, int jd1, int jd2); void matrixvectormultiply(const ap::real_2d_array& a, int i1, int i2, int j1, int j2, bool trans, const ap::real_1d_array& x, int ix1, int ix2, ap::real_value_type alpha, ap::real_1d_array& y, int iy1, int iy2, ap::real_value_type beta); ap::real_value_type pythag2(ap::real_value_type x, ap::real_value_type y); void matrixmatrixmultiply(const ap::real_2d_array& a, int ai1, int ai2, int aj1, int aj2, bool transa, const ap::real_2d_array& b, int bi1, int bi2, int bj1, int bj2, bool transb, ap::real_value_type alpha, ap::real_2d_array& c, int ci1, int ci2, int cj1, int cj2, ap::real_value_type beta, ap::real_1d_array& work); #endif cmtk-3.3.1/libs/Numerics/cholesky.cxx000066400000000000000000000151151276303427400175520ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2010, 2013 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4973 $ // // $LastChangedDate: 2013-10-11 13:31:38 -0700 (Fri, 11 Oct 2013) $ // // $LastChangedBy: torstenrohlfing $ // */ /************************************************************************* Copyright (c) 1992-2007 The University of Tennessee. All rights reserved. Contributors: * Sergey Bochkanov (ALGLIB project). Translation from FORTRAN to pseudocode. See subroutines comments for additional copyrights. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer listed in this license in the documentation and/or other materials provided with the distribution. - Neither the name of the copyright holders nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *************************************************************************/ #include "cholesky.h" /************************************************************************* Cholesky decomposition The algorithm computes Cholesky decomposition of a symmetric positive-definite matrix. The result of an algorithm is a representation of matrix A as A = U'*U or A = L*L'. Input parameters: A - upper or lower triangle of a factorized matrix. array with elements [0..N-1, 0..N-1]. N - size of matrix A. IsUpper - if IsUpper=True, then A contains an upper triangle of a symmetric matrix, otherwise A contains a lower one. Output parameters: A - the result of factorization. If IsUpper=True, then the upper triangle contains matrix U, so that A = U'*U, and the elements below the main diagonal are not modified. Similarly, if IsUpper = False. Result: If the matrix is positive-definite, the function returns True. Otherwise, the function returns False. This means that the factorization could not be carried out. -- LAPACK routine (version 3.0) -- Univ. of Tennessee, Univ. of California Berkeley, NAG Ltd., Courant Institute, Argonne National Lab, and Rice University February 29, 1992 *************************************************************************/ bool spdmatrixcholesky(ap::real_2d_array& a, int n, bool isupper) { bool result; int i; int j; ap::real_value_type ajj; ap::real_value_type v; // // Test the input parameters. // #ifndef NO_AP_ASSERT ap::ap_error::make_assertion(n>=0, "Error in SMatrixCholesky: incorrect function arguments"); #endif // // Quick return if possible // result = true; if( n<=0 ) { return result; } if( isupper ) { // // Compute the Cholesky factorization A = U'*U. // for(j = 0; j <= n-1; j++) { // // Compute U(J,J) and test for non-positive-definiteness. // v = ap::vdotproduct(a.getcolumn(j, 0, j-1), a.getcolumn(j, 0, j-1)); ajj = a(j,j)-v; if( ajj<=0 ) { result = false; return result; } ajj = sqrt(ajj); a(j,j) = ajj; // // Compute elements J+1:N of row J. // if( j. // // $Revision: 2374 $ // // $LastChangedDate: 2010-09-29 11:21:33 -0700 (Wed, 29 Sep 2010) $ // // $LastChangedBy: torstenrohlfing $ // */ /************************************************************************* Copyright (c) 1992-2007 The University of Tennessee. All rights reserved. Contributors: * Sergey Bochkanov (ALGLIB project). Translation from FORTRAN to pseudocode. See subroutines comments for additional copyrights. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer listed in this license in the documentation and/or other materials provided with the distribution. - Neither the name of the copyright holders nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *************************************************************************/ #ifndef _cholesky_h #define _cholesky_h #include "ap.h" /************************************************************************* Cholesky decomposition The algorithm computes Cholesky decomposition of a symmetric positive-definite matrix. The result of an algorithm is a representation of matrix A as A = U'*U or A = L*L'. Input parameters: A - upper or lower triangle of a factorized matrix. array with elements [0..N-1, 0..N-1]. N - size of matrix A. IsUpper - if IsUpper=True, then A contains an upper triangle of a symmetric matrix, otherwise A contains a lower one. Output parameters: A - the result of factorization. If IsUpper=True, then the upper triangle contains matrix U, so that A = U'*U, and the elements below the main diagonal are not modified. Similarly, if IsUpper = False. Result: If the matrix is positive-definite, the function returns True. Otherwise, the function returns False. This means that the factorization could not be carried out. -- LAPACK routine (version 3.0) -- Univ. of Tennessee, Univ. of California Berkeley, NAG Ltd., Courant Institute, Argonne National Lab, and Rice University February 29, 1992 *************************************************************************/ bool spdmatrixcholesky(ap::real_2d_array& a, int n, bool isupper); #endif cmtk-3.3.1/libs/Numerics/det.cxx000066400000000000000000000104241276303427400165030ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2010 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 2376 $ // // $LastChangedDate: 2010-09-29 11:31:12 -0700 (Wed, 29 Sep 2010) $ // // $LastChangedBy: torstenrohlfing $ // */ /************************************************************************* Copyright (c) 2005-2007, Sergey Bochkanov (ALGLIB project). Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer listed in this license in the documentation and/or other materials provided with the distribution. - Neither the name of the copyright holders nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *************************************************************************/ #include "det.h" /************************************************************************* Determinant calculation of the matrix given by its LU decomposition. Input parameters: A - LU decomposition of the matrix (output of RMatrixLU subroutine). Pivots - table of permutations which were made during the LU decomposition. Output of RMatrixLU subroutine. N - size of matrix A. Result: matrix determinant. -- ALGLIB -- Copyright 2005 by Bochkanov Sergey *************************************************************************/ ap::real_value_type rmatrixludet(const ap::real_2d_array& a, const ap::integer_1d_array& pivots, int n) { ap::real_value_type result; int i; int s; result = 1; s = 1; for(i = 0; i <= n-1; i++) { result = result*a(i,i); if( pivots(i)!=i ) { s = -s; } } result = result*s; return result; } /************************************************************************* Calculation of the determinant of a general matrix Input parameters: A - matrix, array[0..N-1, 0..N-1] N - size of matrix A. Result: determinant of matrix A. -- ALGLIB -- Copyright 2005 by Bochkanov Sergey *************************************************************************/ ap::real_value_type rmatrixdet(ap::real_2d_array a, int n) { ap::real_value_type result; ap::integer_1d_array pivots; rmatrixlu(a, n, n, pivots); result = rmatrixludet(a, pivots, n); return result; } cmtk-3.3.1/libs/Numerics/det.h000066400000000000000000000076361276303427400161430ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2010 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 2376 $ // // $LastChangedDate: 2010-09-29 11:31:12 -0700 (Wed, 29 Sep 2010) $ // // $LastChangedBy: torstenrohlfing $ // */ /************************************************************************* Copyright (c) 2005-2007, Sergey Bochkanov (ALGLIB project). Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer listed in this license in the documentation and/or other materials provided with the distribution. - Neither the name of the copyright holders nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *************************************************************************/ #ifndef _det_h #define _det_h #include "ap.h" #include "lu.h" /************************************************************************* Determinant calculation of the matrix given by its LU decomposition. Input parameters: A - LU decomposition of the matrix (output of RMatrixLU subroutine). Pivots - table of permutations which were made during the LU decomposition. Output of RMatrixLU subroutine. N - size of matrix A. Result: matrix determinant. -- ALGLIB -- Copyright 2005 by Bochkanov Sergey *************************************************************************/ ap::real_value_type rmatrixludet(const ap::real_2d_array& a, const ap::integer_1d_array& pivots, int n); /************************************************************************* Calculation of the determinant of a general matrix Input parameters: A - matrix, array[0..N-1, 0..N-1] N - size of matrix A. Result: determinant of matrix A. -- ALGLIB -- Copyright 2005 by Bochkanov Sergey *************************************************************************/ ap::real_value_type rmatrixdet(ap::real_2d_array a, int n); #endif cmtk-3.3.1/libs/Numerics/gammaf.cxx000066400000000000000000000240751276303427400171660ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2010, 2013 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4812 $ // // $LastChangedDate: 2013-09-09 14:26:32 -0700 (Mon, 09 Sep 2013) $ // // $LastChangedBy: torstenrohlfing $ // */ /************************************************************************* Cephes Math Library Release 2.8: June, 2000 Copyright by Stephen L. Moshier Contributors: * Sergey Bochkanov (ALGLIB project). Translation from C to pseudocode. See subroutines comments for additional copyrights. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer listed in this license in the documentation and/or other materials provided with the distribution. - Neither the name of the copyright holders nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *************************************************************************/ #include "gammaf.h" namespace alglib { ap::real_value_type gammastirf(ap::real_value_type x); /************************************************************************* Gamma function Input parameters: X - argument Domain: 0 < X < 171.6 -170 < X < 0, X is not an integer. Relative error: arithmetic domain # trials peak rms IEEE -170,-33 20000 2.3e-15 3.3e-16 IEEE -33, 33 20000 9.4e-16 2.2e-16 IEEE 33, 171.6 20000 2.3e-15 3.2e-16 Cephes Math Library Release 2.8: June, 2000 Original copyright 1984, 1987, 1989, 1992, 2000 by Stephen L. Moshier Translated to AlgoPascal by Bochkanov Sergey (2005, 2006, 2007). *************************************************************************/ ap::real_value_type gamma(ap::real_value_type x) { ap::real_value_type result; ap::real_value_type p; ap::real_value_type pp; ap::real_value_type q; ap::real_value_type qq; ap::real_value_type z; int i; ap::real_value_type sgngam; sgngam = 1; q = fabs(x); if( q>33.0 ) { if( x<0.0 ) { p = ap::ifloor(q); i = ap::round(p); if( i%2==0 ) { sgngam = -1; } z = q-p; if( z>0.5 ) { p = p+1; z = q-p; } z = q*sin(ap::pi()*z); z = fabs(z); z = ap::pi()/(z*gammastirf(q)); } else { z = gammastirf(x); } result = sgngam*z; return result; } z = 1; while(x>=3) { x = x-1; z = z*x; } while(x<0) { if( x>-0.000000001 ) { result = z/((1+0.5772156649015329*x)*x); return result; } z = z/x; x = x+1; } while(x<2) { if( x<0.000000001 ) { result = z/((1+0.5772156649015329*x)*x); return result; } z = z/x; x = x+1.0; } if( x==2 ) { result = z; return result; } x = x-2.0; pp = 1.60119522476751861407E-4; pp = 1.19135147006586384913E-3+x*pp; pp = 1.04213797561761569935E-2+x*pp; pp = 4.76367800457137231464E-2+x*pp; pp = 2.07448227648435975150E-1+x*pp; pp = 4.94214826801497100753E-1+x*pp; pp = 9.99999999999999996796E-1+x*pp; qq = -2.31581873324120129819E-5; qq = 5.39605580493303397842E-4+x*qq; qq = -4.45641913851797240494E-3+x*qq; qq = 1.18139785222060435552E-2+x*qq; qq = 3.58236398605498653373E-2+x*qq; qq = -2.34591795718243348568E-1+x*qq; qq = 7.14304917030273074085E-2+x*qq; qq = 1.00000000000000000320+x*qq; result = z*pp/qq; return result; } /************************************************************************* Natural logarithm of gamma function Input parameters: X - argument Result: logarithm of the absolute value of the Gamma(X). Output parameters: SgnGam - sign(Gamma(X)) Domain: 0 < X < 2.55e305 -2.55e305 < X < 0, X is not an integer. ACCURACY: arithmetic domain # trials peak rms IEEE 0, 3 28000 5.4e-16 1.1e-16 IEEE 2.718, 2.556e305 40000 3.5e-16 8.3e-17 The error criterion was relative when the function magnitude was greater than one but absolute when it was less than one. The following test used the relative error criterion, though at certain points the relative error could be much higher than indicated. IEEE -200, -4 10000 4.8e-16 1.3e-16 Cephes Math Library Release 2.8: June, 2000 Copyright 1984, 1987, 1989, 1992, 2000 by Stephen L. Moshier Translated to AlgoPascal by Bochkanov Sergey (2005, 2006, 2007). *************************************************************************/ ap::real_value_type lngamma(ap::real_value_type x, ap::real_value_type& sgngam) { ap::real_value_type result; ap::real_value_type a; ap::real_value_type b; ap::real_value_type c; ap::real_value_type p; ap::real_value_type q; ap::real_value_type u; ap::real_value_type w; ap::real_value_type z; int i; ap::real_value_type logpi; ap::real_value_type ls2pi; ap::real_value_type tmp; sgngam = 1; logpi = 1.14472988584940017414; ls2pi = 0.91893853320467274178; if( x<-34.0 ) { q = -x; w = lngamma(q, tmp); p = ap::ifloor(q); i = ap::round(p); if( i%2==0 ) { sgngam = -1; } else { sgngam = 1; } z = q-p; if( z>0.5 ) { p = p+1; z = p-q; } z = q*sin(ap::pi()*z); result = logpi-log(z)-w; return result; } if( x<13 ) { z = 1; p = 0; u = x; while(u>=3) { p = p-1; u = x+p; z = z*u; } while(u<2) { z = z/u; p = p+1; u = x+p; } if( z<0 ) { sgngam = -1; z = -z; } else { sgngam = 1; } if( u==2 ) { result = log(z); return result; } p = p-2; x = x+p; b = -1378.25152569120859100; b = -38801.6315134637840924+x*b; b = -331612.992738871184744+x*b; b = -1162370.97492762307383+x*b; b = -1721737.00820839662146+x*b; b = -853555.664245765465627+x*b; c = 1; c = -351.815701436523470549+x*c; c = -17064.2106651881159223+x*c; c = -220528.590553854454839+x*c; c = -1139334.44367982507207+x*c; c = -2532523.07177582951285+x*c; c = -2018891.41433532773231+x*c; p = x*b/c; result = log(z)+p; return result; } q = (x-0.5)*log(x)-x+ls2pi; if( x>100000000 ) { result = q; return result; } p = 1/(x*x); if( x>=1000.0 ) { q = q+((7.9365079365079365079365*0.0001*p-2.7777777777777777777778*0.001)*p+0.0833333333333333333333)/x; } else { a = 8.11614167470508450300*0.0001; a = -5.95061904284301438324*0.0001+p*a; a = 7.93650340457716943945*0.0001+p*a; a = -2.77777777730099687205*0.001+p*a; a = 8.33333333333331927722*0.01+p*a; q = q+a/x; } result = q; return result; } ap::real_value_type gammastirf(ap::real_value_type x) { ap::real_value_type result; ap::real_value_type y; ap::real_value_type w; ap::real_value_type v; ap::real_value_type stir; w = 1/x; stir = 7.87311395793093628397E-4; stir = -2.29549961613378126380E-4+w*stir; stir = -2.68132617805781232825E-3+w*stir; stir = 3.47222221605458667310E-3+w*stir; stir = 8.33333333333482257126E-2+w*stir; w = 1+w*stir; y = exp(x); if( x>143.01608 ) { v = pow((ap::real_value_type)x, (ap::real_value_type)(0.5*x-0.25)); y = v*(v/y); } else { y = pow((ap::real_value_type)x, (ap::real_value_type)(x-0.5))/y; } result = 2.50662827463100050242*y*w; return result; } } // namespace alglib cmtk-3.3.1/libs/Numerics/gammaf.h000066400000000000000000000115431276303427400166070ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2010 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 1871 $ // // $LastChangedDate: 2010-06-22 12:57:14 -0700 (Tue, 22 Jun 2010) $ // // $LastChangedBy: torstenrohlfing $ // */ /************************************************************************* Cephes Math Library Release 2.8: June, 2000 Copyright by Stephen L. Moshier Contributors: * Sergey Bochkanov (ALGLIB project). Translation from C to pseudocode. See subroutines comments for additional copyrights. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer listed in this license in the documentation and/or other materials provided with the distribution. - Neither the name of the copyright holders nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *************************************************************************/ #ifndef _gammaf_h #define _gammaf_h #include "ap.h" namespace alglib { /************************************************************************* Gamma function Input parameters: X - argument Domain: 0 < X < 171.6 -170 < X < 0, X is not an integer. Relative error: arithmetic domain # trials peak rms IEEE -170,-33 20000 2.3e-15 3.3e-16 IEEE -33, 33 20000 9.4e-16 2.2e-16 IEEE 33, 171.6 20000 2.3e-15 3.2e-16 Cephes Math Library Release 2.8: June, 2000 Original copyright 1984, 1987, 1989, 1992, 2000 by Stephen L. Moshier Translated to AlgoPascal by Bochkanov Sergey (2005, 2006, 2007). *************************************************************************/ ap::real_value_type gamma(ap::real_value_type x); /************************************************************************* Natural logarithm of gamma function Input parameters: X - argument Result: logarithm of the absolute value of the Gamma(X). Output parameters: SgnGam - sign(Gamma(X)) Domain: 0 < X < 2.55e305 -2.55e305 < X < 0, X is not an integer. ACCURACY: arithmetic domain # trials peak rms IEEE 0, 3 28000 5.4e-16 1.1e-16 IEEE 2.718, 2.556e305 40000 3.5e-16 8.3e-17 The error criterion was relative when the function magnitude was greater than one but absolute when it was less than one. The following test used the relative error criterion, though at certain points the relative error could be much higher than indicated. IEEE -200, -4 10000 4.8e-16 1.3e-16 Cephes Math Library Release 2.8: June, 2000 Copyright 1984, 1987, 1989, 1992, 2000 by Stephen L. Moshier Translated to AlgoPascal by Bochkanov Sergey (2005, 2006, 2007). *************************************************************************/ ap::real_value_type lngamma(ap::real_value_type x, ap::real_value_type& sgngam); } // namespace alglib #endif cmtk-3.3.1/libs/Numerics/hessenberg.cxx000066400000000000000000000127101276303427400200540ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2009, 2013 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4973 $ // // $LastChangedDate: 2013-10-11 13:31:38 -0700 (Fri, 11 Oct 2013) $ // // $LastChangedBy: torstenrohlfing $ // */ /************************************************************************* Copyright (c) 1992-2007 The University of Tennessee. All rights reserved. Contributors: * Sergey Bochkanov (ALGLIB project). Translation from FORTRAN to pseudocode. See subroutines comments for additional copyrights. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer listed in this license in the documentation and/or other materials provided with the distribution. - Neither the name of the copyright holders nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *************************************************************************/ #include "hessenberg.h" /************************************************************************* Obsolete 1-based subroutine. See RMatrixHessenberg for 0-based replacement. *************************************************************************/ void toupperhessenberg(ap::real_2d_array& a, int n, ap::real_1d_array& tau) { int i; int ip1; int nmi; // ap::real_value_type aii; ap::real_value_type v; ap::real_1d_array t; ap::real_1d_array work; #ifndef NO_AP_ASSERT ap::ap_error::make_assertion(n>=0, "ToUpperHessenberg: incorrect N!"); #endif // // Quick return if possible // if( n<=1 ) { return; } tau.setbounds(1, n-1); t.setbounds(1, n); work.setbounds(1, n); for(i = 1; i <= n-1; i++) { // // Compute elementary reflector H(i) to annihilate A(i+2:ihi,i) // ip1 = i+1; nmi = n-i; ap::vmove(t.getvector(1, nmi), a.getcolumn(i, ip1, n)); generatereflection(t, nmi, v); ap::vmove(a.getcolumn(i, ip1, n), t.getvector(1, nmi)); tau(i) = v; t(1) = 1; // // Apply H(i) to A(1:ihi,i+1:ihi) from the right // applyreflectionfromtheright(a, v, t, 1, n, i+1, n, work); // // Apply H(i) to A(i+1:ihi,i+1:n) from the left // applyreflectionfromtheleft(a, v, t, i+1, n, i+1, n, work); } } /************************************************************************* Obsolete 1-based subroutine. See RMatrixHessenbergUnpackQ for 0-based replacement. *************************************************************************/ void unpackqfromupperhessenberg(const ap::real_2d_array& a, int n, const ap::real_1d_array& tau, ap::real_2d_array& q) { int i; int j; ap::real_1d_array v; ap::real_1d_array work; int ip1; int nmi; if( n==0 ) { return; } // // init // q.setbounds(1, n, 1, n); v.setbounds(1, n); work.setbounds(1, n); for(i = 1; i <= n; i++) { for(j = 1; j <= n; j++) { if( i==j ) { q(i,j) = 1; } else { q(i,j) = 0; } } } // // unpack Q // for(i = 1; i <= n-1; i++) { // // Apply H(i) // ip1 = i+1; nmi = n-i; ap::vmove(v.getvector(1, nmi), a.getcolumn(i, ip1, n)); v(1) = 1; applyreflectionfromtheright(q, tau(i), v, 1, n, i+1, n, work); } } cmtk-3.3.1/libs/Numerics/hessenberg.h000066400000000000000000000071641276303427400175100ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2009, 2013 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4918 $ // // $LastChangedDate: 2013-10-03 13:01:32 -0700 (Thu, 03 Oct 2013) $ // // $LastChangedBy: torstenrohlfing $ // */ /************************************************************************* Copyright (c) 1992-2007 The University of Tennessee. All rights reserved. Contributors: * Sergey Bochkanov (ALGLIB project). Translation from FORTRAN to pseudocode. See subroutines comments for additional copyrights. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer listed in this license in the documentation and/or other materials provided with the distribution. - Neither the name of the copyright holders nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *************************************************************************/ #ifndef _hessenberg_h #define _hessenberg_h #include "ap.h" #include "reflections.h" /************************************************************************* Obsolete 1-based subroutine. See RMatrixHessenberg for 0-based replacement. *************************************************************************/ void toupperhessenberg(ap::real_2d_array& a, int n, ap::real_1d_array& tau); /************************************************************************* Obsolete 1-based subroutine. See RMatrixHessenbergUnpackQ for 0-based replacement. *************************************************************************/ void unpackqfromupperhessenberg(const ap::real_2d_array& a, int n, const ap::real_1d_array& tau, ap::real_2d_array& q); #endif cmtk-3.3.1/libs/Numerics/hsschur.cxx000066400000000000000000001134471276303427400174170ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2011, 2013 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4973 $ // // $LastChangedDate: 2013-10-11 13:31:38 -0700 (Fri, 11 Oct 2013) $ // // $LastChangedBy: torstenrohlfing $ // */ /************************************************************************* Copyright (c) 1992-2007 The University of Tennessee. All rights reserved. Contributors: * Sergey Bochkanov (ALGLIB project). Translation from FORTRAN to pseudocode. See subroutines comments for additional copyrights. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer listed in this license in the documentation and/or other materials provided with the distribution. - Neither the name of the copyright holders nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *************************************************************************/ #include "hsschur.h" void internalauxschur(bool wantt, bool wantz, int n, int ilo, int ihi, ap::real_2d_array& h, ap::real_1d_array& wr, ap::real_1d_array& wi, int iloz, int ihiz, ap::real_2d_array& z, ap::real_1d_array& work, ap::real_1d_array& workv3, ap::real_1d_array& workc1, ap::real_1d_array& works1, int& info); void aux2x2schur(ap::real_value_type& a, ap::real_value_type& b, ap::real_value_type& c, ap::real_value_type& d, ap::real_value_type& rt1r, ap::real_value_type& rt1i, ap::real_value_type& rt2r, ap::real_value_type& rt2i, ap::real_value_type& cs, ap::real_value_type& sn); ap::real_value_type extschursign(ap::real_value_type a, ap::real_value_type b); int extschursigntoone(ap::real_value_type b); void internalschurdecomposition(ap::real_2d_array& h, int n, int tneeded, int zneeded, ap::real_1d_array& wr, ap::real_1d_array& wi, ap::real_2d_array& z, int& info) { ap::real_1d_array work; int i; int i1; int i2 = 0; int ierr; int ii; int itemp; int itn; int its; int j; int k; int l; int maxb; int nr; int ns; int nv; ap::real_value_type absw; ap::real_value_type smlnum; ap::real_value_type tau; ap::real_value_type temp; ap::real_value_type tst1; ap::real_value_type ulp; ap::real_value_type unfl; ap::real_2d_array s; ap::real_1d_array v; ap::real_1d_array vv; ap::real_1d_array workc1; ap::real_1d_array works1; ap::real_1d_array workv3; ap::real_1d_array tmpwr; ap::real_1d_array tmpwi; bool initz; bool wantt; bool wantz; ap::real_value_type cnst; bool failflag; int p1; int p2; // int p3; // int p4; ap::real_value_type vt; // // Set the order of the multi-shift QR algorithm to be used. // If you want to tune algorithm, change this values // ns = 12; maxb = 50; // // Now 2 < NS <= MAXB < NH. // maxb = ap::maxint(3, maxb); ns = ap::minint(maxb, ns); // // Initialize // cnst = 1.5; work.setbounds(1, ap::maxint(n, 1)); s.setbounds(1, ns, 1, ns); v.setbounds(1, ns+1); vv.setbounds(1, ns+1); wr.setbounds(1, ap::maxint(n, 1)); wi.setbounds(1, ap::maxint(n, 1)); workc1.setbounds(1, 1); works1.setbounds(1, 1); workv3.setbounds(1, 3); tmpwr.setbounds(1, ap::maxint(n, 1)); tmpwi.setbounds(1, ap::maxint(n, 1)); #ifndef NO_AP_ASSERT ap::ap_error::make_assertion(n>=0, "InternalSchurDecomposition: incorrect N!"); ap::ap_error::make_assertion(tneeded==0||tneeded==1, "InternalSchurDecomposition: incorrect TNeeded!"); ap::ap_error::make_assertion(zneeded==0||zneeded==1||zneeded==2, "InternalSchurDecomposition: incorrect ZNeeded!"); #endif wantt = tneeded==1; initz = zneeded==2; wantz = zneeded!=0; info = 0; // // Initialize Z, if necessary // if( initz ) { z.setbounds(1, n, 1, n); for(i = 1; i <= n; i++) { for(j = 1; j <= n; j++) { if( i==j ) { z(i,j) = 1; } else { z(i,j) = 0; } } } } // // Quick return if possible // if( n==0 ) { return; } if( n==1 ) { wr(1) = h(1,1); wi(1) = 0; return; } // // Set rows and columns 1 to N to zero below the first // subdiagonal. // for(j = 1; j <= n-2; j++) { for(i = j+2; i <= n; i++) { h(i,j) = 0; } } // // Test if N is sufficiently small // if( ns<=2||ns>n||maxb>=n ) { // // Use the standard ap::real_value_type-shift algorithm // internalauxschur(wantt, wantz, n, 1, n, h, wr, wi, 1, n, z, work, workv3, workc1, works1, info); // // fill entries under diagonal blocks of T with zeros // if( wantt ) { j = 1; while(j<=n) { if( wi(j)==0 ) { for(i = j+1; i <= n; i++) { h(i,j) = 0; } j = j+1; } else { for(i = j+2; i <= n; i++) { h(i,j) = 0; h(i,j+1) = 0; } j = j+2; } } } return; } unfl = ap::minrealnumber; ulp = 2*ap::machineepsilon; smlnum = unfl*(n/ulp); // // I1 and I2 are the indices of the first row and last column of H // to which transformations must be applied. If eigenvalues only are // being computed, I1 and I2 are set inside the main loop. // if( wantt ) { i1 = 1; i2 = n; } // // ITN is the total number of multiple-shift QR iterations allowed. // itn = 30*n; // // The main loop begins here. I is the loop index and decreases from // IHI to ILO in steps of at most MAXB. Each iteration of the loop // works with the active submatrix in rows and columns L to I. // Eigenvalues I+1 to IHI have already converged. Either L = ILO or // H(L,L-1) is negligible so that the matrix splits. // i = n; while(true) { l = 1; if( i<1 ) { // // fill entries under diagonal blocks of T with zeros // if( wantt ) { j = 1; while(j<=n) { if( wi(j)==0 ) { for(i = j+1; i <= n; i++) { h(i,j) = 0; } j = j+1; } else { for(i = j+2; i <= n; i++) { h(i,j) = 0; h(i,j+1) = 0; } j = j+2; } } } // // Exit // return; } // // Perform multiple-shift QR iterations on rows and columns ILO to I // until a submatrix of order at most MAXB splits off at the bottom // because a subdiagonal element has become negligible. // failflag = true; for(its = 0; its <= itn; its++) { // // Look for a single small subdiagonal element. // for(k = i; k >= l+1; k--) { tst1 = fabs(h(k-1,k-1))+fabs(h(k,k)); if( tst1==0 ) { tst1 = upperhessenberg1norm(h, l, i, l, i, work); } if( fabs(h(k,k-1))<=ap::maxreal(ulp*tst1, smlnum) ) { break; } } l = k; if( l>1 ) { // // H(L,L-1) is negligible. // h(l,l-1) = 0; } // // Exit from loop if a submatrix of order <= MAXB has split off. // if( l>=i-maxb+1 ) { failflag = false; break; } // // Now the active submatrix is in rows and columns L to I. If // eigenvalues only are being computed, only the active submatrix // need be transformed. // if( !wantt ) { i1 = l; i2 = i; } if( its==20||its==30 ) { // // Exceptional shifts. // for(ii = i-ns+1; ii <= i; ii++) { wr(ii) = cnst*(fabs(h(ii,ii-1))+fabs(h(ii,ii))); wi(ii) = 0; } } else { // // Use eigenvalues of trailing submatrix of order NS as shifts. // copymatrix(h, i-ns+1, i, i-ns+1, i, s, 1, ns, 1, ns); internalauxschur(false, false, ns, 1, ns, s, tmpwr, tmpwi, 1, ns, z, work, workv3, workc1, works1, ierr); for(p1 = 1; p1 <= ns; p1++) { wr(i-ns+p1) = tmpwr(p1); wi(i-ns+p1) = tmpwi(p1); } if( ierr>0 ) { // // If DLAHQR failed to compute all NS eigenvalues, use the // unconverged diagonal elements as the remaining shifts. // for(ii = 1; ii <= ierr; ii++) { wr(i-ns+ii) = s(ii,ii); wi(i-ns+ii) = 0; } } } // // Form the first column of (G-w(1)) (G-w(2)) . . . (G-w(ns)) // where G is the Hessenberg submatrix H(L:I,L:I) and w is // the vector of shifts (stored in WR and WI). The result is // stored in the local array V. // v(1) = 1; for(ii = 2; ii <= ns+1; ii++) { v(ii) = 0; } nv = 1; for(j = i-ns+1; j <= i; j++) { if( wi(j)>=0 ) { if( wi(j)==0 ) { // // real shift // p1 = nv+1; ap::vmove(&vv(1), &v(1), ap::vlen(1,p1)); matrixvectormultiply(h, l, l+nv, l, l+nv-1, false, vv, 1, nv, 1.0, v, 1, nv+1, -wr(j)); nv = nv+1; } else { if( wi(j)>0 ) { // // complex conjugate pair of shifts // p1 = nv+1; ap::vmove(&vv(1), &v(1), ap::vlen(1,p1)); matrixvectormultiply(h, l, l+nv, l, l+nv-1, false, v, 1, nv, 1.0, vv, 1, nv+1, -2*wr(j)); itemp = vectoridxabsmax(vv, 1, nv+1); temp = 1/ap::maxreal(fabs(vv(itemp)), smlnum); p1 = nv+1; ap::vmul(&vv(1), ap::vlen(1,p1), temp); absw = pythag2(wr(j), wi(j)); temp = temp*absw*absw; matrixvectormultiply(h, l, l+nv+1, l, l+nv, false, vv, 1, nv+1, 1.0, v, 1, nv+2, temp); nv = nv+2; } } // // Scale V(1:NV) so that max(abs(V(i))) = 1. If V is zero, // reset it to the unit vector. // itemp = vectoridxabsmax(v, 1, nv); temp = fabs(v(itemp)); if( temp==0 ) { v(1) = 1; for(ii = 2; ii <= nv; ii++) { v(ii) = 0; } } else { temp = ap::maxreal(temp, smlnum); vt = 1/temp; ap::vmul(&v(1), ap::vlen(1,nv), vt); } } } // // Multiple-shift QR step // for(k = l; k <= i-1; k++) { // // The first iteration of this loop determines a reflection G // from the vector V and applies it from left and right to H, // thus creating a nonzero bulge below the subdiagonal. // // Each subsequent iteration determines a reflection G to // restore the Hessenberg form in the (K-1)th column, and thus // chases the bulge one step toward the bottom of the active // submatrix. NR is the order of G. // nr = ap::minint(ns+1, i-k+1); if( k>l ) { p1 = k-1; p2 = k+nr-1; ap::vmove(v.getvector(1, nr), h.getcolumn(p1, k, p2)); } generatereflection(v, nr, tau); if( k>l ) { h(k,k-1) = v(1); for(ii = k+1; ii <= i; ii++) { h(ii,k-1) = 0; } } v(1) = 1; // // Apply G from the left to transform the rows of the matrix in // columns K to I2. // applyreflectionfromtheleft(h, tau, v, k, k+nr-1, k, i2, work); // // Apply G from the right to transform the columns of the // matrix in rows I1 to min(K+NR,I). // applyreflectionfromtheright(h, tau, v, i1, ap::minint(k+nr, i), k, k+nr-1, work); if( wantz ) { // // Accumulate transformations in the matrix Z // applyreflectionfromtheright(z, tau, v, 1, n, k, k+nr-1, work); } } } // // Failure to converge in remaining number of iterations // if( failflag ) { info = i; return; } // // A submatrix of order <= MAXB in rows and columns L to I has split // off. Use the ap::real_value_type-shift QR algorithm to handle it. // internalauxschur(wantt, wantz, n, l, i, h, wr, wi, 1, n, z, work, workv3, workc1, works1, info); if( info>0 ) { return; } // // Decrement number of remaining iterations, and return to start of // the main loop with a new value of I. // itn = itn-its; i = l-1; } } void internalauxschur(bool wantt, bool wantz, int n, int ilo, int ihi, ap::real_2d_array& h, ap::real_1d_array& wr, ap::real_1d_array& wi, int iloz, int ihiz, ap::real_2d_array& z, ap::real_1d_array& work, ap::real_1d_array& workv3, ap::real_1d_array& workc1, ap::real_1d_array& works1, int& info) { int i; int i1; int i2 = 0; int itn; int its; int j; int k; int l; int m; int nh; int nr; int nz; ap::real_value_type ave; ap::real_value_type cs; ap::real_value_type disc; ap::real_value_type h00; ap::real_value_type h10; ap::real_value_type h11; ap::real_value_type h12; ap::real_value_type h21; ap::real_value_type h22; ap::real_value_type h33; ap::real_value_type h33s; ap::real_value_type h43h34; ap::real_value_type h44; ap::real_value_type h44s; ap::real_value_type s; ap::real_value_type smlnum; ap::real_value_type sn; ap::real_value_type sum; ap::real_value_type t1; ap::real_value_type t2; ap::real_value_type t3; ap::real_value_type tst1; ap::real_value_type unfl; ap::real_value_type v1; ap::real_value_type v2; ap::real_value_type v3; bool failflag; ap::real_value_type dat1; ap::real_value_type dat2; int p1; ap::real_value_type him1im1; ap::real_value_type him1i; ap::real_value_type hiim1; ap::real_value_type hii; ap::real_value_type wrim1; ap::real_value_type wri; ap::real_value_type wiim1; ap::real_value_type wii; ap::real_value_type ulp; info = 0; dat1 = 0.75; dat2 = -0.4375; ulp = ap::machineepsilon; // // Quick return if possible // if( n==0 ) { return; } if( ilo==ihi ) { wr(ilo) = h(ilo,ilo); wi(ilo) = 0; return; } nh = ihi-ilo+1; nz = ihiz-iloz+1; // // Set machine-dependent constants for the stopping criterion. // If norm(H) <= sqrt(OVFL), overflow should not occur. // unfl = ap::minrealnumber; smlnum = unfl*(nh/ulp); // // I1 and I2 are the indices of the first row and last column of H // to which transformations must be applied. If eigenvalues only are // being computed, I1 and I2 are set inside the main loop. // if( wantt ) { i1 = 1; i2 = n; } // // ITN is the total number of QR iterations allowed. // itn = 30*nh; // // The main loop begins here. I is the loop index and decreases from // IHI to ILO in steps of 1 or 2. Each iteration of the loop works // with the active submatrix in rows and columns L to I. // Eigenvalues I+1 to IHI have already converged. Either L = ILO or // H(L,L-1) is negligible so that the matrix splits. // i = ihi; while(true) { l = ilo; if( i= l+1; k--) { tst1 = fabs(h(k-1,k-1))+fabs(h(k,k)); if( tst1==0 ) { tst1 = upperhessenberg1norm(h, l, i, l, i, work); } if( fabs(h(k,k-1))<=ap::maxreal(ulp*tst1, smlnum) ) { break; } } l = k; if( l>ilo ) { // // H(L,L-1) is negligible // h(l,l-1) = 0; } // // Exit from loop if a submatrix of order 1 or 2 has split off. // if( l>=i-1 ) { failflag = false; break; } // // Now the active submatrix is in rows and columns L to I. If // eigenvalues only are being computed, only the active submatrix // need be transformed. // if( !wantt ) { i1 = l; i2 = i; } if( its==10||its==20 ) { // // Exceptional shift. // s = fabs(h(i,i-1))+fabs(h(i-1,i-2)); h44 = dat1*s+h(i,i); h33 = h44; h43h34 = dat2*s*s; } else { // // Prepare to use Francis' ap::real_value_type shift // (i.e. 2nd degree generalized Rayleigh quotient) // h44 = h(i,i); h33 = h(i-1,i-1); h43h34 = h(i,i-1)*h(i-1,i); s = h(i-1,i-2)*h(i-1,i-2); disc = (h33-h44)*0.5; disc = disc*disc+h43h34; if( disc>0 ) { // // Real roots: use Wilkinson's shift twice // disc = sqrt(disc); ave = 0.5*(h33+h44); if( fabs(h33)-fabs(h44)>0 ) { h33 = h33*h44-h43h34; h44 = h33/(extschursign(disc, ave)+ave); } else { h44 = extschursign(disc, ave)+ave; } h33 = h44; h43h34 = 0; } } // // Look for two consecutive small subdiagonal elements. // for(m = i-2; m >= l; m--) { // // Determine the effect of starting the ap::real_value_type-shift QR // iteration at row M, and see if this would make H(M,M-1) // negligible. // h11 = h(m,m); h22 = h(m+1,m+1); h21 = h(m+1,m); h12 = h(m,m+1); h44s = h44-h11; h33s = h33-h11; v1 = (h33s*h44s-h43h34)/h21+h12; v2 = h22-h11-h33s-h44s; v3 = h(m+2,m+1); s = fabs(v1)+fabs(v2)+fabs(v3); v1 = v1/s; v2 = v2/s; v3 = v3/s; workv3(1) = v1; workv3(2) = v2; workv3(3) = v3; if( m==l ) { break; } h00 = h(m-1,m-1); h10 = h(m,m-1); tst1 = fabs(v1)*(fabs(h00)+fabs(h11)+fabs(h22)); if( fabs(h10)*(fabs(v2)+fabs(v3))<=ulp*tst1 ) { break; } } // // Double-shift QR step // for(k = m; k <= i-1; k++) { // // The first iteration of this loop determines a reflection G // from the vector V and applies it from left and right to H, // thus creating a nonzero bulge below the subdiagonal. // // Each subsequent iteration determines a reflection G to // restore the Hessenberg form in the (K-1)th column, and thus // chases the bulge one step toward the bottom of the active // submatrix. NR is the order of G. // nr = ap::minint(3, i-k+1); if( k>m ) { for(p1 = 1; p1 <= nr; p1++) { workv3(p1) = h(k+p1-1,k-1); } } generatereflection(workv3, nr, t1); if( k>m ) { h(k,k-1) = workv3(1); h(k+1,k-1) = 0; if( kl ) { h(k,k-1) = -h(k,k-1); } } v2 = workv3(2); t2 = t1*v2; if( nr==3 ) { v3 = workv3(3); t3 = t1*v3; // // Apply G from the left to transform the rows of the matrix // in columns K to I2. // for(j = k; j <= i2; j++) { sum = h(k,j)+v2*h(k+1,j)+v3*h(k+2,j); h(k,j) = h(k,j)-sum*t1; h(k+1,j) = h(k+1,j)-sum*t2; h(k+2,j) = h(k+2,j)-sum*t3; } // // Apply G from the right to transform the columns of the // matrix in rows I1 to min(K+3,I). // for(j = i1; j <= ap::minint(k+3, i); j++) { sum = h(j,k)+v2*h(j,k+1)+v3*h(j,k+2); h(j,k) = h(j,k)-sum*t1; h(j,k+1) = h(j,k+1)-sum*t2; h(j,k+2) = h(j,k+2)-sum*t3; } if( wantz ) { // // Accumulate transformations in the matrix Z // for(j = iloz; j <= ihiz; j++) { sum = z(j,k)+v2*z(j,k+1)+v3*z(j,k+2); z(j,k) = z(j,k)-sum*t1; z(j,k+1) = z(j,k+1)-sum*t2; z(j,k+2) = z(j,k+2)-sum*t3; } } } else { if( nr==2 ) { // // Apply G from the left to transform the rows of the matrix // in columns K to I2. // for(j = k; j <= i2; j++) { sum = h(k,j)+v2*h(k+1,j); h(k,j) = h(k,j)-sum*t1; h(k+1,j) = h(k+1,j)-sum*t2; } // // Apply G from the right to transform the columns of the // matrix in rows I1 to min(K+3,I). // for(j = i1; j <= i; j++) { sum = h(j,k)+v2*h(j,k+1); h(j,k) = h(j,k)-sum*t1; h(j,k+1) = h(j,k+1)-sum*t2; } if( wantz ) { // // Accumulate transformations in the matrix Z // for(j = iloz; j <= ihiz; j++) { sum = z(j,k)+v2*z(j,k+1); z(j,k) = z(j,k)-sum*t1; z(j,k+1) = z(j,k+1)-sum*t2; } } } } } } if( failflag ) { // // Failure to converge in remaining number of iterations // info = i; return; } if( l==i ) { // // H(I,I-1) is negligible: one eigenvalue has converged. // wr(i) = h(i,i); wi(i) = 0; } else { if( l==i-1 ) { // // H(I-1,I-2) is negligible: a pair of eigenvalues have converged. // // Transform the 2-by-2 submatrix to standard Schur form, // and compute and store the eigenvalues. // him1im1 = h(i-1,i-1); him1i = h(i-1,i); hiim1 = h(i,i-1); hii = h(i,i); aux2x2schur(him1im1, him1i, hiim1, hii, wrim1, wiim1, wri, wii, cs, sn); wr(i-1) = wrim1; wi(i-1) = wiim1; wr(i) = wri; wi(i) = wii; h(i-1,i-1) = him1im1; h(i-1,i) = him1i; h(i,i-1) = hiim1; h(i,i) = hii; if( wantt ) { // // Apply the transformation to the rest of H. // if( i2>i ) { workc1(1) = cs; works1(1) = sn; applyrotationsfromtheleft(true, i-1, i, i+1, i2, workc1, works1, h, work); } workc1(1) = cs; works1(1) = sn; applyrotationsfromtheright(true, i1, i-2, i-1, i, workc1, works1, h, work); } if( wantz ) { // // Apply the transformation to Z. // workc1(1) = cs; works1(1) = sn; applyrotationsfromtheright(true, iloz, iloz+nz-1, i-1, i, workc1, works1, z, work); } } } // // Decrement number of remaining iterations, and return to start of // the main loop with new value of I. // itn = itn-its; i = l-1; } } void aux2x2schur(ap::real_value_type& a, ap::real_value_type& b, ap::real_value_type& c, ap::real_value_type& d, ap::real_value_type& rt1r, ap::real_value_type& rt1i, ap::real_value_type& rt2r, ap::real_value_type& rt2i, ap::real_value_type& cs, ap::real_value_type& sn) { ap::real_value_type multpl; ap::real_value_type aa; ap::real_value_type bb; ap::real_value_type bcmax; ap::real_value_type bcmis; ap::real_value_type cc; ap::real_value_type cs1; ap::real_value_type dd; ap::real_value_type eps; ap::real_value_type p; ap::real_value_type sab; ap::real_value_type sac; ap::real_value_type scl; ap::real_value_type sigma; ap::real_value_type sn1; ap::real_value_type tau; ap::real_value_type temp; ap::real_value_type z; multpl = 4.0; eps = ap::machineepsilon; if( c==0 ) { cs = 1; sn = 0; } else { if( b==0 ) { // // Swap rows and columns // cs = 0; sn = 1; temp = d; d = a; a = temp; b = -c; c = 0; } else { if( a-d==0&&extschursigntoone(b)!=extschursigntoone(c) ) { cs = 1; sn = 0; } else { temp = a-d; p = 0.5*temp; bcmax = ap::maxreal(fabs(b), fabs(c)); bcmis = ap::minreal(fabs(b), fabs(c))*extschursigntoone(b)*extschursigntoone(c); scl = ap::maxreal(fabs(p), bcmax); z = p/scl*p+bcmax/scl*bcmis; // // If Z is of the order of the machine accuracy, postpone the // decision on the nature of eigenvalues // if( z>=multpl*eps ) { // // Real eigenvalues. Compute A and D. // z = p+extschursign(sqrt(scl)*sqrt(z), p); a = d+z; d = d-bcmax/z*bcmis; // // Compute B and the rotation matrix // tau = pythag2(c, z); cs = z/tau; sn = c/tau; b = b-c; c = 0; } else { // // Complex eigenvalues, or real (almost) equal eigenvalues. // Make diagonal elements equal. // sigma = b+c; tau = pythag2(sigma, temp); cs = sqrt(0.5*(1+fabs(sigma)/tau)); sn = -p/(tau*cs)*extschursign(ap::real_value_type(1), sigma); // // Compute [ AA BB ] = [ A B ] [ CS -SN ] // [ CC DD ] [ C D ] [ SN CS ] // aa = a*cs+b*sn; bb = -a*sn+b*cs; cc = c*cs+d*sn; dd = -c*sn+d*cs; // // Compute [ A B ] = [ CS SN ] [ AA BB ] // [ C D ] [-SN CS ] [ CC DD ] // a = aa*cs+cc*sn; b = bb*cs+dd*sn; c = -aa*sn+cc*cs; d = -bb*sn+dd*cs; temp = 0.5*(a+d); a = temp; d = temp; if( c!=0 ) { if( b!=0 ) { if( extschursigntoone(b)==extschursigntoone(c) ) { // // Real eigenvalues: reduce to upper triangular form // sab = sqrt(fabs(b)); sac = sqrt(fabs(c)); p = extschursign(sab*sac, c); tau = 1/sqrt(fabs(b+c)); a = temp+p; d = temp-p; b = b-c; c = 0; cs1 = sab*tau; sn1 = sac*tau; temp = cs*cs1-sn*sn1; sn = cs*sn1+sn*cs1; cs = temp; } } else { b = -c; c = 0; temp = cs; cs = -sn; sn = temp; } } } } } } // // Store eigenvalues in (RT1R,RT1I) and (RT2R,RT2I). // rt1r = a; rt2r = d; if( c==0 ) { rt1i = 0; rt2i = 0; } else { rt1i = sqrt(fabs(b))*sqrt(fabs(c)); rt2i = -rt1i; } } ap::real_value_type extschursign(ap::real_value_type a, ap::real_value_type b) { ap::real_value_type result; if( b>=0 ) { result = fabs(a); } else { result = -fabs(a); } return result; } int extschursigntoone(ap::real_value_type b) { int result; if( b>=0 ) { result = 1; } else { result = -1; } return result; } cmtk-3.3.1/libs/Numerics/hsschur.h000066400000000000000000000063021276303427400170330ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2009, 2013 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4863 $ // // $LastChangedDate: 2013-09-24 09:45:06 -0700 (Tue, 24 Sep 2013) $ // // $LastChangedBy: torstenrohlfing $ // */ /************************************************************************* Copyright (c) 1992-2007 The University of Tennessee. All rights reserved. Contributors: * Sergey Bochkanov (ALGLIB project). Translation from FORTRAN to pseudocode. See subroutines comments for additional copyrights. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer listed in this license in the documentation and/or other materials provided with the distribution. - Neither the name of the copyright holders nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *************************************************************************/ #ifndef _hsschur_h #define _hsschur_h #include "ap.h" #include "blas.h" #include "reflections.h" #include "rotations.h" void internalschurdecomposition(ap::real_2d_array& h, int n, int tneeded, int zneeded, ap::real_1d_array& wr, ap::real_1d_array& wi, ap::real_2d_array& z, int& info); #endif cmtk-3.3.1/libs/Numerics/ibetaf.cxx000066400000000000000000000336661276303427400171760ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2010, 2013 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4973 $ // // $LastChangedDate: 2013-10-11 13:31:38 -0700 (Fri, 11 Oct 2013) $ // // $LastChangedBy: torstenrohlfing $ // */ /************************************************************************* Cephes Math Library Release 2.8: June, 2000 Copyright by Stephen L. Moshier Contributors: * Sergey Bochkanov (ALGLIB project). Translation from C to pseudocode. See subroutines comments for additional copyrights. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer listed in this license in the documentation and/or other materials provided with the distribution. - Neither the name of the copyright holders nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *************************************************************************/ #include "ibetaf.h" namespace alglib { ap::real_value_type incompletebetafe(ap::real_value_type a, ap::real_value_type b, ap::real_value_type x, ap::real_value_type big, ap::real_value_type biginv); ap::real_value_type incompletebetafe2(ap::real_value_type a, ap::real_value_type b, ap::real_value_type x, ap::real_value_type big, ap::real_value_type biginv); ap::real_value_type incompletebetaps(ap::real_value_type a, ap::real_value_type b, ap::real_value_type x, ap::real_value_type maxgam); /************************************************************************* Incomplete beta integral Returns incomplete beta integral of the arguments, evaluated from zero to x. The function is defined as x - - | (a+b) | | a-1 b-1 ----------- | t (1-t) dt. - - | | | (a) | (b) - 0 The domain of definition is 0 <= x <= 1. In this implementation a and b are restricted to positive values. The integral from x to 1 may be obtained by the symmetry relation 1 - incbet( a, b, x ) = incbet( b, a, 1-x ). The integral is evaluated by a continued fraction expansion or, when b*x is small, by a power series. ACCURACY: Tested at uniformly distributed random points (a,b,x) with a and b in "domain" and x between 0 and 1. Relative error arithmetic domain # trials peak rms IEEE 0,5 10000 6.9e-15 4.5e-16 IEEE 0,85 250000 2.2e-13 1.7e-14 IEEE 0,1000 30000 5.3e-12 6.3e-13 IEEE 0,10000 250000 9.3e-11 7.1e-12 IEEE 0,100000 10000 8.7e-10 4.8e-11 Outputs smaller than the IEEE gradual underflow threshold were excluded from these statistics. Cephes Math Library, Release 2.8: June, 2000 Copyright 1984, 1995, 2000 by Stephen L. Moshier *************************************************************************/ ap::real_value_type incompletebeta(ap::real_value_type a, ap::real_value_type b, ap::real_value_type x) { ap::real_value_type result; ap::real_value_type t; ap::real_value_type xc; ap::real_value_type w; ap::real_value_type y; int flag; ap::real_value_type sg; ap::real_value_type big; ap::real_value_type biginv; ap::real_value_type maxgam; ap::real_value_type minlog; ap::real_value_type maxlog; big = 4.503599627370496e15; biginv = 2.22044604925031308085e-16; maxgam = 171.624376956302725; minlog = log(ap::minrealnumber); maxlog = log(ap::maxrealnumber); #ifndef NO_AP_ASSERT ap::ap_error::make_assertion(a>0&&b>0, "Domain error in IncompleteBeta"); ap::ap_error::make_assertion(x>=0&&x<=1, "Domain error in IncompleteBeta"); #endif if( x==0 ) { result = 0; return result; } if( x==1 ) { result = 1; return result; } flag = 0; if( b*x<=1.0&&x<=0.95 ) { result = incompletebetaps(a, b, x, maxgam); return result; } w = 1.0-x; if( x>a/(a+b) ) { flag = 1; t = a; a = b; b = t; xc = x; x = w; } else { xc = w; } if( flag==1&&b*x<=1.0&&x<=0.95 ) { t = incompletebetaps(a, b, x, maxgam); if( t<=ap::machineepsilon ) { result = 1.0-ap::machineepsilon; } else { result = 1.0-t; } return result; } y = x*(a+b-2.0)-(a-1.0); if( y<0.0 ) { w = incompletebetafe(a, b, x, big, biginv); } else { w = incompletebetafe2(a, b, x, big, biginv)/xc; } y = a*log(x); t = b*log(xc); if( a+bbig ) { pkm2 = pkm2*biginv; pkm1 = pkm1*biginv; qkm2 = qkm2*biginv; qkm1 = qkm1*biginv; } if( fabs(qk)big ) { pkm2 = pkm2*biginv; pkm1 = pkm1*biginv; qkm2 = qkm2*biginv; qkm1 = qkm1*biginv; } if( fabs(qk)z) { u = (n-b)*x/n; t = t*u; v = t/(a+n); s = s+v; n = n+1.0; } s = s+t1; s = s+ai; u = a*log(x); if( a+b. // // $Revision: 2372 $ // // $LastChangedDate: 2010-09-29 11:13:08 -0700 (Wed, 29 Sep 2010) $ // // $LastChangedBy: torstenrohlfing $ // */ /************************************************************************* Cephes Math Library Release 2.8: June, 2000 Copyright by Stephen L. Moshier Contributors: * Sergey Bochkanov (ALGLIB project). Translation from C to pseudocode. See subroutines comments for additional copyrights. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer listed in this license in the documentation and/or other materials provided with the distribution. - Neither the name of the copyright holders nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *************************************************************************/ #ifndef _ibetaf_h #define _ibetaf_h #include "ap.h" #include "gammaf.h" #include "normaldistr.h" namespace alglib { /************************************************************************* Incomplete beta integral Returns incomplete beta integral of the arguments, evaluated from zero to x. The function is defined as x - - | (a+b) | | a-1 b-1 ----------- | t (1-t) dt. - - | | | (a) | (b) - 0 The domain of definition is 0 <= x <= 1. In this implementation a and b are restricted to positive values. The integral from x to 1 may be obtained by the symmetry relation 1 - incbet( a, b, x ) = incbet( b, a, 1-x ). The integral is evaluated by a continued fraction expansion or, when b*x is small, by a power series. ACCURACY: Tested at uniformly distributed random points (a,b,x) with a and b in "domain" and x between 0 and 1. Relative error arithmetic domain # trials peak rms IEEE 0,5 10000 6.9e-15 4.5e-16 IEEE 0,85 250000 2.2e-13 1.7e-14 IEEE 0,1000 30000 5.3e-12 6.3e-13 IEEE 0,10000 250000 9.3e-11 7.1e-12 IEEE 0,100000 10000 8.7e-10 4.8e-11 Outputs smaller than the IEEE gradual underflow threshold were excluded from these statistics. Cephes Math Library, Release 2.8: June, 2000 Copyright 1984, 1995, 2000 by Stephen L. Moshier *************************************************************************/ ap::real_value_type incompletebeta(ap::real_value_type a, ap::real_value_type b, ap::real_value_type x); } // namespace alglib #endif cmtk-3.3.1/libs/Numerics/igammaf.cxx000066400000000000000000000153431276303427400173350ustar00rootroot00000000000000/************************************************************************* Cephes Math Library Release 2.8: June, 2000 Copyright by Stephen L. Moshier Contributors: * Sergey Bochkanov (ALGLIB project). Translation from C to pseudocode. See subroutines comments for additional copyrights. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer listed in this license in the documentation and/or other materials provided with the distribution. - Neither the name of the copyright holders nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *************************************************************************/ #include "igammaf.h" /************************************************************************* Incomplete gamma integral The function is defined by x - 1 | | -t a-1 igam(a,x) = ----- | e t dt. - | | | (a) - 0 In this implementation both arguments must be positive. The integral is evaluated by either a power series or continued fraction expansion, depending on the relative values of a and x. ACCURACY: Relative error: arithmetic domain # trials peak rms IEEE 0,30 200000 3.6e-14 2.9e-15 IEEE 0,100 300000 9.9e-14 1.5e-14 Cephes Math Library Release 2.8: June, 2000 Copyright 1985, 1987, 2000 by Stephen L. Moshier *************************************************************************/ ap::real_value_type incompletegamma(ap::real_value_type a, ap::real_value_type x) { ap::real_value_type result; ap::real_value_type igammaepsilon; ap::real_value_type ans; ap::real_value_type ax; ap::real_value_type c; ap::real_value_type r; ap::real_value_type tmp; igammaepsilon = 0.000000000000001; if( x<=0||a<=0 ) { result = 0; return result; } if( x>1&&x>a ) { result = 1-incompletegammac(a, x); return result; } ax = a*log(x)-x-lngamma(a, tmp); if( ax<-709.78271289338399 ) { result = 0; return result; } ax = exp(ax); r = a; c = 1; ans = 1; do { r = r+1; c = c*x/r; ans = ans+c; } while(c/ans>igammaepsilon); result = ans*ax/a; return result; } /************************************************************************* Complemented incomplete gamma integral The function is defined by igamc(a,x) = 1 - igam(a,x) inf. - 1 | | -t a-1 = ----- | e t dt. - | | | (a) - x In this implementation both arguments must be positive. The integral is evaluated by either a power series or continued fraction expansion, depending on the relative values of a and x. ACCURACY: Tested at random a, x. a x Relative error: arithmetic domain domain # trials peak rms IEEE 0.5,100 0,100 200000 1.9e-14 1.7e-15 IEEE 0.01,0.5 0,100 200000 1.4e-13 1.6e-15 Cephes Math Library Release 2.8: June, 2000 Copyright 1985, 1987, 2000 by Stephen L. Moshier *************************************************************************/ ap::real_value_type incompletegammac(ap::real_value_type a, ap::real_value_type x) { ap::real_value_type result; ap::real_value_type igammaepsilon; ap::real_value_type igammabignumber; ap::real_value_type igammabignumberinv; ap::real_value_type ans; ap::real_value_type ax; ap::real_value_type c; ap::real_value_type yc; ap::real_value_type r; ap::real_value_type t; ap::real_value_type y; ap::real_value_type z; ap::real_value_type pk; ap::real_value_type pkm1; ap::real_value_type pkm2; ap::real_value_type qk; ap::real_value_type qkm1; ap::real_value_type qkm2; ap::real_value_type tmp; igammaepsilon = 0.000000000000001; igammabignumber = 4503599627370496.0; igammabignumberinv = 2.22044604925031308085*0.0000000000000001; if( x<=0||a<=0 ) { result = 1; return result; } if( x<1||xigammabignumber ) { pkm2 = pkm2*igammabignumberinv; pkm1 = pkm1*igammabignumberinv; qkm2 = qkm2*igammabignumberinv; qkm1 = qkm1*igammabignumberinv; } } while(t>igammaepsilon); result = ans*ax; return result; } cmtk-3.3.1/libs/Numerics/igammaf.h000066400000000000000000000102041276303427400167510ustar00rootroot00000000000000/************************************************************************* Cephes Math Library Release 2.8: June, 2000 Copyright by Stephen L. Moshier Contributors: * Sergey Bochkanov (ALGLIB project). Translation from C to pseudocode. See subroutines comments for additional copyrights. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer listed in this license in the documentation and/or other materials provided with the distribution. - Neither the name of the copyright holders nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *************************************************************************/ #ifndef _igammaf_h #define _igammaf_h #include "ap.h" #include "gammaf.h" #include "normaldistr.h" /************************************************************************* Incomplete gamma integral The function is defined by x - 1 | | -t a-1 igam(a,x) = ----- | e t dt. - | | | (a) - 0 In this implementation both arguments must be positive. The integral is evaluated by either a power series or continued fraction expansion, depending on the relative values of a and x. ACCURACY: Relative error: arithmetic domain # trials peak rms IEEE 0,30 200000 3.6e-14 2.9e-15 IEEE 0,100 300000 9.9e-14 1.5e-14 Cephes Math Library Release 2.8: June, 2000 Copyright 1985, 1987, 2000 by Stephen L. Moshier *************************************************************************/ ap::real_value_type incompletegamma(ap::real_value_type a, ap::real_value_type x); /************************************************************************* Complemented incomplete gamma integral The function is defined by igamc(a,x) = 1 - igam(a,x) inf. - 1 | | -t a-1 = ----- | e t dt. - | | | (a) - x In this implementation both arguments must be positive. The integral is evaluated by either a power series or continued fraction expansion, depending on the relative values of a and x. ACCURACY: Tested at random a, x. a x Relative error: arithmetic domain domain # trials peak rms IEEE 0.5,100 0,100 200000 1.9e-14 1.7e-15 IEEE 0.01,0.5 0,100 200000 1.4e-13 1.6e-15 Cephes Math Library Release 2.8: June, 2000 Copyright 1985, 1987, 2000 by Stephen L. Moshier *************************************************************************/ ap::real_value_type incompletegammac(ap::real_value_type a, ap::real_value_type x); #endif cmtk-3.3.1/libs/Numerics/lbfgsb.cxx000066400000000000000000001663561276303427400172060ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2012, 2013 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4948 $ // // $LastChangedDate: 2013-10-08 13:49:31 -0700 (Tue, 08 Oct 2013) $ // // $LastChangedBy: torstenrohlfing $ // */ /************************************************************************* NEOS, November 1994. (Latest revision June 1996.) Optimization Technology Center. Argonne National Laboratory and Northwestern University. Written by Ciyou Zhu in collaboration with R.H. Byrd, P. Lu-Chen and J. Nocedal. Contributors: * Sergey Bochkanov (ALGLIB project). Translation from FORTRAN to pseudocode. This software is freely available, but we expect that all publications describing work using this software, or all commercial products using it, quote at least one of the references given below: * R. H. Byrd, P. Lu and J. Nocedal. A Limited Memory Algorithm for Bound Constrained Optimization, (1995), SIAM Journal on Scientific and Statistical Computing , 16, 5, pp. 1190-1208. * C. Zhu, R.H. Byrd and J. Nocedal. L-BFGS-B: Algorithm 778: L-BFGS-B, FORTRAN routines for large scale bound constrained optimization (1997), ACM Transactions on Mathematical Software, Vol 23, Num. 4, pp. 550 - 560. *************************************************************************/ #include "lbfgsb.h" namespace ap { void lbfgsbactive ( const int& n, const ap::real_1d_array& l, const ap::real_1d_array& u, const ap::integer_1d_array& nbd, ap::real_1d_array& x, ap::integer_1d_array& iwhere, bool& prjctd, bool& cnstnd, bool& boxed) { int nbdd; int i; nbdd = 0; prjctd = false; cnstnd = false; boxed = true; for(i = 1; i <= n; i++) { if( nbd(i)>0 ) { if( nbd(i)<=2&&x(i)<=l(i) ) { if( x(i)=2&&x(i)>=u(i) ) { if( x(i)>u(i) ) { prjctd = true; x(i) = u(i); } nbdd = nbdd+1; } } } } for(i = 1; i <= n; i++) { if( nbd(i)!=2 ) { boxed = false; } if( nbd(i)==0 ) { iwhere(i) = -1; } else { cnstnd = true; if( nbd(i)==2&&u(i)-l(i)<=0 ) { iwhere(i) = 3; } else { iwhere(i) = 0; } } } } void lbfgsbbmv(const int&, const ap::real_2d_array& sy, ap::real_2d_array& wt, const int& col, const ap::real_1d_array& v, ap::real_1d_array& p, int& info, ap::real_1d_array& workvec) { int i; int k; int i2; ap::real_value_type s; if( col==0 ) { return; } p(col+1) = v(col+1); for(i = 2; i <= col; i++) { i2 = col+i; s = 0.0; for(k = 1; k <= i-1; k++) { s = s+sy(i,k)*v(k)/sy(k,k); } p(i2) = v(i2)+s; } ap::vmove(workvec.getvector(1, col), p.getvector(col+1, col+col)); lbfgsbdtrsl(wt, col, workvec, 11, info); ap::vmove(p.getvector(col+1, col+col), workvec.getvector(1, col)); if( info!=0 ) { return; } for(i = 1; i <= col; i++) { p(i) = v(i)/sqrt(sy(i,i)); } ap::vmove(workvec.getvector(1, col), p.getvector(col+1, col+col)); lbfgsbdtrsl(wt, col, workvec, 1, info); ap::vmove(p.getvector(col+1, col+col), workvec.getvector(1, col)); if( info!=0 ) { return; } for(i = 1; i <= col; i++) { p(i) = -p(i)/sqrt(sy(i,i)); } for(i = 1; i <= col; i++) { s = 0; for(k = i+1; k <= col; k++) { s = s+sy(k,i)*p(col+k)/sy(i,i); } p(i) = p(i)+s; } } void lbfgsbcauchy(const int& n, const ap::real_1d_array& x, const ap::real_1d_array& l, const ap::real_1d_array& u, const ap::integer_1d_array& nbd, const ap::real_1d_array& g, ap::integer_1d_array& iorder, ap::integer_1d_array& iwhere, ap::real_1d_array& t, ap::real_1d_array& d, ap::real_1d_array& xcp, const int& m, const ap::real_2d_array& wy, const ap::real_2d_array& ws, const ap::real_2d_array& sy, ap::real_2d_array& wt, const ap::real_value_type& theta, const int& col, const int& head, ap::real_1d_array& p, ap::real_1d_array& c, ap::real_1d_array& wbp, ap::real_1d_array& v, int& nint, const ap::real_1d_array&, const ap::real_1d_array&, const ap::real_value_type& sbgnrm, int& info, ap::real_1d_array& workvec) { bool xlower; bool xupper; bool bnded; int i; int j; int col2; int nfree; int nbreak; int pointr; int ibp; int nleft; int ibkmin; int iter; ap::real_value_type f1; ap::real_value_type f2; ap::real_value_type dt; ap::real_value_type dtm; ap::real_value_type tsum; ap::real_value_type dibp; ap::real_value_type zibp; ap::real_value_type dibp2; ap::real_value_type bkmin; ap::real_value_type tu = 0; ap::real_value_type tl = 0; ap::real_value_type wmc; ap::real_value_type wmp; ap::real_value_type wmw; ap::real_value_type tj; ap::real_value_type tj0; ap::real_value_type neggi; ap::real_value_type f2org; ap::real_value_type tmpv; if( sbgnrm<=0 ) { ap::vmove(xcp.getvector(1, n), x.getvector(1, n)); return; } bnded = true; nfree = n+1; nbreak = 0; ibkmin = 0; bkmin = 0; col2 = 2*col; f1 = 0; for(i = 1; i <= col2; i++) { p(i) = 0; } for(i = 1; i <= n; i++) { neggi = -g(i); if( iwhere(i)!=3&&iwhere(i)!=-1 ) { tl = 0; tu = 0; if( nbd(i)<=2 ) { tl = x(i)-l(i); } if( nbd(i)>=2 ) { tu = u(i)-x(i); } xlower = nbd(i)<=2&&tl<=0; xupper = nbd(i)>=2&&tu<=0; iwhere(i) = 0; if( xlower ) { if( neggi<=0 ) { iwhere(i) = 1; } } else { if( xupper ) { if( neggi>=0 ) { iwhere(i) = 2; } } else { if( fabs(neggi)<=0 ) { iwhere(i) = -3; } } } } pointr = head; if( iwhere(i)!=0&&iwhere(i)!=-1 ) { d(i) = 0; } else { d(i) = neggi; f1 = f1-neggi*neggi; for(j = 1; j <= col; j++) { p(j) = p(j)+wy(i,pointr)*neggi; p(col+j) = p(col+j)+ws(i,pointr)*neggi; pointr = pointr%m+1; } if( nbd(i)<=2&&nbd(i)!=0&&neggi<0 ) { nbreak = nbreak+1; iorder(nbreak) = i; t(nbreak) = tl/(-neggi); if( nbreak==1||t(nbreak)=2&&neggi>0 ) { nbreak = nbreak+1; iorder(nbreak) = i; t(nbreak) = tu/neggi; if( nbreak==1||t(nbreak)0 ) { bnded = false; } } } } } if( theta!=1 ) { ap::vmul(p.getvector(col+1, col+col), theta); } ap::vmove(xcp.getvector(1, n), x.getvector(1, n)); if( nbreak==0&&nfree==n+1 ) { return; } for(j = 1; j <= col2; j++) { c(j) = 0; } f2 = -theta*f1; f2org = f2; if( col>0 ) { lbfgsbbmv(m, sy, wt, col, p, v, info, workvec); if( info!=0 ) { return; } tmpv = ap::vdotproduct(v.getvector(1, col2), p.getvector(1, col2)); f2 = f2-tmpv; } dtm = -f1/f2; tsum = 0; nint = 1; if( nbreak!=0 ) { nleft = nbreak; iter = 1; tj = 0; while(true) { tj0 = tj; if( iter==1 ) { tj = bkmin; ibp = iorder(ibkmin); } else { if( iter==2 ) { if( ibkmin!=nbreak ) { t(ibkmin) = t(nbreak); iorder(ibkmin) = iorder(nbreak); } } lbfgsbhpsolb(nleft, t, iorder, iter-2); tj = t(nleft); ibp = iorder(nleft); } dt = tj-tj0; if( dtm
      0 ) { zibp = u(ibp)-x(ibp); xcp(ibp) = u(ibp); iwhere(ibp) = 2; } else { zibp = l(ibp)-x(ibp); xcp(ibp) = l(ibp); iwhere(ibp) = 1; } if( nleft==0&&nbreak==n ) { dtm = dt; if( col>0 ) { ap::vadd(c.getvector(1, col2), p.getvector(1, col2), dtm); } return; } nint = nint+1; dibp2 = ap::sqr(dibp); f1 = f1+dt*f2+dibp2-theta*dibp*zibp; f2 = f2-theta*dibp2; if( col>0 ) { ap::vadd(c.getvector(1, col2), p.getvector(1, col2), dt); pointr = head; for(j = 1; j <= col; j++) { wbp(j) = wy(ibp,pointr); wbp(col+j) = theta*ws(ibp,pointr); pointr = pointr%m+1; } lbfgsbbmv(m, sy, wt, col, wbp, v, info, workvec); if( info!=0 ) { return; } wmc = ap::vdotproduct(c.getvector(1, col2), v.getvector(1, col2)); wmp = ap::vdotproduct(p.getvector(1, col2), v.getvector(1, col2)); wmw = ap::vdotproduct(wbp.getvector(1, col2), v.getvector(1, col2)); ap::vsub(p.getvector(1, col2), wbp.getvector(1, col2), dibp); f1 = f1+dibp*wmc; f2 = f2+2.0*dibp*wmp-dibp2*wmw; } f2 = ap::maxreal(ap::machineepsilon*f2org, f2); if( nleft>0 ) { dtm = -f1/f2; continue; } else { if( bnded ) { f1 = 0; f2 = 0; dtm = 0; } else { dtm = -f1/f2; } } break; } } if( dtm<=0 ) { dtm = 0; } tsum = tsum+dtm; ap::vadd(xcp.getvector(1, n), d.getvector(1, n), tsum); if( col>0 ) { ap::vadd(c.getvector(1, col2), p.getvector(1, col2), dtm); } } void lbfgsbcmprlb(const int& n, const int& m, const ap::real_1d_array& x, const ap::real_1d_array& g, const ap::real_2d_array& ws, const ap::real_2d_array& wy, const ap::real_2d_array& sy, ap::real_2d_array& wt, const ap::real_1d_array& z, ap::real_1d_array& r, ap::real_1d_array& wa, const ap::integer_1d_array& index, const ap::real_value_type& theta, const int& col, const int& head, const int& nfree, const bool& cnstnd, int& info, ap::real_1d_array& workvec, ap::real_1d_array& workvec2) { int i; int j; int k; int pointr; ap::real_value_type a1; ap::real_value_type a2; if( !cnstnd&&col>0 ) { for(i = 1; i <= n; i++) { r(i) = -g(i); } } else { for(i = 1; i <= nfree; i++) { k = index(i); r(i) = -theta*(z(k)-x(k))-g(k); } ap::vmove(workvec2.getvector(1, 2*m), wa.getvector(2*m+1, 4*m)); lbfgsbbmv(m, sy, wt, col, workvec2, wa, info, workvec); ap::vmove(wa.getvector(2*m+1, 4*m), workvec2.getvector(1, 2*m)); if( info!=0 ) { info = -8; return; } pointr = head; for(j = 1; j <= col; j++) { a1 = wa(j); a2 = theta*wa(col+j); for(i = 1; i <= nfree; i++) { k = index(i); r(i) = r(i)+wy(k,pointr)*a1+ws(k,pointr)*a2; } pointr = pointr%m+1; } } } void lbfgsberrclb(const int& n, const int& m, const ap::real_value_type& factr, const ap::real_1d_array& l, const ap::real_1d_array& u, const ap::integer_1d_array& nbd, int& task, int& info, int& k) { int i; if( n<=0 ) { task = 2; } if( m<=0 ) { task = 2; } if( m>n ) { task = 2; } if( factr<0 ) { task = 2; } for(i = 1; i <= n; i++) { if( nbd(i)<0||nbd(i)>3 ) { task = 2; info = -6; k = i; } if( nbd(i)==2 ) { if( l(i)>u(i) ) { task = 2; info = -7; k = i; } } } } void lbfgsbformk(const int& n, const int& nsub, const ap::integer_1d_array& ind, const int& nenter, const int& ileave, const ap::integer_1d_array& indx2, const int& iupdat, const bool& updatd, ap::real_2d_array& wn, ap::real_2d_array& wn1, const int& m, const ap::real_2d_array& ws, const ap::real_2d_array& wy, const ap::real_2d_array& sy, const ap::real_value_type& theta, const int& col, const int& head, int& info, ap::real_1d_array& workvec, ap::real_2d_array& workmat) { int ipntr; int jpntr; int iy; int iis; int jy; int js; int is1; int js1; int k1; int i; int k; int col2; int pbegin; int pend; int dbegin; int dend; int upcl; ap::real_value_type temp1; ap::real_value_type temp2; ap::real_value_type temp3; ap::real_value_type temp4; ap::real_value_type v; int j; if( updatd ) { if( iupdat>m ) { for(jy = 1; jy <= m-1; jy++) { js = m+jy; ap::vmove(wn1.getcolumn(jy, jy, m-1), wn1.getcolumn(jy+1, jy+1, m)); ap::vmove(wn1.getcolumn(js, js, js+m-jy-1), wn1.getcolumn(js+1, js+1, js+m-jy)); ap::vmove(wn1.getcolumn(jy, m+1, m+m-1), wn1.getcolumn(jy+1, m+2, m+m)); } } pbegin = 1; pend = nsub; dbegin = nsub+1; dend = n; iy = col; iis = m+col; ipntr = head+col-1; if( ipntr>m ) { ipntr = ipntr-m; } jpntr = head; for(jy = 1; jy <= col; jy++) { js = m+jy; temp1 = 0; temp2 = 0; temp3 = 0; for(k = pbegin; k <= pend; k++) { k1 = ind(k); temp1 = temp1+wy(k1,ipntr)*wy(k1,jpntr); } for(k = dbegin; k <= dend; k++) { k1 = ind(k); temp2 = temp2+ws(k1,ipntr)*ws(k1,jpntr); temp3 = temp3+ws(k1,ipntr)*wy(k1,jpntr); } wn1(iy,jy) = temp1; wn1(iis,js) = temp2; wn1(iis,jy) = temp3; jpntr = jpntr%m+1; } jy = col; jpntr = head+col-1; if( jpntr>m ) { jpntr = jpntr-m; } ipntr = head; for(i = 1; i <= col; i++) { iis = m+i; temp3 = 0; for(k = pbegin; k <= pend; k++) { k1 = ind(k); temp3 = temp3+ws(k1,ipntr)*wy(k1,jpntr); } ipntr = ipntr%m+1; wn1(iis,jy) = temp3; } upcl = col-1; } else { upcl = col; } ipntr = head; for(iy = 1; iy <= upcl; iy++) { iis = m+iy; jpntr = head; for(jy = 1; jy <= iy; jy++) { js = m+jy; temp1 = 0; temp2 = 0; temp3 = 0; temp4 = 0; for(k = 1; k <= nenter; k++) { k1 = indx2(k); temp1 = temp1+wy(k1,ipntr)*wy(k1,jpntr); temp2 = temp2+ws(k1,ipntr)*ws(k1,jpntr); } for(k = ileave; k <= n; k++) { k1 = indx2(k); temp3 = temp3+wy(k1,ipntr)*wy(k1,jpntr); temp4 = temp4+ws(k1,ipntr)*ws(k1,jpntr); } wn1(iy,jy) = wn1(iy,jy)+temp1-temp3; wn1(iis,js) = wn1(iis,js)-temp2+temp4; jpntr = jpntr%m+1; } ipntr = ipntr%m+1; } ipntr = head; for(iis = m+1; iis <= m+upcl; iis++) { jpntr = head; for(jy = 1; jy <= upcl; jy++) { temp1 = 0; temp3 = 0; for(k = 1; k <= nenter; k++) { k1 = indx2(k); temp1 = temp1+ws(k1,ipntr)*wy(k1,jpntr); } for(k = ileave; k <= n; k++) { k1 = indx2(k); temp3 = temp3+ws(k1,ipntr)*wy(k1,jpntr); } if( iis<=jy+m ) { wn1(iis,jy) = wn1(iis,jy)+temp1-temp3; } else { wn1(iis,jy) = wn1(iis,jy)-temp1+temp3; } jpntr = jpntr%m+1; } ipntr = ipntr%m+1; } for(iy = 1; iy <= col; iy++) { iis = col+iy; is1 = m+iy; for(jy = 1; jy <= iy; jy++) { js = col+jy; js1 = m+jy; wn(jy,iy) = wn1(iy,jy)/theta; wn(js,iis) = wn1(is1,js1)*theta; } for(jy = 1; jy <= iy-1; jy++) { wn(jy,iis) = -wn1(is1,jy); } for(jy = iy; jy <= col; jy++) { wn(jy,iis) = wn1(is1,jy); } wn(iy,iy) = wn(iy,iy)+sy(iy,iy); } info = 0; if( !lbfgsbdpofa(wn, col) ) { info = -1; return; } col2 = 2*col; for(js = col+1; js <= col2; js++) { ap::vmove(workvec.getvector(1, col), wn.getcolumn(js, 1, col)); lbfgsbdtrsl(wn, col, workvec, 11, info); ap::vmove(wn.getcolumn(js, 1, col), workvec.getvector(1, col)); } for(iis = col+1; iis <= col2; iis++) { for(js = iis; js <= col2; js++) { v = ap::vdotproduct(wn.getcolumn(iis, 1, col), wn.getcolumn(js, 1, col)); wn(iis,js) = wn(iis,js)+v; } } for(j = 1; j <= col; j++) { ap::vmove(workmat.getrow(j, 1, col), wn.getrow(col+j, col+1, col+col)); } info = 0; if( !lbfgsbdpofa(workmat, col) ) { info = -2; return; } for(j = 1; j <= col; j++) { ap::vmove(wn.getrow(col+j, col+1, col+col), workmat.getrow(j, 1, col)); } } void lbfgsbformt(const int&, ap::real_2d_array& wt, const ap::real_2d_array& sy, const ap::real_2d_array& ss, const int& col, const ap::real_value_type& theta, int& info) { int i; int j; int k; int k1; ap::real_value_type ddum; for(j = 1; j <= col; j++) { wt(1,j) = theta*ss(1,j); } for(i = 2; i <= col; i++) { for(j = i; j <= col; j++) { k1 = ap::minint(i, j)-1; ddum = 0; for(k = 1; k <= k1; k++) { ddum = ddum+sy(i,k)*sy(j,k)/sy(k,k); } wt(i,j) = ddum+theta*ss(i,j); } } info = 0; if( !lbfgsbdpofa(wt, col) ) { info = -3; } } void lbfgsbfreev(const int& n, int& nfree, ap::integer_1d_array& index, int& nenter, int& ileave, ap::integer_1d_array& indx2, const ap::integer_1d_array& iwhere, bool& wrk, const bool& updatd, const bool& cnstnd, const int& iter) { int iact; int i; int k; nenter = 0; ileave = n+1; if( iter>0&&cnstnd ) { for(i = 1; i <= nfree; i++) { k = index(i); if( iwhere(k)>0 ) { ileave = ileave-1; indx2(ileave) = k; } } for(i = 1+nfree; i <= n; i++) { k = index(i); if( iwhere(k)<=0 ) { nenter = nenter+1; indx2(nenter) = k; } } } wrk = ileave0||updatd; nfree = 0; iact = n+1; for(i = 1; i <= n; i++) { if( iwhere(i)<=0 ) { nfree = nfree+1; index(nfree) = i; } else { iact = iact-1; index(iact) = i; } } } void lbfgsbhpsolb(const int& n, ap::real_1d_array& t, ap::integer_1d_array& iorder, const int& iheap) { int i; int j; int k; int indxin; int indxou; ap::real_value_type ddum; ap::real_value_type dout; if( iheap==0 ) { for(k = 2; k <= n; k++) { ddum = t(k); indxin = iorder(k); i = k; while(true) { if( i>1 ) { j = i/2; if( ddum1 ) { i = 1; dout = t(1); indxou = iorder(1); ddum = t(n); indxin = iorder(n); while(true) { j = i+i; if( j<=n-1 ) { if( t(j+1)=0 ) { stpmx = 0; } else { if( a1*stpmx0&&nbd(i)>=2 ) { a2 = u(i)-x(i); if( a2<=0 ) { stpmx = 0; } else { if( a1*stpmx>a2 ) { stpmx = a2/a1; } } } } } } } } if( iter==0&&!boxed ) { stp = ap::minreal(1/dnrm, stpmx); } else { stp = 1; } ap::vmove(t.getvector(1, n), x.getvector(1, n)); ap::vmove(r.getvector(1, n), g.getvector(1, n)); fold = f; ifun = 0; iback = 0; csave = 0; } v = ap::vdotproduct(g.getvector(1, n), d.getvector(1, n)); gd = v; if( ifun==0 ) { gdold = gd; if( gd>=0 ) { info = -4; return; } } lbfgsbdcsrch(f, gd, stp, ftol, gtol, xtol, ap::real_value_type(0), stpmx, csave, isave, dsave, addinfo); xstep = stp*dnrm; if( csave!=4&&csave!=3 ) { task = 1; ifun = ifun+1; nfgv = nfgv+1; iback = ifun-1; if( stp==1 ) { ap::vmove(x.getvector(1, n), z.getvector(1, n)); } else { for(i = 1; i <= n; i++) { x(i) = stp*d(i)+t(i); } } } else { task = 5; } } void lbfgsbmatupd(const int& n, const int& m, ap::real_2d_array& ws, ap::real_2d_array& wy, ap::real_2d_array& sy, ap::real_2d_array& ss, const ap::real_1d_array& d, const ap::real_1d_array& r, int& itail, const int& iupdat, int& col, int& head, ap::real_value_type& theta, const ap::real_value_type& rr, const ap::real_value_type& dr, const ap::real_value_type& stp, const ap::real_value_type& dtd) { int j; int pointr; ap::real_value_type v; if( iupdat<=m ) { col = iupdat; itail = (head+iupdat-2)%m+1; } else { itail = itail%m+1; head = head%m+1; } ap::vmove(ws.getcolumn(itail, 1, n), d.getvector(1, n)); ap::vmove(wy.getcolumn(itail, 1, n), r.getvector(1, n)); theta = rr/dr; if( iupdat>m ) { for(j = 1; j <= col-1; j++) { ap::vmove(ss.getcolumn(j, 1, j), ss.getcolumn(j+1, 2, j+1)); ap::vmove(sy.getcolumn(j, j, col-1), sy.getcolumn(j+1, j+1, col)); } } pointr = head; for(j = 1; j <= col-1; j++) { v = ap::vdotproduct(d.getvector(1, n), wy.getcolumn(pointr, 1, n)); sy(col,j) = v; v = ap::vdotproduct(ws.getcolumn(pointr, 1, n), d.getvector(1, n)); ss(j,col) = v; pointr = pointr%m+1; } if( stp==1 ) { ss(col,col) = dtd; } else { ss(col,col) = stp*stp*dtd; } sy(col,col) = dr; } void lbfgsbprojgr(const int& n, const ap::real_1d_array& l, const ap::real_1d_array& u, const ap::integer_1d_array& nbd, const ap::real_1d_array& x, const ap::real_1d_array& g, ap::real_value_type& sbgnrm) { int i; ap::real_value_type gi; sbgnrm = 0; for(i = 1; i <= n; i++) { gi = g(i); if( nbd(i)!=0 ) { if( gi<0 ) { if( nbd(i)>=2 ) { gi = ap::maxreal(x(i)-u(i), gi); } } else { if( nbd(i)<=2 ) { gi = ap::minreal(x(i)-l(i), gi); } } } sbgnrm = ap::maxreal(sbgnrm, fabs(gi)); } } void lbfgsbsubsm(const int&, const int& m, const int& nsub, const ap::integer_1d_array& ind, const ap::real_1d_array& l, const ap::real_1d_array& u, const ap::integer_1d_array& nbd, ap::real_1d_array& x, ap::real_1d_array& d, const ap::real_2d_array& ws, const ap::real_2d_array& wy, const ap::real_value_type& theta, const int& col, const int& head, int& iword, ap::real_1d_array& wv, ap::real_2d_array& wn, int& info) { int pointr; int col2; int ibd = 0; int jy; int js; int i; int j; int k; ap::real_value_type alpha; ap::real_value_type dk; ap::real_value_type temp1; ap::real_value_type temp2; if( nsub<=0 ) { return; } pointr = head; for(i = 1; i <= col; i++) { temp1 = 0; temp2 = 0; for(j = 1; j <= nsub; j++) { k = ind(j); temp1 = temp1+wy(k,pointr)*d(j); temp2 = temp2+ws(k,pointr)*d(j); } wv(i) = temp1; wv(col+i) = theta*temp2; pointr = pointr%m+1; } col2 = 2*col; lbfgsbdtrsl(wn, col2, wv, 11, info); if( info!=0 ) { return; } for(i = 1; i <= col; i++) { wv(i) = -wv(i); } lbfgsbdtrsl(wn, col2, wv, 1, info); if( info!=0 ) { return; } pointr = head; for(jy = 1; jy <= col; jy++) { js = col+jy; for(i = 1; i <= nsub; i++) { k = ind(i); d(i) = d(i)+wy(k,pointr)*wv(jy)/theta+ws(k,pointr)*wv(js); } pointr = pointr%m+1; } for(i = 1; i <= nsub; i++) { d(i) = d(i)/theta; } alpha = 1; temp1 = alpha; for(i = 1; i <= nsub; i++) { k = ind(i); dk = d(i); if( nbd(k)!=0 ) { if( dk<0&&nbd(k)<=2 ) { temp2 = l(k)-x(k); if( temp2>=0 ) { temp1 = 0; } else { if( dk*alpha0&&nbd(k)>=2 ) { temp2 = u(k)-x(k); if( temp2<=0 ) { temp1 = 0; } else { if( dk*alpha>temp2 ) { temp1 = temp2/dk; } } } } if( temp10 ) { x(k) = u(k); d(ibd) = 0; } else { if( dk<0 ) { x(k) = l(k); d(ibd) = 0; } } } for(i = 1; i <= nsub; i++) { k = ind(i); x(k) = x(k)+alpha*d(i); } if( alpha<1 ) { iword = 1; } else { iword = 0; } } void lbfgsbdcsrch(const ap::real_value_type& f, const ap::real_value_type& g, ap::real_value_type& stp, const ap::real_value_type& ftol, const ap::real_value_type& gtol, const ap::real_value_type& xtol, const ap::real_value_type& stpmin, const ap::real_value_type& stpmax, int& task, ap::integer_1d_array& isave, ap::real_1d_array& dsave, int& addinfo) { bool brackt; int stage; ap::real_value_type finit; ap::real_value_type ftest; ap::real_value_type fm; ap::real_value_type fx; ap::real_value_type fxm; ap::real_value_type fy; ap::real_value_type fym; ap::real_value_type ginit; ap::real_value_type gtest; ap::real_value_type gm; ap::real_value_type gx; ap::real_value_type gxm; ap::real_value_type gy; ap::real_value_type gym; ap::real_value_type stx; ap::real_value_type sty; ap::real_value_type stmin; ap::real_value_type stmax; ap::real_value_type width; ap::real_value_type width1; ap::real_value_type xtrapl; ap::real_value_type xtrapu; xtrapl = 1.1E0; xtrapu = 4.0E0; while(true) { if( task==0 ) { if( stpstpmax ) { task = 2; addinfo = 0; } if( g>=0 ) { task = 2; addinfo = 0; } if( ftol<0 ) { task = 2; addinfo = 0; } if( gtol<0 ) { task = 2; addinfo = 0; } if( xtol<0 ) { task = 2; addinfo = 0; } if( stpmin<0 ) { task = 2; addinfo = 0; } if( stpmax=0 ) { stage = 2; } if( brackt&&(stp<=stmin||stp>=stmax) ) { task = 3; addinfo = 1; } if( brackt&&stmax-stmin<=xtol*stmax ) { task = 3; addinfo = 2; } if( stp==stpmax&&f<=ftest&&g<=gtest ) { task = 3; addinfo = 3; } if( stp==stpmin&&(f>ftest||g>=gtest) ) { task = 3; addinfo = 4; } if( f<=ftest&&fabs(g)<=gtol*(-ginit) ) { task = 4; addinfo = -1; } if( task==3||task==4 ) { break; } if( stage==1&&f<=fx&&f>ftest ) { fm = f-stp*gtest; fxm = fx-stx*gtest; fym = fy-sty*gtest; gm = g-gtest; gxm = gx-gtest; gym = gy-gtest; lbfgsbdcstep(stx, fxm, gxm, sty, fym, gym, stp, fm, gm, brackt, stmin, stmax); fx = fxm+stx*gtest; fy = fym+sty*gtest; gx = gxm+gtest; gy = gym+gtest; } else { lbfgsbdcstep(stx, fx, gx, sty, fy, gy, stp, f, g, brackt, stmin, stmax); } if( brackt ) { if( fabs(sty-stx)>=0.66*width1 ) { stp = stx+0.5*(sty-stx); } width1 = width; width = fabs(sty-stx); } if( brackt ) { stmin = ap::minreal(stx, sty); stmax = ap::maxreal(stx, sty); } else { stmin = stp+xtrapl*(stp-stx); stmax = stp+xtrapu*(stp-stx); } stp = ap::maxreal(stp, stpmin); stp = ap::minreal(stp, stpmax); if( (brackt&&(stp<=stmin||stp>=stmax))||(brackt&&stmax-stmin<=xtol*stmax) ) { stp = stx; } task = 1; break; } if( brackt ) { isave(1) = 1; } else { isave(1) = 0; } isave(2) = stage; dsave(1) = ginit; dsave(2) = gtest; dsave(3) = gx; dsave(4) = gy; dsave(5) = finit; dsave(6) = fx; dsave(7) = fy; dsave(8) = stx; dsave(9) = sty; dsave(10) = stmin; dsave(11) = stmax; dsave(12) = width; dsave(13) = width1; } void lbfgsbdcstep(ap::real_value_type& stx, ap::real_value_type& fx, ap::real_value_type& dx, ap::real_value_type& sty, ap::real_value_type& fy, ap::real_value_type& dy, ap::real_value_type& stp, const ap::real_value_type& fp, const ap::real_value_type& dp, bool& brackt, const ap::real_value_type& stpmin, const ap::real_value_type& stpmax) { ap::real_value_type gamma; ap::real_value_type p; ap::real_value_type q; ap::real_value_type r; ap::real_value_type s; ap::real_value_type sgnd; ap::real_value_type stpc; ap::real_value_type stpf; ap::real_value_type stpq; ap::real_value_type theta; sgnd = dp*(dx/fabs(dx)); if( fp>fx ) { theta = 3*(fx-fp)/(stp-stx)+dx+dp; s = ap::maxreal(fabs(theta), ap::maxreal(fabs(dx), fabs(dp))); gamma = s*sqrt(ap::sqr(theta/s)-dx/s*(dp/s)); if( stpstx ) { gamma = -gamma; } p = gamma-dp+theta; q = gamma-dp+gamma+dx; r = p/q; stpc = stp+r*(stx-stp); stpq = stp+dp/(dp-dx)*(stx-stp); if( fabs(stpc-stp)>fabs(stpq-stp) ) { stpf = stpc; } else { stpf = stpq; } brackt = true; } else { if( fabs(dp)stx ) { gamma = -gamma; } p = gamma-dp+theta; q = gamma+(dx-dp)+gamma; r = p/q; if( r<0&&gamma!=0 ) { stpc = stp+r*(stx-stp); } else { if( stp>stx ) { stpc = stpmax; } else { stpc = stpmin; } } stpq = stp+dp/(dp-dx)*(stx-stp); if( brackt ) { if( fabs(stpc-stp)stx ) { stpf = ap::minreal(stp+0.66*(sty-stp), stpf); } else { stpf = ap::maxreal(stp+0.66*(sty-stp), stpf); } } else { if( fabs(stpc-stp)>fabs(stpq-stp) ) { stpf = stpc; } else { stpf = stpq; } stpf = ap::minreal(stpmax, stpf); stpf = ap::maxreal(stpmin, stpf); } } else { if( brackt ) { theta = 3*(fp-fy)/(sty-stp)+dy+dp; s = ap::maxreal(fabs(theta), ap::maxreal(fabs(dy), fabs(dp))); gamma = s*sqrt(ap::sqr(theta/s)-dy/s*(dp/s)); if( stp>sty ) { gamma = -gamma; } p = gamma-dp+theta; q = gamma-dp+gamma+dy; r = p/q; stpc = stp+r*(sty-stp); stpf = stpc; } else { if( stp>stx ) { stpf = stpmax; } else { stpf = stpmin; } } } } } if( fp>fx ) { sty = stp; fy = fp; dy = dp; } else { if( sgnd<0 ) { sty = stx; fy = fx; dy = dx; } stx = stp; fx = fp; dx = dp; } stp = stpf; } bool additionallbfgsbstoppingcriterion(int, const ap::real_1d_array&, ap::real_value_type, const ap::real_1d_array&) { bool result; result = false; return result; } bool lbfgsbdpofa(ap::real_2d_array& a, const int& n) { bool result; ap::real_value_type t; ap::real_value_type s; ap::real_value_type v; int j; int jm1; int k; for(j = 1; j <= n; j++) { s = 0.0; jm1 = j-1; if( jm1>=1 ) { for(k = 1; k <= jm1; k++) { v = ap::vdotproduct(a.getcolumn(k, 1, k-1), a.getcolumn(j, 1, k-1)); t = a(k,j)-v; t = t/a(k,k); a(k,j) = t; s = s+t*t; } } s = a(j,j)-s; if( s<=0.0 ) { result = false; return result; } a(j,j) = sqrt(s); } result = true; return result; } void lbfgsbdtrsl(ap::real_2d_array& t, const int& n, ap::real_1d_array& b, const int& job, int& info) { ap::real_value_type temp; ap::real_value_type v; int cse; int j; int jj; for(j = 1; j <= n; j++) { if( t(j,j)==0.0 ) { info = j; return; } } info = 0; cse = 1; if( job%10!=0 ) { cse = 2; } if( job%100/10!=0 ) { cse = cse+2; } if( cse==1 ) { b(1) = b(1)/t(1,1); if( n<2 ) { return; } for(j = 2; j <= n; j++) { temp = -b(j-1); ap::vadd(b.getvector(j, n), t.getcolumn(j-1, j, n), temp); b(j) = b(j)/t(j,j); } return; } if( cse==2 ) { b(n) = b(n)/t(n,n); if( n<2 ) { return; } for(jj = 2; jj <= n; jj++) { j = n-jj+1; temp = -b(j+1); ap::vadd(b.getvector(1, j), t.getcolumn(j+1, 1, j), temp); b(j) = b(j)/t(j,j); } return; } if( cse==3 ) { b(n) = b(n)/t(n,n); if( n<2 ) { return; } for(jj = 2; jj <= n; jj++) { j = n-jj+1; v = ap::vdotproduct(t.getcolumn(j, j+1, j+1+jj-1-1), b.getvector(j+1, j+1+jj-1-1)); b(j) = b(j)-v; b(j) = b(j)/t(j,j); } return; } if( cse==4 ) { b(1) = b(1)/t(1,1); if( n<2 ) { return; } for(j = 2; j <= n; j++) { v = ap::vdotproduct(t.getcolumn(j, 1, j-1), b.getvector(1, j-1)); b(j) = b(j)-v; b(j) = b(j)/t(j,j); } return; } } void lbfgsbnewiteration(const ap::real_1d_array&, ap::real_value_type, const ap::real_1d_array&) { } /************************************************************************* The subroutine minimizes the function F(x) of N arguments with simple constraints using a quasi-Newton method (LBFGS scheme) which is optimized to use a minimum amount of memory. The subroutine generates the approximation of an inverse Hessian matrix by using information about the last M steps of the algorithm (instead of N). It lessens a required amount of memory from a value of order N^2 to a value of order 2*N*M. This subroutine uses the FuncGrad subroutine which calculates the value of the function F and gradient G in point X. The programmer should define the FuncGrad subroutine by himself. It should be noted that the subroutine doesn't need to waste time for memory allocation of array G, because the memory is allocated in calling the subroutine. Setting a dimension of array G each time when calling a subroutine will excessively slow down an algorithm. The programmer could also redefine the LBFGSNewIteration subroutine which is called on each new step. The current point X, the function value F and the gradient G are passed into this subroutine. It is reasonable to redefine the subroutine for better debugging, for example, to visualize the solution process. Input parameters: N - problem dimension. N>0 M - number of corrections in the BFGS scheme of Hessian approximation update. Recommended value: 3<=M<=7. The smaller value causes worse convergence, the bigger will not cause a considerably better convergence, but will cause a fall in the performance. M<=N. X - initial solution approximation. Array whose index ranges from 1 to N. EpsG - positive number which defines a precision of search. The subroutine finishes its work if the condition ||G|| < EpsG is satisfied, where ||.|| means Euclidian norm, G - gradient projection onto a feasible set, X - current approximation. EpsF - positive number which defines a precision of search. The subroutine finishes its work if on iteration number k+1 the condition |F(k+1)-F(k)| <= EpsF*max{|F(k)|, |F(k+1)|, 1} is satisfied. EpsX - positive number which defines a precision of search. The subroutine finishes its work if on iteration number k+1 the condition |X(k+1)-X(k)| <= EpsX is satisfied. MaxIts - maximum number of iterations. If MaxIts=0, the number of iterations is unlimited. NBD - constraint type. If NBD(i) is equal to: * 0, X(i) has no constraints, * 1, X(i) has only lower boundary, * 2, X(i) has both lower and upper boundaries, * 3, X(i) has only upper boundary, Array whose index ranges from 1 to N. L - lower boundaries of X(i) variables. Array whose index ranges from 1 to N. U - upper boundaries of X(i) variables. Array whose index ranges from 1 to N. Output parameters: X - solution approximation. Array whose index ranges from 1 to N. Info - a return code: * -2 unknown internal error, * -1 wrong parameters were specified, * 0 interrupted by user, * 1 relative function decreasing is less or equal to EpsF, * 2 step is less or equal to EpsX, * 4 gradient norm is less or equal to EpsG, * 5 number of iterations exceeds MaxIts. FuncGrad routine description. User-defined. Input parameters: X - array whose index ranges from 1 to N. Output parameters: F - function value at X. G - function gradient. Array whose index ranges from 1 to N. The memory for array G has already been allocated in the calling subroutine, and it isn't necessary to allocate it in the FuncGrad subroutine. NEOS, November 1994. (Latest revision June 1996.) Optimization Technology Center. Argonne National Laboratory and Northwestern University. Written by Ciyou Zhu in collaboration with R.H. Byrd, P. Lu-Chen and J. Nocedal. *************************************************************************/ void lbfgsbminimize ( FunctionAndGradient *const functionAndGradient, const int& n, const int& m, ap::real_1d_array& x, const ap::real_value_type& epsg, const ap::real_value_type& epsf, const ap::real_value_type& epsx, const int& maxits, const ap::integer_1d_array& nbd, const ap::real_1d_array& l, const ap::real_1d_array& u, int& info ) { ap::real_value_type f; ap::real_1d_array g; ap::real_1d_array xold; ap::real_1d_array xdiff; ap::real_2d_array ws; ap::real_2d_array wy; ap::real_2d_array sy; ap::real_2d_array ss; ap::real_2d_array yy; ap::real_2d_array wt; ap::real_2d_array wn; ap::real_2d_array snd; ap::real_1d_array z; ap::real_1d_array r; ap::real_1d_array d; ap::real_1d_array t; ap::real_1d_array wa; ap::real_1d_array sg; ap::real_1d_array sgo; ap::real_1d_array yg; ap::real_1d_array ygo; ap::integer_1d_array index; ap::integer_1d_array iwhere; ap::integer_1d_array indx2; int csave; ap::boolean_1d_array lsave; ap::integer_1d_array isave; ap::real_1d_array dsave; int task = 0; bool prjctd; bool cnstnd; bool boxed; bool updatd; bool wrk; int i; int k; int nintol; int iback; int nskip; int head; int col; int iter; int itail; int iupdat; int nint; int nfgv; int internalinfo; int ifun; int iword; int nfree; int ileave; int nenter; ap::real_value_type theta; ap::real_value_type fold = 0; ap::real_value_type dr; ap::real_value_type rr; ap::real_value_type dnrm; ap::real_value_type xstep; ap::real_value_type sbgnrm; ap::real_value_type ddum; ap::real_value_type dtd; ap::real_value_type gd; ap::real_value_type gdold = 0; ap::real_value_type stp; ap::real_value_type stpmx; ap::real_value_type tf; ap::real_1d_array workvec; ap::real_1d_array workvec2; ap::real_1d_array dsave13; ap::real_1d_array wa0; ap::real_1d_array wa1; ap::real_1d_array wa2; ap::real_1d_array wa3; ap::real_2d_array workmat; ap::integer_1d_array isave2; workvec.setbounds(1, m); workvec2.setbounds(1, 2*m); workmat.setbounds(1, m, 1, m); isave2.setbounds(1, 2); dsave13.setbounds(1, 13); wa0.setbounds(1, 2*m); wa1.setbounds(1, 2*m); wa2.setbounds(1, 2*m); wa3.setbounds(1, 2*m); g.setbounds(1, n); xold.setbounds(1, n); xdiff.setbounds(1, n); ws.setbounds(1, n, 1, m); wy.setbounds(1, n, 1, m); sy.setbounds(1, m, 1, m); ss.setbounds(1, m, 1, m); yy.setbounds(1, m, 1, m); wt.setbounds(1, m, 1, m); wn.setbounds(1, 2*m, 1, 2*m); snd.setbounds(1, 2*m, 1, 2*m); z.setbounds(1, n); r.setbounds(1, n); d.setbounds(1, n); t.setbounds(1, n); wa.setbounds(1, 8*m); sg.setbounds(1, m); sgo.setbounds(1, m); yg.setbounds(1, m); ygo.setbounds(1, m); index.setbounds(1, n); iwhere.setbounds(1, n); indx2.setbounds(1, n); lsave.setbounds(1, 4); isave.setbounds(1, 23); dsave.setbounds(1, 29); col = 0; head = 1; theta = 1; iupdat = 0; updatd = false; iter = 0; nfgv = 0; nint = 0; nintol = 0; nskip = 0; nfree = n; internalinfo = 0; ap::lbfgsberrclb(n, m, epsf, l, u, nbd, task, internalinfo, k); if( task==2||maxits<0||epsg<0||epsx<0 ) { info = -1; return; } ap::lbfgsbactive(n, l, u, nbd, x, iwhere, prjctd, cnstnd, boxed); ap::vmove(xold.getvector(1, n), x.getvector(1, n)); functionAndGradient->Evaluate(x, f, g); nfgv = 1; ap::lbfgsbprojgr(n, l, u, nbd, x, g, sbgnrm); if( sbgnrm<=epsg ) { info = 4; return; } while(true) { iword = -1; if( !cnstnd&&col>0 ) { ap::vmove(z.getvector(1, n), x.getvector(1, n)); wrk = updatd; nint = 0; } else { ap::vmove(wa0.getvector(1, 2*m), wa.getvector(1, 2*m)); ap::vmove(wa1.getvector(1, 2*m), wa.getvector(2*m+1, 4*m)); ap::vmove(wa2.getvector(1, 2*m), wa.getvector(4*m+1, 6*m)); ap::vmove(wa3.getvector(1, 2*m), wa.getvector(6*m+1, 8*m)); ap::lbfgsbcauchy(n, x, l, u, nbd, g, indx2, iwhere, t, d, z, m, wy, ws, sy, wt, theta, col, head, wa0, wa1, wa2, wa3, nint, sg, yg, sbgnrm, internalinfo, workvec); ap::vmove(wa.getvector(1, 2*m), wa0.getvector(1, 2*m)); ap::vmove(wa.getvector(2*m+1, 4*m), wa1.getvector(1, 2*m)); ap::vmove(wa.getvector(4*m+1, 6*m), wa2.getvector(1, 2*m)); ap::vmove(wa.getvector(6*m+1, 8*m), wa3.getvector(1, 2*m)); if( internalinfo!=0 ) { internalinfo = 0; col = 0; head = 1; theta = 1; iupdat = 0; updatd = false; continue; } nintol = nintol+nint; ap::lbfgsbfreev(n, nfree, index, nenter, ileave, indx2, iwhere, wrk, updatd, cnstnd, iter); } if( nfree!=0&&col!=0 ) { if( wrk ) { ap::lbfgsbformk(n, nfree, index, nenter, ileave, indx2, iupdat, updatd, wn, snd, m, ws, wy, sy, theta, col, head, internalinfo, workvec, workmat); } if( internalinfo!=0 ) { internalinfo = 0; col = 0; head = 1; theta = 1; iupdat = 0; updatd = false; continue; } ap::lbfgsbcmprlb(n, m, x, g, ws, wy, sy, wt, z, r, wa, index, theta, col, head, nfree, cnstnd, internalinfo, workvec, workvec2); if( internalinfo==0 ) { ap::lbfgsbsubsm(n, m, nfree, index, l, u, nbd, z, r, ws, wy, theta, col, head, iword, wa, wn, internalinfo); } if( internalinfo!=0 ) { internalinfo = 0; col = 0; head = 1; theta = 1; iupdat = 0; updatd = false; continue; } } for(i = 1; i <= n; i++) { d(i) = z(i)-x(i); } task = 0; while(true) { ap::lbfgsblnsrlb(n, l, u, nbd, x, f, fold, gd, gdold, g, d, r, t, z, stp, dnrm, dtd, xstep, stpmx, iter, ifun, iback, nfgv, internalinfo, task, boxed, cnstnd, csave, isave2, dsave13); if( internalinfo!=0||iback>=20||task!=1 ) { break; } functionAndGradient->Evaluate(x, f, g); } if( internalinfo!=0 ) { ap::vmove(x.getvector(1, n), t.getvector(1, n)); ap::vmove(g.getvector(1, n), r.getvector(1, n)); f = fold; if( col==0 ) { task = 2; iter = iter+1; functionAndGradient->NextIteration( iter ); info = -2; return; } else { internalinfo = 0; col = 0; head = 1; theta = 1; iupdat = 0; updatd = false; continue; } } iter = iter+1; functionAndGradient->NextIteration( iter ); ap::lbfgsbnewiteration(x, f, g); ap::lbfgsbprojgr(n, l, u, nbd, x, g, sbgnrm); if( sbgnrm<=epsg ) { info = 4; return; } ap::vmove(xdiff.getvector(1, n), xold.getvector(1, n)); ap::vsub(xdiff.getvector(1, n), x.getvector(1, n)); tf = ap::vdotproduct(xdiff.getvector(1, n), xdiff.getvector(1, n)); tf = sqrt(tf); if( tf<=epsx ) { info = 2; return; } ddum = ap::maxreal(fabs(fold), ap::maxreal(fabs(f), ap::real_value_type(1))); if( fold-f<=epsf*ddum ) { info = 1; return; } if( iter>maxits&&maxits>0 ) { info = 5; return; } if( additionallbfgsbstoppingcriterion(iter, x, f, g) ) { info = 0; return; } ap::vmove(xold.getvector(1, n), x.getvector(1, n)); for(i = 1; i <= n; i++) { r(i) = g(i)-r(i); } rr = ap::vdotproduct(r.getvector(1, n), r.getvector(1, n)); if( stp==1 ) { dr = gd-gdold; ddum = -gdold; } else { dr = (gd-gdold)*stp; ap::vmul(d.getvector(1, n), stp); ddum = -gdold*stp; } if( dr<=ap::machineepsilon*ddum ) { nskip = nskip+1; updatd = false; } else { updatd = true; iupdat = iupdat+1; ap::lbfgsbmatupd(n, m, ws, wy, sy, ss, d, r, itail, iupdat, col, head, theta, rr, dr, stp, dtd); ap::lbfgsbformt(m, wt, sy, ss, col, theta, internalinfo); if( internalinfo!=0 ) { internalinfo = 0; col = 0; head = 1; theta = 1; iupdat = 0; updatd = false; continue; } } } } } // namespace ap cmtk-3.3.1/libs/Numerics/lbfgsb.h000066400000000000000000000343271276303427400166230ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2011 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 3236 $ // // $LastChangedDate: 2011-05-17 15:16:24 -0700 (Tue, 17 May 2011) $ // // $LastChangedBy: torstenrohlfing $ // */ /************************************************************************* NEOS, November 1994. (Latest revision June 1996.) Optimization Technology Center. Argonne National Laboratory and Northwestern University. Written by Ciyou Zhu in collaboration with R.H. Byrd, P. Lu-Chen and J. Nocedal. Contributors: * Sergey Bochkanov (ALGLIB project). Translation from FORTRAN to pseudocode. This software is freely available, but we expect that all publications describing work using this software, or all commercial products using it, quote at least one of the references given below: * R. H. Byrd, P. Lu and J. Nocedal. A Limited Memory Algorithm for Bound Constrained Optimization, (1995), SIAM Journal on Scientific and Statistical Computing , 16, 5, pp. 1190-1208. * C. Zhu, R.H. Byrd and J. Nocedal. L-BFGS-B: Algorithm 778: L-BFGS-B, FORTRAN routines for large scale bound constrained optimization (1997), ACM Transactions on Mathematical Software, Vol 23, Num. 4, pp. 550 - 560. *************************************************************************/ #ifndef _lbfgsb_h #define _lbfgsb_h #include "ap.h" /*----------------------------------------------- This routines must be defined by you: void funcgrad(const ap::real_1d_array& x, ap::real_value_type& f, ap::real_1d_array& g); -----------------------------------------------*/ namespace ap { /// Class that creates a callback-like connection to a CMTK functional class. class FunctionAndGradient { public: /// Virtual destructor. virtual ~FunctionAndGradient() {} /// Evaluate function value and gradient. virtual void Evaluate( const ap::real_1d_array& x, ap::real_value_type& f, ap::real_1d_array& g ) = 0; /// Get notified when L-BFGS-B goes into next iteration. virtual void NextIteration( const int //!< The current iteration number. ) {}; }; } // namespace ap /************************************************************************* The subroutine minimizes the function F(x) of N arguments with simple constraints using a quasi-Newton method (LBFGS scheme) which is optimized to use a minimum amount of memory. The subroutine generates the approximation of an inverse Hessian matrix by using information about the last M steps of the algorithm (instead of N). It lessens a required amount of memory from a value of order N^2 to a value of order 2*N*M. This subroutine uses the FuncGrad subroutine which calculates the value of the function F and gradient G in point X. The programmer should define the FuncGrad subroutine by himself. It should be noted that the subroutine doesn't need to waste time for memory allocation of array G, because the memory is allocated in calling the subroutine. Setting a dimension of array G each time when calling a subroutine will excessively slow down an algorithm. The programmer could also redefine the LBFGSNewIteration subroutine which is called on each new step. The current point X, the function value F and the gradient G are passed into this subroutine. It is reasonable to redefine the subroutine for better debugging, for example, to visualize the solution process. Input parameters: N - problem dimension. N>0 M - number of corrections in the BFGS scheme of Hessian approximation update. Recommended value: 3<=M<=7. The smaller value causes worse convergence, the bigger will not cause a considerably better convergence, but will cause a fall in the performance. M<=N. X - initial solution approximation. Array whose index ranges from 1 to N. EpsG - positive number which defines a precision of search. The subroutine finishes its work if the condition ||G|| < EpsG is satisfied, where ||.|| means Euclidian norm, G - gradient projection onto a feasible set, X - current approximation. EpsF - positive number which defines a precision of search. The subroutine finishes its work if on iteration number k+1 the condition |F(k+1)-F(k)| <= EpsF*max{|F(k)|, |F(k+1)|, 1} is satisfied. EpsX - positive number which defines a precision of search. The subroutine finishes its work if on iteration number k+1 the condition |X(k+1)-X(k)| <= EpsX is satisfied. MaxIts - maximum number of iterations. If MaxIts=0, the number of iterations is unlimited. NBD - constraint type. If NBD(i) is equal to: * 0, X(i) has no constraints, * 1, X(i) has only lower boundary, * 2, X(i) has both lower and upper boundaries, * 3, X(i) has only upper boundary, Array whose index ranges from 1 to N. L - lower boundaries of X(i) variables. Array whose index ranges from 1 to N. U - upper boundaries of X(i) variables. Array whose index ranges from 1 to N. Output parameters: X - solution approximation. Array whose index ranges from 1 to N. Info - a return code: * -2 unknown internal error, * -1 wrong parameters were specified, * 0 interrupted by user, * 1 relative function decreasing is less or equal to EpsF, * 2 step is less or equal to EpsX, * 4 gradient norm is less or equal to EpsG, * 5 number of iterations exceeds MaxIts. FuncGrad routine description. User-defined. Input parameters: X - array whose index ranges from 1 to N. Output parameters: F - function value at X. G - function gradient. Array whose index ranges from 1 to N. The memory for array G has already been allocated in the calling subroutine, and it isn't necessary to allocate it in the FuncGrad subroutine. NEOS, November 1994. (Latest revision June 1996.) Optimization Technology Center. Argonne National Laboratory and Northwestern University. Written by Ciyou Zhu in collaboration with R.H. Byrd, P. Lu-Chen and J. Nocedal. *************************************************************************/ namespace ap { void lbfgsbminimize ( FunctionAndGradient *const functionAndGradient, const int& n, const int& m, ap::real_1d_array& x, const ap::real_value_type& epsg, const ap::real_value_type& epsf, const ap::real_value_type& epsx, const int& maxits, const ap::integer_1d_array& nbd, const ap::real_1d_array& l, const ap::real_1d_array& u, int& info ); void lbfgsbactive(const int& n, const ap::real_1d_array& l, const ap::real_1d_array& u, const ap::integer_1d_array& nbd, ap::real_1d_array& x, ap::integer_1d_array& iwhere, bool& prjctd, bool& cnstnd, bool& boxed); void lbfgsbbmv(const int& m, const ap::real_2d_array& sy, ap::real_2d_array& wt, const int& col, const ap::real_1d_array& v, ap::real_1d_array& p, int& info, ap::real_1d_array& workvec); void lbfgsbcauchy(const int& n, const ap::real_1d_array& x, const ap::real_1d_array& l, const ap::real_1d_array& u, const ap::integer_1d_array& nbd, const ap::real_1d_array& g, ap::integer_1d_array& iorder, ap::integer_1d_array& iwhere, ap::real_1d_array& t, ap::real_1d_array& d, ap::real_1d_array& xcp, const int& m, const ap::real_2d_array& wy, const ap::real_2d_array& ws, const ap::real_2d_array& sy, ap::real_2d_array& wt, const ap::real_value_type& theta, const int& col, const int& head, ap::real_1d_array& p, ap::real_1d_array& c, ap::real_1d_array& wbp, ap::real_1d_array& v, int& nint, const ap::real_1d_array& sg, const ap::real_1d_array& yg, const ap::real_value_type& sbgnrm, int& info, ap::real_1d_array& workvec); void lbfgsbcmprlb(const int& n, const int& m, const ap::real_1d_array& x, const ap::real_1d_array& g, const ap::real_2d_array& ws, const ap::real_2d_array& wy, const ap::real_2d_array& sy, ap::real_2d_array& wt, const ap::real_1d_array& z, ap::real_1d_array& r, ap::real_1d_array& wa, const ap::integer_1d_array& index, const ap::real_value_type& theta, const int& col, const int& head, const int& nfree, const bool& cnstnd, int& info, ap::real_1d_array& workvec, ap::real_1d_array& workvec2); void lbfgsberrclb(const int& n, const int& m, const ap::real_value_type& factr, const ap::real_1d_array& l, const ap::real_1d_array& u, const ap::integer_1d_array& nbd, int& task, int& info, int& k); void lbfgsbformk(const int& n, const int& nsub, const ap::integer_1d_array& ind, const int& nenter, const int& ileave, const ap::integer_1d_array& indx2, const int& iupdat, const bool& updatd, ap::real_2d_array& wn, ap::real_2d_array& wn1, const int& m, const ap::real_2d_array& ws, const ap::real_2d_array& wy, const ap::real_2d_array& sy, const ap::real_value_type& theta, const int& col, const int& head, int& info, ap::real_1d_array& workvec, ap::real_2d_array& workmat); void lbfgsbformt(const int& m, ap::real_2d_array& wt, const ap::real_2d_array& sy, const ap::real_2d_array& ss, const int& col, const ap::real_value_type& theta, int& info); void lbfgsbfreev(const int& n, int& nfree, ap::integer_1d_array& index, int& nenter, int& ileave, ap::integer_1d_array& indx2, const ap::integer_1d_array& iwhere, bool& wrk, const bool& updatd, const bool& cnstnd, const int& iter); void lbfgsbhpsolb(const int& n, ap::real_1d_array& t, ap::integer_1d_array& iorder, const int& iheap); void lbfgsblnsrlb(const int& n, const ap::real_1d_array& l, const ap::real_1d_array& u, const ap::integer_1d_array& nbd, ap::real_1d_array& x, const ap::real_value_type& f, ap::real_value_type& fold, ap::real_value_type& gd, ap::real_value_type& gdold, const ap::real_1d_array& g, const ap::real_1d_array& d, ap::real_1d_array& r, ap::real_1d_array& t, const ap::real_1d_array& z, ap::real_value_type& stp, ap::real_value_type& dnrm, ap::real_value_type& dtd, ap::real_value_type& xstep, ap::real_value_type& stpmx, const int& iter, int& ifun, int& iback, int& nfgv, int& info, int& task, const bool& boxed, const bool& cnstnd, int& csave, ap::integer_1d_array& isave, ap::real_1d_array& dsave); void lbfgsbmatupd(const int& n, const int& m, ap::real_2d_array& ws, ap::real_2d_array& wy, ap::real_2d_array& sy, ap::real_2d_array& ss, const ap::real_1d_array& d, const ap::real_1d_array& r, int& itail, const int& iupdat, int& col, int& head, ap::real_value_type& theta, const ap::real_value_type& rr, const ap::real_value_type& dr, const ap::real_value_type& stp, const ap::real_value_type& dtd); void lbfgsbprojgr(const int& n, const ap::real_1d_array& l, const ap::real_1d_array& u, const ap::integer_1d_array& nbd, const ap::real_1d_array& x, const ap::real_1d_array& g, ap::real_value_type& sbgnrm); void lbfgsbsubsm(const int& n, const int& m, const int& nsub, const ap::integer_1d_array& ind, const ap::real_1d_array& l, const ap::real_1d_array& u, const ap::integer_1d_array& nbd, ap::real_1d_array& x, ap::real_1d_array& d, const ap::real_2d_array& ws, const ap::real_2d_array& wy, const ap::real_value_type& theta, const int& col, const int& head, int& iword, ap::real_1d_array& wv, ap::real_2d_array& wn, int& info); void lbfgsbdcsrch(const ap::real_value_type& f, const ap::real_value_type& g, ap::real_value_type& stp, const ap::real_value_type& ftol, const ap::real_value_type& gtol, const ap::real_value_type& xtol, const ap::real_value_type& stpmin, const ap::real_value_type& stpmax, int& task, ap::integer_1d_array& isave, ap::real_1d_array& dsave, int& addinfo); void lbfgsbdcstep(ap::real_value_type& stx, ap::real_value_type& fx, ap::real_value_type& dx, ap::real_value_type& sty, ap::real_value_type& fy, ap::real_value_type& dy, ap::real_value_type& stp, const ap::real_value_type& fp, const ap::real_value_type& dp, bool& brackt, const ap::real_value_type& stpmin, const ap::real_value_type& stpmax); bool additionallbfgsbstoppingcriterion(int iter, const ap::real_1d_array& x, ap::real_value_type f, const ap::real_1d_array& g); bool lbfgsbdpofa(ap::real_2d_array& a, const int& n); void lbfgsbdtrsl(ap::real_2d_array& t, const int& n, ap::real_1d_array& b, const int& job, int& info); void lbfgsbnewiteration(const ap::real_1d_array& x, ap::real_value_type f, const ap::real_1d_array& g); } // namespace ap #endif cmtk-3.3.1/libs/Numerics/lq.cxx000066400000000000000000000154741276303427400163550ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2011, 2013 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4973 $ // // $LastChangedDate: 2013-10-11 13:31:38 -0700 (Fri, 11 Oct 2013) $ // // $LastChangedBy: torstenrohlfing $ // */ /************************************************************************* Copyright (c) 2005-2007, Sergey Bochkanov (ALGLIB project). Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer listed in this license in the documentation and/or other materials provided with the distribution. - Neither the name of the copyright holders nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *************************************************************************/ #include "lq.h" /************************************************************************* LQ decomposition of a rectangular matrix of size MxN Input parameters: A - matrix A whose indexes range within [0..M-1, 0..N-1]. M - number of rows in matrix A. N - number of columns in matrix A. Output parameters: A - matrices L and Q in compact form (see below) Tau - array of scalar factors which are used to form matrix Q. Array whose index ranges within [0..Min(M,N)-1]. Matrix A is represented as A = LQ, where Q is an orthogonal matrix of size MxM, L - lower triangular (or lower trapezoid) matrix of size M x N. The elements of matrix L are located on and below the main diagonal of matrix A. The elements which are located in Tau array and above the main diagonal of matrix A are used to form matrix Q as follows: Matrix Q is represented as a product of elementary reflections Q = H(k-1)*H(k-2)*...*H(1)*H(0), where k = min(m,n), and each H(i) is of the form H(i) = 1 - tau * v * (v^T) where tau is a scalar stored in Tau[I]; v - real vector, so that v(0:i-1)=0, v(i) = 1, v(i+1:n-1) stored in A(i,i+1:n-1). -- ALGLIB -- Copyright 2005-2007 by Bochkanov Sergey *************************************************************************/ void rmatrixlq(ap::real_2d_array& a, int m, int n, ap::real_1d_array& tau) { ap::real_1d_array work; ap::real_1d_array t; int i; int k; int minmn; ap::real_value_type tmp; minmn = ap::minint(m, n); work.setbounds(0, m); t.setbounds(0, n); tau.setbounds(0, minmn-1); k = ap::minint(m, n); for(i = 0; i <= k-1; i++) { // // Generate elementary reflector H(i) to annihilate A(i,i+1:n-1) // ap::vmove(&t(1), &a(i, i), ap::vlen(1,n-i)); generatereflection(t, n-i, tmp); tau(i) = tmp; ap::vmove(&a(i, i), &t(1), ap::vlen(i,n-1)); t(1) = 1; if( i=0. N - number of columns in given matrix A. N>=0. Tau - scalar factors which are used to form Q. Output of the RMatrixLQ subroutine. QRows - required number of rows in matrix Q. N>=QRows>=0. Output parameters: Q - first QRows rows of matrix Q. Array whose indexes range within [0..QRows-1, 0..N-1]. If QRows=0, the array remains unchanged. -- ALGLIB -- Copyright 2005 by Bochkanov Sergey *************************************************************************/ void rmatrixlqunpackq(const ap::real_2d_array& a, int m, int n, const ap::real_1d_array& tau, int qrows, ap::real_2d_array& q) { int i; int j; int k; int minmn; ap::real_1d_array v; ap::real_1d_array work; #ifndef NO_AP_ASSERT ap::ap_error::make_assertion(qrows<=n, "RMatrixLQUnpackQ: QRows>N!"); #endif if( m<=0||n<=0||qrows<=0 ) { return; } // // init // minmn = ap::minint(m, n); k = ap::minint(minmn, qrows); q.setbounds(0, qrows-1, 0, n-1); v.setbounds(0, n); work.setbounds(0, qrows); for(i = 0; i <= qrows-1; i++) { for(j = 0; j <= n-1; j++) { if( i==j ) { q(i,j) = 1; } else { q(i,j) = 0; } } } // // unpack Q // for(i = k-1; i >= 0; i--) { // // Apply H(i) // ap::vmove(&v(1), &a(i, i), ap::vlen(1,n-i)); v(1) = 1; applyreflectionfromtheright(q, tau(i), v, 0, qrows-1, i, n-1, work); } } cmtk-3.3.1/libs/Numerics/lq.h000066400000000000000000000121671276303427400157760ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2010 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4865 $ // // $LastChangedDate: 2013-09-24 09:49:11 -0700 (Tue, 24 Sep 2013) $ // // $LastChangedBy: torstenrohlfing $ // */ /************************************************************************* Copyright (c) 2005-2007, Sergey Bochkanov (ALGLIB project). Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer listed in this license in the documentation and/or other materials provided with the distribution. - Neither the name of the copyright holders nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *************************************************************************/ #ifndef _lq_h #define _lq_h #include "ap.h" #include "reflections.h" /************************************************************************* LQ decomposition of a rectangular matrix of size MxN Input parameters: A - matrix A whose indexes range within [0..M-1, 0..N-1]. M - number of rows in matrix A. N - number of columns in matrix A. Output parameters: A - matrices L and Q in compact form (see below) Tau - array of scalar factors which are used to form matrix Q. Array whose index ranges within [0..Min(M,N)-1]. Matrix A is represented as A = LQ, where Q is an orthogonal matrix of size MxM, L - lower triangular (or lower trapezoid) matrix of size M x N. The elements of matrix L are located on and below the main diagonal of matrix A. The elements which are located in Tau array and above the main diagonal of matrix A are used to form matrix Q as follows: Matrix Q is represented as a product of elementary reflections Q = H(k-1)*H(k-2)*...*H(1)*H(0), where k = min(m,n), and each H(i) is of the form H(i) = 1 - tau * v * (v^T) where tau is a scalar stored in Tau[I]; v - real vector, so that v(0:i-1)=0, v(i) = 1, v(i+1:n-1) stored in A(i,i+1:n-1). -- ALGLIB -- Copyright 2005-2007 by Bochkanov Sergey *************************************************************************/ void rmatrixlq(ap::real_2d_array& a, int m, int n, ap::real_1d_array& tau); /************************************************************************* Partial unpacking of matrix Q from the LQ decomposition of a matrix A Input parameters: A - matrices L and Q in compact form. Output of RMatrixLQ subroutine. M - number of rows in given matrix A. M>=0. N - number of columns in given matrix A. N>=0. Tau - scalar factors which are used to form Q. Output of the RMatrixLQ subroutine. QRows - required number of rows in matrix Q. N>=QRows>=0. Output parameters: Q - first QRows rows of matrix Q. Array whose indexes range within [0..QRows-1, 0..N-1]. If QRows=0, the array remains unchanged. -- ALGLIB -- Copyright 2005 by Bochkanov Sergey *************************************************************************/ void rmatrixlqunpackq(const ap::real_2d_array& a, int m, int n, const ap::real_1d_array& tau, int qrows, ap::real_2d_array& q); #endif cmtk-3.3.1/libs/Numerics/lu.cxx000066400000000000000000000251551276303427400163560ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2010, 2013 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4973 $ // // $LastChangedDate: 2013-10-11 13:31:38 -0700 (Fri, 11 Oct 2013) $ // // $LastChangedBy: torstenrohlfing $ // */ /************************************************************************* Copyright (c) 1992-2007 The University of Tennessee. All rights reserved. Contributors: * Sergey Bochkanov (ALGLIB project). Translation from FORTRAN to pseudocode. See subroutines comments for additional copyrights. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer listed in this license in the documentation and/or other materials provided with the distribution. - Neither the name of the copyright holders nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *************************************************************************/ #include "lu.h" static const int lunb = 8; void rmatrixlu2(ap::real_2d_array& a, int m, int n, ap::integer_1d_array& pivots); /************************************************************************* LU decomposition of a general matrix of size MxN The subroutine calculates the LU decomposition of a rectangular general matrix with partial pivoting (with row permutations). Input parameters: A - matrix A whose indexes range within [0..M-1, 0..N-1]. M - number of rows in matrix A. N - number of columns in matrix A. Output parameters: A - matrices L and U in compact form (see below). Array whose indexes range within [0..M-1, 0..N-1]. Pivots - permutation matrix in compact form (see below). Array whose index ranges within [0..Min(M-1,N-1)]. Matrix A is represented as A = P * L * U, where P is a permutation matrix, matrix L - lower triangular (or lower trapezoid, if M>N) matrix, U - upper triangular (or upper trapezoid, if M=1, "RMatrixLU internal error"); #endif nb = lunb; // // Decide what to use - blocked or unblocked code // if( n<=1||ap::minint(m, n)<=nb||nb==1 ) { // // Unblocked code // rmatrixlu2(a, m, n, pivots); } else { // // Blocked code. // First, prepare temporary matrix and indices // b.setbounds(0, m-1, 0, nb-1); t.setbounds(0, n-1); pivots.setbounds(0, ap::minint(m, n)-1); minmn = ap::minint(m, n); j1 = 0; j2 = ap::minint(minmn, nb)-1; // // Main cycle // while(j1=0&&n>=0, "Error in LUDecomposition: incorrect function arguments"); #endif // // Quick return if possible // if( m==0||n==0 ) { return; } for(j = 0; j <= ap::minint(m-1, n-1); j++) { // // Find pivot and test for singularity. // jp = j; for(i = j+1; i <= m-1; i++) { if( fabs(a(i,j))>fabs(a(jp,j)) ) { jp = i; } } pivots(j) = jp; if( a(jp,j)!=0 ) { // //Apply the interchange to rows // if( jp!=j ) { ap::vmove(&t1(0), &a(j, 0), ap::vlen(0,n-1)); ap::vmove(&a(j, 0), &a(jp, 0), ap::vlen(0,n-1)); ap::vmove(&a(jp, 0), &t1(0), ap::vlen(0,n-1)); } // //Compute elements J+1:M of J-th column. // if( j. // // $Revision: 2378 $ // // $LastChangedDate: 2010-09-29 11:39:45 -0700 (Wed, 29 Sep 2010) $ // // $LastChangedBy: torstenrohlfing $ // */ /************************************************************************* Copyright (c) 1992-2007 The University of Tennessee. All rights reserved. Contributors: * Sergey Bochkanov (ALGLIB project). Translation from FORTRAN to pseudocode. See subroutines comments for additional copyrights. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer listed in this license in the documentation and/or other materials provided with the distribution. - Neither the name of the copyright holders nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *************************************************************************/ #ifndef _lu_h #define _lu_h #include "ap.h" /************************************************************************* LU decomposition of a general matrix of size MxN The subroutine calculates the LU decomposition of a rectangular general matrix with partial pivoting (with row permutations). Input parameters: A - matrix A whose indexes range within [0..M-1, 0..N-1]. M - number of rows in matrix A. N - number of columns in matrix A. Output parameters: A - matrices L and U in compact form (see below). Array whose indexes range within [0..M-1, 0..N-1]. Pivots - permutation matrix in compact form (see below). Array whose index ranges within [0..Min(M-1,N-1)]. Matrix A is represented as A = P * L * U, where P is a permutation matrix, matrix L - lower triangular (or lower trapezoid, if M>N) matrix, U - upper triangular (or upper trapezoid, if M. // // $Revision: 4918 $ // // $LastChangedDate: 2013-10-03 13:01:32 -0700 (Thu, 03 Oct 2013) $ // // $LastChangedBy: torstenrohlfing $ // */ /************************** mersenne.cpp ********************************** * Author: Agner Fog * Date created: 2001 * Last modified: 2008-11-16 * Project: randomc.h * Platform: Any C++ * Description: * Random Number generator of type 'Mersenne Twister' * * This random number generator is described in the article by * M. Matsumoto & T. Nishimura, in: * ACM Transactions on Modeling and Computer Simulation, * vol. 8, no. 1, 1998, pp. 3-30. * Details on the initialization scheme can be found at * http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/emt.html * * Further documentation: * The file ran-instructions.pdf contains further documentation and * instructions. * * Copyright 2001-2008 by Agner Fog. * GNU General Public License http://www.gnu.org/licenses/gpl.html *******************************************************************************/ #include "randomc.h" void CRandomMersenne::Init0(int seed) { // Seed generator const uint32_t factor = 1812433253UL; mt[0]= seed; for (mti=1; mti < MERS_N; mti++) { mt[mti] = (factor * (mt[mti-1] ^ (mt[mti-1] >> 30)) + mti); } } void CRandomMersenne::RandomInit(int seed) { // Initialize and seed Init0(seed); // Randomize some more for (int i = 0; i < 37; i++) BRandom(); } uint32_t CRandomMersenne::BRandom() { // Generate 32 random bits uint32_t y; if (mti >= MERS_N) { // Generate MERS_N words at one time const uint32_t LOWER_MASK = (1LU << MERS_R) - 1; // Lower MERS_R bits const uint32_t UPPER_MASK = 0xFFFFFFFF << MERS_R; // Upper (32 - MERS_R) bits static const uint32_t mag01[2] = {0, MERS_A}; int kk; for (kk=0; kk < MERS_N-MERS_M; kk++) { y = (mt[kk] & UPPER_MASK) | (mt[kk+1] & LOWER_MASK); mt[kk] = mt[kk+MERS_M] ^ (y >> 1) ^ mag01[y & 1];} for (; kk < MERS_N-1; kk++) { y = (mt[kk] & UPPER_MASK) | (mt[kk+1] & LOWER_MASK); mt[kk] = mt[kk+(MERS_M-MERS_N)] ^ (y >> 1) ^ mag01[y & 1];} y = (mt[MERS_N-1] & UPPER_MASK) | (mt[0] & LOWER_MASK); mt[MERS_N-1] = mt[MERS_M-1] ^ (y >> 1) ^ mag01[y & 1]; mti = 0; } y = mt[mti++]; // Tempering (May be omitted): y ^= y >> MERS_U; y ^= (y << MERS_S) & MERS_B; y ^= (y << MERS_T) & MERS_C; y ^= y >> MERS_L; return y; } ap::real_value_type CRandomMersenne::Random() { // Output random float number in the interval 0 <= x < 1 // Multiply by 2^(-32) return (ap::real_value_type)BRandom() * (1./(65536.*65536.)); } cmtk-3.3.1/libs/Numerics/normaldistr.cxx000066400000000000000000000256641276303427400203010ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2010, 2013 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4918 $ // // $LastChangedDate: 2013-10-03 13:01:32 -0700 (Thu, 03 Oct 2013) $ // // $LastChangedBy: torstenrohlfing $ // */ /************************************************************************* Cephes Math Library Release 2.8: June, 2000 Copyright 1984, 1987, 1988, 1992, 2000 by Stephen L. Moshier Contributors: * Sergey Bochkanov (ALGLIB project). Translation from C to pseudocode. See subroutines comments for additional copyrights. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer listed in this license in the documentation and/or other materials provided with the distribution. - Neither the name of the copyright holders nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *************************************************************************/ #include "normaldistr.h" namespace alglib { /************************************************************************* Error function The integral is x - 2 | | 2 erf(x) = -------- | exp( - t ) dt. sqrt(pi) | | - 0 For 0 <= |x| < 1, erf(x) = x * P4(x**2)/Q5(x**2); otherwise erf(x) = 1 - erfc(x). ACCURACY: Relative error: arithmetic domain # trials peak rms IEEE 0,1 30000 3.7e-16 1.0e-16 Cephes Math Library Release 2.8: June, 2000 Copyright 1984, 1987, 1988, 1992, 2000 by Stephen L. Moshier *************************************************************************/ ap::real_value_type erf(ap::real_value_type x) { ap::real_value_type result; ap::real_value_type xsq; ap::real_value_type s; ap::real_value_type p; ap::real_value_type q; s = ap::sign(x); x = fabs(x); if( x<0.5 ) { xsq = x*x; p = 0.007547728033418631287834; p = 0.288805137207594084924010+xsq*p; p = 14.3383842191748205576712+xsq*p; p = 38.0140318123903008244444+xsq*p; p = 3017.82788536507577809226+xsq*p; p = 7404.07142710151470082064+xsq*p; p = 80437.3630960840172832162+xsq*p; q = 0.0; q = 1.00000000000000000000000+xsq*q; q = 38.0190713951939403753468+xsq*q; q = 658.070155459240506326937+xsq*q; q = 6379.60017324428279487120+xsq*q; q = 34216.5257924628539769006+xsq*q; q = 80437.3630960840172826266+xsq*q; result = s*1.1283791670955125738961589031*x*p/q; return result; } if( x>=10 ) { result = s; return result; } result = s*(1-erfc(x)); return result; } /************************************************************************* Complementary error function 1 - erf(x) = inf. - 2 | | 2 erfc(x) = -------- | exp( - t ) dt sqrt(pi) | | - x For small x, erfc(x) = 1 - erf(x); otherwise rational approximations are computed. ACCURACY: Relative error: arithmetic domain # trials peak rms IEEE 0,26.6417 30000 5.7e-14 1.5e-14 Cephes Math Library Release 2.8: June, 2000 Copyright 1984, 1987, 1988, 1992, 2000 by Stephen L. Moshier *************************************************************************/ ap::real_value_type erfc(ap::real_value_type x) { ap::real_value_type result; ap::real_value_type p; ap::real_value_type q; if( x<0 ) { result = 2-erfc(-x); return result; } if( x<0.5 ) { result = 1.0-erf(x); return result; } if( x>=10 ) { result = 0; return result; } p = 0.0; p = 0.5641877825507397413087057563+x*p; p = 9.675807882987265400604202961+x*p; p = 77.08161730368428609781633646+x*p; p = 368.5196154710010637133875746+x*p; p = 1143.262070703886173606073338+x*p; p = 2320.439590251635247384768711+x*p; p = 2898.0293292167655611275846+x*p; p = 1826.3348842295112592168999+x*p; q = 1.0; q = 17.14980943627607849376131193+x*q; q = 137.1255960500622202878443578+x*q; q = 661.7361207107653469211984771+x*q; q = 2094.384367789539593790281779+x*q; q = 4429.612803883682726711528526+x*q; q = 6089.5424232724435504633068+x*q; q = 4958.82756472114071495438422+x*q; q = 1826.3348842295112595576438+x*q; result = exp(-ap::sqr(x))*p/q; return result; } /************************************************************************* Inverse of Normal distribution function Returns the argument, x, for which the area under the Gaussian probability density function (integrated from minus infinity to x) is equal to y. For small arguments 0 < y < exp(-2), the program computes z = sqrt( -2.0 * log(y) ); then the approximation is x = z - log(z)/z - (1/z) P(1/z) / Q(1/z). There are two rational functions P/Q, one for 0 < y < exp(-32) and the other for y up to exp(-2). For larger arguments, w = y - 0.5, and x/sqrt(2pi) = w + w**3 R(w**2)/S(w**2)). ACCURACY: Relative error: arithmetic domain # trials peak rms IEEE 0.125, 1 20000 7.2e-16 1.3e-16 IEEE 3e-308, 0.135 50000 4.6e-16 9.8e-17 Cephes Math Library Release 2.8: June, 2000 Copyright 1984, 1987, 1988, 1992, 2000 by Stephen L. Moshier *************************************************************************/ ap::real_value_type invnormaldistribution(ap::real_value_type y0) { ap::real_value_type result; ap::real_value_type expm2; ap::real_value_type s2pi; ap::real_value_type x; ap::real_value_type y; ap::real_value_type z; ap::real_value_type y2; ap::real_value_type x0; ap::real_value_type x1; int code; ap::real_value_type p0; ap::real_value_type q0; ap::real_value_type p1; ap::real_value_type q1; ap::real_value_type p2; ap::real_value_type q2; expm2 = 0.13533528323661269189; s2pi = 2.50662827463100050242; if( y0<=0 ) { result = -ap::maxrealnumber; return result; } if( y0>=1 ) { result = ap::maxrealnumber; return result; } code = 1; y = y0; if( y>1.0-expm2 ) { y = 1.0-y; code = 0; } if( y>expm2 ) { y = y-0.5; y2 = y*y; p0 = -59.9633501014107895267; p0 = 98.0010754185999661536+y2*p0; p0 = -56.6762857469070293439+y2*p0; p0 = 13.9312609387279679503+y2*p0; p0 = -1.23916583867381258016+y2*p0; q0 = 1; q0 = 1.95448858338141759834+y2*q0; q0 = 4.67627912898881538453+y2*q0; q0 = 86.3602421390890590575+y2*q0; q0 = -225.462687854119370527+y2*q0; q0 = 200.260212380060660359+y2*q0; q0 = -82.0372256168333339912+y2*q0; q0 = 15.9056225126211695515+y2*q0; q0 = -1.18331621121330003142+y2*q0; x = y+y*y2*p0/q0; x = x*s2pi; result = x; return result; } x = sqrt(-2.0*log(y)); x0 = x-log(x)/x; z = 1.0/x; if( x<8.0 ) { p1 = 4.05544892305962419923; p1 = 31.5251094599893866154+z*p1; p1 = 57.1628192246421288162+z*p1; p1 = 44.0805073893200834700+z*p1; p1 = 14.6849561928858024014+z*p1; p1 = 2.18663306850790267539+z*p1; p1 = -1.40256079171354495875*0.1+z*p1; p1 = -3.50424626827848203418*0.01+z*p1; p1 = -8.57456785154685413611*0.0001+z*p1; q1 = 1; q1 = 15.7799883256466749731+z*q1; q1 = 45.3907635128879210584+z*q1; q1 = 41.3172038254672030440+z*q1; q1 = 15.0425385692907503408+z*q1; q1 = 2.50464946208309415979+z*q1; q1 = -1.42182922854787788574*0.1+z*q1; q1 = -3.80806407691578277194*0.01+z*q1; q1 = -9.33259480895457427372*0.0001+z*q1; x1 = z*p1/q1; } else { p2 = 3.23774891776946035970; p2 = 6.91522889068984211695+z*p2; p2 = 3.93881025292474443415+z*p2; p2 = 1.33303460815807542389+z*p2; p2 = 2.01485389549179081538*0.1+z*p2; p2 = 1.23716634817820021358*0.01+z*p2; p2 = 3.01581553508235416007*0.0001+z*p2; p2 = 2.65806974686737550832*0.000001+z*p2; p2 = 6.23974539184983293730*0.000000001+z*p2; q2 = 1; q2 = 6.02427039364742014255+z*q2; q2 = 3.67983563856160859403+z*q2; q2 = 1.37702099489081330271+z*q2; q2 = 2.16236993594496635890*0.1+z*q2; q2 = 1.34204006088543189037*0.01+z*q2; q2 = 3.28014464682127739104*0.0001+z*q2; q2 = 2.89247864745380683936*0.000001+z*q2; q2 = 6.79019408009981274425*0.000000001+z*q2; x1 = z*p2/q2; } x = x0-x1; if( code!=0 ) { x = -x; } result = x; return result; } } // namespace alglib cmtk-3.3.1/libs/Numerics/normaldistr.h000066400000000000000000000132421276303427400177130ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2010, 2013 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4918 $ // // $LastChangedDate: 2013-10-03 13:01:32 -0700 (Thu, 03 Oct 2013) $ // // $LastChangedBy: torstenrohlfing $ // */ /************************************************************************* Cephes Math Library Release 2.8: June, 2000 Copyright 1984, 1987, 1988, 1992, 2000 by Stephen L. Moshier Contributors: * Sergey Bochkanov (ALGLIB project). Translation from C to pseudocode. See subroutines comments for additional copyrights. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer listed in this license in the documentation and/or other materials provided with the distribution. - Neither the name of the copyright holders nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *************************************************************************/ #ifndef _normaldistr_h #define _normaldistr_h #include "ap.h" namespace alglib { /************************************************************************* Error function The integral is x - 2 | | 2 erf(x) = -------- | exp( - t ) dt. sqrt(pi) | | - 0 For 0 <= |x| < 1, erf(x) = x * P4(x**2)/Q5(x**2); otherwise erf(x) = 1 - erfc(x). ACCURACY: Relative error: arithmetic domain # trials peak rms IEEE 0,1 30000 3.7e-16 1.0e-16 Cephes Math Library Release 2.8: June, 2000 Copyright 1984, 1987, 1988, 1992, 2000 by Stephen L. Moshier *************************************************************************/ ap::real_value_type erf(ap::real_value_type x); /************************************************************************* Complementary error function 1 - erf(x) = inf. - 2 | | 2 erfc(x) = -------- | exp( - t ) dt sqrt(pi) | | - x For small x, erfc(x) = 1 - erf(x); otherwise rational approximations are computed. ACCURACY: Relative error: arithmetic domain # trials peak rms IEEE 0,26.6417 30000 5.7e-14 1.5e-14 Cephes Math Library Release 2.8: June, 2000 Copyright 1984, 1987, 1988, 1992, 2000 by Stephen L. Moshier *************************************************************************/ ap::real_value_type erfc(ap::real_value_type x); /************************************************************************* Inverse of Normal distribution function Returns the argument, x, for which the area under the Gaussian probability density function (integrated from minus infinity to x) is equal to y. For small arguments 0 < y < exp(-2), the program computes z = sqrt( -2.0 * log(y) ); then the approximation is x = z - log(z)/z - (1/z) P(1/z) / Q(1/z). There are two rational functions P/Q, one for 0 < y < exp(-32) and the other for y up to exp(-2). For larger arguments, w = y - 0.5, and x/sqrt(2pi) = w + w**3 R(w**2)/S(w**2)). ACCURACY: Relative error: arithmetic domain # trials peak rms IEEE 0.125, 1 20000 7.2e-16 1.3e-16 IEEE 3e-308, 0.135 50000 4.6e-16 9.8e-17 Cephes Math Library Release 2.8: June, 2000 Copyright 1984, 1987, 1988, 1992, 2000 by Stephen L. Moshier *************************************************************************/ ap::real_value_type invnormaldistribution(ap::real_value_type y0); } // namespace alglib #endif cmtk-3.3.1/libs/Numerics/nsevd.cxx000066400000000000000000001643151276303427400170570ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2011, 2013 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4918 $ // // $LastChangedDate: 2013-10-03 13:01:32 -0700 (Thu, 03 Oct 2013) $ // // $LastChangedBy: torstenrohlfing $ // */ /************************************************************************* Copyright (c) 1992-2007 The University of Tennessee. All rights reserved. Contributors: * Sergey Bochkanov (ALGLIB project). Translation from FORTRAN to pseudocode. See subroutines comments for additional copyrights. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer listed in this license in the documentation and/or other materials provided with the distribution. - Neither the name of the copyright holders nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *************************************************************************/ #include "nsevd.h" void internaltrevc(const ap::real_2d_array& t, int n, int side, int howmny, ap::boolean_1d_array vselect, ap::real_2d_array& vl, ap::real_2d_array& vr, int& m, int& info); void internalhsevdlaln2(const bool& ltrans, const int& na, const int& nw, const ap::real_value_type& smin, const ap::real_value_type& ca, const ap::real_2d_array& a, const ap::real_value_type& d1, const ap::real_value_type& d2, const ap::real_2d_array& b, const ap::real_value_type& wr, const ap::real_value_type& wi, ap::boolean_1d_array& rswap4, ap::boolean_1d_array& zswap4, ap::integer_2d_array& ipivot44, ap::real_1d_array& civ4, ap::real_1d_array& crv4, ap::real_2d_array& x, ap::real_value_type& scl, ap::real_value_type& xnorm, int& info); void internalhsevdladiv(const ap::real_value_type& a, const ap::real_value_type& b, const ap::real_value_type& c, const ap::real_value_type& d, ap::real_value_type& p, ap::real_value_type& q); void internaltrevc(const ap::real_2d_array& t, int n, int side, int howmny, ap::boolean_1d_array vselect, ap::real_2d_array& vl, ap::real_2d_array& vr, int& m, int& info) { bool allv; bool bothv; bool leftv; bool over; bool pair; bool rightv; bool somev; int i; int ierr; int ii; int ip; int iis; int j; int j1; int j2; int jnxt; int k; int ki; int n2; ap::real_value_type beta; ap::real_value_type bignum; ap::real_value_type emax; ap::real_value_type rec; ap::real_value_type remax; ap::real_value_type scl; ap::real_value_type smin; ap::real_value_type smlnum; ap::real_value_type ulp; ap::real_value_type unfl; ap::real_value_type vcrit; ap::real_value_type vmax; ap::real_value_type wi; ap::real_value_type wr; ap::real_value_type xnorm; ap::real_2d_array x; ap::real_1d_array work; ap::real_1d_array temp; ap::real_2d_array temp11; ap::real_2d_array temp22; ap::real_2d_array temp11b; ap::real_2d_array temp21b; ap::real_2d_array temp12b; ap::real_2d_array temp22b; bool skipflag; int k1; int k2; int k3; int k4; ap::real_value_type vt; ap::boolean_1d_array rswap4; ap::boolean_1d_array zswap4; ap::integer_2d_array ipivot44; ap::real_1d_array civ4; ap::real_1d_array crv4; x.setbounds(1, 2, 1, 2); temp11.setbounds(1, 1, 1, 1); temp11b.setbounds(1, 1, 1, 1); temp21b.setbounds(1, 2, 1, 1); temp12b.setbounds(1, 1, 1, 2); temp22b.setbounds(1, 2, 1, 2); temp22.setbounds(1, 2, 1, 2); work.setbounds(1, 3*n); temp.setbounds(1, n); rswap4.setbounds(1, 4); zswap4.setbounds(1, 4); ipivot44.setbounds(1, 4, 1, 4); civ4.setbounds(1, 4); crv4.setbounds(1, 4); if( howmny!=1 ) { if( side==1||side==3 ) { vr.setbounds(1, n, 1, n); } if( side==2||side==3 ) { vl.setbounds(1, n, 1, n); } } // // Decode and test the input parameters // bothv = side==3; rightv = side==1||bothv; leftv = side==2||bothv; allv = howmny==2; over = howmny==1; somev = howmny==3; info = 0; if( n<0 ) { info = -2; return; } if( !rightv&&!leftv ) { info = -3; return; } if( !allv&&!over&&!somev ) { info = -4; return; } // // Set M to the number of columns required to store the selected // eigenvectors, standardize the array SELECT if necessary, and // test MM. // if( somev ) { m = 0; pair = false; for(j = 1; j <= n; j++) { if( pair ) { pair = false; vselect(j) = false; } else { if( j= 1; ki--) { skipflag = false; if( ip==1 ) { skipflag = true; } else { if( ki!=1 ) { if( t(ki,ki-1)!=0 ) { ip = -1; } } if( somev ) { if( ip==0 ) { if( !vselect(ki) ) { skipflag = true; } } else { if( !vselect(ki-1) ) { skipflag = true; } } } } if( !skipflag ) { // // Compute the KI-th eigenvalue (WR,WI). // wr = t(ki,ki); wi = 0; if( ip!=0 ) { wi = sqrt(fabs(t(ki,ki-1)))*sqrt(fabs(t(ki-1,ki))); } smin = ap::maxreal(ulp*(fabs(wr)+fabs(wi)), smlnum); if( ip==0 ) { // // Real right eigenvector // work(ki+n) = 1; // // Form right-hand side // for(k = 1; k <= ki-1; k++) { work(k+n) = -t(k,ki); } // // Solve the upper quasi-triangular system: // (T(1:KI-1,1:KI-1) - WR)*X = SCALE*WORK. // jnxt = ki-1; for(j = ki-1; j >= 1; j--) { if( j>jnxt ) { continue; } j1 = j; j2 = j; jnxt = j-1; if( j>1 ) { if( t(j,j-1)!=0 ) { j1 = j-1; jnxt = j-2; } } if( j1==j2 ) { // // 1-by-1 diagonal block // temp11(1,1) = t(j,j); temp11b(1,1) = work(j+n); internalhsevdlaln2(false, 1, 1, smin, ap::real_value_type(1), temp11, 1.0, 1.0, temp11b, wr, 0.0, rswap4, zswap4, ipivot44, civ4, crv4, x, scl, xnorm, ierr); // // Scale X(1,1) to avoid overflow when updating // the right-hand side. // if( xnorm>1 ) { if( work(j)>bignum/xnorm ) { x(1,1) = x(1,1)/xnorm; scl = scl/xnorm; } } // // Scale if necessary // if( scl!=1 ) { k1 = n+1; k2 = n+ki; ap::vmul(&work(k1), ap::vlen(k1,k2), scl); } work(j+n) = x(1,1); // // Update right-hand side // k1 = 1+n; k2 = j-1+n; k3 = j-1; vt = -x(1,1); ap::vadd(work.getvector(k1, k2), t.getcolumn(j, 1, k3), vt); } else { // // 2-by-2 diagonal block // temp22(1,1) = t(j-1,j-1); temp22(1,2) = t(j-1,j); temp22(2,1) = t(j,j-1); temp22(2,2) = t(j,j); temp21b(1,1) = work(j-1+n); temp21b(2,1) = work(j+n); internalhsevdlaln2(false, 2, 1, smin, 1.0, temp22, 1.0, 1.0, temp21b, wr, ap::real_value_type(0), rswap4, zswap4, ipivot44, civ4, crv4, x, scl, xnorm, ierr); // // Scale X(1,1) and X(2,1) to avoid overflow when // updating the right-hand side. // if( xnorm>1 ) { beta = ap::maxreal(work(j-1), work(j)); if( beta>bignum/xnorm ) { x(1,1) = x(1,1)/xnorm; x(2,1) = x(2,1)/xnorm; scl = scl/xnorm; } } // // Scale if necessary // if( scl!=1 ) { k1 = 1+n; k2 = ki+n; ap::vmul(&work(k1), ap::vlen(k1,k2), scl); } work(j-1+n) = x(1,1); work(j+n) = x(2,1); // // Update right-hand side // k1 = 1+n; k2 = j-2+n; k3 = j-2; k4 = j-1; vt = -x(1,1); ap::vadd(work.getvector(k1, k2), t.getcolumn(k4, 1, k3), vt); vt = -x(2,1); ap::vadd(work.getvector(k1, k2), t.getcolumn(j, 1, k3), vt); } } // // Copy the vector x or Q*x to VR and normalize. // if( !over ) { k1 = 1+n; k2 = ki+n; ap::vmove(vr.getcolumn(iis, 1, ki), work.getvector(k1, k2)); ii = columnidxabsmax(vr, 1, ki, iis); remax = 1/fabs(vr(ii,iis)); ap::vmul(vr.getcolumn(iis, 1, ki), remax); for(k = ki+1; k <= n; k++) { vr(k,iis) = 0; } } else { if( ki>1 ) { ap::vmove(temp.getvector(1, n), vr.getcolumn(ki, 1, n)); matrixvectormultiply(vr, 1, n, 1, ki-1, false, work, 1+n, ki-1+n, 1.0, temp, 1, n, work(ki+n)); ap::vmove(vr.getcolumn(ki, 1, n), temp.getvector(1, n)); } ii = columnidxabsmax(vr, 1, n, ki); remax = 1/fabs(vr(ii,ki)); ap::vmul(vr.getcolumn(ki, 1, n), remax); } } else { // // Complex right eigenvector. // // Initial solve // [ (T(KI-1,KI-1) T(KI-1,KI) ) - (WR + I* WI)]*X = 0. // [ (T(KI,KI-1) T(KI,KI) ) ] // if( fabs(t(ki-1,ki))>=fabs(t(ki,ki-1)) ) { work(ki-1+n) = 1; work(ki+n2) = wi/t(ki-1,ki); } else { work(ki-1+n) = -wi/t(ki,ki-1); work(ki+n2) = 1; } work(ki+n) = 0; work(ki-1+n2) = 0; // // Form right-hand side // for(k = 1; k <= ki-2; k++) { work(k+n) = -work(ki-1+n)*t(k,ki-1); work(k+n2) = -work(ki+n2)*t(k,ki); } // // Solve upper quasi-triangular system: // (T(1:KI-2,1:KI-2) - (WR+i*WI))*X = SCALE*(WORK+i*WORK2) // jnxt = ki-2; for(j = ki-2; j >= 1; j--) { if( j>jnxt ) { continue; } j1 = j; j2 = j; jnxt = j-1; if( j>1 ) { if( t(j,j-1)!=0 ) { j1 = j-1; jnxt = j-2; } } if( j1==j2 ) { // // 1-by-1 diagonal block // temp11(1,1) = t(j,j); temp12b(1,1) = work(j+n); temp12b(1,2) = work(j+n+n); internalhsevdlaln2(false, 1, 2, smin, 1.0, temp11, 1.0, 1.0, temp12b, wr, wi, rswap4, zswap4, ipivot44, civ4, crv4, x, scl, xnorm, ierr); // // Scale X(1,1) and X(1,2) to avoid overflow when // updating the right-hand side. // if( xnorm>1 ) { if( work(j)>bignum/xnorm ) { x(1,1) = x(1,1)/xnorm; x(1,2) = x(1,2)/xnorm; scl = scl/xnorm; } } // // Scale if necessary // if( scl!=1 ) { k1 = 1+n; k2 = ki+n; ap::vmul(&work(k1), ap::vlen(k1,k2), scl); k1 = 1+n2; k2 = ki+n2; ap::vmul(&work(k1), ap::vlen(k1,k2), scl); } work(j+n) = x(1,1); work(j+n2) = x(1,2); // // Update the right-hand side // k1 = 1+n; k2 = j-1+n; k3 = 1; k4 = j-1; vt = -x(1,1); ap::vadd(work.getvector(k1, k2), t.getcolumn(j, k3, k4), vt); k1 = 1+n2; k2 = j-1+n2; k3 = 1; k4 = j-1; vt = -x(1,2); ap::vadd(work.getvector(k1, k2), t.getcolumn(j, k3, k4), vt); } else { // // 2-by-2 diagonal block // temp22(1,1) = t(j-1,j-1); temp22(1,2) = t(j-1,j); temp22(2,1) = t(j,j-1); temp22(2,2) = t(j,j); temp22b(1,1) = work(j-1+n); temp22b(1,2) = work(j-1+n+n); temp22b(2,1) = work(j+n); temp22b(2,2) = work(j+n+n); internalhsevdlaln2(false, 2, 2, smin, 1.0, temp22, 1.0, 1.0, temp22b, wr, wi, rswap4, zswap4, ipivot44, civ4, crv4, x, scl, xnorm, ierr); // // Scale X to avoid overflow when updating // the right-hand side. // if( xnorm>1 ) { beta = ap::maxreal(work(j-1), work(j)); if( beta>bignum/xnorm ) { rec = 1/xnorm; x(1,1) = x(1,1)*rec; x(1,2) = x(1,2)*rec; x(2,1) = x(2,1)*rec; x(2,2) = x(2,2)*rec; scl = scl*rec; } } // // Scale if necessary // if( scl!=1 ) { ap::vmul(&work(1+n), ap::vlen(1+n,ki+n), scl); ap::vmul(&work(1+n2), ap::vlen(1+n2,ki+n2), scl); } work(j-1+n) = x(1,1); work(j+n) = x(2,1); work(j-1+n2) = x(1,2); work(j+n2) = x(2,2); // // Update the right-hand side // vt = -x(1,1); ap::vadd(work.getvector(n+1, n+j-2), t.getcolumn(j-1, 1, j-2), vt); vt = -x(2,1); ap::vadd(work.getvector(n+1, n+j-2), t.getcolumn(j, 1, j-2), vt); vt = -x(1,2); ap::vadd(work.getvector(n2+1, n2+j-2), t.getcolumn(j-1, 1, j-2), vt); vt = -x(2,2); ap::vadd(work.getvector(n2+1, n2+j-2), t.getcolumn(j, 1, j-2), vt); } } // // Copy the vector x or Q*x to VR and normalize. // if( !over ) { ap::vmove(vr.getcolumn(iis-1, 1, ki), work.getvector(n+1, n+ki)); ap::vmove(vr.getcolumn(iis, 1, ki), work.getvector(n2+1, n2+ki)); emax = 0; for(k = 1; k <= ki; k++) { emax = ap::maxreal(emax, fabs(vr(k,iis-1))+fabs(vr(k,iis))); } remax = 1/emax; ap::vmul(vr.getcolumn(iis-1, 1, ki), remax); ap::vmul(vr.getcolumn(iis, 1, ki), remax); for(k = ki+1; k <= n; k++) { vr(k,iis-1) = 0; vr(k,iis) = 0; } } else { if( ki>2 ) { ap::vmove(temp.getvector(1, n), vr.getcolumn(ki-1, 1, n)); matrixvectormultiply(vr, 1, n, 1, ki-2, false, work, 1+n, ki-2+n, 1.0, temp, 1, n, work(ki-1+n)); ap::vmove(vr.getcolumn(ki-1, 1, n), temp.getvector(1, n)); ap::vmove(temp.getvector(1, n), vr.getcolumn(ki, 1, n)); matrixvectormultiply(vr, 1, n, 1, ki-2, false, work, 1+n2, ki-2+n2, 1.0, temp, 1, n, work(ki+n2)); ap::vmove(vr.getcolumn(ki, 1, n), temp.getvector(1, n)); } else { vt = work(ki-1+n); ap::vmul(vr.getcolumn(ki-1, 1, n), vt); vt = work(ki+n2); ap::vmul(vr.getcolumn(ki, 1, n), vt); } emax = 0; for(k = 1; k <= n; k++) { emax = ap::maxreal(emax, fabs(vr(k,ki-1))+fabs(vr(k,ki))); } remax = 1/emax; ap::vmul(vr.getcolumn(ki-1, 1, n), remax); ap::vmul(vr.getcolumn(ki, 1, n), remax); } } iis = iis-1; if( ip!=0 ) { iis = iis-1; } } if( ip==1 ) { ip = 0; } if( ip==-1 ) { ip = 1; } } } if( leftv ) { // // Compute left eigenvectors. // ip = 0; iis = 1; for(ki = 1; ki <= n; ki++) { skipflag = false; if( ip==-1 ) { skipflag = true; } else { if( ki!=n ) { if( t(ki+1,ki)!=0 ) { ip = 1; } } if( somev ) { if( !vselect(ki) ) { skipflag = true; } } } if( !skipflag ) { // // Compute the KI-th eigenvalue (WR,WI). // wr = t(ki,ki); wi = 0; if( ip!=0 ) { wi = sqrt(fabs(t(ki,ki+1)))*sqrt(fabs(t(ki+1,ki))); } smin = ap::maxreal(ulp*(fabs(wr)+fabs(wi)), smlnum); if( ip==0 ) { // // Real left eigenvector. // work(ki+n) = 1; // // Form right-hand side // for(k = ki+1; k <= n; k++) { work(k+n) = -t(ki,k); } // // Solve the quasi-triangular system: // (T(KI+1:N,KI+1:N) - WR)'*X = SCALE*WORK // vmax = 1; vcrit = bignum; jnxt = ki+1; for(j = ki+1; j <= n; j++) { if( jvcrit ) { rec = 1/vmax; ap::vmul(&work(ki+n), ap::vlen(ki+n,n+n), rec); vmax = 1; vcrit = bignum; } vt = ap::vdotproduct(t.getcolumn(j, ki+1, j-1), work.getvector(ki+1+n, j-1+n)); work(j+n) = work(j+n)-vt; // // Solve (T(J,J)-WR)'*X = WORK // temp11(1,1) = t(j,j); temp11b(1,1) = work(j+n); internalhsevdlaln2(false, 1, 1, smin, 1.0, temp11, 1.0, 1.0, temp11b, wr, ap::real_value_type(0), rswap4, zswap4, ipivot44, civ4, crv4, x, scl, xnorm, ierr); // // Scale if necessary // if( scl!=1 ) { ap::vmul(&work(ki+n), ap::vlen(ki+n,n+n), scl); } work(j+n) = x(1,1); vmax = ap::maxreal(fabs(work(j+n)), vmax); vcrit = bignum/vmax; } else { // // 2-by-2 diagonal block // // Scale if necessary to avoid overflow when forming // the right-hand side. // beta = ap::maxreal(work(j), work(j+1)); if( beta>vcrit ) { rec = 1/vmax; ap::vmul(&work(ki+n), ap::vlen(ki+n,n+n), rec); vmax = 1; vcrit = bignum; } vt = ap::vdotproduct(t.getcolumn(j, ki+1, j-1), work.getvector(ki+1+n, j-1+n)); work(j+n) = work(j+n)-vt; vt = ap::vdotproduct(t.getcolumn(j+1, ki+1, j-1), work.getvector(ki+1+n, j-1+n)); work(j+1+n) = work(j+1+n)-vt; // // Solve // [T(J,J)-WR T(J,J+1) ]'* X = SCALE*( WORK1 ) // [T(J+1,J) T(J+1,J+1)-WR] ( WORK2 ) // temp22(1,1) = t(j,j); temp22(1,2) = t(j,j+1); temp22(2,1) = t(j+1,j); temp22(2,2) = t(j+1,j+1); temp21b(1,1) = work(j+n); temp21b(2,1) = work(j+1+n); internalhsevdlaln2(true, 2, 1, smin, 1.0, temp22, 1.0, 1.0, temp21b, wr, ap::real_value_type(0), rswap4, zswap4, ipivot44, civ4, crv4, x, scl, xnorm, ierr); // // Scale if necessary // if( scl!=1 ) { ap::vmul(&work(ki+n), ap::vlen(ki+n,n+n), scl); } work(j+n) = x(1,1); work(j+1+n) = x(2,1); vmax = ap::maxreal(fabs(work(j+n)), ap::maxreal(fabs(work(j+1+n)), vmax)); vcrit = bignum/vmax; } } // // Copy the vector x or Q*x to VL and normalize. // if( !over ) { ap::vmove(vl.getcolumn(iis, ki, n), work.getvector(ki+n, n+n)); ii = columnidxabsmax(vl, ki, n, iis); remax = 1/fabs(vl(ii,iis)); ap::vmul(vl.getcolumn(iis, ki, n), remax); for(k = 1; k <= ki-1; k++) { vl(k,iis) = 0; } } else { if( ki=fabs(t(ki+1,ki)) ) { work(ki+n) = wi/t(ki,ki+1); work(ki+1+n2) = 1; } else { work(ki+n) = 1; work(ki+1+n2) = -wi/t(ki+1,ki); } work(ki+1+n) = 0; work(ki+n2) = 0; // // Form right-hand side // for(k = ki+2; k <= n; k++) { work(k+n) = -work(ki+n)*t(ki,k); work(k+n2) = -work(ki+1+n2)*t(ki+1,k); } // // Solve complex quasi-triangular system: // ( T(KI+2,N:KI+2,N) - (WR-i*WI) )*X = WORK1+i*WORK2 // vmax = 1; vcrit = bignum; jnxt = ki+2; for(j = ki+2; j <= n; j++) { if( jvcrit ) { rec = 1/vmax; ap::vmul(&work(ki+n), ap::vlen(ki+n,n+n), rec); ap::vmul(&work(ki+n2), ap::vlen(ki+n2,n+n2), rec); vmax = 1; vcrit = bignum; } vt = ap::vdotproduct(t.getcolumn(j, ki+2, j-1), work.getvector(ki+2+n, j-1+n)); work(j+n) = work(j+n)-vt; vt = ap::vdotproduct(t.getcolumn(j, ki+2, j-1), work.getvector(ki+2+n2, j-1+n2)); work(j+n2) = work(j+n2)-vt; // // Solve (T(J,J)-(WR-i*WI))*(X11+i*X12)= WK+I*WK2 // temp11(1,1) = t(j,j); temp12b(1,1) = work(j+n); temp12b(1,2) = work(j+n+n); internalhsevdlaln2(false, 1, 2, smin, 1.0, temp11, 1.0, 1.0, temp12b, wr, -wi, rswap4, zswap4, ipivot44, civ4, crv4, x, scl, xnorm, ierr); // // Scale if necessary // if( scl!=1 ) { ap::vmul(&work(ki+n), ap::vlen(ki+n,n+n), scl); ap::vmul(&work(ki+n2), ap::vlen(ki+n2,n+n2), scl); } work(j+n) = x(1,1); work(j+n2) = x(1,2); vmax = ap::maxreal(fabs(work(j+n)), ap::maxreal(fabs(work(j+n2)), vmax)); vcrit = bignum/vmax; } else { // // 2-by-2 diagonal block // // Scale if necessary to avoid overflow when forming // the right-hand side elements. // beta = ap::maxreal(work(j), work(j+1)); if( beta>vcrit ) { rec = 1/vmax; ap::vmul(&work(ki+n), ap::vlen(ki+n,n+n), rec); ap::vmul(&work(ki+n2), ap::vlen(ki+n2,n+n2), rec); vmax = 1; vcrit = bignum; } vt = ap::vdotproduct(t.getcolumn(j, ki+2, j-1), work.getvector(ki+2+n, j-1+n)); work(j+n) = work(j+n)-vt; vt = ap::vdotproduct(t.getcolumn(j, ki+2, j-1), work.getvector(ki+2+n2, j-1+n2)); work(j+n2) = work(j+n2)-vt; vt = ap::vdotproduct(t.getcolumn(j+1, ki+2, j-1), work.getvector(ki+2+n, j-1+n)); work(j+1+n) = work(j+1+n)-vt; vt = ap::vdotproduct(t.getcolumn(j+1, ki+2, j-1), work.getvector(ki+2+n2, j-1+n2)); work(j+1+n2) = work(j+1+n2)-vt; // // Solve 2-by-2 complex linear equation // ([T(j,j) T(j,j+1) ]'-(wr-i*wi)*I)*X = SCALE*B // ([T(j+1,j) T(j+1,j+1)] ) // temp22(1,1) = t(j,j); temp22(1,2) = t(j,j+1); temp22(2,1) = t(j+1,j); temp22(2,2) = t(j+1,j+1); temp22b(1,1) = work(j+n); temp22b(1,2) = work(j+n+n); temp22b(2,1) = work(j+1+n); temp22b(2,2) = work(j+1+n+n); internalhsevdlaln2(true, 2, 2, smin, 1.0, temp22, 1.0, 1.0, temp22b, wr, -wi, rswap4, zswap4, ipivot44, civ4, crv4, x, scl, xnorm, ierr); // // Scale if necessary // if( scl!=1 ) { ap::vmul(&work(ki+n), ap::vlen(ki+n,n+n), scl); ap::vmul(&work(ki+n2), ap::vlen(ki+n2,n+n2), scl); } work(j+n) = x(1,1); work(j+n2) = x(1,2); work(j+1+n) = x(2,1); work(j+1+n2) = x(2,2); vmax = ap::maxreal(fabs(x(1,1)), vmax); vmax = ap::maxreal(fabs(x(1,2)), vmax); vmax = ap::maxreal(fabs(x(2,1)), vmax); vmax = ap::maxreal(fabs(x(2,2)), vmax); vcrit = bignum/vmax; } } // // Copy the vector x or Q*x to VL and normalize. // if( !over ) { ap::vmove(vl.getcolumn(iis, ki, n), work.getvector(ki+n, n+n)); ap::vmove(vl.getcolumn(iis+1, ki, n), work.getvector(ki+n2, n+n2)); emax = 0; for(k = ki; k <= n; k++) { emax = ap::maxreal(emax, fabs(vl(k,iis))+fabs(vl(k,iis+1))); } remax = 1/emax; ap::vmul(vl.getcolumn(iis, ki, n), remax); ap::vmul(vl.getcolumn(iis+1, ki, n), remax); for(k = 1; k <= ki-1; k++) { vl(k,iis) = 0; vl(k,iis+1) = 0; } } else { if( ki1 ) { if( bnorm>bignum*cnorm ) { scl = 1/bnorm; } } // // Compute X // x(1,1) = b(1,1)*scl/csr; xnorm = fabs(x(1,1)); } else { // // Complex 1x1 system (w is complex) // // C = ca A - w D // csr = ca*a(1,1)-wr*d1; csi = -wi*d1; cnorm = fabs(csr)+fabs(csi); // // If | C | < SMINI, use C = SMINI // if( cnorm1 ) { if( bnorm>bignum*cnorm ) { scl = 1/bnorm; } } // // Compute X // internalhsevdladiv(scl*b(1,1), scl*b(1,2), csr, csi, tmp1, tmp2); x(1,1) = tmp1; x(1,2) = tmp2; xnorm = fabs(x(1,1))+fabs(x(1,2)); } } else { // // 2x2 System // // Compute the real part of C = ca A - w D (or ca A' - w D ) // crv4(1+0) = ca*a(1,1)-wr*d1; crv4(2+2) = ca*a(2,2)-wr*d2; if( ltrans ) { crv4(1+2) = ca*a(2,1); crv4(2+0) = ca*a(1,2); } else { crv4(2+0) = ca*a(2,1); crv4(1+2) = ca*a(1,2); } if( nw==1 ) { // // Real 2x2 system (w is real) // // Find the largest element in C // cmax = 0; icmax = 0; for(j = 1; j <= 4; j++) { if( fabs(crv4(j))>cmax ) { cmax = fabs(crv4(j)); icmax = j; } } // // If norm(C) < SMINI, use SMINI*identity. // if( cmax1 ) { if( bnorm>bignum*smini ) { scl = 1/bnorm; } } temp = scl/smini; x(1,1) = temp*b(1,1); x(2,1) = temp*b(2,1); xnorm = temp*bnorm; info = 1; return; } // // Gaussian elimination with complete pivoting. // ur11 = crv4(icmax); cr21 = crv4(ipivot44(2,icmax)); ur12 = crv4(ipivot44(3,icmax)); cr22 = crv4(ipivot44(4,icmax)); ur11r = 1/ur11; lr21 = ur11r*cr21; ur22 = cr22-ur12*lr21; // // If smaller pivot < SMINI, use SMINI // if( fabs(ur22)1&&fabs(ur22)<1 ) { if( bbnd>=bignum*fabs(ur22) ) { scl = 1/bbnd; } } xr2 = br2*scl/ur22; xr1 = scl*br1*ur11r-xr2*(ur11r*ur12); if( zswap4(icmax) ) { x(1,1) = xr2; x(2,1) = xr1; } else { x(1,1) = xr1; x(2,1) = xr2; } xnorm = ap::maxreal(fabs(xr1), fabs(xr2)); // // Further scaling if norm(A) norm(X) > overflow // if( xnorm>1&&cmax>1 ) { if( xnorm>bignum/cmax ) { temp = cmax/bignum; x(1,1) = temp*x(1,1); x(2,1) = temp*x(2,1); xnorm = temp*xnorm; scl = temp*scl; } } } else { // // Complex 2x2 system (w is complex) // // Find the largest element in C // civ4(1+0) = -wi*d1; civ4(2+0) = 0; civ4(1+2) = 0; civ4(2+2) = -wi*d2; cmax = 0; icmax = 0; for(j = 1; j <= 4; j++) { if( fabs(crv4(j))+fabs(civ4(j))>cmax ) { cmax = fabs(crv4(j))+fabs(civ4(j)); icmax = j; } } // // If norm(C) < SMINI, use SMINI*identity. // if( cmax1 ) { if( bnorm>bignum*smini ) { scl = 1/bnorm; } } temp = scl/smini; x(1,1) = temp*b(1,1); x(2,1) = temp*b(2,1); x(1,2) = temp*b(1,2); x(2,2) = temp*b(2,2); xnorm = temp*bnorm; info = 1; return; } // // Gaussian elimination with complete pivoting. // ur11 = crv4(icmax); ui11 = civ4(icmax); cr21 = crv4(ipivot44(2,icmax)); ci21 = civ4(ipivot44(2,icmax)); ur12 = crv4(ipivot44(3,icmax)); ui12 = civ4(ipivot44(3,icmax)); cr22 = crv4(ipivot44(4,icmax)); ci22 = civ4(ipivot44(4,icmax)); if( icmax==1||icmax==4 ) { // // Code when off-diagonals of pivoted C are real // if( fabs(ur11)>fabs(ui11) ) { temp = ui11/ur11; ur11r = 1/(ur11*(1+ap::sqr(temp))); ui11r = -temp*ur11r; } else { temp = ur11/ui11; ui11r = -1/(ui11*(1+ap::sqr(temp))); ur11r = -temp*ui11r; } lr21 = cr21*ur11r; li21 = cr21*ui11r; ur12s = ur12*ur11r; ui12s = ur12*ui11r; ur22 = cr22-ur12*lr21; ui22 = ci22-ur12*li21; } else { // // Code when diagonals of pivoted C are real // ur11r = 1/ur11; ui11r = 0; lr21 = cr21*ur11r; li21 = ci21*ur11r; ur12s = ur12*ur11r; ui12s = ui12*ur11r; ur22 = cr22-ur12*lr21+ui12*li21; ui22 = -ur12*li21-ui12*lr21; } u22abs = fabs(ur22)+fabs(ui22); // // If smaller pivot < SMINI, use SMINI // if( u22abs1&&u22abs<1 ) { if( bbnd>=bignum*u22abs ) { scl = 1/bbnd; br1 = scl*br1; bi1 = scl*bi1; br2 = scl*br2; bi2 = scl*bi2; } } internalhsevdladiv(br2, bi2, ur22, ui22, xr2, xi2); xr1 = ur11r*br1-ui11r*bi1-ur12s*xr2+ui12s*xi2; xi1 = ui11r*br1+ur11r*bi1-ui12s*xr2-ur12s*xi2; if( zswap4(icmax) ) { x(1,1) = xr2; x(2,1) = xr1; x(1,2) = xi2; x(2,2) = xi1; } else { x(1,1) = xr1; x(2,1) = xr2; x(1,2) = xi1; x(2,2) = xi2; } xnorm = ap::maxreal(fabs(xr1)+fabs(xi1), fabs(xr2)+fabs(xi2)); // // Further scaling if norm(A) norm(X) > overflow // if( xnorm>1&&cmax>1 ) { if( xnorm>bignum/cmax ) { temp = cmax/bignum; x(1,1) = temp*x(1,1); x(2,1) = temp*x(2,1); x(1,2) = temp*x(1,2); x(2,2) = temp*x(2,2); xnorm = temp*xnorm; scl = temp*scl; } } } } } void internalhsevdladiv(const ap::real_value_type& a, const ap::real_value_type& b, const ap::real_value_type& c, const ap::real_value_type& d, ap::real_value_type& p, ap::real_value_type& q) { ap::real_value_type e; ap::real_value_type f; if( fabs(d). // // $Revision: 4918 $ // // $LastChangedDate: 2013-10-03 13:01:32 -0700 (Thu, 03 Oct 2013) $ // // $LastChangedBy: torstenrohlfing $ // */ /************************************************************************* Copyright (c) 1992-2007 The University of Tennessee. All rights reserved. Contributors: * Sergey Bochkanov (ALGLIB project). Translation from FORTRAN to pseudocode. See subroutines comments for additional copyrights. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer listed in this license in the documentation and/or other materials provided with the distribution. - Neither the name of the copyright holders nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *************************************************************************/ #ifndef _nsevd_h #define _nsevd_h #include "ap.h" #include "blas.h" #include "reflections.h" #include "rotations.h" #include "hsschur.h" #include "hessenberg.h" /************************************************************************* Finding eigenvalues and eigenvectors of a general matrix The algorithm finds eigenvalues and eigenvectors of a general matrix by using the QR algorithm with multiple shifts. The algorithm can find eigenvalues and both left and right eigenvectors. The right eigenvector is a vector x such that A*x = w*x, and the left eigenvector is a vector y such that y'*A = w*y' (here y' implies a complex conjugate transposition of vector y). Input parameters: A - matrix. Array whose indexes range within [0..N-1, 0..N-1]. N - size of matrix A. VNeeded - flag controlling whether eigenvectors are needed or not. If VNeeded is equal to: * 0, eigenvectors are not returned; * 1, right eigenvectors are returned; * 2, left eigenvectors are returned; * 3, both left and right eigenvectors are returned. Output parameters: WR - real parts of eigenvalues. Array whose index ranges within [0..N-1]. WR - imaginary parts of eigenvalues. Array whose index ranges within [0..N-1]. VL, VR - arrays of left and right eigenvectors (if they are needed). If WI[i]=0, the respective eigenvalue is a real number, and it corresponds to the column number I of matrices VL/VR. If WI[i]>0, we have a pair of complex conjugate numbers with positive and negative imaginary parts: the first eigenvalue WR[i] + sqrt(-1)*WI[i]; the second eigenvalue WR[i+1] + sqrt(-1)*WI[i+1]; WI[i]>0 WI[i+1] = -WI[i] < 0 In that case, the eigenvector corresponding to the first eigenvalue is located in i and i+1 columns of matrices VL/VR (the column number i contains the real part, and the column number i+1 contains the imaginary part), and the vector corresponding to the second eigenvalue is a complex conjugate to the first vector. Arrays whose indexes range within [0..N-1, 0..N-1]. Result: True, if the algorithm has converged. False, if the algorithm has not converged. Note 1: Some users may ask the following question: what if WI[N-1]>0? WI[N] must contain an eigenvalue which is complex conjugate to the N-th eigenvalue, but the array has only size N? The answer is as follows: such a situation cannot occur because the algorithm finds a pairs of eigenvalues, therefore, if WI[i]>0, I is strictly less than N-1. Note 2: The algorithm performance depends on the value of the internal parameter NS of the InternalSchurDecomposition subroutine which defines the number of shifts in the QR algorithm (similarly to the block width in block-matrix algorithms of linear algebra). If you require maximum performance on your machine, it is recommended to adjust this parameter manually. See also the InternalTREVC subroutine. The algorithm is based on the LAPACK 3.0 library. *************************************************************************/ bool rmatrixevd(ap::real_2d_array a, int n, int vneeded, ap::real_1d_array& wr, ap::real_1d_array& wi, ap::real_2d_array& vl, ap::real_2d_array& vr); #endif cmtk-3.3.1/libs/Numerics/qr.cxx000066400000000000000000000206431276303427400163550ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2010, 2013 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4973 $ // // $LastChangedDate: 2013-10-11 13:31:38 -0700 (Fri, 11 Oct 2013) $ // // $LastChangedBy: torstenrohlfing $ // */ /************************************************************************* Copyright (c) 1992-2007 The University of Tennessee. All rights reserved. Contributors: * Sergey Bochkanov (ALGLIB project). Translation from FORTRAN to pseudocode. See subroutines comments for additional copyrights. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer listed in this license in the documentation and/or other materials provided with the distribution. - Neither the name of the copyright holders nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *************************************************************************/ #include "qr.h" /************************************************************************* QR decomposition of a rectangular matrix of size MxN Input parameters: A - matrix A whose indexes range within [0..M-1, 0..N-1]. M - number of rows in matrix A. N - number of columns in matrix A. Output parameters: A - matrices Q and R in compact form (see below). Tau - array of scalar factors which are used to form matrix Q. Array whose index ranges within [0.. Min(M-1,N-1)]. Matrix A is represented as A = QR, where Q is an orthogonal matrix of size MxM, R - upper triangular (or upper trapezoid) matrix of size M x N. The elements of matrix R are located on and above the main diagonal of matrix A. The elements which are located in Tau array and below the main diagonal of matrix A are used to form matrix Q as follows: Matrix Q is represented as a product of elementary reflections Q = H(0)*H(2)*...*H(k-1), where k = min(m,n), and each H(i) is in the form H(i) = 1 - tau * v * (v^T) where tau is a scalar stored in Tau[I]; v - real vector, so that v(0:i-1) = 0, v(i) = 1, v(i+1:m-1) stored in A(i+1:m-1,i). -- LAPACK routine (version 3.0) -- Univ. of Tennessee, Univ. of California Berkeley, NAG Ltd., Courant Institute, Argonne National Lab, and Rice University February 29, 1992. Translation from FORTRAN to pseudocode (AlgoPascal) by Sergey Bochkanov, ALGLIB project, 2005-2007. *************************************************************************/ void rmatrixqr(ap::real_2d_array& a, int m, int n, ap::real_1d_array& tau) { ap::real_1d_array work; ap::real_1d_array t; int i; int k; int minmn; ap::real_value_type tmp; if( m<=0||n<=0 ) { return; } minmn = ap::minint(m, n); work.setbounds(0, n-1); t.setbounds(1, m); tau.setbounds(0, minmn-1); // // Test the input arguments // k = minmn; for(i = 0; i <= k-1; i++) { // // Generate elementary reflector H(i) to annihilate A(i+1:m,i) // ap::vmove(t.getvector(1, m-i), a.getcolumn(i, i, m-1)); generatereflection(t, m-i, tmp); tau(i) = tmp; ap::vmove(a.getcolumn(i, i, m-1), t.getvector(1, m-i)); t(1) = 1; if( i=0. N - number of columns in given matrix A. N>=0. Tau - scalar factors which are used to form Q. Output of the RMatrixQR subroutine. QColumns - required number of columns of matrix Q. M>=QColumns>=0. Output parameters: Q - first QColumns columns of matrix Q. Array whose indexes range within [0..M-1, 0..QColumns-1]. If QColumns=0, the array remains unchanged. -- ALGLIB -- Copyright 2005 by Bochkanov Sergey *************************************************************************/ void rmatrixqrunpackq(const ap::real_2d_array& a, int m, int n, const ap::real_1d_array& tau, int qcolumns, ap::real_2d_array& q) { int i; int j; int k; int minmn; ap::real_1d_array v; ap::real_1d_array work; #ifndef NO_AP_ASSERT ap::ap_error::make_assertion(qcolumns<=m, "UnpackQFromQR: QColumns>M!"); #endif if( m<=0||n<=0||qcolumns<=0 ) { return; } // // init // minmn = ap::minint(m, n); k = ap::minint(minmn, qcolumns); q.setbounds(0, m-1, 0, qcolumns-1); v.setbounds(1, m); work.setbounds(0, qcolumns-1); for(i = 0; i <= m-1; i++) { for(j = 0; j <= qcolumns-1; j++) { if( i==j ) { q(i,j) = 1; } else { q(i,j) = 0; } } } // // unpack Q // for(i = k-1; i >= 0; i--) { // // Apply H(i) // ap::vmove(v.getvector(1, m-i), a.getcolumn(i, i, m-1)); v(1) = 1; applyreflectionfromtheleft(q, tau(i), v, i, m-1, 0, qcolumns-1, work); } } /************************************************************************* Unpacking of matrix R from the QR decomposition of a matrix A Input parameters: A - matrices Q and R in compact form. Output of RMatrixQR subroutine. M - number of rows in given matrix A. M>=0. N - number of columns in given matrix A. N>=0. Output parameters: R - matrix R, array[0..M-1, 0..N-1]. -- ALGLIB -- Copyright 2005 by Bochkanov Sergey *************************************************************************/ void rmatrixqrunpackr(const ap::real_2d_array& a, int m, int n, ap::real_2d_array& r) { int i; int k; if( m<=0||n<=0 ) { return; } k = ap::minint(m, n); r.setbounds(0, m-1, 0, n-1); for(i = 0; i <= n-1; i++) { r(0,i) = 0; } for(i = 1; i <= m-1; i++) { ap::vmove(&r(i, 0), &r(0, 0), ap::vlen(0,n-1)); } for(i = 0; i <= k-1; i++) { ap::vmove(&r(i, i), &a(i, i), ap::vlen(i,n-1)); } } cmtk-3.3.1/libs/Numerics/qr.h000066400000000000000000000143011276303427400157740ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2010 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 2377 $ // // $LastChangedDate: 2010-09-29 11:38:36 -0700 (Wed, 29 Sep 2010) $ // // $LastChangedBy: torstenrohlfing $ // */ /************************************************************************* Copyright (c) 1992-2007 The University of Tennessee. All rights reserved. Contributors: * Sergey Bochkanov (ALGLIB project). Translation from FORTRAN to pseudocode. See subroutines comments for additional copyrights. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer listed in this license in the documentation and/or other materials provided with the distribution. - Neither the name of the copyright holders nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *************************************************************************/ #ifndef _qr_h #define _qr_h #include "ap.h" #include "reflections.h" /************************************************************************* QR decomposition of a rectangular matrix of size MxN Input parameters: A - matrix A whose indexes range within [0..M-1, 0..N-1]. M - number of rows in matrix A. N - number of columns in matrix A. Output parameters: A - matrices Q and R in compact form (see below). Tau - array of scalar factors which are used to form matrix Q. Array whose index ranges within [0.. Min(M-1,N-1)]. Matrix A is represented as A = QR, where Q is an orthogonal matrix of size MxM, R - upper triangular (or upper trapezoid) matrix of size M x N. The elements of matrix R are located on and above the main diagonal of matrix A. The elements which are located in Tau array and below the main diagonal of matrix A are used to form matrix Q as follows: Matrix Q is represented as a product of elementary reflections Q = H(0)*H(2)*...*H(k-1), where k = min(m,n), and each H(i) is in the form H(i) = 1 - tau * v * (v^T) where tau is a scalar stored in Tau[I]; v - real vector, so that v(0:i-1) = 0, v(i) = 1, v(i+1:m-1) stored in A(i+1:m-1,i). -- LAPACK routine (version 3.0) -- Univ. of Tennessee, Univ. of California Berkeley, NAG Ltd., Courant Institute, Argonne National Lab, and Rice University February 29, 1992. Translation from FORTRAN to pseudocode (AlgoPascal) by Sergey Bochkanov, ALGLIB project, 2005-2007. *************************************************************************/ void rmatrixqr(ap::real_2d_array& a, int m, int n, ap::real_1d_array& tau); /************************************************************************* Partial unpacking of matrix Q from the QR decomposition of a matrix A Input parameters: A - matrices Q and R in compact form. Output of RMatrixQR subroutine. M - number of rows in given matrix A. M>=0. N - number of columns in given matrix A. N>=0. Tau - scalar factors which are used to form Q. Output of the RMatrixQR subroutine. QColumns - required number of columns of matrix Q. M>=QColumns>=0. Output parameters: Q - first QColumns columns of matrix Q. Array whose indexes range within [0..M-1, 0..QColumns-1]. If QColumns=0, the array remains unchanged. -- ALGLIB -- Copyright 2005 by Bochkanov Sergey *************************************************************************/ void rmatrixqrunpackq(const ap::real_2d_array& a, int m, int n, const ap::real_1d_array& tau, int qcolumns, ap::real_2d_array& q); /************************************************************************* Unpacking of matrix R from the QR decomposition of a matrix A Input parameters: A - matrices Q and R in compact form. Output of RMatrixQR subroutine. M - number of rows in given matrix A. M>=0. N - number of columns in given matrix A. N>=0. Output parameters: R - matrix R, array[0..M-1, 0..N-1]. -- ALGLIB -- Copyright 2005 by Bochkanov Sergey *************************************************************************/ void rmatrixqrunpackr(const ap::real_2d_array& a, int m, int n, ap::real_2d_array& r); #endif cmtk-3.3.1/libs/Numerics/randomc.h000066400000000000000000000175641276303427400170130ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2010 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4945 $ // // $LastChangedDate: 2013-10-04 14:00:06 -0700 (Fri, 04 Oct 2013) $ // // $LastChangedBy: torstenrohlfing $ // */ #include "ap.h" /***************************** randomc.h ********************************** * Author: Agner Fog * Date created: 1997 * Last modified: 2008-11-16 * Project: randomc.h * Source URL: www.agner.org/random * * Description: * This header file contains class declarations and other definitions for the * randomc class library of uniform random number generators in C++ language. * * Overview of classes: * ==================== * * class CRandomMersenne: * Random number generator of type Mersenne twister. * Source file mersenne.cpp * * class CRandomMother: * Random number generator of type Mother-of-All (Multiply with carry). * Source file mother.cpp * * class CRandomSFMT: * Random number generator of type SIMD-oriented Fast Mersenne Twister. * The class definition is not included here because it is not * portable to all platforms. See sfmt.h and sfmt.cpp for details. * * Member functions (methods): * =========================== * * All these classes have identical member functions: * * Constructor(int seed): * The seed can be any integer. The time may be used as seed. * Executing a program twice with the same seed will give the same sequence * of random numbers. A different seed will give a different sequence. * * void RandomInit(int seed); * Re-initializes the random number generator with a new seed. * * void RandomInitByArray(int const seeds[], int NumSeeds); * In CRandomMersenne and CRandomSFMT only: Use this function if you want * to initialize with a seed with more than 32 bits. All bits in the seeds[] * array will influence the sequence of random numbers generated. NumSeeds * is the number of entries in the seeds[] array. * * ap::real_value_type Random(); * Gives a floating point random number in the interval 0 <= x < 1. * The resolution is 32 bits in CRandomMother and CRandomMersenne, and * 52 bits in CRandomSFMT. * * int IRandom(int min, int max); * Gives an integer random number in the interval min <= x <= max. * (max-min < MAXINT). * The precision is 2^-32 (defined as the difference in frequency between * possible output values). The frequencies are exact if max-min+1 is a * power of 2. * * int IRandomX(int min, int max); * Same as IRandom, but exact. In CRandomMersenne and CRandomSFMT only. * The frequencies of all output values are exactly the same for an * infinitely long sequence. (Only relevant for extremely long sequences). * * uint32_t BRandom(); * Gives 32 random bits. * * * Example: * ======== * The file EX-RAN.CPP contains an example of how to generate random numbers. * * * Library version: * ================ * Optimized versions of these random number generators are provided as function * libraries in randoma.zip. These function libraries are coded in assembly * language and support only x86 platforms, including 32-bit and 64-bit * Windows, Linux, BSD, Mac OS-X (Intel based). Use randoma.h from randoma.zip * * * Non-uniform random number generators: * ===================================== * Random number generators with various non-uniform distributions are * available in stocc.zip (www.agner.org/random). * * * Further documentation: * ====================== * The file ran-instructions.pdf contains further documentation and * instructions for these random number generators. * * Copyright 1997-2008 by Agner Fog. * GNU General Public License http://www.gnu.org/licenses/gpl.html *******************************************************************************/ #ifndef RANDOMC_H #define RANDOMC_H #include // Define integer types with known size: int32_t, uint32_t, int64_t, uint64_t. // If this doesn't work then insert compiler-specific definitions here: #if defined(HAVE_INTTYPES_H) // Compilers supporting C99 or C++0x have inttypes.h defining these integer types #include #define INT64_SUPPORTED // Remove this if the compiler doesn't support 64-bit integers #elif defined(_WIN16) || defined(__MSDOS__) || defined(_MSDOS) // 16 bit systems use long int for 32 bit integer typedef signed long int int32_t; typedef unsigned long int uint32_t; #elif defined(_MSC_VER) // Microsoft have their own definition typedef signed __int32 int32_t; typedef unsigned __int32 uint32_t; typedef signed __int64 int64_t; typedef unsigned __int64 uint64_t; #define INT64_SUPPORTED // Remove this if the compiler doesn't support 64-bit integers #else // This works with most compilers typedef signed int int32_t; typedef unsigned int uint32_t; typedef long long int64_t; typedef unsigned long long uint64_t; #define INT64_SUPPORTED // Remove this if the compiler doesn't support 64-bit integers #endif /*********************************************************************** System-specific user interface functions ***********************************************************************/ void EndOfProgram(void); // System-specific exit code (userintf.cpp) void FatalError(const char *ErrorText);// System-specific error reporting (userintf.cpp) #if defined(__cplusplus) // class definitions only in C++ /*********************************************************************** Define random number generator classes ***********************************************************************/ class CRandomMersenne { // Encapsulate random number generator // Choose which version of Mersenne Twister you want: #if 0 // Define constants for type MT11213A: #define MERS_N 351 #define MERS_M 175 #define MERS_R 19 #define MERS_U 11 #define MERS_S 7 #define MERS_T 15 #define MERS_L 17 #define MERS_A 0xE4BD75F5 #define MERS_B 0x655E5280 #define MERS_C 0xFFD58000 #else // or constants for type MT19937: #define MERS_N 624 #define MERS_M 397 #define MERS_R 31 #define MERS_U 11 #define MERS_S 7 #define MERS_T 15 #define MERS_L 18 #define MERS_A 0x9908B0DF #define MERS_B 0x9D2C5680 #define MERS_C 0xEFC60000 #endif public: CRandomMersenne(int seed) { // Constructor RandomInit(seed); } void RandomInit(int seed); // Re-seed ap::real_value_type Random(); // Output random float uint32_t BRandom(); // Output random bits private: void Init0(int seed); // Basic initialization procedure uint32_t mt[MERS_N]; // State vector int mti; // Index into mt }; class CRandomMother { // Encapsulate random number generator public: void RandomInit(int seed); // Initialization ap::real_value_type Random(); // Get floating point random number uint32_t BRandom(); // Output random bits CRandomMother(int seed) { // Constructor RandomInit(seed);} protected: uint32_t x[5]; // History buffer }; #endif // __cplusplus #endif // RANDOMC_H cmtk-3.3.1/libs/Numerics/reflections.cxx000066400000000000000000000225421276303427400202500ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2011 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 3193 $ // // $LastChangedDate: 2011-04-28 16:15:14 -0700 (Thu, 28 Apr 2011) $ // // $LastChangedBy: torstenrohlfing $ // */ /************************************************************************* Copyright (c) 1992-2007 The University of Tennessee. All rights reserved. Contributors: * Sergey Bochkanov (ALGLIB project). Translation from FORTRAN to pseudocode. See subroutines comments for additional copyrights. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer listed in this license in the documentation and/or other materials provided with the distribution. - Neither the name of the copyright holders nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *************************************************************************/ #include "reflections.h" /************************************************************************* Generation of an elementary reflection transformation The subroutine generates elementary reflection H of order N, so that, for a given X, the following equality holds true: ( X(1) ) ( Beta ) H * ( .. ) = ( 0 ) ( X(n) ) ( 0 ) where ( V(1) ) H = 1 - Tau * ( .. ) * ( V(1), ..., V(n) ) ( V(n) ) where the first component of vector V equals 1. Input parameters: X - vector. Array whose index ranges within [1..N]. N - reflection order. Output parameters: X - components from 2 to N are replaced with vector V. The first component is replaced with parameter Beta. Tau - scalar value Tau. If X is a null vector, Tau equals 0, otherwise 1 <= Tau <= 2. This subroutine is the modification of the DLARFG subroutines from the LAPACK library. It has a similar functionality except for the fact that it doesn’t handle errors when the intermediate results cause an overflow. MODIFICATIONS: 24.12.2005 sign(Alpha) was replaced with an analogous to the Fortran SIGN code. -- LAPACK auxiliary routine (version 3.0) -- Univ. of Tennessee, Univ. of California Berkeley, NAG Ltd., Courant Institute, Argonne National Lab, and Rice University September 30, 1994 *************************************************************************/ void generatereflection(ap::real_1d_array& x, int n, ap::real_value_type& tau) { int j; ap::real_value_type alpha; ap::real_value_type xnorm; ap::real_value_type v; ap::real_value_type beta; ap::real_value_type mx; // // Executable Statements .. // if( n<=1 ) { tau = 0; return; } // // XNORM = DNRM2( N-1, X, INCX ) // alpha = x(1); mx = 0; for(j = 2; j <= n; j++) { mx = ap::maxreal(fabs(x(j)), mx); } xnorm = 0; if( mx!=0 ) { for(j = 2; j <= n; j++) { xnorm = xnorm+ap::sqr(x(j)/mx); } xnorm = sqrt(xnorm)*mx; } if( xnorm==0 ) { // // H = I // tau = 0; return; } // // general case // mx = ap::maxreal(fabs(alpha), fabs(xnorm)); beta = -mx*sqrt(ap::sqr(alpha/mx)+ap::sqr(xnorm/mx)); if( alpha<0 ) { beta = -beta; } tau = (beta-alpha)/beta; v = 1/(alpha-beta); ap::vmul(&x(2), ap::vlen(2,n), v); x(1) = beta; } /************************************************************************* Application of an elementary reflection to a rectangular matrix of size MxN The algorithm pre-multiplies the matrix by an elementary reflection transformation which is given by column V and scalar Tau (see the description of the GenerateReflection procedure). Not the whole matrix but only a part of it is transformed (rows from M1 to M2, columns from N1 to N2). Only the elements of this submatrix are changed. Input parameters: C - matrix to be transformed. Tau - scalar defining the transformation. V - column defining the transformation. Array whose index ranges within [1..M2-M1+1]. M1, M2 - range of rows to be transformed. N1, N2 - range of columns to be transformed. WORK - working array whose indexes goes from N1 to N2. Output parameters: C - the result of multiplying the input matrix C by the transformation matrix which is given by Tau and V. If N1>N2 or M1>M2, C is not modified. -- LAPACK auxiliary routine (version 3.0) -- Univ. of Tennessee, Univ. of California Berkeley, NAG Ltd., Courant Institute, Argonne National Lab, and Rice University September 30, 1994 *************************************************************************/ void applyreflectionfromtheleft(ap::real_2d_array& c, ap::real_value_type tau, const ap::real_1d_array& v, int m1, int m2, int n1, int n2, ap::real_1d_array& work) { ap::real_value_type t; int i; if( tau==0||n1>n2||m1>m2 ) { return; } // // w := C' * v // for(i = n1; i <= n2; i++) { work(i) = 0; } for(i = m1; i <= m2; i++) { t = v(i+1-m1); ap::vadd(&work(n1), &c(i, n1), ap::vlen(n1,n2), t); } // // C := C - tau * v * w' // for(i = m1; i <= m2; i++) { t = v(i-m1+1)*tau; ap::vsub(&c(i, n1), &work(n1), ap::vlen(n1,n2), t); } } /************************************************************************* Application of an elementary reflection to a rectangular matrix of size MxN The algorithm post-multiplies the matrix by an elementary reflection transformation which is given by column V and scalar Tau (see the description of the GenerateReflection procedure). Not the whole matrix but only a part of it is transformed (rows from M1 to M2, columns from N1 to N2). Only the elements of this submatrix are changed. Input parameters: C - matrix to be transformed. Tau - scalar defining the transformation. V - column defining the transformation. Array whose index ranges within [1..N2-N1+1]. M1, M2 - range of rows to be transformed. N1, N2 - range of columns to be transformed. WORK - working array whose indexes goes from M1 to M2. Output parameters: C - the result of multiplying the input matrix C by the transformation matrix which is given by Tau and V. If N1>N2 or M1>M2, C is not modified. -- LAPACK auxiliary routine (version 3.0) -- Univ. of Tennessee, Univ. of California Berkeley, NAG Ltd., Courant Institute, Argonne National Lab, and Rice University September 30, 1994 *************************************************************************/ void applyreflectionfromtheright(ap::real_2d_array& c, ap::real_value_type tau, const ap::real_1d_array& v, int m1, int m2, int n1, int n2, ap::real_1d_array& work) { ap::real_value_type t; int i; if( tau==0||n1>n2||m1>m2 ) { return; } // // w := C * v // for(i = m1; i <= m2; i++) { t = ap::vdotproduct(&c(i, n1), &v(1), ap::vlen(n1,n2)); work(i) = t; } // // C := C - w * v' // for(i = m1; i <= m2; i++) { t = work(i)*tau; ap::vsub(&c(i, n1), &v(1), ap::vlen(n1,n2), t); } } cmtk-3.3.1/libs/Numerics/reflections.h000066400000000000000000000167321276303427400177010ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // Copyright 2004-2009 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 47 $ // // $LastChangedDate: 2009-06-12 21:27:52 -0700 (Fri, 12 Jun 2009) $ // // $LastChangedBy: torstenrohlfing $ // */ /************************************************************************* Copyright (c) 1992-2007 The University of Tennessee. All rights reserved. Contributors: * Sergey Bochkanov (ALGLIB project). Translation from FORTRAN to pseudocode. See subroutines comments for additional copyrights. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer listed in this license in the documentation and/or other materials provided with the distribution. - Neither the name of the copyright holders nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *************************************************************************/ #ifndef _reflections_h #define _reflections_h #include "ap.h" /************************************************************************* Generation of an elementary reflection transformation The subroutine generates elementary reflection H of order N, so that, for a given X, the following equality holds true: ( X(1) ) ( Beta ) H * ( .. ) = ( 0 ) ( X(n) ) ( 0 ) where ( V(1) ) H = 1 - Tau * ( .. ) * ( V(1), ..., V(n) ) ( V(n) ) where the first component of vector V equals 1. Input parameters: X - vector. Array whose index ranges within [1..N]. N - reflection order. Output parameters: X - components from 2 to N are replaced with vector V. The first component is replaced with parameter Beta. Tau - scalar value Tau. If X is a null vector, Tau equals 0, otherwise 1 <= Tau <= 2. This subroutine is the modification of the DLARFG subroutines from the LAPACK library. It has a similar functionality except for the fact that it doesn’t handle errors when the intermediate results cause an overflow. MODIFICATIONS: 24.12.2005 sign(Alpha) was replaced with an analogous to the Fortran SIGN code. -- LAPACK auxiliary routine (version 3.0) -- Univ. of Tennessee, Univ. of California Berkeley, NAG Ltd., Courant Institute, Argonne National Lab, and Rice University September 30, 1994 *************************************************************************/ void generatereflection(ap::real_1d_array& x, int n, ap::real_value_type& tau); /************************************************************************* Application of an elementary reflection to a rectangular matrix of size MxN The algorithm pre-multiplies the matrix by an elementary reflection transformation which is given by column V and scalar Tau (see the description of the GenerateReflection procedure). Not the whole matrix but only a part of it is transformed (rows from M1 to M2, columns from N1 to N2). Only the elements of this submatrix are changed. Input parameters: C - matrix to be transformed. Tau - scalar defining the transformation. V - column defining the transformation. Array whose index ranges within [1..M2-M1+1]. M1, M2 - range of rows to be transformed. N1, N2 - range of columns to be transformed. WORK - working array whose indexes goes from N1 to N2. Output parameters: C - the result of multiplying the input matrix C by the transformation matrix which is given by Tau and V. If N1>N2 or M1>M2, C is not modified. -- LAPACK auxiliary routine (version 3.0) -- Univ. of Tennessee, Univ. of California Berkeley, NAG Ltd., Courant Institute, Argonne National Lab, and Rice University September 30, 1994 *************************************************************************/ void applyreflectionfromtheleft(ap::real_2d_array& c, ap::real_value_type tau, const ap::real_1d_array& v, int m1, int m2, int n1, int n2, ap::real_1d_array& work); /************************************************************************* Application of an elementary reflection to a rectangular matrix of size MxN The algorithm post-multiplies the matrix by an elementary reflection transformation which is given by column V and scalar Tau (see the description of the GenerateReflection procedure). Not the whole matrix but only a part of it is transformed (rows from M1 to M2, columns from N1 to N2). Only the elements of this submatrix are changed. Input parameters: C - matrix to be transformed. Tau - scalar defining the transformation. V - column defining the transformation. Array whose index ranges within [1..N2-N1+1]. M1, M2 - range of rows to be transformed. N1, N2 - range of columns to be transformed. WORK - working array whose indexes goes from M1 to M2. Output parameters: C - the result of multiplying the input matrix C by the transformation matrix which is given by Tau and V. If N1>N2 or M1>M2, C is not modified. -- LAPACK auxiliary routine (version 3.0) -- Univ. of Tennessee, Univ. of California Berkeley, NAG Ltd., Courant Institute, Argonne National Lab, and Rice University September 30, 1994 *************************************************************************/ void applyreflectionfromtheright(ap::real_2d_array& c, ap::real_value_type tau, const ap::real_1d_array& v, int m1, int m2, int n1, int n2, ap::real_1d_array& work); #endif cmtk-3.3.1/libs/Numerics/rotations.cxx000066400000000000000000000275301276303427400177570ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // Copyright 2004-2009 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 2153 $ // // $LastChangedDate: 2010-08-04 11:02:28 -0700 (Wed, 04 Aug 2010) $ // // $LastChangedBy: torstenrohlfing $ // */ /************************************************************************* Copyright (c) 1992-2007 The University of Tennessee. All rights reserved. Contributors: * Sergey Bochkanov (ALGLIB project). Translation from FORTRAN to pseudocode. See subroutines comments for additional copyrights. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer listed in this license in the documentation and/or other materials provided with the distribution. - Neither the name of the copyright holders nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *************************************************************************/ #include "rotations.h" /************************************************************************* Application of a sequence of elementary rotations to a matrix The algorithm pre-multiplies the matrix by a sequence of rotation transformations which is given by arrays C and S. Depending on the value of the IsForward parameter either 1 and 2, 3 and 4 and so on (if IsForward=true) rows are rotated, or the rows N and N-1, N-2 and N-3 and so on, are rotated. Not the whole matrix but only a part of it is transformed (rows from M1 to M2, columns from N1 to N2). Only the elements of this submatrix are changed. Input parameters: IsForward - the sequence of the rotation application. M1,M2 - the range of rows to be transformed. N1, N2 - the range of columns to be transformed. C,S - transformation coefficients. Array whose index ranges within [1..M2-M1]. A - processed matrix. WORK - working array whose index ranges within [N1..N2]. Output parameters: A - transformed matrix. Utility subroutine. *************************************************************************/ void applyrotationsfromtheleft(bool isforward, int m1, int m2, int n1, int n2, const ap::real_1d_array& c, const ap::real_1d_array& s, ap::real_2d_array& a, ap::real_1d_array& work) { int j; int jp1; ap::real_value_type ctemp; ap::real_value_type stemp; ap::real_value_type temp; if( m1>m2||n1>n2 ) { return; } // // Form P * A // if( isforward ) { if( n1!=n2 ) { // // Common case: N1<>N2 // for(j = m1; j <= m2-1; j++) { ctemp = c(j-m1+1); stemp = s(j-m1+1); if( ctemp!=1||stemp!=0 ) { jp1 = j+1; ap::vmove(&work(n1), &a(jp1, n1), ap::vlen(n1,n2), ctemp); ap::vsub(&work(n1), &a(j, n1), ap::vlen(n1,n2), stemp); ap::vmul(&a(j, n1), ap::vlen(n1,n2), ctemp); ap::vadd(&a(j, n1), &a(jp1, n1), ap::vlen(n1,n2), stemp); ap::vmove(&a(jp1, n1), &work(n1), ap::vlen(n1,n2)); } } } else { // // Special case: N1=N2 // for(j = m1; j <= m2-1; j++) { ctemp = c(j-m1+1); stemp = s(j-m1+1); if( ctemp!=1||stemp!=0 ) { temp = a(j+1,n1); a(j+1,n1) = ctemp*temp-stemp*a(j,n1); a(j,n1) = stemp*temp+ctemp*a(j,n1); } } } } else { if( n1!=n2 ) { // // Common case: N1<>N2 // for(j = m2-1; j >= m1; j--) { ctemp = c(j-m1+1); stemp = s(j-m1+1); if( ctemp!=1||stemp!=0 ) { jp1 = j+1; ap::vmove(&work(n1), &a(jp1, n1), ap::vlen(n1,n2), ctemp); ap::vsub(&work(n1), &a(j, n1), ap::vlen(n1,n2), stemp); ap::vmul(&a(j, n1), ap::vlen(n1,n2), ctemp); ap::vadd(&a(j, n1), &a(jp1, n1), ap::vlen(n1,n2), stemp); ap::vmove(&a(jp1, n1), &work(n1), ap::vlen(n1,n2)); } } } else { // // Special case: N1=N2 // for(j = m2-1; j >= m1; j--) { ctemp = c(j-m1+1); stemp = s(j-m1+1); if( ctemp!=1||stemp!=0 ) { temp = a(j+1,n1); a(j+1,n1) = ctemp*temp-stemp*a(j,n1); a(j,n1) = stemp*temp+ctemp*a(j,n1); } } } } } /************************************************************************* Application of a sequence of elementary rotations to a matrix The algorithm post-multiplies the matrix by a sequence of rotation transformations which is given by arrays C and S. Depending on the value of the IsForward parameter either 1 and 2, 3 and 4 and so on (if IsForward=true) rows are rotated, or the rows N and N-1, N-2 and N-3 and so on are rotated. Not the whole matrix but only a part of it is transformed (rows from M1 to M2, columns from N1 to N2). Only the elements of this submatrix are changed. Input parameters: IsForward - the sequence of the rotation application. M1,M2 - the range of rows to be transformed. N1, N2 - the range of columns to be transformed. C,S - transformation coefficients. Array whose index ranges within [1..N2-N1]. A - processed matrix. WORK - working array whose index ranges within [M1..M2]. Output parameters: A - transformed matrix. Utility subroutine. *************************************************************************/ void applyrotationsfromtheright(bool isforward, int m1, int m2, int n1, int n2, const ap::real_1d_array& c, const ap::real_1d_array& s, ap::real_2d_array& a, ap::real_1d_array& work) { int j; int jp1; ap::real_value_type ctemp; ap::real_value_type stemp; ap::real_value_type temp; // // Form A * P' // if( isforward ) { if( m1!=m2 ) { // // Common case: M1<>M2 // for(j = n1; j <= n2-1; j++) { ctemp = c(j-n1+1); stemp = s(j-n1+1); if( ctemp!=1||stemp!=0 ) { jp1 = j+1; ap::vmove(work.getvector(m1, m2), a.getcolumn(jp1, m1, m2), ctemp); ap::vsub(work.getvector(m1, m2), a.getcolumn(j, m1, m2), stemp); ap::vmul(a.getcolumn(j, m1, m2), ctemp); ap::vadd(a.getcolumn(j, m1, m2), a.getcolumn(jp1, m1, m2), stemp); ap::vmove(a.getcolumn(jp1, m1, m2), work.getvector(m1, m2)); } } } else { // // Special case: M1=M2 // for(j = n1; j <= n2-1; j++) { ctemp = c(j-n1+1); stemp = s(j-n1+1); if( ctemp!=1||stemp!=0 ) { temp = a(m1,j+1); a(m1,j+1) = ctemp*temp-stemp*a(m1,j); a(m1,j) = stemp*temp+ctemp*a(m1,j); } } } } else { if( m1!=m2 ) { // // Common case: M1<>M2 // for(j = n2-1; j >= n1; j--) { ctemp = c(j-n1+1); stemp = s(j-n1+1); if( ctemp!=1||stemp!=0 ) { jp1 = j+1; ap::vmove(work.getvector(m1, m2), a.getcolumn(jp1, m1, m2), ctemp); ap::vsub(work.getvector(m1, m2), a.getcolumn(j, m1, m2), stemp); ap::vmul(a.getcolumn(j, m1, m2), ctemp); ap::vadd(a.getcolumn(j, m1, m2), a.getcolumn(jp1, m1, m2), stemp); ap::vmove(a.getcolumn(jp1, m1, m2), work.getvector(m1, m2)); } } } else { // // Special case: M1=M2 // for(j = n2-1; j >= n1; j--) { ctemp = c(j-n1+1); stemp = s(j-n1+1); if( ctemp!=1||stemp!=0 ) { temp = a(m1,j+1); a(m1,j+1) = ctemp*temp-stemp*a(m1,j); a(m1,j) = stemp*temp+ctemp*a(m1,j); } } } } } /************************************************************************* The subroutine generates the elementary rotation, so that: [ CS SN ] . [ F ] = [ R ] [ -SN CS ] [ G ] [ 0 ] CS**2 + SN**2 = 1 *************************************************************************/ void generaterotation(ap::real_value_type f, ap::real_value_type g, ap::real_value_type& cs, ap::real_value_type& sn, ap::real_value_type& r) { ap::real_value_type f1; ap::real_value_type g1; if( g==0 ) { cs = 1; sn = 0; r = f; } else { if( f==0 ) { cs = 0; sn = 1; r = g; } else { f1 = f; g1 = g; r = sqrt(ap::sqr(f1)+ap::sqr(g1)); cs = f1/r; sn = g1/r; if( fabs(f)>fabs(g)&&cs<0 ) { cs = -cs; sn = -sn; r = -r; } } } } cmtk-3.3.1/libs/Numerics/rotations.h000066400000000000000000000140771276303427400174060ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // Copyright 2004-2009 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 47 $ // // $LastChangedDate: 2009-06-12 21:27:52 -0700 (Fri, 12 Jun 2009) $ // // $LastChangedBy: torstenrohlfing $ // */ /************************************************************************* Copyright (c) 1992-2007 The University of Tennessee. All rights reserved. Contributors: * Sergey Bochkanov (ALGLIB project). Translation from FORTRAN to pseudocode. See subroutines comments for additional copyrights. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer listed in this license in the documentation and/or other materials provided with the distribution. - Neither the name of the copyright holders nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *************************************************************************/ #ifndef _rotations_h #define _rotations_h #include "ap.h" /************************************************************************* Application of a sequence of elementary rotations to a matrix The algorithm pre-multiplies the matrix by a sequence of rotation transformations which is given by arrays C and S. Depending on the value of the IsForward parameter either 1 and 2, 3 and 4 and so on (if IsForward=true) rows are rotated, or the rows N and N-1, N-2 and N-3 and so on, are rotated. Not the whole matrix but only a part of it is transformed (rows from M1 to M2, columns from N1 to N2). Only the elements of this submatrix are changed. Input parameters: IsForward - the sequence of the rotation application. M1,M2 - the range of rows to be transformed. N1, N2 - the range of columns to be transformed. C,S - transformation coefficients. Array whose index ranges within [1..M2-M1]. A - processed matrix. WORK - working array whose index ranges within [N1..N2]. Output parameters: A - transformed matrix. Utility subroutine. *************************************************************************/ void applyrotationsfromtheleft(bool isforward, int m1, int m2, int n1, int n2, const ap::real_1d_array& c, const ap::real_1d_array& s, ap::real_2d_array& a, ap::real_1d_array& work); /************************************************************************* Application of a sequence of elementary rotations to a matrix The algorithm post-multiplies the matrix by a sequence of rotation transformations which is given by arrays C and S. Depending on the value of the IsForward parameter either 1 and 2, 3 and 4 and so on (if IsForward=true) rows are rotated, or the rows N and N-1, N-2 and N-3 and so on are rotated. Not the whole matrix but only a part of it is transformed (rows from M1 to M2, columns from N1 to N2). Only the elements of this submatrix are changed. Input parameters: IsForward - the sequence of the rotation application. M1,M2 - the range of rows to be transformed. N1, N2 - the range of columns to be transformed. C,S - transformation coefficients. Array whose index ranges within [1..N2-N1]. A - processed matrix. WORK - working array whose index ranges within [M1..M2]. Output parameters: A - transformed matrix. Utility subroutine. *************************************************************************/ void applyrotationsfromtheright(bool isforward, int m1, int m2, int n1, int n2, const ap::real_1d_array& c, const ap::real_1d_array& s, ap::real_2d_array& a, ap::real_1d_array& work); /************************************************************************* The subroutine generates the elementary rotation, so that: [ CS SN ] . [ F ] = [ R ] [ -SN CS ] [ G ] [ 0 ] CS**2 + SN**2 = 1 *************************************************************************/ void generaterotation(ap::real_value_type f, ap::real_value_type g, ap::real_value_type& cs, ap::real_value_type& sn, ap::real_value_type& r); #endif cmtk-3.3.1/libs/Numerics/sblas.cxx000066400000000000000000000136511276303427400170400ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2011 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 3193 $ // // $LastChangedDate: 2011-04-28 16:15:14 -0700 (Thu, 28 Apr 2011) $ // // $LastChangedBy: torstenrohlfing $ // */ /************************************************************************* Copyright (c) 1992-2007 The University of Tennessee. All rights reserved. Contributors: * Sergey Bochkanov (ALGLIB project). Translation from FORTRAN to pseudocode. See subroutines comments for additional copyrights. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer listed in this license in the documentation and/or other materials provided with the distribution. - Neither the name of the copyright holders nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *************************************************************************/ #include "sblas.h" void symmetricmatrixvectormultiply(const ap::real_2d_array& a, bool isupper, int i1, int i2, const ap::real_1d_array& x, ap::real_value_type alpha, ap::real_1d_array& y) { int i; int ba1; int by1; int by2; int bx1; int bx2; int n; ap::real_value_type v; n = i2-i1+1; if( n<=0 ) { return; } // // Let A = L + D + U, where // L is strictly lower triangular (main diagonal is zero) // D is diagonal // U is strictly upper triangular (main diagonal is zero) // // A*x = L*x + D*x + U*x // // Calculate D*x first // for(i = i1; i <= i2; i++) { y(i-i1+1) = a(i,i)*x(i-i1+1); } // // Add L*x + U*x // if( isupper ) { for(i = i1; i <= i2-1; i++) { // // Add L*x to the result // v = x(i-i1+1); by1 = i-i1+2; by2 = n; ba1 = i+1; ap::vadd(&y(by1), &a(i, ba1), ap::vlen(by1,by2), v); // // Add U*x to the result // bx1 = i-i1+2; bx2 = n; ba1 = i+1; v = ap::vdotproduct(&x(bx1), &a(i, ba1), ap::vlen(bx1,bx2)); y(i-i1+1) = y(i-i1+1)+v; } } else { for(i = i1+1; i <= i2; i++) { // // Add L*x to the result // bx1 = 1; bx2 = i-i1; ba1 = i1; v = ap::vdotproduct(&x(bx1), &a(i, ba1), ap::vlen(bx1,bx2)); y(i-i1+1) = y(i-i1+1)+v; // // Add U*x to the result // v = x(i-i1+1); by1 = 1; by2 = i-i1; ba1 = i1; ap::vadd(&y(by1), &a(i, ba1), ap::vlen(by1,by2), v); } } ap::vmul(&y(1), ap::vlen(1,n), alpha); } void symmetricrank2update(ap::real_2d_array& a, bool isupper, int i1, int i2, const ap::real_1d_array& x, const ap::real_1d_array& y, ap::real_1d_array& t, ap::real_value_type alpha) { int i; int tp1; int tp2; ap::real_value_type v; if( isupper ) { for(i = i1; i <= i2; i++) { tp1 = i+1-i1; tp2 = i2-i1+1; v = x(i+1-i1); ap::vmove(&t(tp1), &y(tp1), ap::vlen(tp1,tp2), v); v = y(i+1-i1); ap::vadd(&t(tp1), &x(tp1), ap::vlen(tp1,tp2), v); ap::vmul(&t(tp1), ap::vlen(tp1,tp2), alpha); ap::vadd(&a(i, i), &t(tp1), ap::vlen(i,i2)); } } else { for(i = i1; i <= i2; i++) { tp1 = 1; tp2 = i+1-i1; v = x(i+1-i1); ap::vmove(&t(tp1), &y(tp1), ap::vlen(tp1,tp2), v); v = y(i+1-i1); ap::vadd(&t(tp1), &x(tp1), ap::vlen(tp1,tp2), v); ap::vmul(&t(tp1), ap::vlen(tp1,tp2), alpha); ap::vadd(&a(i, i1), &t(tp1), ap::vlen(i1,i)); } } } cmtk-3.3.1/libs/Numerics/sblas.h000066400000000000000000000065141276303427400164650ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // Copyright 2004-2009 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 47 $ // // $LastChangedDate: 2009-06-12 21:27:52 -0700 (Fri, 12 Jun 2009) $ // // $LastChangedBy: torstenrohlfing $ // */ /************************************************************************* Copyright (c) 1992-2007 The University of Tennessee. All rights reserved. Contributors: * Sergey Bochkanov (ALGLIB project). Translation from FORTRAN to pseudocode. See subroutines comments for additional copyrights. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer listed in this license in the documentation and/or other materials provided with the distribution. - Neither the name of the copyright holders nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *************************************************************************/ #ifndef _sblas_h #define _sblas_h #include "ap.h" void symmetricmatrixvectormultiply(const ap::real_2d_array& a, bool isupper, int i1, int i2, const ap::real_1d_array& x, ap::real_value_type alpha, ap::real_1d_array& y); void symmetricrank2update(ap::real_2d_array& a, bool isupper, int i1, int i2, const ap::real_1d_array& x, const ap::real_1d_array& y, ap::real_1d_array& t, ap::real_value_type alpha); #endif cmtk-3.3.1/libs/Numerics/sevd.cxx000066400000000000000000000111121276303427400166630ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2010, 2013 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4973 $ // // $LastChangedDate: 2013-10-11 13:31:38 -0700 (Fri, 11 Oct 2013) $ // // $LastChangedBy: torstenrohlfing $ // */ /************************************************************************* Copyright (c) 2005-2007, Sergey Bochkanov (ALGLIB project). Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer listed in this license in the documentation and/or other materials provided with the distribution. - Neither the name of the copyright holders nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *************************************************************************/ #include "sevd.h" /************************************************************************* Finding the eigenvalues and eigenvectors of a symmetric matrix The algorithm finds eigen pairs of a symmetric matrix by reducing it to tridiagonal form and using the QL/QR algorithm. Input parameters: A - symmetric matrix which is given by its upper or lower triangular part. Array whose indexes range within [0..N-1, 0..N-1]. N - size of matrix A. IsUpper - storage format. ZNeeded - flag controlling whether the eigenvectors are needed or not. If ZNeeded is equal to: * 0, the eigenvectors are not returned; * 1, the eigenvectors are returned. Output parameters: D - eigenvalues in ascending order. Array whose index ranges within [0..N-1]. Z - if ZNeeded is equal to: * 0, Z hasn’t changed; * 1, Z contains the eigenvectors. Array whose indexes range within [0..N-1, 0..N-1]. The eigenvectors are stored in the matrix columns. Result: True, if the algorithm has converged. False, if the algorithm hasn't converged (rare case). -- ALGLIB -- Copyright 2005-2008 by Bochkanov Sergey *************************************************************************/ bool smatrixevd(ap::real_2d_array a, int n, int zneeded, bool isupper, ap::real_1d_array& d, ap::real_2d_array& z) { bool result; ap::real_1d_array tau; ap::real_1d_array e; #ifndef NO_AP_ASSERT ap::ap_error::make_assertion(zneeded==0||zneeded==1, "SMatrixEVD: incorrect ZNeeded"); #endif smatrixtd(a, n, isupper, tau, d, e); if( zneeded==1 ) { smatrixtdunpackq(a, n, isupper, tau, z); } result = smatrixtdevd(d, e, n, zneeded, z); return result; } cmtk-3.3.1/libs/Numerics/sevd.h000066400000000000000000000105571276303427400163240ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2010 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 2375 $ // // $LastChangedDate: 2010-09-29 11:23:38 -0700 (Wed, 29 Sep 2010) $ // // $LastChangedBy: torstenrohlfing $ // */ /************************************************************************* Copyright (c) 2005-2007, Sergey Bochkanov (ALGLIB project). Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer listed in this license in the documentation and/or other materials provided with the distribution. - Neither the name of the copyright holders nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *************************************************************************/ #ifndef _sevd_h #define _sevd_h #include "ap.h" #include "blas.h" #include "rotations.h" #include "tdevd.h" #include "sblas.h" #include "reflections.h" #include "tridiagonal.h" /************************************************************************* Finding the eigenvalues and eigenvectors of a symmetric matrix The algorithm finds eigen pairs of a symmetric matrix by reducing it to tridiagonal form and using the QL/QR algorithm. Input parameters: A - symmetric matrix which is given by its upper or lower triangular part. Array whose indexes range within [0..N-1, 0..N-1]. N - size of matrix A. IsUpper - storage format. ZNeeded - flag controlling whether the eigenvectors are needed or not. If ZNeeded is equal to: * 0, the eigenvectors are not returned; * 1, the eigenvectors are returned. Output parameters: D - eigenvalues in ascending order. Array whose index ranges within [0..N-1]. Z - if ZNeeded is equal to: * 0, Z hasn’t changed; * 1, Z contains the eigenvectors. Array whose indexes range within [0..N-1, 0..N-1]. The eigenvectors are stored in the matrix columns. Result: True, if the algorithm has converged. False, if the algorithm hasn't converged (rare case). -- ALGLIB -- Copyright 2005-2008 by Bochkanov Sergey *************************************************************************/ bool smatrixevd(ap::real_2d_array a, int n, int zneeded, bool isupper, ap::real_1d_array& d, ap::real_2d_array& z); #endif cmtk-3.3.1/libs/Numerics/spddet.cxx000066400000000000000000000070761276303427400172230ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2010, 2013 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4863 $ // // $LastChangedDate: 2013-09-24 09:45:06 -0700 (Tue, 24 Sep 2013) $ // // $LastChangedBy: torstenrohlfing $ // */ /************************************************************************* Copyright (c) 2005-2007, Sergey Bochkanov (ALGLIB project). Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer listed in this license in the documentation and/or other materials provided with the distribution. - Neither the name of the copyright holders nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *************************************************************************/ #include "spddet.h" /************************************************************************* Determinant calculation of the matrix given by the Cholesky decomposition. Input parameters: A - Cholesky decomposition, output of SMatrixCholesky subroutine. N - size of matrix A. As the determinant is equal to the product of squares of diagonal elements, it’s not necessary to specify which triangle - lower or upper - the matrix is stored in. Result: matrix determinant. -- ALGLIB -- Copyright 2005-2008 by Bochkanov Sergey *************************************************************************/ ap::real_value_type spdmatrixcholeskydet(const ap::real_2d_array& a, int n) { ap::real_value_type result; int i; result = 1; for(i = 0; i <= n-1; i++) { result = result*ap::sqr(a(i,i)); } return result; } cmtk-3.3.1/libs/Numerics/spddet.h000066400000000000000000000067241276303427400166470ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2010 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4863 $ // // $LastChangedDate: 2013-09-24 09:45:06 -0700 (Tue, 24 Sep 2013) $ // // $LastChangedBy: torstenrohlfing $ // */ /************************************************************************* Copyright (c) 2005-2007, Sergey Bochkanov (ALGLIB project). Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer listed in this license in the documentation and/or other materials provided with the distribution. - Neither the name of the copyright holders nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *************************************************************************/ #ifndef _spddet_h #define _spddet_h #include "ap.h" #include "cholesky.h" /************************************************************************* Determinant calculation of the matrix given by the Cholesky decomposition. Input parameters: A - Cholesky decomposition, output of SMatrixCholesky subroutine. N - size of matrix A. As the determinant is equal to the product of squares of diagonal elements, it’s not necessary to specify which triangle - lower or upper - the matrix is stored in. Result: matrix determinant. -- ALGLIB -- Copyright 2005-2008 by Bochkanov Sergey *************************************************************************/ ap::real_value_type spdmatrixcholeskydet(const ap::real_2d_array& a, int n); #endif cmtk-3.3.1/libs/Numerics/studenttdistr.cxx000066400000000000000000000136441276303427400206560ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2010, 2013 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4973 $ // // $LastChangedDate: 2013-10-11 13:31:38 -0700 (Fri, 11 Oct 2013) $ // // $LastChangedBy: torstenrohlfing $ // */ /************************************************************************* Cephes Math Library Release 2.8: June, 2000 Copyright 1984, 1987, 1995, 2000 by Stephen L. Moshier Contributors: * Sergey Bochkanov (ALGLIB project). Translation from C to pseudocode. See subroutines comments for additional copyrights. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer listed in this license in the documentation and/or other materials provided with the distribution. - Neither the name of the copyright holders nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *************************************************************************/ #include "studenttdistr.h" namespace alglib { /************************************************************************* Student's t distribution Computes the integral from minus infinity to t of the Student t distribution with integer k > 0 degrees of freedom: t - | | - | 2 -(k+1)/2 | ( (k+1)/2 ) | ( x ) ---------------------- | ( 1 + --- ) dx - | ( k ) sqrt( k pi ) | ( k/2 ) | | | - -inf. Relation to incomplete beta integral: 1 - stdtr(k,t) = 0.5 * incbet( k/2, 1/2, z ) where z = k/(k + t**2). For t < -2, this is the method of computation. For higher t, a direct method is derived from integration by parts. Since the function is symmetric about t=0, the area under the right tail of the density is found by calling the function with -t instead of t. ACCURACY: Tested at random 1 <= k <= 25. The "domain" refers to t. Relative error: arithmetic domain # trials peak rms IEEE -100,-2 50000 5.9e-15 1.4e-15 IEEE -2,100 500000 2.7e-15 4.9e-17 Cephes Math Library Release 2.8: June, 2000 Copyright 1984, 1987, 1995, 2000 by Stephen L. Moshier *************************************************************************/ ap::real_value_type studenttdistribution(int k, ap::real_value_type t) { ap::real_value_type result; ap::real_value_type x; ap::real_value_type rk; ap::real_value_type z; ap::real_value_type f; ap::real_value_type tz; ap::real_value_type p; ap::real_value_type xsqk; int j; #ifndef NO_AP_ASSERT ap::ap_error::make_assertion(k>0, "Domain error in StudentTDistribution"); #endif if( t==0 ) { result = 0.5; return result; } if( t<-2.0 ) { rk = k; z = rk/(rk+t*t); result = 0.5*incompletebeta(0.5*rk, 0.5, z); return result; } if( t<0 ) { x = -t; } else { x = t; } rk = k; z = 1.0+x*x/rk; if( k%2!=0 ) { xsqk = x/sqrt(rk); p = atan(xsqk); if( k>1 ) { f = 1.0; tz = 1.0; j = 3; while(j<=k-2&&tz/f>ap::machineepsilon) { tz = tz*((j-1)/(z*j)); f = f+tz; j = j+2; } p = p+f*xsqk/z; } p = p*2.0/ap::pi(); } else { f = 1.0; tz = 1.0; j = 2; while(j<=k-2&&tz/f>ap::machineepsilon) { tz = tz*((j-1)/(z*j)); f = f+tz; j = j+2; } p = f*x/sqrt(z*rk); } if( t<0 ) { p = -p; } result = 0.5+0.5*p; return result; } } // namespace alglib cmtk-3.3.1/libs/Numerics/studenttdistr.h000066400000000000000000000112151276303427400202730ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2010 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 2372 $ // // $LastChangedDate: 2010-09-29 11:13:08 -0700 (Wed, 29 Sep 2010) $ // // $LastChangedBy: torstenrohlfing $ // */ /************************************************************************* Cephes Math Library Release 2.8: June, 2000 Copyright 1984, 1987, 1995, 2000 by Stephen L. Moshier Contributors: * Sergey Bochkanov (ALGLIB project). Translation from C to pseudocode. See subroutines comments for additional copyrights. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer listed in this license in the documentation and/or other materials provided with the distribution. - Neither the name of the copyright holders nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *************************************************************************/ #ifndef _studenttdistr_h #define _studenttdistr_h #include "ap.h" #include "gammaf.h" #include "normaldistr.h" #include "ibetaf.h" namespace alglib { /************************************************************************* Student's t distribution Computes the integral from minus infinity to t of the Student t distribution with integer k > 0 degrees of freedom: t - | | - | 2 -(k+1)/2 | ( (k+1)/2 ) | ( x ) ---------------------- | ( 1 + --- ) dx - | ( k ) sqrt( k pi ) | ( k/2 ) | | | - -inf. Relation to incomplete beta integral: 1 - stdtr(k,t) = 0.5 * incbet( k/2, 1/2, z ) where z = k/(k + t**2). For t < -2, this is the method of computation. For higher t, a direct method is derived from integration by parts. Since the function is symmetric about t=0, the area under the right tail of the density is found by calling the function with -t instead of t. ACCURACY: Tested at random 1 <= k <= 25. The "domain" refers to t. Relative error: arithmetic domain # trials peak rms IEEE -100,-2 50000 5.9e-15 1.4e-15 IEEE -2,100 500000 2.7e-15 4.9e-17 Cephes Math Library Release 2.8: June, 2000 Copyright 1984, 1987, 1995, 2000 by Stephen L. Moshier *************************************************************************/ ap::real_value_type studenttdistribution(int k, ap::real_value_type t); } // namespace alglib #endif cmtk-3.3.1/libs/Numerics/studentttests.cxx000066400000000000000000000133531276303427400206700ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2009, 2013 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4863 $ // // $LastChangedDate: 2013-09-24 09:45:06 -0700 (Tue, 24 Sep 2013) $ // // $LastChangedBy: torstenrohlfing $ // */ /************************************************************************* Copyright (c) 2007, Sergey Bochkanov (ALGLIB project). Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer listed in this license in the documentation and/or other materials provided with the distribution. - Neither the name of the copyright holders nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *************************************************************************/ #include "studentttests.h" namespace alglib { /************************************************************************* Two-sample pooled test This test checks three hypotheses about the mean of the given samples. The following tests are performed: * two-tailed test (null hypothesis - the means are equal) * left-tailed test (null hypothesis - the mean of the first sample is greater than or equal to the mean of the second sample) * right-tailed test (null hypothesis - the mean of the first sample is less than or equal to the mean of the second sample). Test is based on the following assumptions: * given samples have normal distributions * dispersions are equal * samples are independent. Input parameters: X - sample 1. Array whose index goes from 0 to N-1. N - size of sample. Y - sample 2. Array whose index goes from 0 to M-1. M - size of sample. Output parameters: T - Value of T statistic BothTails - p-value for two-tailed test. If BothTails is less than the given significance level the null hypothesis is rejected. LeftTail - p-value for left-tailed test. If LeftTail is less than the given significance level, the null hypothesis is rejected. RightTail - p-value for right-tailed test. If RightTail is less than the given significance level the null hypothesis is rejected. -- ALGLIB -- Copyright 18.09.2006 by Bochkanov Sergey *************************************************************************/ void studentttest2(const ap::real_1d_array& x, int n, const ap::real_1d_array& y, int m, ap::real_value_type& t, ap::real_value_type& bothtails, ap::real_value_type& lefttail, ap::real_value_type& righttail) { int i; ap::real_value_type xmean; ap::real_value_type ymean; ap::real_value_type s; ap::real_value_type p; if( n<=1||m<=1 ) { bothtails = 1.0; lefttail = 1.0; righttail = 1.0; return; } // // Mean // xmean = 0; for(i = 0; i <= n-1; i++) { xmean = xmean+x(i); } xmean = xmean/n; ymean = 0; for(i = 0; i <= m-1; i++) { ymean = ymean+y(i); } ymean = ymean/m; // // S // s = 0; for(i = 0; i <= n-1; i++) { s = s+ap::sqr(x(i)-xmean); } for(i = 0; i <= m-1; i++) { s = s+ap::sqr(y(i)-ymean); } s = sqrt(s*(ap::real_value_type(1)/ap::real_value_type(n)+ap::real_value_type(1)/ap::real_value_type(m))/(n+m-2)); if( s==0 ) { bothtails = 1.0; lefttail = 1.0; righttail = 1.0; return; } // // Statistic (t) // t = (xmean-ymean)/s; if ( s < 0 ) { // stop here } p = studenttdistribution(n+m-2, t); bothtails = 2*ap::minreal(p, 1-p); lefttail = p; righttail = 1-p; } } // namespace alglib cmtk-3.3.1/libs/Numerics/studentttests.h000066400000000000000000000113511276303427400203110ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2010 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4863 $ // // $LastChangedDate: 2013-09-24 09:45:06 -0700 (Tue, 24 Sep 2013) $ // // $LastChangedBy: torstenrohlfing $ // */ /************************************************************************* Copyright (c) 2007, Sergey Bochkanov (ALGLIB project). Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer listed in this license in the documentation and/or other materials provided with the distribution. - Neither the name of the copyright holders nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *************************************************************************/ #ifndef _studentttests_h #define _studentttests_h #include "ap.h" #include "gammaf.h" #include "normaldistr.h" #include "ibetaf.h" #include "studenttdistr.h" namespace alglib { /************************************************************************* Two-sample pooled test This test checks three hypotheses about the mean of the given samples. The following tests are performed: * two-tailed test (null hypothesis - the means are equal) * left-tailed test (null hypothesis - the mean of the first sample is greater than or equal to the mean of the second sample) * right-tailed test (null hypothesis - the mean of the first sample is less than or equal to the mean of the second sample). Test is based on the following assumptions: * given samples have normal distributions * dispersions are equal * samples are independent. Input parameters: X - sample 1. Array whose index goes from 0 to N-1. N - size of sample. Y - sample 2. Array whose index goes from 0 to M-1. M - size of sample. Output parameters: T - Value of T statistic BothTails - p-value for two-tailed test. If BothTails is less than the given significance level the null hypothesis is rejected. LeftTail - p-value for left-tailed test. If LeftTail is less than the given significance level, the null hypothesis is rejected. RightTail - p-value for right-tailed test. If RightTail is less than the given significance level the null hypothesis is rejected. -- ALGLIB -- Copyright 18.09.2006 by Bochkanov Sergey *************************************************************************/ void studentttest2(const ap::real_1d_array& x, int n, const ap::real_1d_array& y, int m, ap::real_value_type& t, ap::real_value_type& bothtails, ap::real_value_type& lefttail, ap::real_value_type& righttail); } // namespace alglib #endif cmtk-3.3.1/libs/Numerics/svd.cxx000066400000000000000000000320151276303427400165230ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2010, 2013 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4968 $ // // $LastChangedDate: 2013-10-11 10:54:41 -0700 (Fri, 11 Oct 2013) $ // // $LastChangedBy: torstenrohlfing $ // */ /************************************************************************* Copyright (c) 2005-2007, Sergey Bochkanov (ALGLIB project). Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer listed in this license in the documentation and/or other materials provided with the distribution. - Neither the name of the copyright holders nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *************************************************************************/ #include "svd.h" /************************************************************************* Singular value decomposition of a rectangular matrix. The algorithm calculates the singular value decomposition of a matrix of size MxN: A = U * S * V^T The algorithm finds the singular values and, optionally, matrices U and V^T. The algorithm can find both first min(M,N) columns of matrix U and rows of matrix V^T (singular vectors), and matrices U and V^T wholly (of sizes MxM and NxN respectively). Take into account that the subroutine does not return matrix V but V^T. Input parameters: A - matrix to be decomposed. Array whose indexes range within [0..M-1, 0..N-1]. M - number of rows in matrix A. N - number of columns in matrix A. UNeeded - 0, 1 or 2. See the description of the parameter U. VTNeeded - 0, 1 or 2. See the description of the parameter VT. AdditionalMemory - If the parameter: * equals 0, the algorithm doesn’t use additional memory (lower requirements, lower performance). * equals 1, the algorithm uses additional memory of size min(M,N)*min(M,N) of real numbers. It often speeds up the algorithm. * equals 2, the algorithm uses additional memory of size M*min(M,N) of real numbers. It allows to get a maximum performance. The recommended value of the parameter is 2. Output parameters: W - contains singular values in descending order. U - if UNeeded=0, U isn't changed, the left singular vectors are not calculated. if Uneeded=1, U contains left singular vectors (first min(M,N) columns of matrix U). Array whose indexes range within [0..M-1, 0..Min(M,N)-1]. if UNeeded=2, U contains matrix U wholly. Array whose indexes range within [0..M-1, 0..M-1]. VT - if VTNeeded=0, VT isn’t changed, the right singular vectors are not calculated. if VTNeeded=1, VT contains right singular vectors (first min(M,N) rows of matrix V^T). Array whose indexes range within [0..min(M,N)-1, 0..N-1]. if VTNeeded=2, VT contains matrix V^T wholly. Array whose indexes range within [0..N-1, 0..N-1]. -- ALGLIB -- Copyright 2005 by Bochkanov Sergey *************************************************************************/ bool rmatrixsvd(ap::real_2d_array a, int m, int n, int uneeded, int vtneeded, int additionalmemory, ap::real_1d_array& w, ap::real_2d_array& u, ap::real_2d_array& vt) { bool result; ap::real_1d_array tauq; ap::real_1d_array taup; ap::real_1d_array tau; ap::real_1d_array e; ap::real_1d_array work; ap::real_2d_array t2; bool isupper; int minmn; int ncu; int nrvt; int nru; int ncvt; int i; int j; // int im1; // ap::real_value_type sm; result = true; if( m==0||n==0 ) { return result; } #ifndef NO_AP_ASSERT ap::ap_error::make_assertion(uneeded>=0&&uneeded<=2, "SVDDecomposition: wrong parameters!"); ap::ap_error::make_assertion(vtneeded>=0&&vtneeded<=2, "SVDDecomposition: wrong parameters!"); ap::ap_error::make_assertion(additionalmemory>=0&&additionalmemory<=2, "SVDDecomposition: wrong parameters!"); #endif // // initialize // minmn = ap::minint(m, n); w.setbounds(1, minmn); ncu = 0; nru = 0; if( uneeded==1 ) { nru = m; ncu = minmn; u.setbounds(0, nru-1, 0, ncu-1); } if( uneeded==2 ) { nru = m; ncu = m; u.setbounds(0, nru-1, 0, ncu-1); } nrvt = 0; ncvt = 0; if( vtneeded==1 ) { nrvt = minmn; ncvt = n; vt.setbounds(0, nrvt-1, 0, ncvt-1); } if( vtneeded==2 ) { nrvt = n; ncvt = n; vt.setbounds(0, nrvt-1, 0, ncvt-1); } // // M much larger than N // Use bidiagonal reduction with QR-decomposition // if( m>1.6*n ) { if( uneeded==0 ) { // // No left singular vectors to be computed // rmatrixqr(a, m, n, tau); for(i = 0; i <= n-1; i++) { for(j = 0; j <= i-1; j++) { a(i,j) = 0; } } rmatrixbd(a, n, n, tauq, taup); rmatrixbdunpackpt(a, n, n, taup, nrvt, vt); rmatrixbdunpackdiagonals(a, n, n, isupper, w, e); result = rmatrixbdsvd(w, e, n, isupper, false, u, 0, a, 0, vt, ncvt); return result; } else { // // Left singular vectors (may be full matrix U) to be computed // rmatrixqr(a, m, n, tau); rmatrixqrunpackq(a, m, n, tau, ncu, u); for(i = 0; i <= n-1; i++) { for(j = 0; j <= i-1; j++) { a(i,j) = 0; } } rmatrixbd(a, n, n, tauq, taup); rmatrixbdunpackpt(a, n, n, taup, nrvt, vt); rmatrixbdunpackdiagonals(a, n, n, isupper, w, e); if( additionalmemory<1 ) { // // No additional memory can be used // rmatrixbdmultiplybyq(a, n, n, tauq, u, m, n, true, false); result = rmatrixbdsvd(w, e, n, isupper, false, u, m, a, 0, vt, ncvt); } else { // // Large U. Transforming intermediate matrix T2 // work.setbounds(1, ap::maxint(m, n)); rmatrixbdunpackq(a, n, n, tauq, n, t2); copymatrix(u, 0, m-1, 0, n-1, a, 0, m-1, 0, n-1); inplacetranspose(t2, 0, n-1, 0, n-1, work); result = rmatrixbdsvd(w, e, n, isupper, false, u, 0, t2, n, vt, ncvt); matrixmatrixmultiply(a, 0, m-1, 0, n-1, false, t2, 0, n-1, 0, n-1, true, 1.0, u, 0, m-1, 0, n-1, 0.0, work); } return result; } } // // N much larger than M // Use bidiagonal reduction with LQ-decomposition // if( n>1.6*m ) { if( vtneeded==0 ) { // // No right singular vectors to be computed // rmatrixlq(a, m, n, tau); for(i = 0; i <= m-1; i++) { for(j = i+1; j <= m-1; j++) { a(i,j) = 0; } } rmatrixbd(a, m, m, tauq, taup); rmatrixbdunpackq(a, m, m, tauq, ncu, u); rmatrixbdunpackdiagonals(a, m, m, isupper, w, e); work.setbounds(1, m); inplacetranspose(u, 0, nru-1, 0, ncu-1, work); result = rmatrixbdsvd(w, e, m, isupper, false, a, 0, u, nru, vt, 0); inplacetranspose(u, 0, nru-1, 0, ncu-1, work); return result; } else { // // Right singular vectors (may be full matrix VT) to be computed // rmatrixlq(a, m, n, tau); rmatrixlqunpackq(a, m, n, tau, nrvt, vt); for(i = 0; i <= m-1; i++) { for(j = i+1; j <= m-1; j++) { a(i,j) = 0; } } rmatrixbd(a, m, m, tauq, taup); rmatrixbdunpackq(a, m, m, tauq, ncu, u); rmatrixbdunpackdiagonals(a, m, m, isupper, w, e); work.setbounds(1, ap::maxint(m, n)); inplacetranspose(u, 0, nru-1, 0, ncu-1, work); if( additionalmemory<1 ) { // // No additional memory available // rmatrixbdmultiplybyp(a, m, m, taup, vt, m, n, false, true); result = rmatrixbdsvd(w, e, m, isupper, false, a, 0, u, nru, vt, n); } else { // // Large VT. Transforming intermediate matrix T2 // rmatrixbdunpackpt(a, m, m, taup, m, t2); result = rmatrixbdsvd(w, e, m, isupper, false, a, 0, u, nru, t2, m); copymatrix(vt, 0, m-1, 0, n-1, a, 0, m-1, 0, n-1); matrixmatrixmultiply(t2, 0, m-1, 0, m-1, false, a, 0, m-1, 0, n-1, false, 1.0, vt, 0, m-1, 0, n-1, 0.0, work); } inplacetranspose(u, 0, nru-1, 0, ncu-1, work); return result; } } // // M<=N // We can use inplace transposition of U to get rid of columnwise operations // if( m<=n ) { rmatrixbd(a, m, n, tauq, taup); rmatrixbdunpackq(a, m, n, tauq, ncu, u); rmatrixbdunpackpt(a, m, n, taup, nrvt, vt); rmatrixbdunpackdiagonals(a, m, n, isupper, w, e); work.setbounds(1, m); inplacetranspose(u, 0, nru-1, 0, ncu-1, work); result = rmatrixbdsvd(w, e, minmn, isupper, false, a, 0, u, nru, vt, ncvt); inplacetranspose(u, 0, nru-1, 0, ncu-1, work); return result; } // // Simple bidiagonal reduction // rmatrixbd(a, m, n, tauq, taup); rmatrixbdunpackq(a, m, n, tauq, ncu, u); rmatrixbdunpackpt(a, m, n, taup, nrvt, vt); rmatrixbdunpackdiagonals(a, m, n, isupper, w, e); if( additionalmemory<2||uneeded==0 ) { // // We cant use additional memory or there is no need in such operations // result = rmatrixbdsvd(w, e, minmn, isupper, false, u, nru, a, 0, vt, ncvt); } else { // // We can use additional memory // t2.setbounds(0, minmn-1, 0, m-1); copyandtranspose(u, 0, m-1, 0, minmn-1, t2, 0, minmn-1, 0, m-1); result = rmatrixbdsvd(w, e, minmn, isupper, false, u, 0, t2, m, vt, ncvt); copyandtranspose(t2, 0, minmn-1, 0, m-1, u, 0, m-1, 0, minmn-1); } return result; } cmtk-3.3.1/libs/Numerics/svd.h000066400000000000000000000133661276303427400161600ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2010 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 2373 $ // // $LastChangedDate: 2010-09-29 11:17:31 -0700 (Wed, 29 Sep 2010) $ // // $LastChangedBy: torstenrohlfing $ // */ /************************************************************************* Copyright (c) 2005-2007, Sergey Bochkanov (ALGLIB project). Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer listed in this license in the documentation and/or other materials provided with the distribution. - Neither the name of the copyright holders nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *************************************************************************/ #ifndef _svd_h #define _svd_h #include "ap.h" #include "reflections.h" #include "bidiagonal.h" #include "qr.h" #include "lq.h" #include "blas.h" #include "rotations.h" #include "bdsvd.h" /************************************************************************* Singular value decomposition of a rectangular matrix. The algorithm calculates the singular value decomposition of a matrix of size MxN: A = U * S * V^T The algorithm finds the singular values and, optionally, matrices U and V^T. The algorithm can find both first min(M,N) columns of matrix U and rows of matrix V^T (singular vectors), and matrices U and V^T wholly (of sizes MxM and NxN respectively). Take into account that the subroutine does not return matrix V but V^T. Input parameters: A - matrix to be decomposed. Array whose indexes range within [0..M-1, 0..N-1]. M - number of rows in matrix A. N - number of columns in matrix A. UNeeded - 0, 1 or 2. See the description of the parameter U. VTNeeded - 0, 1 or 2. See the description of the parameter VT. AdditionalMemory - If the parameter: * equals 0, the algorithm doesn’t use additional memory (lower requirements, lower performance). * equals 1, the algorithm uses additional memory of size min(M,N)*min(M,N) of real numbers. It often speeds up the algorithm. * equals 2, the algorithm uses additional memory of size M*min(M,N) of real numbers. It allows to get a maximum performance. The recommended value of the parameter is 2. Output parameters: W - contains singular values in descending order. U - if UNeeded=0, U isn't changed, the left singular vectors are not calculated. if Uneeded=1, U contains left singular vectors (first min(M,N) columns of matrix U). Array whose indexes range within [0..M-1, 0..Min(M,N)-1]. if UNeeded=2, U contains matrix U wholly. Array whose indexes range within [0..M-1, 0..M-1]. VT - if VTNeeded=0, VT isn’t changed, the right singular vectors are not calculated. if VTNeeded=1, VT contains right singular vectors (first min(M,N) rows of matrix V^T). Array whose indexes range within [0..min(M,N)-1, 0..N-1]. if VTNeeded=2, VT contains matrix V^T wholly. Array whose indexes range within [0..N-1, 0..N-1]. -- ALGLIB -- Copyright 2005 by Bochkanov Sergey *************************************************************************/ bool rmatrixsvd(ap::real_2d_array a, int m, int n, int uneeded, int vtneeded, int additionalmemory, ap::real_1d_array& w, ap::real_2d_array& u, ap::real_2d_array& vt); #endif cmtk-3.3.1/libs/Numerics/tdevd.cxx000066400000000000000000001033751276303427400170450ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2011, 2013 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4973 $ // // $LastChangedDate: 2013-10-11 13:31:38 -0700 (Fri, 11 Oct 2013) $ // // $LastChangedBy: torstenrohlfing $ // */ /************************************************************************* Copyright (c) 1992-2007 The University of Tennessee. All rights reserved. Contributors: * Sergey Bochkanov (ALGLIB project). Translation from FORTRAN to pseudocode. See subroutines comments for additional copyrights. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer listed in this license in the documentation and/or other materials provided with the distribution. - Neither the name of the copyright holders nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *************************************************************************/ #include "tdevd.h" void tdevde2(const ap::real_value_type& a, const ap::real_value_type& b, const ap::real_value_type& c, ap::real_value_type& rt1, ap::real_value_type& rt2); void tdevdev2(const ap::real_value_type& a, const ap::real_value_type& b, const ap::real_value_type& c, ap::real_value_type& rt1, ap::real_value_type& rt2, ap::real_value_type& cs1, ap::real_value_type& sn1); ap::real_value_type tdevdpythag(ap::real_value_type a, ap::real_value_type b); ap::real_value_type tdevdextsign(ap::real_value_type a, ap::real_value_type b); /************************************************************************* Finding the eigenvalues and eigenvectors of a tridiagonal symmetric matrix The algorithm finds the eigen pairs of a tridiagonal symmetric matrix by using an QL/QR algorithm with implicit shifts. Input parameters: D - the main diagonal of a tridiagonal matrix. Array whose index ranges within [0..N-1]. E - the secondary diagonal of a tridiagonal matrix. Array whose index ranges within [0..N-2]. N - size of matrix A. ZNeeded - flag controlling whether the eigenvectors are needed or not. If ZNeeded is equal to: * 0, the eigenvectors are not needed; * 1, the eigenvectors of a tridiagonal matrix are multiplied by the square matrix Z. It is used if the tridiagonal matrix is obtained by the similarity transformation of a symmetric matrix; * 2, the eigenvectors of a tridiagonal matrix replace the square matrix Z; * 3, matrix Z contains the first row of the eigenvectors matrix. Z - if ZNeeded=1, Z contains the square matrix by which the eigenvectors are multiplied. Array whose indexes range within [0..N-1, 0..N-1]. Output parameters: D - eigenvalues in ascending order. Array whose index ranges within [0..N-1]. Z - if ZNeeded is equal to: * 0, Z hasn’t changed; * 1, Z contains the product of a given matrix (from the left) and the eigenvectors matrix (from the right); * 2, Z contains the eigenvectors. * 3, Z contains the first row of the eigenvectors matrix. If ZNeeded<3, Z is the array whose indexes range within [0..N-1, 0..N-1]. In that case, the eigenvectors are stored in the matrix columns. If ZNeeded=3, Z is the array whose indexes range within [0..0, 0..N-1]. Result: True, if the algorithm has converged. False, if the algorithm hasn't converged. -- LAPACK routine (version 3.0) -- Univ. of Tennessee, Univ. of California Berkeley, NAG Ltd., Courant Institute, Argonne National Lab, and Rice University September 30, 1994 *************************************************************************/ bool smatrixtdevd(ap::real_1d_array& d, ap::real_1d_array e, int n, int zneeded, ap::real_2d_array& z) { bool result; ap::real_1d_array d1; ap::real_1d_array e1; ap::real_2d_array z1; int i; // // Prepare 1-based task // d1.setbounds(1, n); e1.setbounds(1, n); ap::vmove(&d1(1), &d(0), ap::vlen(1,n)); if( n>1 ) { ap::vmove(&e1(1), &e(0), ap::vlen(1,n-1)); } if( zneeded==1 ) { z1.setbounds(1, n, 1, n); for(i = 1; i <= n; i++) { ap::vmove(&z1(i, 1), &z(i-1, 0), ap::vlen(1,n)); } } // // Solve 1-based task // result = tridiagonalevd(d1, e1, n, zneeded, z1); if( !result ) { return result; } // // Convert back to 0-based result // ap::vmove(&d(0), &d1(1), ap::vlen(0,n-1)); if( zneeded!=0 ) { if( zneeded==1 ) { for(i = 1; i <= n; i++) { ap::vmove(&z(i-1, 0), &z1(i, 1), ap::vlen(0,n-1)); } return result; } if( zneeded==2 ) { z.setbounds(0, n-1, 0, n-1); for(i = 1; i <= n; i++) { ap::vmove(&z(i-1, 0), &z1(i, 1), ap::vlen(0,n-1)); } return result; } if( zneeded==3 ) { z.setbounds(0, 0, 0, n-1); ap::vmove(&z(0, 0), &z1(1, 1), ap::vlen(0,n-1)); return result; } #ifndef NO_AP_ASSERT ap::ap_error::make_assertion(false, "SMatrixTDEVD: Incorrect ZNeeded!"); #endif } return result; } /************************************************************************* Obsolete 1-based subroutine. *************************************************************************/ bool tridiagonalevd(ap::real_1d_array& d, ap::real_1d_array e, int n, int zneeded, ap::real_2d_array& z) { bool result; int maxit; int i; // int icompz; int ii; int iscale; int j; int jtot; int k; int t; int l; int l1; int lend; int lendm1; int lendp1; int lendsv; int lm1; int lsv; int m; int mm1; int nm1; int nmaxit; int tmpint; ap::real_value_type anorm; ap::real_value_type b; ap::real_value_type c; ap::real_value_type eps; ap::real_value_type eps2; ap::real_value_type f; ap::real_value_type g; ap::real_value_type p; ap::real_value_type r; ap::real_value_type rt1; ap::real_value_type rt2; ap::real_value_type s; ap::real_value_type safmax; ap::real_value_type safmin; ap::real_value_type ssfmax; ap::real_value_type ssfmin; ap::real_value_type tst; ap::real_value_type tmp; ap::real_1d_array work1; ap::real_1d_array work2; ap::real_1d_array workc; ap::real_1d_array works; ap::real_1d_array wtemp; bool gotoflag; int zrows; bool wastranspose; #ifndef NO_AP_ASSERT ap::ap_error::make_assertion(zneeded>=0&&zneeded<=3, "TridiagonalEVD: Incorrent ZNeeded"); #endif // // Quick return if possible // if( zneeded<0||zneeded>3 ) { result = false; return result; } result = true; if( n==0 ) { return result; } if( n==1 ) { if( zneeded==2||zneeded==3 ) { z.setbounds(1, 1, 1, 1); z(1,1) = 1; } return result; } maxit = 30; // // Initialize arrays // wtemp.setbounds(1, n); work1.setbounds(1, n-1); work2.setbounds(1, n-1); workc.setbounds(1, n); works.setbounds(1, n); // // Determine the unit roundoff and over/underflow thresholds. // eps = ap::machineepsilon; eps2 = ap::sqr(eps); safmin = ap::minrealnumber; safmax = ap::maxrealnumber; ssfmax = sqrt(safmax)/3; ssfmin = sqrt(safmin)/eps2; // // Prepare Z // // Here we are using transposition to get rid of column operations // // wastranspose = false; if( zneeded==0 ) { zrows = 0; } if( zneeded==1 ) { zrows = n; } if( zneeded==2 ) { zrows = n; } if( zneeded==3 ) { zrows = 1; } if( zneeded==1 ) { wastranspose = true; inplacetranspose(z, 1, n, 1, n, wtemp); } if( zneeded==2 ) { wastranspose = true; z.setbounds(1, n, 1, n); for(i = 1; i <= n; i++) { for(j = 1; j <= n; j++) { if( i==j ) { z(i,j) = 1; } else { z(i,j) = 0; } } } } if( zneeded==3 ) { wastranspose = false; z.setbounds(1, 1, 1, n); for(j = 1; j <= n; j++) { if( j==1 ) { z(1,j) = 1; } else { z(1,j) = 0; } } } nmaxit = n*maxit; jtot = 0; // // Determine where the matrix splits and choose QL or QR iteration // for each block, according to whether top or bottom diagonal // element is smaller. // l1 = 1; nm1 = n-1; while(true) { if( l1>n ) { break; } if( l1>1 ) { e(l1-1) = 0; } gotoflag = false; if( l1<=nm1 ) { for(m = l1; m <= nm1; m++) { tst = fabs(e(m)); if( tst==0 ) { gotoflag = true; break; } if( tst<=sqrt(fabs(d(m)))*sqrt(fabs(d(m+1)))*eps ) { e(m) = 0; gotoflag = true; break; } } } if( !gotoflag ) { m = n; } // // label 30: // l = l1; lsv = l; lend = m; lendsv = lend; l1 = m+1; if( lend==l ) { continue; } // // Scale submatrix in rows and columns L to LEND // if( l==lend ) { anorm = fabs(d(l)); } else { anorm = ap::maxreal(fabs(d(l))+fabs(e(l)), fabs(e(lend-1))+fabs(d(lend))); for(i = l+1; i <= lend-1; i++) { anorm = ap::maxreal(anorm, fabs(d(i))+fabs(e(i))+fabs(e(i-1))); } } iscale = 0; if( anorm==0 ) { continue; } if( anorm>ssfmax ) { iscale = 1; tmp = ssfmax/anorm; tmpint = lend-1; ap::vmul(&d(l), ap::vlen(l,lend), tmp); ap::vmul(&e(l), ap::vlen(l,tmpint), tmp); } if( anorml ) { // // QL Iteration // // Look for small subdiagonal element. // while(true) { gotoflag = false; if( l!=lend ) { lendm1 = lend-1; for(m = l; m <= lendm1; m++) { tst = ap::sqr(fabs(e(m))); if( tst<=eps2*fabs(d(m))*fabs(d(m+1))+safmin ) { gotoflag = true; break; } } } if( !gotoflag ) { m = lend; } if( m0 ) { tdevdev2(d(l), e(l), d(l+1), rt1, rt2, c, s); work1(l) = c; work2(l) = s; workc(1) = work1(l); works(1) = work2(l); if( !wastranspose ) { applyrotationsfromtheright(false, 1, zrows, l, l+1, workc, works, z, wtemp); } else { applyrotationsfromtheleft(false, l, l+1, 1, zrows, workc, works, z, wtemp); } } else { tdevde2(d(l), e(l), d(l+1), rt1, rt2); } d(l) = rt1; d(l+1) = rt2; e(l) = 0; l = l+2; if( l<=lend ) { continue; } // // GOTO 140 // break; } if( jtot==nmaxit ) { // // GOTO 140 // break; } jtot = jtot+1; // // Form shift. // g = (d(l+1)-p)/(2*e(l)); r = tdevdpythag(g, ap::real_value_type(1)); g = d(m)-p+e(l)/(g+tdevdextsign(r, g)); s = 1; c = 1; p = 0; // // Inner loop // mm1 = m-1; for(i = mm1; i >= l; i--) { f = s*e(i); b = c*e(i); generaterotation(g, f, c, s, r); if( i!=m-1 ) { e(i+1) = r; } g = d(i+1)-p; r = (d(i)-g)*s+2*c*b; p = s*r; d(i+1) = g+p; g = c*r-b; // // If eigenvectors are desired, then save rotations. // if( zneeded>0 ) { work1(i) = c; work2(i) = -s; } } // // If eigenvectors are desired, then apply saved rotations. // if( zneeded>0 ) { for(i = l; i <= m-1; i++) { workc(i-l+1) = work1(i); works(i-l+1) = work2(i); } if( !wastranspose ) { applyrotationsfromtheright(false, 1, zrows, l, m, workc, works, z, wtemp); } else { applyrotationsfromtheleft(false, l, m, 1, zrows, workc, works, z, wtemp); } } d(l) = d(l)-p; e(l) = g; continue; } // // Eigenvalue found. // d(l) = p; l = l+1; if( l<=lend ) { continue; } break; } } else { // // QR Iteration // // Look for small superdiagonal element. // while(true) { gotoflag = false; if( l!=lend ) { lendp1 = lend+1; for(m = l; m >= lendp1; m--) { tst = ap::sqr(fabs(e(m-1))); if( tst<=eps2*fabs(d(m))*fabs(d(m-1))+safmin ) { gotoflag = true; break; } } } if( !gotoflag ) { m = lend; } if( m>lend ) { e(m-1) = 0; } p = d(l); if( m!=l ) { // // If remaining matrix is 2-by-2, use DLAE2 or SLAEV2 // to compute its eigensystem. // if( m==l-1 ) { if( zneeded>0 ) { tdevdev2(d(l-1), e(l-1), d(l), rt1, rt2, c, s); work1(m) = c; work2(m) = s; workc(1) = c; works(1) = s; if( !wastranspose ) { applyrotationsfromtheright(true, 1, zrows, l-1, l, workc, works, z, wtemp); } else { applyrotationsfromtheleft(true, l-1, l, 1, zrows, workc, works, z, wtemp); } } else { tdevde2(d(l-1), e(l-1), d(l), rt1, rt2); } d(l-1) = rt1; d(l) = rt2; e(l-1) = 0; l = l-2; if( l>=lend ) { continue; } break; } if( jtot==nmaxit ) { break; } jtot = jtot+1; // // Form shift. // g = (d(l-1)-p)/(2*e(l-1)); r = tdevdpythag(g, ap::real_value_type(1)); g = d(m)-p+e(l-1)/(g+tdevdextsign(r, g)); s = 1; c = 1; p = 0; // // Inner loop // lm1 = l-1; for(i = m; i <= lm1; i++) { f = s*e(i); b = c*e(i); generaterotation(g, f, c, s, r); if( i!=m ) { e(i-1) = r; } g = d(i)-p; r = (d(i+1)-g)*s+2*c*b; p = s*r; d(i) = g+p; g = c*r-b; // // If eigenvectors are desired, then save rotations. // if( zneeded>0 ) { work1(i) = c; work2(i) = s; } } // // If eigenvectors are desired, then apply saved rotations. // if( zneeded>0 ) { for(i = m; i <= l-1; i++) { workc(i-m+1) = work1(i); works(i-m+1) = work2(i); } if( !wastranspose ) { applyrotationsfromtheright(true, 1, zrows, m, l, workc, works, z, wtemp); } else { applyrotationsfromtheleft(true, m, l, 1, zrows, workc, works, z, wtemp); } } d(l) = d(l)-p; e(lm1) = g; continue; } // // Eigenvalue found. // d(l) = p; l = l-1; if( l>=lend ) { continue; } break; } } // // Undo scaling if necessary // if( iscale==1 ) { tmp = anorm/ssfmax; tmpint = lendsv-1; ap::vmul(&d(lsv), ap::vlen(lsv,lendsv), tmp); ap::vmul(&e(lsv), ap::vlen(lsv,tmpint), tmp); } if( iscale==2 ) { tmp = anorm/ssfmin; tmpint = lendsv-1; ap::vmul(&d(lsv), ap::vlen(lsv,lendsv), tmp); ap::vmul(&e(lsv), ap::vlen(lsv,tmpint), tmp); } // // Check for no convergence to an eigenvalue after a total // of N*MAXIT iterations. // if( jtot>=nmaxit ) { result = false; if( wastranspose ) { inplacetranspose(z, 1, n, 1, n, wtemp); } return result; } } // // Order eigenvalues and eigenvectors. // if( zneeded==0 ) { // // Sort // if( n==1 ) { return result; } if( n==2 ) { if( d(1)>d(2) ) { tmp = d(1); d(1) = d(2); d(2) = tmp; } return result; } i = 2; do { t = i; while(t!=1) { k = t/2; if( d(k)>=d(t) ) { t = 1; } else { tmp = d(k); d(k) = d(t); d(t) = tmp; t = k; } } i = i+1; } while(i<=n); i = n-1; do { tmp = d(i+1); d(i+1) = d(1); d(+1) = tmp; t = 1; while(t!=0) { k = 2*t; if( k>i ) { t = 0; } else { if( kd(k) ) { k = k+1; } } if( d(t)>=d(k) ) { t = 0; } else { tmp = d(k); d(k) = d(t); d(t) = tmp; t = k; } } } i = i-1; } while(i>=1); } else { // // Use Selection Sort to minimize swaps of eigenvectors // for(ii = 2; ii <= n; ii++) { i = ii-1; k = i; p = d(i); for(j = ii; j <= n; j++) { if( d(j)

      fabs(c) ) { acmx = a; acmn = c; } else { acmx = c; acmn = a; } if( adf>ab ) { rt = adf*sqrt(1+ap::sqr(ab/adf)); } else { if( adf0 ) { rt1 = 0.5*(sm+rt); // // Order of execution important. // To get fully accurate smaller eigenvalue, // next line needs to be executed in higher precision. // rt2 = acmx/rt1*acmn-b/rt1*b; } else { // // Includes case RT1 = RT2 = 0 // rt1 = 0.5*rt; rt2 = -0.5*rt; } } } /************************************************************************* DLAEV2 computes the eigendecomposition of a 2-by-2 symmetric matrix [ A B ] [ B C ]. On return, RT1 is the eigenvalue of larger absolute value, RT2 is the eigenvalue of smaller absolute value, and (CS1,SN1) is the unit right eigenvector for RT1, giving the decomposition [ CS1 SN1 ] [ A B ] [ CS1 -SN1 ] = [ RT1 0 ] [-SN1 CS1 ] [ B C ] [ SN1 CS1 ] [ 0 RT2 ]. -- LAPACK auxiliary routine (version 3.0) -- Univ. of Tennessee, Univ. of California Berkeley, NAG Ltd., Courant Institute, Argonne National Lab, and Rice University October 31, 1992 *************************************************************************/ void tdevdev2(const ap::real_value_type& a, const ap::real_value_type& b, const ap::real_value_type& c, ap::real_value_type& rt1, ap::real_value_type& rt2, ap::real_value_type& cs1, ap::real_value_type& sn1) { int sgn1; int sgn2; ap::real_value_type ab; ap::real_value_type acmn; ap::real_value_type acmx; ap::real_value_type acs; ap::real_value_type adf; ap::real_value_type cs; ap::real_value_type ct; ap::real_value_type df; ap::real_value_type rt; ap::real_value_type sm; ap::real_value_type tb; ap::real_value_type tn; // // Compute the eigenvalues // sm = a+c; df = a-c; adf = fabs(df); tb = b+b; ab = fabs(tb); if( fabs(a)>fabs(c) ) { acmx = a; acmn = c; } else { acmx = c; acmn = a; } if( adf>ab ) { rt = adf*sqrt(1+ap::sqr(ab/adf)); } else { if( adf0 ) { rt1 = 0.5*(sm+rt); sgn1 = 1; // // Order of execution important. // To get fully accurate smaller eigenvalue, // next line needs to be executed in higher precision. // rt2 = acmx/rt1*acmn-b/rt1*b; } else { // // Includes case RT1 = RT2 = 0 // rt1 = 0.5*rt; rt2 = -0.5*rt; sgn1 = 1; } } // // Compute the eigenvector // if( df>=0 ) { cs = df+rt; sgn2 = 1; } else { cs = df-rt; sgn2 = -1; } acs = fabs(cs); if( acs>ab ) { ct = -tb/cs; sn1 = 1/sqrt(1+ct*ct); cs1 = ct*sn1; } else { if( ab==0 ) { cs1 = 1; sn1 = 0; } else { tn = -cs/tb; cs1 = 1/sqrt(1+tn*tn); sn1 = tn*cs1; } } if( sgn1==sgn2 ) { tn = cs1; cs1 = -sn1; sn1 = tn; } } /************************************************************************* Internal routine *************************************************************************/ ap::real_value_type tdevdpythag(ap::real_value_type a, ap::real_value_type b) { ap::real_value_type result; if( fabs(a)=0 ) { result = fabs(a); } else { result = -fabs(a); } return result; } cmtk-3.3.1/libs/Numerics/tdevd.h000066400000000000000000000135041276303427400164640ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2010 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 2377 $ // // $LastChangedDate: 2010-09-29 11:38:36 -0700 (Wed, 29 Sep 2010) $ // // $LastChangedBy: torstenrohlfing $ // */ /************************************************************************* Copyright (c) 1992-2007 The University of Tennessee. All rights reserved. Contributors: * Sergey Bochkanov (ALGLIB project). Translation from FORTRAN to pseudocode. See subroutines comments for additional copyrights. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer listed in this license in the documentation and/or other materials provided with the distribution. - Neither the name of the copyright holders nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *************************************************************************/ #ifndef _tdevd_h #define _tdevd_h #include "ap.h" #include "blas.h" #include "rotations.h" /************************************************************************* Finding the eigenvalues and eigenvectors of a tridiagonal symmetric matrix The algorithm finds the eigen pairs of a tridiagonal symmetric matrix by using an QL/QR algorithm with implicit shifts. Input parameters: D - the main diagonal of a tridiagonal matrix. Array whose index ranges within [0..N-1]. E - the secondary diagonal of a tridiagonal matrix. Array whose index ranges within [0..N-2]. N - size of matrix A. ZNeeded - flag controlling whether the eigenvectors are needed or not. If ZNeeded is equal to: * 0, the eigenvectors are not needed; * 1, the eigenvectors of a tridiagonal matrix are multiplied by the square matrix Z. It is used if the tridiagonal matrix is obtained by the similarity transformation of a symmetric matrix; * 2, the eigenvectors of a tridiagonal matrix replace the square matrix Z; * 3, matrix Z contains the first row of the eigenvectors matrix. Z - if ZNeeded=1, Z contains the square matrix by which the eigenvectors are multiplied. Array whose indexes range within [0..N-1, 0..N-1]. Output parameters: D - eigenvalues in ascending order. Array whose index ranges within [0..N-1]. Z - if ZNeeded is equal to: * 0, Z hasn’t changed; * 1, Z contains the product of a given matrix (from the left) and the eigenvectors matrix (from the right); * 2, Z contains the eigenvectors. * 3, Z contains the first row of the eigenvectors matrix. If ZNeeded<3, Z is the array whose indexes range within [0..N-1, 0..N-1]. In that case, the eigenvectors are stored in the matrix columns. If ZNeeded=3, Z is the array whose indexes range within [0..0, 0..N-1]. Result: True, if the algorithm has converged. False, if the algorithm hasn't converged. -- LAPACK routine (version 3.0) -- Univ. of Tennessee, Univ. of California Berkeley, NAG Ltd., Courant Institute, Argonne National Lab, and Rice University September 30, 1994 *************************************************************************/ bool smatrixtdevd(ap::real_1d_array& d, ap::real_1d_array e, int n, int zneeded, ap::real_2d_array& z); /************************************************************************* Obsolete 1-based subroutine. *************************************************************************/ bool tridiagonalevd(ap::real_1d_array& d, ap::real_1d_array e, int n, int zneeded, ap::real_2d_array& z); #endif cmtk-3.3.1/libs/Numerics/tridiagonal.cxx000066400000000000000000000271341276303427400202320ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2010 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 2375 $ // // $LastChangedDate: 2010-09-29 11:23:38 -0700 (Wed, 29 Sep 2010) $ // // $LastChangedBy: torstenrohlfing $ // */ /************************************************************************* Copyright (c) 1992-2007 The University of Tennessee. All rights reserved. Contributors: * Sergey Bochkanov (ALGLIB project). Translation from FORTRAN to pseudocode. See subroutines comments for additional copyrights. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer listed in this license in the documentation and/or other materials provided with the distribution. - Neither the name of the copyright holders nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *************************************************************************/ #include "tridiagonal.h" /************************************************************************* Reduction of a symmetric matrix which is given by its higher or lower triangular part to a tridiagonal matrix using orthogonal similarity transformation: Q'*A*Q=T. Input parameters: A - matrix to be transformed array with elements [0..N-1, 0..N-1]. N - size of matrix A. IsUpper - storage format. If IsUpper = True, then matrix A is given by its upper triangle, and the lower triangle is not used and not modified by the algorithm, and vice versa if IsUpper = False. Output parameters: A - matrices T and Q in compact form (see lower) Tau - array of factors which are forming matrices H(i) array with elements [0..N-2]. D - main diagonal of symmetric matrix T. array with elements [0..N-1]. E - secondary diagonal of symmetric matrix T. array with elements [0..N-2]. If IsUpper=True, the matrix Q is represented as a product of elementary reflectors Q = H(n-2) . . . H(2) H(0). Each H(i) has the form H(i) = I - tau * v * v' where tau is a real scalar, and v is a real vector with v(i+1:n-1) = 0, v(i) = 1, v(0:i-1) is stored on exit in A(0:i-1,i+1), and tau in TAU(i). If IsUpper=False, the matrix Q is represented as a product of elementary reflectors Q = H(0) H(2) . . . H(n-2). Each H(i) has the form H(i) = I - tau * v * v' where tau is a real scalar, and v is a real vector with v(0:i) = 0, v(i+1) = 1, v(i+2:n-1) is stored on exit in A(i+2:n-1,i), and tau in TAU(i). The contents of A on exit are illustrated by the following examples with n = 5: if UPLO = 'U': if UPLO = 'L': ( d e v1 v2 v3 ) ( d ) ( d e v2 v3 ) ( e d ) ( d e v3 ) ( v0 e d ) ( d e ) ( v0 v1 e d ) ( d ) ( v0 v1 v2 e d ) where d and e denote diagonal and off-diagonal elements of T, and vi denotes an element of the vector defining H(i). -- LAPACK routine (version 3.0) -- Univ. of Tennessee, Univ. of California Berkeley, NAG Ltd., Courant Institute, Argonne National Lab, and Rice University October 31, 1992 *************************************************************************/ void smatrixtd(ap::real_2d_array& a, int n, bool isupper, ap::real_1d_array& tau, ap::real_1d_array& d, ap::real_1d_array& e) { int i; ap::real_value_type alpha; ap::real_value_type taui; ap::real_value_type v; ap::real_1d_array t; ap::real_1d_array t2; ap::real_1d_array t3; if( n<=0 ) { return; } t.setbounds(1, n); t2.setbounds(1, n); t3.setbounds(1, n); if( n>1 ) { tau.setbounds(0, n-2); } d.setbounds(0, n-1); if( n>1 ) { e.setbounds(0, n-2); } if( isupper ) { // // Reduce the upper triangle of A // for(i = n-2; i >= 0; i--) { // // Generate elementary reflector H() = E - tau * v * v' // if( i>=1 ) { ap::vmove(t.getvector(2, i+1), a.getcolumn(i+1, 0, i-1)); } t(1) = a(i,i+1); generatereflection(t, i+1, taui); if( i>=1 ) { ap::vmove(a.getcolumn(i+1, 0, i-1), t.getvector(2, i+1)); } a(i,i+1) = t(1); e(i) = a(i,i+1); if( taui!=0 ) { // // Apply H from both sides to A // a(i,i+1) = 1; // // Compute x := tau * A * v storing x in TAU // ap::vmove(t.getvector(1, i+1), a.getcolumn(i+1, 0, i)); symmetricmatrixvectormultiply(a, isupper, 0, i, t, taui, t3); ap::vmove(&tau(0), &t3(1), ap::vlen(0,i)); // // Compute w := x - 1/2 * tau * (x'*v) * v // v = ap::vdotproduct(tau.getvector(0, i), a.getcolumn(i+1, 0, i)); alpha = -0.5*taui*v; ap::vadd(tau.getvector(0, i), a.getcolumn(i+1, 0, i), alpha); // // Apply the transformation as a rank-2 update: // A := A - v * w' - w * v' // ap::vmove(t.getvector(1, i+1), a.getcolumn(i+1, 0, i)); ap::vmove(&t3(1), &tau(0), ap::vlen(1,i+1)); symmetricrank2update(a, isupper, 0, i, t, t3, t2, ap::real_value_type(-1)); a(i,i+1) = e(i); } d(i+1) = a(i+1,i+1); tau(i) = taui; } d(0) = a(0,0); } else { // // Reduce the lower triangle of A // for(i = 0; i <= n-2; i++) { // // Generate elementary reflector H = E - tau * v * v' // ap::vmove(t.getvector(1, n-i-1), a.getcolumn(i, i+1, n-1)); generatereflection(t, n-i-1, taui); ap::vmove(a.getcolumn(i, i+1, n-1), t.getvector(1, n-i-1)); e(i) = a(i+1,i); if( taui!=0 ) { // // Apply H from both sides to A // a(i+1,i) = 1; // // Compute x := tau * A * v storing y in TAU // ap::vmove(t.getvector(1, n-i-1), a.getcolumn(i, i+1, n-1)); symmetricmatrixvectormultiply(a, isupper, i+1, n-1, t, taui, t2); ap::vmove(&tau(i), &t2(1), ap::vlen(i,n-2)); // // Compute w := x - 1/2 * tau * (x'*v) * v // v = ap::vdotproduct(tau.getvector(i, n-2), a.getcolumn(i, i+1, n-1)); alpha = -0.5*taui*v; ap::vadd(tau.getvector(i, n-2), a.getcolumn(i, i+1, n-1), alpha); // // Apply the transformation as a rank-2 update: // A := A - v * w' - w * v' // // ap::vmove(t.getvector(1, n-i-1), a.getcolumn(i, i+1, n-1)); ap::vmove(&t2(1), &tau(i), ap::vlen(1,n-i-1)); symmetricrank2update(a, isupper, i+1, n-1, t, t2, t3, ap::real_value_type(-1)); a(i+1,i) = e(i); } d(i) = a(i,i); tau(i) = taui; } d(n-1) = a(n-1,n-1); } } /************************************************************************* Unpacking matrix Q which reduces symmetric matrix to a tridiagonal form. Input parameters: A - the result of a SMatrixTD subroutine N - size of matrix A. IsUpper - storage format (a parameter of SMatrixTD subroutine) Tau - the result of a SMatrixTD subroutine Output parameters: Q - transformation matrix. array with elements [0..N-1, 0..N-1]. -- ALGLIB -- Copyright 2005-2008 by Bochkanov Sergey *************************************************************************/ void smatrixtdunpackq(const ap::real_2d_array& a, const int& n, const bool& isupper, const ap::real_1d_array& tau, ap::real_2d_array& q) { int i; int j; ap::real_1d_array v; ap::real_1d_array work; if( n==0 ) { return; } // // init // q.setbounds(0, n-1, 0, n-1); v.setbounds(1, n); work.setbounds(0, n-1); for(i = 0; i <= n-1; i++) { for(j = 0; j <= n-1; j++) { if( i==j ) { q(i,j) = 1; } else { q(i,j) = 0; } } } // // unpack Q // if( isupper ) { for(i = 0; i <= n-2; i++) { // // Apply H(i) // ap::vmove(v.getvector(1, i+1), a.getcolumn(i+1, 0, i)); v(i+1) = 1; applyreflectionfromtheleft(q, tau(i), v, 0, i, 0, n-1, work); } } else { for(i = n-2; i >= 0; i--) { // // Apply H(i) // ap::vmove(v.getvector(1, n-i-1), a.getcolumn(i, i+1, n-1)); v(1) = 1; applyreflectionfromtheleft(q, tau(i), v, i+1, n-1, 0, n-1, work); } } } cmtk-3.3.1/libs/Numerics/tridiagonal.h000066400000000000000000000146021276303427400176530ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2010 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 2375 $ // // $LastChangedDate: 2010-09-29 11:23:38 -0700 (Wed, 29 Sep 2010) $ // // $LastChangedBy: torstenrohlfing $ // */ /************************************************************************* Copyright (c) 1992-2007 The University of Tennessee. All rights reserved. Contributors: * Sergey Bochkanov (ALGLIB project). Translation from FORTRAN to pseudocode. See subroutines comments for additional copyrights. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer listed in this license in the documentation and/or other materials provided with the distribution. - Neither the name of the copyright holders nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *************************************************************************/ #ifndef _tridiagonal_h #define _tridiagonal_h #include "ap.h" #include "sblas.h" #include "reflections.h" /************************************************************************* Reduction of a symmetric matrix which is given by its higher or lower triangular part to a tridiagonal matrix using orthogonal similarity transformation: Q'*A*Q=T. Input parameters: A - matrix to be transformed array with elements [0..N-1, 0..N-1]. N - size of matrix A. IsUpper - storage format. If IsUpper = True, then matrix A is given by its upper triangle, and the lower triangle is not used and not modified by the algorithm, and vice versa if IsUpper = False. Output parameters: A - matrices T and Q in compact form (see lower) Tau - array of factors which are forming matrices H(i) array with elements [0..N-2]. D - main diagonal of symmetric matrix T. array with elements [0..N-1]. E - secondary diagonal of symmetric matrix T. array with elements [0..N-2]. If IsUpper=True, the matrix Q is represented as a product of elementary reflectors Q = H(n-2) . . . H(2) H(0). Each H(i) has the form H(i) = I - tau * v * v' where tau is a real scalar, and v is a real vector with v(i+1:n-1) = 0, v(i) = 1, v(0:i-1) is stored on exit in A(0:i-1,i+1), and tau in TAU(i). If IsUpper=False, the matrix Q is represented as a product of elementary reflectors Q = H(0) H(2) . . . H(n-2). Each H(i) has the form H(i) = I - tau * v * v' where tau is a real scalar, and v is a real vector with v(0:i) = 0, v(i+1) = 1, v(i+2:n-1) is stored on exit in A(i+2:n-1,i), and tau in TAU(i). The contents of A on exit are illustrated by the following examples with n = 5: if UPLO = 'U': if UPLO = 'L': ( d e v1 v2 v3 ) ( d ) ( d e v2 v3 ) ( e d ) ( d e v3 ) ( v0 e d ) ( d e ) ( v0 v1 e d ) ( d ) ( v0 v1 v2 e d ) where d and e denote diagonal and off-diagonal elements of T, and vi denotes an element of the vector defining H(i). -- LAPACK routine (version 3.0) -- Univ. of Tennessee, Univ. of California Berkeley, NAG Ltd., Courant Institute, Argonne National Lab, and Rice University October 31, 1992 *************************************************************************/ void smatrixtd(ap::real_2d_array& a, int n, bool isupper, ap::real_1d_array& tau, ap::real_1d_array& d, ap::real_1d_array& e); /************************************************************************* Unpacking matrix Q which reduces symmetric matrix to a tridiagonal form. Input parameters: A - the result of a SMatrixTD subroutine N - size of matrix A. IsUpper - storage format (a parameter of SMatrixTD subroutine) Tau - the result of a SMatrixTD subroutine Output parameters: Q - transformation matrix. array with elements [0..N-1, 0..N-1]. -- ALGLIB -- Copyright 2005-2008 by Bochkanov Sergey *************************************************************************/ void smatrixtdunpackq(const ap::real_2d_array& a, const int& n, const bool& isupper, const ap::real_1d_array& tau, ap::real_2d_array& q); #endif cmtk-3.3.1/libs/Pipeline/000077500000000000000000000000001276303427400151625ustar00rootroot00000000000000cmtk-3.3.1/libs/Pipeline/CMakeLists.txt000066400000000000000000000037101276303427400177230ustar00rootroot00000000000000## ## Copyright 1997-2009 Torsten Rohlfing ## ## Copyright 2004-2012 SRI International ## ## This file is part of the Computational Morphometry Toolkit. ## ## http://www.nitrc.org/projects/cmtk/ ## ## The Computational Morphometry Toolkit is free software: you can ## redistribute it and/or modify it under the terms of the GNU General Public ## License as published by the Free Software Foundation, either version 3 of ## the License, or (at your option) any later version. ## ## The Computational Morphometry Toolkit is distributed in the hope that it ## will be useful, but WITHOUT ANY WARRANTY; without even the implied ## warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License along ## with the Computational Morphometry Toolkit. If not, see ## . ## ## $Revision: 4418 $ ## ## $LastChangedDate: 2012-06-04 15:55:54 -0700 (Mon, 04 Jun 2012) $ ## ## $LastChangedBy: torstenrohlfing $ ## # Sources of non-templated classes. SET(cmtkPipeline_SRCS cmtkColormap.cxx cmtkImage.cxx cmtkImageRGB.cxx cmtkImageToImageRGB.cxx cmtkObject.cxx cmtkPipelineObject.cxx cmtkPlane.cxx cmtkRenderer.cxx ) ADD_LIBRARY(cmtkPipeline ${cmtkPipeline_SRCS}) TARGET_LINK_LIBRARIES(cmtkPipeline cmtkBase cmtkSystem cmtkNumerics) IF(CMTK_LIBRARY_PROPERTIES) SET_TARGET_PROPERTIES(cmtkPipeline PROPERTIES ${CMTK_LIBRARY_PROPERTIES}) ENDIF(CMTK_LIBRARY_PROPERTIES) INSTALL(TARGETS cmtkPipeline RUNTIME DESTINATION ${CMTK_INSTALL_BIN_DIR} COMPONENT runtime LIBRARY DESTINATION ${CMTK_INSTALL_LIB_DIR} COMPONENT runtime ARCHIVE DESTINATION ${CMTK_INSTALL_LIB_DIR} COMPONENT libraries) FILE(GLOB files_h "${CMAKE_CURRENT_SOURCE_DIR}/*.h") FILE(GLOB files_txx "${CMAKE_CURRENT_SOURCE_DIR}/*.txx") INSTALL(FILES ${files_h} ${files_txx} DESTINATION ${CMTK_INSTALL_INCLUDE_DIR}/Pipeline COMPONENT headers) cmtk-3.3.1/libs/Pipeline/cmtkColormap.cxx000066400000000000000000000301641276303427400203450ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2013 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4882 $ // // $LastChangedDate: 2013-09-27 15:16:36 -0700 (Fri, 27 Sep 2013) $ // // $LastChangedBy: torstenrohlfing $ // */ #include "cmtkColormap.h" #include #ifdef HAVE_IEEEFP_H # include #endif #include #include #include namespace cmtk { /** \addtogroup Pipeline */ //@{ const char *Colormap::StandardColormaps[] = { "Gray", "Red", "Green", "Blue", "Rainbow", "Labels", NULL }; Colormap::Colormap() { TableEntries = 256; Gamma = 0; DataRange[0] = 0; DataRange[1] = 4095; HueRange[0] = 0; HueRange[1] = 4095; ValueRange[0] = 0; ValueRange[1] = 4095; SaturationRange[0] = 0; SaturationRange[1] = 4095; Reverse = false; this->InvDataRangeWidth = 4095; CreateSystemLabelColorMap( this->LabelColorMap ); this->SetStandardColormap( PALETTE_GRAY ); } void Colormap::SetFromStudy( const Study* study ) { if ( ! study ) return; // if there is a user-defined map, use that. if ( study->GetHaveUserColorMap() ) { LabelColorMap = study->GetUserLabelMap(); } // copy all other settings anyway, just in case. this->SetStandardColormap( study->GetStandardColormap() ); this->SetReverse( study->GetReverseColormap() ); this->SetDataRange( study->GetBlack(), study->GetWhite() ); this->SetGamma( study->GetGamma() ); } void Colormap::SetStandardColormap( const int index ) { HaveUserMap = false; SetGamma( 0 ); switch ( index ) { case PALETTE_GRAY : SetHueRange( 0, 0 ); SetSaturationRange( 0, 0 ); SetValueRange( 0, 1 ); break; case PALETTE_RED : SetHueRange( 0, 0 ); SetSaturationRange( 1, 1 ); SetValueRange( 0, 1 ); break; case PALETTE_GREEN : SetHueRange( 0.33, 0.33 ); SetSaturationRange( 1, 1 ); SetValueRange( 0, 1 ); break; case PALETTE_BLUE : SetHueRange( 0.66, 0.66 ); SetSaturationRange( 1, 1 ); SetValueRange( 0, 1 ); break; case PALETTE_RAINBOW : SetHueRange( 0.66, 0 ); SetSaturationRange( 1, 1 ); SetValueRange( 1, 1 ); break; case PALETTE_LABELS: default: HaveUserMap = true; // nothing to do for user map; hopefully, there is one ;) break; } } void Colormap::Apply( void *const outPtr, const TypedArray* inPtr, const bool generateAlpha ) { if ( ( outPtr == NULL ) || (inPtr == NULL) ) return; if ( (LookupTable.empty()) || (!TableEntries) ) { memset( outPtr, 0, 3 * inPtr->GetDataSize() ); return; } int size = inPtr->GetDataSize(); if ( generateAlpha ) { switch ( inPtr->GetType() ) { case TYPE_BYTE: ApplyPrimitive( (RGBA*) outPtr, (unsigned char*)inPtr->GetDataPtr(), size, inPtr->GetPaddingFlag(), *((unsigned char*)inPtr->GetPaddingPtr()) ); break; case TYPE_CHAR: ApplyPrimitive( (RGBA*) outPtr, (char*)inPtr->GetDataPtr(), size, inPtr->GetPaddingFlag(), *((char*)inPtr->GetPaddingPtr()) ); break; case TYPE_SHORT: ApplyPrimitive( (RGBA*) outPtr, (short*)inPtr->GetDataPtr(), size, inPtr->GetPaddingFlag(), *((short*)inPtr->GetPaddingPtr()) ); break; case TYPE_USHORT: ApplyPrimitive( (RGBA*) outPtr, (unsigned short*)inPtr->GetDataPtr(), size, inPtr->GetPaddingFlag(), *((unsigned short*)inPtr->GetPaddingPtr()) ); break; case TYPE_INT: ApplyPrimitive( (RGBA*) outPtr, (int*)inPtr->GetDataPtr(), size, inPtr->GetPaddingFlag(), *((int*)inPtr->GetPaddingPtr()) ); break; case TYPE_UINT: ApplyPrimitive( (RGBA*) outPtr, (unsigned int*)inPtr->GetDataPtr(), size, inPtr->GetPaddingFlag(), *((unsigned int*)inPtr->GetPaddingPtr()) ); break; case TYPE_FLOAT: ApplyPrimitive( (RGBA*) outPtr, (float*)inPtr->GetDataPtr(), size, inPtr->GetPaddingFlag(), *((float*)inPtr->GetPaddingPtr()) ); break; case TYPE_DOUBLE: ApplyPrimitive( (RGBA*) outPtr, (double*)inPtr->GetDataPtr(), size, inPtr->GetPaddingFlag(), *((double*)inPtr->GetPaddingPtr()) ); break; case TYPE_NONE: break; } } else { switch ( inPtr->GetType() ) { case TYPE_BYTE: ApplyPrimitive( (RGB*) outPtr, (unsigned char*)inPtr->GetDataPtr(), size, inPtr->GetPaddingFlag(), *((unsigned char*)inPtr->GetPaddingPtr()) ); break; case TYPE_CHAR: ApplyPrimitive( (RGB*) outPtr, (char*)inPtr->GetDataPtr(), size, inPtr->GetPaddingFlag(), *((char*)inPtr->GetPaddingPtr()) ); break; case TYPE_SHORT: ApplyPrimitive( (RGB*) outPtr, (short*)inPtr->GetDataPtr(), size, inPtr->GetPaddingFlag(), *((short*)inPtr->GetPaddingPtr()) ); break; case TYPE_USHORT: ApplyPrimitive( (RGB*) outPtr, (unsigned short*)inPtr->GetDataPtr(), size, inPtr->GetPaddingFlag(), *((unsigned short*)inPtr->GetPaddingPtr()) ); break; case TYPE_INT: ApplyPrimitive( (RGB*) outPtr, (int*)inPtr->GetDataPtr(), size, inPtr->GetPaddingFlag(), *((int*)inPtr->GetPaddingPtr()) ); break; case TYPE_UINT: ApplyPrimitive( (RGB*) outPtr, (unsigned int*)inPtr->GetDataPtr(), size, inPtr->GetPaddingFlag(), *((unsigned int*)inPtr->GetPaddingPtr()) ); break; case TYPE_FLOAT: ApplyPrimitive( (RGB*) outPtr, (float*)inPtr->GetDataPtr(), size, inPtr->GetPaddingFlag(), *((float*)inPtr->GetPaddingPtr()) ); break; case TYPE_DOUBLE: ApplyPrimitive( (RGB*) outPtr, (double*)inPtr->GetDataPtr(), size, inPtr->GetPaddingFlag(), *((double*)inPtr->GetPaddingPtr()) ); break; case TYPE_NONE: break; } } } template void Colormap::ApplyPrimitive ( RGB *const outPtr, const T* inPtr, const unsigned int count, const bool paddingFlag, const T paddingData ) const { if ( Reverse ) { for ( unsigned int index = 0; index < count; ++index ) { T data = inPtr[index]; if ( (paddingFlag && (data == paddingData)) || !finite( data ) ) data = 0; if ( data <= DataRange[0] ) outPtr[index] = LookupTable[LookupTable.size()-1]; else if ( data >= DataRange[1] ) outPtr[index] = LookupTable[0]; else outPtr[index] = LookupTable[ LookupTable.size() - 1 - (int)( (data - DataRange[0]) * InvDataRangeWidth ) ]; } } else { for ( unsigned int index = 0; index < count; ++index ) { T data = inPtr[index]; if ( (paddingFlag && (data == paddingData)) || !finite( data ) ) data = 0; if ( data <= DataRange[0] ) outPtr[index] = LookupTable[0]; else if ( data >= DataRange[1] ) outPtr[index] = LookupTable[LookupTable.size()-1]; else outPtr[index] = LookupTable[ (int)( (data - DataRange[0]) * InvDataRangeWidth ) ]; } } } template void Colormap::ApplyPrimitive ( RGBA *const outPtr, const T* inPtr, const unsigned int count, const bool paddingFlag, const T paddingData ) const { if ( Reverse ) { for ( unsigned int index = 0; index < count; ++index ) { T data = inPtr[index]; if ( (paddingFlag && (data == paddingData)) || !finite( data ) ) data = 0; if ( data <= DataRange[0] ) outPtr[index] = LookupTable[LookupTable.size()-1]; else if ( inPtr[index] >= DataRange[1] ) outPtr[index] = LookupTable[0]; else outPtr[index] = LookupTable[ LookupTable.size() - 1 - (int)( (data - DataRange[0]) * InvDataRangeWidth ) ]; outPtr[index].Alpha = 255; } } else { for ( unsigned int index = 0; index < count; ++index ) { T data = inPtr[index]; if ( (paddingFlag && (data == paddingData)) || !finite( data ) ) data = 0; if ( data <= DataRange[0] ) outPtr[index] = LookupTable[0]; else if ( data >= DataRange[1] ) outPtr[index] = LookupTable[LookupTable.size()-1]; else outPtr[index] = LookupTable[ (int)( (data - DataRange[0]) * InvDataRangeWidth ) ]; outPtr[index].Alpha = 255; } } } void Colormap::Execute () { if ( HaveUserMap ) { // if user map exists, set table entry count to number of table entries. SegmentationLabelMap::const_iterator it = LabelColorMap.begin(); int rangeFrom = it->first, rangeTo = it->first; while ( it != LabelColorMap.end() ) { rangeFrom = std::min( rangeFrom, it->first ); rangeTo = std::max( rangeTo, it->first ); ++it; } TableEntries = (rangeTo - rangeFrom + 1); DataRange[0] = rangeFrom; DataRange[1] = rangeTo; } else { TableEntries = 256; } LookupTable.resize( TableEntries ); if ( DataRange[0] != DataRange[1] ) InvDataRangeWidth = (1.0 * (TableEntries - 1)) / (DataRange[1] - DataRange[0]); else InvDataRangeWidth = 0; if ( HaveUserMap ) { // if user map exists, build table. for ( size_t index = 0; index < LookupTable.size(); ++index ) { SegmentationLabelMap::const_iterator it = LabelColorMap.find( index ); if ( it != LabelColorMap.end() ) { const byte* rgb = it->second.GetRGB(); LookupTable[index].R = rgb[0]; LookupTable[index].G = rgb[1]; LookupTable[index].B = rgb[2]; } else { LookupTable[index].R = LookupTable[index].G = LookupTable[index].B = 0; } } } else { // if no user-defined map, create map from HSV ramps. Types::DataItem H = HueRange[0]; const Types::DataItem Hstep = (HueRange[1] - HueRange[0]) / (LookupTable.size() - 1); Types::DataItem S = SaturationRange[0]; const Types::DataItem Sstep = (SaturationRange[1] - SaturationRange[0]) / (LookupTable.size() - 1); Types::DataItem V = ValueRange[0]; const Types::DataItem Vstep = (ValueRange[1] - ValueRange[0]) / (LookupTable.size() - 1); if ( Gamma > 0 ) { for ( size_t index = 0; index < LookupTable.size(); ++index, H += Hstep, S += Sstep, V += Vstep ) { if ( V > 0 ) { Types::DataItem Vgamma = exp( log(V) * (1/Gamma) ); HSV2RGB( LookupTable[index], H, S, Vgamma ); } else { HSV2RGB( LookupTable[index], H, S, V ); } } } else { for ( size_t index = 0; index < LookupTable.size(); ++index, H += Hstep, S += Sstep, V += Vstep ) { HSV2RGB( LookupTable[index], H, S, V ); } } } } void Colormap::HSV2RGB( RGB& rgb, Types::DataItem H, Types::DataItem S, Types::DataItem V ) { const Types::DataItem max = 1.0; const Types::DataItem third = 1.0 / 3.0; Types::DataItem R, G, B; // compute rgb assuming S = 1.0; if (H <= third) { // red -> green G = 3 * std::max( H, 0 ); R = 1.0 - G; B = 0.0; } else if (H >= third && H <= 2.0*third) { // green -> blue B = 3 * (H - third); G = 1.0 - B; R = 0.0; } else { // blue -> red R = 3 * (H - 2.0 / 3); B = 1.0 - R; G = 0.0; } // add Saturation to the equation. S = S / max; (R *= S) += (1.0 - S); (G *= S) += (1.0 - S); (B *= S) += (1.0 - S); // Use value to get actual RGB // normalize RGB first then apply value V = 3 * V / (R + G + B); R *= V; G *= V; B *= V; if (R > max) R = max; if (G > max) G = max; if (B > max) B = max; rgb.R = static_cast( floor(255 * R) ); rgb.G = static_cast( floor(255 * G) ); rgb.B = static_cast( floor(255 * B) ); } } // namespace cmtk cmtk-3.3.1/libs/Pipeline/cmtkColormap.h000066400000000000000000000156131276303427400177740ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2011 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 2752 $ // // $LastChangedDate: 2011-01-17 11:33:31 -0800 (Mon, 17 Jan 2011) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkColormap_h_included_ #define __cmtkColormap_h_included_ #include #include #include #include #include #include namespace cmtk { /** \addtogroup Pipeline */ //@{ /** Class representing a configurable (HSV) colormap. * Ranges for H, S, and V can be defined. For all data values in a given * range, colors are interpolated linearly from these ranges. The number of * discrete colors can be chosen by the user (default is 256). For any given * set of parameters the lookup table is precomputed, so the actual lookup * of several subsequent images can be done very efficiently. */ class Colormap : /// Inherit basic functions from generic pipeline object. public PipelineObject { public: /// Create new Colormap object. static Colormap* New() { return new Colormap(); } /// Flag for user-defined colormaps. igsClassParameter(bool,HaveUserMap); /// Two-value array defining the Hue range of the colormap. igsClassParameter2Array(Types::DataItem,HueRange); /// Two-value array defining the Saturation range of the colormap. igsClassParameter2Array(cmtk::Types::DataItem,SaturationRange); /// Two-value array defining the Value range of the colormap. igsClassParameter2Array(cmtk::Types::DataItem,ValueRange); // Gamma correction coefficient. igsClassParameter(cmtk::Types::DataItem,Gamma); /// The number of entries in the colormap, ie. the number of discrete colors. igsClassParameter(int,TableEntries); /** Two-value array defining the range of data values to map. * All values below the lower bound will be mapped to the first color in the * table while all values above the upper bound are mapped to the final color * in the table. */ igsClassParameter2Array(cmtk::Types::DataItem,DataRange); /** Reverse order of table entries. */ igsClassParameter(bool,Reverse); /** Chose one out of five predefined colormaps. *\param index The index of the desired standard colormap. Valid values are * 0 (Greylevel), 1 (Red), 2 (Green), 3 (Blue), 4 (Rainbow), 5 (Inverse * Rainbow). PALETTE_XXX constants are available for convenient access. */ void SetStandardColormap( const int index ); /// NULL-terminated list of standard colormap names. static const char *StandardColormaps[]; /** Apply this colormap to an image to get an RGB presentation. *\param outPtr Pointer to a suffiently big memory segment that will hold * the resulting RGB data. The data will be stored as three unsigned * 8-bit values per pixel, representing the red, green, and blue components * of that pixel. *\param inPtr Pointer to a TypedArray object containing the data to be * converted. The primitive data type can be any of the types supported by * TypedArray, eg. byte, short, float etc. *\param generateAlpha If this flag is set, a constant alpha value will be * generated for each pixel, resulting in 32 bits of aRGB data per pixel, * rather than 24 bits of RGB data. Default value for this parameter is off. */ void Apply( void *const outPtr, const TypedArray* inPtr, const bool generateAlpha = false ); /// Set colormap parameters from Study object. void SetFromStudy( const Study* study ); /// Convert HSV color to RGB. static void HSV2RGB( RGB& rgb, Types::DataItem H, Types::DataItem S, Types::DataItem V ); protected: /// Default constructor. Colormap(); /** Virtual destructor. */ virtual ~Colormap() {} /** Execute function. * Called by the Update() function inherited from Object, this function * computes the lookup table using the parameters specified. */ virtual void Execute(); private: /** Color lookup table. * This array holds the precomputed R, G, and B color components for * "TableEntries" distinct data values in the range DataRange[0] throgh * DataRange[1]. */ std::vector LookupTable; /// Precomputed scaling factor for data value to table index conversion. Types::DataItem InvDataRangeWidth; /** Apply table lookup for a particular primitive data type. * "T" is a template parameter specifying the primitive data type to lookup * in the color table, eg. byte, short, float etc. *\param outPtr Pointer to an array holding the RGB pixel data after table * lookup. *\param inPtr Pointer to the primitive data array of type T. *\param count Number of values in the array pointed to by inPtr. As inPtr * is not a TypedArray anymore, we have to make this explicit. *\param paddingFlag Flag for use of padding data. *\param paddingData Padding value. Values equal to this are ignored if "paddingFlag" is true. *\see Apply */ template void ApplyPrimitive( RGB *const outPtr, const T* inPtr, const unsigned int count, const bool paddingFlag, const T paddingData ) const; /** Apply table lookup with constant alpha for one primitive data type. * "T" is a template parameter specifying the primitive data type to lookup * in the color table, eg. byte, short, float etc. *\param outPtr Pointer to an array holding the aRGB pixel data after table * lookup. *\param inPtr Pointer to the primitive data array of type T. *\param count Number of values in the array pointed to by inPtr. As inPtr * is not a TypedArray anymore, we have to make this explicit. *\param paddingFlag Flag for use of padding data. *\param paddingData Padding value. Values equal to this are ignored if "paddingFlag" is true. *\see Apply */ template void ApplyPrimitive( RGBA *const outPtr, const T* inPtr, const unsigned int count, const bool paddingFlag, const T paddingData ) const; /// Label color map: is system-defined by default or can be read from file. SegmentationLabelMap LabelColorMap; }; //@} } // namespace cmtk #endif // #ifndef __cmtkColormap_h_included_ cmtk-3.3.1/libs/Pipeline/cmtkFilter.h000066400000000000000000000046761276303427400174540ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // Copyright 2004-2009 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 2752 $ // // $LastChangedDate: 2011-01-17 11:33:31 -0800 (Mon, 17 Jan 2011) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkFilter_h_included_ #define __cmtkFilter_h_included_ #include #include namespace cmtk { /** \addtogroup Pipeline */ //@{ /** Basic filter class. * This class combines the data source functions inherited from Source * with an additional input port. It therefore serves as a template for * all classes transforming an input into an output object. Both, input and * output type are defined by template parameters "I" and "O", respectively. * "O" is passed directly to the Source parent class. *\see Source */ template class Filter : public Source { public: /// Replace the current Input object with a new one. void SetInput ( I *const input ) { this->ReplaceObject( Input, input ); } /** Update this object. * Check for changes in the Input object first, then call inherited Update() * function from Object. *\see Object#Update */ virtual long Update () { this->CheckInputForUpdate( Input ); return this->PipelineObject::Update(); } protected: /// Default constructor. Filter() { Input = NULL; } /** Destructor. * Unregister from the Input object if one was set. */ virtual ~Filter() { if ( Input ) Input->Delete(); } /// The actual input object. I *Input; }; //@} } // namespace cmtk #endif // #ifndef __cmtkFilter_h_included_ cmtk-3.3.1/libs/Pipeline/cmtkImage.cxx000066400000000000000000000101661276303427400176130ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2013 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4812 $ // // $LastChangedDate: 2013-09-09 14:26:32 -0700 (Mon, 09 Sep 2013) $ // // $LastChangedBy: torstenrohlfing $ // */ #include namespace cmtk { /** \addtogroup Pipeline */ //@{ Image::Image () : Data( NULL ) { DataType = TYPE_NONE; } TypedArray::SmartPtr Image::GetData() { if ( ! Data ) { if ( DataType == TYPE_NONE ) return TypedArray::SmartPtr( NULL ); else { Data = TypedArray::SmartPtr( TypedArray::Create( DataType, Dims[0] * Dims[1] ) ); this->UpdateModifiedTime(); } } else { if ( ( Data->GetType() != DataType ) || ( Data->GetDataSize() != (Dims[0] * Dims[1]) ) ) { Data = TypedArray::SmartPtr( NULL ); this->UpdateModifiedTime(); return this->GetData(); } } return Data; } void Image::SetData( TypedArray::SmartPtr& data ) { Data = data; if ( Data ) DataType = Data->GetType(); this->UpdateModifiedTime(); } void Image::SetFromScalarImage ( const ScalarImage& scalarImage ) { this->SetDims( scalarImage.GetDims()[0], scalarImage.GetDims()[1] ); TypedArray::SmartPtr pixelData = scalarImage.GetPixelData(); if ( pixelData ) pixelData = TypedArray::SmartPtr( pixelData->Clone() ); this->SetData( pixelData ); this->SetSpacing( scalarImage.GetPixelSize() ); this->SetOrigin( scalarImage.GetImageOrigin().begin() ); this->SetDirectionX( scalarImage.GetImageDirectionX().begin() ); this->SetDirectionY( scalarImage.GetImageDirectionY().begin() ); this->UpdateModifiedTime(); } double Image::GetDataAt( const int x, const int y, const double def ) { const TypedArray* data = this->GetData(); Types::DataItem result; if ( data->Get( result, x+Dims[0]*y ) ) { return result; } else { return def; } } double Image::GetDataAt( const int index, const double def ) { const TypedArray *data = this->GetData(); Types::DataItem result; if ( data->Get( result, index ) ) { return result; } else { return def; } } void Image::SetDataAt( const int x, const int y, const double value ) { this->GetData()->Set( value, x+Dims[0]*y ); } void Image::SetDataAt( const int index, const double value ) { this->GetData()->Set( value, index ); } double Image::GetDataAt( const double x, const double y, const double def ) { const TypedArray *data = this->GetData(); const unsigned int idxX = static_cast( x / Spacing[0] ); const unsigned int idxY = static_cast( y / Spacing[1] ); if ( (idxX > Dims[0]-2) || (idxY > Dims[1]-2) ) return def; int offset = idxX + Dims[0] * idxY; Types::DataItem result[4]; if ( ! data->Get( result[0], offset ) ) { return def; } if ( ! data->Get( result[1], offset + 1 ) ) { return def; } if ( ! data->Get( result[2], offset + Dims[0] ) ) { return def; } if ( ! data->Get( result[3], offset + Dims[0] + 1 ) ) { return def; } const double relX = ( x - idxX * Spacing[0] ) / Spacing[0]; const double relY = ( y - idxY * Spacing[1] ) / Spacing[1]; return (1-relY) * ( (1-relX) * result[0] + (relX) * result[1] ) + (relY) * ( (1-relX) * result[2] + (relX) * result[3] ); } } // namespace cmtk cmtk-3.3.1/libs/Pipeline/cmtkImage.h000066400000000000000000000104671276303427400172440ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2013 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4812 $ // // $LastChangedDate: 2013-09-09 14:26:32 -0700 (Mon, 09 Sep 2013) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkImage_h_included_ #define __cmtkImage_h_included_ #include #include #include #include #include #include namespace cmtk { /** \addtogroup Pipeline */ //@{ /** Class for 2D image data. */ class Image : /// Inherit geometry information from Plane. public Plane { public: /// Create new object. static Image* New() { return new Image; } /// Scalar data type. igsClassParameter(ScalarDataType,DataType); /// Return a pointer to the object holding the image data. TypedArray::SmartPtr GetData(); /** Replace existing data array by new one. * BEWARE! The size of the new array must match precisely the dimensions * as specified by the inherited Dims[] array. */ void SetData( TypedArray::SmartPtr& data ); /** Copy non-pipelined scalar image. */ void SetFromScalarImage( const ScalarImage& scalarImage ); /** Return data at a certain grid location (pixel). *\param x Index of requested pixel in x-direction. Valid range is * [0..Dims[0]-1]. *\param y Index of requested pixel in y-direction. Valid range is * [0..Dims[1]-1]. *\param def Value returned when there is no valid data at the queried * position. This parameter defaults to zero. *\return The value of the pixel at position (x,y) or the value of parameter * def if there was no valid data for this position. */ double GetDataAt( const int x, const int y, const double def = 0 ); /** Set data at a certain grid location (pixel). *\param x Index of requested pixel in x-direction. Valid range is * [0..Dims[0]-1]. *\param y Index of requested pixel in y-direction. Valid range is * [0..Dims[1]-1]. *\param value Value to set pixel to. */ void SetDataAt( const int x, const int y, const double value = 0 ); /** Return data at a certain grid location (index). *\param index Index of requested pixel. Valid range is * [0..GetNumPixels()-1]. *\param def Value returned when there is no valid data at the queried * position. This parameter defaults to zero. *\return The value of the pixel at position (x,y) or the value of parameter * def if there was no valid data for this position. */ double GetDataAt( const int index, const double def = 0 ); /** Set data at a certain grid location (index). *\param index Index of requested pixel. Valid range is [0..GetNumPixels()-1]. *\param value Value to set pixel to. */ void SetDataAt( const int index, const double value = 0 ); /** Return data at a certain grid location (in world coordinates). *\param x Location of requested pixel in x-direction. *\param y Location of requested pixel in y-direction. *\param def Value returned when there is no valid data at the specified * position. This parameter defaults to zero. *\return The value of the pixel at position (x,y) or the value of parameter * "def" if there was no valid data for this position. */ double GetDataAt( const double x, const double y, const double def = 0 ); protected: /// Default constructor. Image(); /// Destructor. virtual ~Image() {}; /// The actual image data. TypedArray::SmartPtr Data; }; //@} } // namespace cmtk #endif cmtk-3.3.1/libs/Pipeline/cmtkImageRGB.cxx000066400000000000000000000075471276303427400201570ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2011, 2013 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4876 $ // // $LastChangedDate: 2013-09-24 13:09:58 -0700 (Tue, 24 Sep 2013) $ // // $LastChangedBy: torstenrohlfing $ // */ #include #include namespace cmtk { /** \addtogroup Pipeline */ //@{ ImageRGB *ImageRGB::New() { return new ImageRGB; } ImageRGB::ImageRGB() { Data = NULL; DataSize = 0; AlphaChannel = IMAGE_RGB; BytesPerPixel = 3; } ImageRGB::~ImageRGB() { Memory::ArrayC::Delete( this->Data ); } byte* ImageRGB::GetDataPtr( const bool forceAlloc ) { if ( ! forceAlloc ) return Data; if ( Data == NULL ) { DataSize = BytesPerPixel * this->Plane::GetNumPixels(); Data = Memory::ArrayC::Allocate( DataSize ); } else { if ( DataSize != (BytesPerPixel * this->Plane::GetNumPixels()) ) { Memory::ArrayC::Delete( Data ); Data = NULL; return this->GetDataPtr( true /* forceAlloc */ ); } } return Data; } void ImageRGB::SetAlphaChannel( const ImageAlphaToggle alphaChannel, const bool convertData ) { if ( alphaChannel != AlphaChannel ) { AlphaChannel = alphaChannel; BytesPerPixel = (AlphaChannel == IMAGE_RGB) ? 3 : 4; byte *oldData = Data; // fake initialization by setting Data to NULL Data = NULL; this->GetDataPtr( true /* forceAlloc */ ); // convert old image data if ( convertData ) { byte *fromPtr = oldData; byte *toPtr = Data; unsigned int numberOfPixels = this->GetNumPixels(); if ( AlphaChannel == IMAGE_RGB ) { // from RGBA to RGB: remove alpha value for ( unsigned int i = 0; i < numberOfPixels; ++i, fromPtr += 4, toPtr += 3 ) { toPtr[0] = fromPtr[0]; toPtr[1] = fromPtr[1]; toPtr[2] = fromPtr[2]; } } else { // from RGB to RGBA: set alpha to opaque for ( unsigned int i = 0; i < numberOfPixels; ++i, fromPtr += 3, toPtr += 4 ) { toPtr[0] = fromPtr[0]; toPtr[1] = fromPtr[1]; toPtr[2] = fromPtr[2]; toPtr[3] = 255; } } } Memory::ArrayC::Delete( oldData ); } } void ImageRGB::GetPixel( RGBA& rgb, const int index ) { byte* pixelPtr = Data + (BytesPerPixel * index); rgb.R = pixelPtr[0]; rgb.G = pixelPtr[1]; rgb.B = pixelPtr[2]; if ( AlphaChannel == IMAGE_RGBA ) rgb.Alpha = pixelPtr[3]; else rgb.Alpha = 255; } void ImageRGB::SetPixel( const int index, const RGBA& rgb ) { byte* pixelPtr = Data + (BytesPerPixel * index); pixelPtr[0] = rgb.R; pixelPtr[1] = rgb.G; pixelPtr[2] = rgb.B; if ( AlphaChannel == IMAGE_RGBA ) pixelPtr[3] = rgb.Alpha; } void ImageRGB::GetPixel( RGB& rgb, const int index ) { byte* pixelPtr = Data + (BytesPerPixel * index); rgb.R = pixelPtr[0]; rgb.G = pixelPtr[1]; rgb.B = pixelPtr[2]; } void ImageRGB::SetPixel( const int index, const RGB& rgb ) { byte* pixelPtr = Data + (BytesPerPixel * index); pixelPtr[0] = rgb.R; pixelPtr[1] = rgb.G; pixelPtr[2] = rgb.B; } } // namespace cmtk cmtk-3.3.1/libs/Pipeline/cmtkImageRGB.h000066400000000000000000000106131276303427400175700ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2011, 2013 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4876 $ // // $LastChangedDate: 2013-09-24 13:09:58 -0700 (Tue, 24 Sep 2013) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkImageRGB_h_included_ #define __cmtkImageRGB_h_included_ #include #include #include #include #include namespace cmtk { /** \addtogroup Pipeline */ //@{ /// Type definition for the status of the alpha-channel presence flag. typedef enum { /// Image has no alpha channel (3 bytes per pixel). IMAGE_RGB, /// Image has an alpha channel (4 bytes per pixel). IMAGE_RGBA } ImageAlphaToggle; /** Class to represent ready-to-display RGB image data. */ class ImageRGB : /// Inherit geometry from Plane. public Plane { public: /// Construct new class instance. static ImageRGB* New(); /** Get pointer to RGB data. * This function checks whether a data array of the appropriate size exists. * If not, the old array is freed and a new one with the correct size is * created. */ const byte *GetDataPtr() const { return this->Data; } /** Get pointer to RGB data. * This function checks whether a data array of the appropriate size exists. * If not, the old array is freed and a new one with the correct size is * created. *\param forceAlloc If this flag is true, then a data array of * appropriate size is allocated if it had not been done before. */ byte *GetDataPtr( const bool forceAlloc ); /** Return RGBA pixel data. * If this is actually an RGB image only, the alpha component of the returned * pixel will be set to 255 (opaque). */ void GetPixel( RGBA& rgb, const int index ); /** Set RGBA pixel data. * If this is actually an RGB image only, the target pixel's alpha value will * be set to 255 (opaque). */ void SetPixel( const int index, const RGBA& rgb ); /** Return RGB pixel data. * If this is actually an RGBA image, the requested pixel's alpha component * will be ignored. */ void GetPixel( RGB& rgb, const int index ); /** Set RGB pixel data. * If this is actually an RGBA image, the target pixel's alpha value will be * set to 255 (opaque). */ void SetPixel( const int index, const RGB& rgb ); /** Set alpha channel flag. * This function is used to toggle the image between RGB and RGB+Alpha modes. */ void SetAlphaChannel( const ImageAlphaToggle alphaChannel, const bool convertData = false ); /** Return current image mode. */ ImageAlphaToggle GetAlphaChannel() const { return AlphaChannel; } protected: /// Default costructor. ImageRGB(); /** Destructor. * Free image data array if one has been allocated. */ ~ImageRGB(); private: /** Pointer to the RGB image data. * Every pixel is stored as three subsequent values R, G, B, and possibly * Alpha. These are all in the range 0 (black) to 255 (maximum intensity). */ byte *Data; /** The current image mode (RGB or RGB+Alpha). */ ImageAlphaToggle AlphaChannel; /** The number of bytes per pixel associated with the current image mode. *\see #AlphaChannel */ unsigned int BytesPerPixel; /** The number of bytes allocated for the currently allocated Data array. * Note that this is NOT the number of pixels which, depending on the state * of "AlphaChannel", is only 1/4 or 1/3 of this value. *\see #AlphaChannel */ unsigned int DataSize; }; //@} } // namespace cmtk #endif // #ifndef __cmtkImageRGB_h_included_ cmtk-3.3.1/libs/Pipeline/cmtkImageToImageRGB.cxx000066400000000000000000000066131276303427400214160ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2010 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 2398 $ // // $LastChangedDate: 2010-10-05 14:54:37 -0700 (Tue, 05 Oct 2010) $ // // $LastChangedBy: torstenrohlfing $ // */ #include namespace cmtk { /** \addtogroup Pipeline */ //@{ ImageToImageRGB::ImageToImageRGB() : CheckerboxPadding( true ) { this->m_Image = NULL; this->m_Colormap = NULL; AlphaMode = AlphaModeNone; this->RegisterInput( &this->m_Image ); this->RegisterInput( &this->m_Colormap ); } ImageToImageRGB::~ImageToImageRGB() { if ( this->m_Image ) this->m_Image->Unregister(); if ( this->m_Colormap ) this->m_Colormap->Unregister(); } void ImageToImageRGB::SetInput( Image *const image ) { this->ReplaceObject( this->m_Image, image ); } void ImageToImageRGB::SetColormap( Colormap *const colormap ) { this->ReplaceObject( this->m_Colormap, colormap ); } void ImageToImageRGB::Execute() { if ( (this->m_Image == NULL) || (this->m_Colormap == NULL) ) return; const TypedArray *inPtr = this->m_Image->GetData(); if ( !inPtr ) return; ImageRGB *output = this->GetOutput(); output->CopyStructure( this->m_Image ); if ( AlphaMode == AlphaModeNone ) output->SetAlphaChannel( IMAGE_RGB ); else output->SetAlphaChannel( IMAGE_RGBA ); void *outPtr = output->GetDataPtr( true /* forceAlloc */ ); switch ( AlphaMode ) { case AlphaModeNone: this->m_Colormap->Apply( outPtr, inPtr ); if ( inPtr->GetPaddingFlag() ) this->MarkPaddingData( output->GetDims( AXIS_X ), output->GetDims( AXIS_Y ), static_cast( outPtr ), inPtr ); break; case AlphaModeConst: this->m_Colormap->Apply( outPtr, inPtr, true /* generateAlpha */ ); if ( inPtr->GetPaddingFlag() ) this->MarkPaddingData( output->GetDims( AXIS_X ), output->GetDims( AXIS_Y ), static_cast( outPtr ), inPtr ); break; } this->UpdateExecuteTime(); } template void ImageToImageRGB::MarkPaddingData ( const unsigned int dimsx, const unsigned int dimsy, T *const rgba, const TypedArray* data ) const { T* p = rgba; unsigned int idx = 0; byte bright = 170; byte dark = 80; if ( !this->CheckerboxPadding ) bright = dark = 0; for ( unsigned int y = 0; y < dimsy; ++y ) { for ( unsigned int x = 0; x < dimsx; ++x, ++idx, ++p ) { if ( data->PaddingDataAt( idx ) ) { if ( ((x >> 4)&1) ^ ((y >> 4)&1) ) { p->R = p->G = p->B = bright; } else { p->R = p->G = p->B = dark; } } } } } } // namespace cmtk cmtk-3.3.1/libs/Pipeline/cmtkImageToImageRGB.h000066400000000000000000000062121276303427400210360ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2010 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 2398 $ // // $LastChangedDate: 2010-10-05 14:54:37 -0700 (Tue, 05 Oct 2010) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkImageToImageRGB_h_included_ #define __cmtkImageToImageRGB_h_included_ #include #include #include #include #include #include namespace cmtk { /** \addtogroup Pipeline */ //@{ /** Filter to convert image to RGB image using a color lookup table. */ class ImageToImageRGB : /// This is a filter with multiple input. public MultiFilter { public: /// Create new object. static ImageToImageRGB* New() { return new ImageToImageRGB; } /// Enumeration of Alpha modes. typedef enum { /// Do not use alpha channel (generate 3 byte-per-pixel RGB data). AlphaModeNone, /// Use constant alpha value (opaque). AlphaModeConst } igsAlphaMode; /** This flag detemines if and how an alpha (transparancy) ramp is used. * If this flag is said, the attached Colormap object will generate * transparency information in addition to the usual RGB data. As a result, * and RGBA image will be generated instead of a plain RGB image. */ igsClassParameter(igsAlphaMode,AlphaMode); /// Use checkerboard pattern to fill PaddingData areas. igsClassParameter(bool,CheckerboxPadding); /// Convert image to RGB image. virtual void Execute(); /// Set an input image. void SetInput( Image *const image ); /// Set a colormap. void SetColormap( Colormap *const colormap ); protected: /// Default constructor. ImageToImageRGB(); /** Destructor. * Dereference all input and output objects. */ virtual ~ImageToImageRGB(); private: /// The input image. Image *m_Image; /// The actual colormap. Colormap *m_Colormap; /// Convenience definition of the parent class type. typedef MultiFilter Superclass; /// Overwrite padded regions with checkerboard pattern. template void MarkPaddingData( const unsigned int dimsx, const unsigned int dimsy, T *const rgba, const TypedArray* data ) const; }; //@} } // namespace cmtk #endif // #ifndef __cmtkImageToImageRGB_h_included_ cmtk-3.3.1/libs/Pipeline/cmtkMultiFilter.h000066400000000000000000000062031276303427400204530ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // Copyright 2004-2009 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 2752 $ // // $LastChangedDate: 2011-01-17 11:33:31 -0800 (Mon, 17 Jan 2011) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkMultiFilter_h_included_ #define __cmtkMultiFilter_h_included_ #include #include #include #include namespace cmtk { /** \addtogroup Pipeline */ //@{ /** Filter with several inputs. * This class combines the data source functions inherited from Source * with update control for an arbitrary number of input port. It therefore * serves as a template for all classes transforming more than one input into * an output object. For just one input, Filter is probably more efficient * as it gets along without the STL "list" class. *\see Source *\see Filter */ template class MultiFilter : public Source { public: template void RegisterInput( I** input ) { if ( input ) { this->m_InputList.push_back( (PipelineObject**) input ); } } template void UnregisterInput( const I** input ) { if ( input ) { InputListType::iterator it = this->m_InputList.begin(); while ( it != this->m_InputList.end() ) { if ( *it == input ) { this->m_InputList.erase( it ); } ++it; } } } /** Update this object. * Check for changes in all input objects first, then call inherited Update() * function from PipelineObject. *\see PipelineObject#Update */ virtual long Update () { InputListType::iterator it = this->m_InputList.begin(); while ( it != this->m_InputList.end() ) { if ( **it ) this->CheckInputForUpdate( **it ); ++it; } return this->PipelineObject::Update(); } protected: /// Default constructor. MultiFilter() {} /** Destructor. * Empty list of input objects. */ virtual ~MultiFilter() { while ( ! this->m_InputList.empty() ) this->m_InputList.pop_back(); } /// Type for the STL list holding pointers to PipelineObjects. typedef std::list InputListType; /// The actual input object. InputListType m_InputList; }; //@} } // namespace cmtk #endif // #ifndef __cmtkMultiFilter_h_included_ cmtk-3.3.1/libs/Pipeline/cmtkObject.cxx000066400000000000000000000024701276303427400177760ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2010 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 2398 $ // // $LastChangedDate: 2010-10-05 14:54:37 -0700 (Tue, 05 Oct 2010) $ // // $LastChangedBy: torstenrohlfing $ // */ #include #include // GJ replaced above with: #ifdef __APPLE__ # include #else # include #endif namespace cmtk { /** \addtogroup Pipeline */ //@{ long CurrentTime = 1; } // namespace cmtk cmtk-3.3.1/libs/Pipeline/cmtkObject.h000066400000000000000000000212761276303427400174300ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2011 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 2752 $ // // $LastChangedDate: 2011-01-17 11:33:31 -0800 (Mon, 17 Jan 2011) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkObject_h_included_ #define __cmtkObject_h_included_ #include #ifndef NULL #define NULL 0 #endif /** Macro to define a protected pointer class parameter and public read and * write access member functions. */ #define cmtkGetSetMacroObject(t,name) \ protected: t *m_##name; \ public: void Set##name( t *const v ) { if (v) v->Reference(); if (this->m_##name) this->m_##name->Delete(); this->m_##name = v; } \ t* Get##name() { return this->m_##name; } \ const t* Get##name() const { return this->m_##name; } \ void Get##name( t*& to ) { to = this->m_##name; } /** Macro to define a protected scalar class parameter and public read and * write access member functions. */ #define igsClassParameter(type,name) \ public: type name; \ void Set##name( const type v ) { if ( name != v ) this->UpdateModifiedTime(); name = v; } \ type Get##name() const { return name; } \ void Get##name( type& to ) { to = name; } /** Macro to define a protected scalar class parameter and public read and * write access member functions. */ #define cmtkClassParameter(type,name) \ public: type m_##name; \ void Set##name( const type v ) { if ( this->m_##name != v ) this->UpdateModifiedTime(); this->m_##name = v; } \ type Get##name() const { return this->m_##name; } \ void Get##name( type& to ) { to = this->m_##name; } /** Macro to define a protected 2D array class parameter and public read and * write access member functions. */ #define igsClassParameter2Array(type,name) \ protected: type name[2]; \ public: void Set##name( const type v0, const type v1 ) \ { if ( (name[0] != v0) || (name[1] != v1) ) this->UpdateModifiedTime(); name[0] = v0; name[1] = v1; } \ void SetByIndex##name( const int axis, const type v ) \ { if (name[axis] != v) this->UpdateModifiedTime(); name[axis] = v; } \ void Set##name( const type* v ) \ { if ( (name[0] != v[0]) || (name[1] != v[1]) ) this->UpdateModifiedTime(); name[0] = v[0]; name[1] = v[1]; } \ void Get##name( type &v0, type &v1 ) const \ { v0 = name[0]; v1 = name[1]; } \ void Get##name( type *const v ) const \ { v[0] = name[0]; v[1] = name[1]; } \ const type* Get##name() const { return name; } \ type* Get##name() { return name; } \ type Get##name( const int i ) const { return name[i]; } /** Macro to define a protected 3D array class parameter and public read and * write access member functions. */ #define igsClassParameter3Array(type,name) \ protected: type name[3]; \ public: void Set##name( const type v0, const type v1, const type v2 ) \ { if ( (name[0] != v0) || (name[1] != v1) || (name[2] != v2) ) this->UpdateModifiedTime(); name[0] = v0; name[1] = v1; name[2] = v2; } \ void SetByIndex##name( const int axis, const type v ) \ { if (name[axis] != v) this->UpdateModifiedTime(); name[axis] = v; } \ void Set##name( const type* v ) \ { if ( (name[0] != v[0]) || (name[1] != v[1]) || (name[2] != v[2]) ) this->UpdateModifiedTime(); name[0] = v[0]; name[1] = v[1]; name[2] = v[2]; } \ void Get##name( type &v0, type &v1, type &v2 ) const \ { v0 = name[0]; v1 = name[1]; v2 = name[2]; } \ void Get##name( type *const v ) const \ { v[0] = name[0]; v[1] = name[1]; v[2] = name[2]; } \ const type* Get##name() const { return name; } \ type* Get##name() { return name; } \ type Get##name( const int i ) const { return name[i]; } namespace cmtk { /** \addtogroup Pipeline */ //@{ /** Static counter representing the current (discrete) processing time. * This variable is incremented with every query using * Object::GetCurrentTime(). Direct access MUST NOT occur in order * to prevent inconsistent object times. *\name igsCurrentTime *\see PipelineObject#GetCurrentTime */ extern long CurrentTime; /** Base class for all reference counted objects. * This class to some extent shadows VTK's respective class. However, our * class should make be a little more runtime- and memory-efficient. */ class Object { public: /** Register another object as this objects owner. * The reference counter of this object is also incremented. *\return The new value of the reference counter. *\see ReferenceCount */ virtual int Reference() const { return ++ReferenceCount; } /** Destroy this object. * This function decrements this object's reference counter. If the updated * reference count is zero, this object is deleted. */ virtual void Delete() { if ( (--ReferenceCount) <= 0 ) delete this; } /** Directly set the reference counter. * This function should be used CAREFULLY. */ void SetReferenceCount ( const int referenceCount ) { ReferenceCount = referenceCount; } /** Get the reference counter. */ int GetReferenceCount () const { return ReferenceCount; } /// Return this objects last modification time. long GetModifiedTime() const { return ModifiedTime; } /// Set time of last modification to current time. void UpdateModifiedTime() { ModifiedTime = this->GetCurrentTime(); } /** Explicitly set time of last modification. * Only monotone updates, ie. setting the update time to a more recent time, * are allowed. */ void UpdateModifiedTime( long modifiedTime ) { if ( modifiedTime > ModifiedTime ) ModifiedTime = modifiedTime; } /** Utility function: Replace one reference counted object by another. * References are updated accordingly. Substitution of a pointer by itself * does not have any effect. The function is NULL safe. *\param to Reference to a pointer to be replaced by the pointer given as * "from" parameter. If both pointers are different, the reference counter * of this object is decremented before overwriting the pointer. It is safe * to pass references to NULL pointers here. *\param from The reference counter of the object pointed to by this pointer * is increased if a pointer substitution takes place. It is safe to pass a * NULL pointer here. *\return "True" is returned if there was an actual replacement. If an object * was basically replaced by itself, "false" is returned instead. Note that * only POINTERS are compared to find out which of both has happened. */ template bool ReplaceObject( C*& to, C *const from ) { if ( from == to ) return false; if ( from ) from->Reference(); if ( to ) to->Delete(); to = from; this->UpdateModifiedTime(); return true; } /** Default constructor. * Set the reference counter to zero initially. */ Object() { ReferenceCount = 1; ModifiedTime = 0; } /** Virtual constructor. * This function is only present to ensure that derived classes can have a * virtual destructor function. */ virtual ~Object() {}; /** Query the time processing counter. * Immediately after returning the present time, the time counter is * advanced. *\return The present time. *\see igsCurrentTime */ static long GetCurrentTime () { return CurrentTime++; } private: /// The actual reference counter. mutable int ReferenceCount; /** Last modification time. * This is the time of the last modification made to this object or one of * its input parameters. */ long ModifiedTime; }; //@} } // namespace cmtk #endif // #ifndef __cmtkObject_h_included_ cmtk-3.3.1/libs/Pipeline/cmtkPipelineObject.cxx000066400000000000000000000044621276303427400214670ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // Copyright 2004-2009 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 2398 $ // // $LastChangedDate: 2010-10-05 14:54:37 -0700 (Tue, 05 Oct 2010) $ // // $LastChangedBy: torstenrohlfing $ // */ #include namespace cmtk { /** \addtogroup Pipeline */ //@{ PipelineObject::PipelineObject() { Owner = NULL; // We may use 0 here as the actual time starts at "1". So newly created // objects are never marked up-to-date. ExecuteTime = 0; ExecutePending = 0; } int PipelineObject::Register( PipelineObject *const owner ) { if ( owner ) { Owner = owner; } this->Object::Reference(); return this->GetReferenceCount(); } void PipelineObject::Unregister( PipelineObject *const owner ) { // Does the primary owner unregister? Then set primary owner to "none". if ( Owner == owner ) Owner = NULL; this->Delete(); } long PipelineObject::Update() { this->CheckInputForUpdate( Owner ); return this->ExecuteIfNecessary(); } int PipelineObject::CheckInputForUpdate( PipelineObject *const object ) { if ( object ) { const long ObjectTime = object->Update(); if ( ObjectTime > ExecuteTime ) { ExecutePending = 1; return 1; } } return 0; } long PipelineObject::ExecuteIfNecessary() { if ( (this->GetModifiedTime() > ExecuteTime) || ExecutePending ) { this->Execute(); this->UpdateExecuteTime(); } return ExecuteTime; } } // namespace cmtk cmtk-3.3.1/libs/Pipeline/cmtkPipelineObject.h000066400000000000000000000115671276303427400211200ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2010 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 2752 $ // // $LastChangedDate: 2011-01-17 11:33:31 -0800 (Mon, 17 Jan 2011) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkPipelineObject_h_included_ #define __cmtkPipelineObject_h_included_ #include #include #include #include namespace cmtk { /** \addtogroup Pipeline */ //@{ /** Base class for all reference counted and linked objects. */ class PipelineObject : /// Inherit reference-counting object. public Object { protected: /** This object's owner. * The owner is the object queried for updates first when this object is * asked to update itself. */ PipelineObject *Owner; public: // void SetOwner( PipelineObject *const owner ) { Owner = owner; } const PipelineObject* GetOwner() const { return Owner; } /** Register another object as this objects owner. * The reference counter of this object is also incremented. *\param owner The object to be registered as the owner of this object. * If this parameter is not given, the current owner is left untouched. In * this case, only the reference counter is modified. *\return The new value of the reference counter. *\see ReferenceCount */ int Register( PipelineObject *const owner = NULL ); /** Unregister one owner object. * This function decrements this object's reference counter. If the updated * reference count is zero, this object is destroyed. */ void Unregister( PipelineObject *const owner = NULL ); /** Check for update. * This function first checks whether since its last execution its owner * has been modified. In this case, the Execute() function is called to * update the current object with the new input. Derived classes may override * this function if they have more than one input object, for instance. * * Such derived implementations can then use the CheckForUpdate() and * ExecuteIfNecessary() member functions for convenient state checking and * execution. *\see Execute *\see CheckInputForUpdate *\see ExecuteIfNecessary */ virtual long Update (); /** Execute the current object. * Derived classes need to override this function in order to make the * respective instance up-to-date. */ virtual void Execute () { this->UpdateExecuteTime(); } protected: /** Default constructor. * Set the reference counter to zero initially. */ PipelineObject(); /** Destructor. * This is defined virtual so that derived classes are enabled to provide * their own virtual destructor functions. */ virtual ~PipelineObject() {}; /// Set time of last execution to current time. void UpdateExecuteTime() { ExecuteTime = this->GetCurrentTime(); ExecutePending = 0; } /** Compare input for update. * For the given input object (Owner for example), the Update() function * is called. Afterwards, the returned execution time of the input object * is compared to the current object's modification time. The later of both * times is then set as the current object's modification time. */ virtual int CheckInputForUpdate( PipelineObject *const object ); /** Execute an update if object was modified after last execution. *\return The new time of last execution. */ virtual long ExecuteIfNecessary(); private: /** Last execution time. * This is the time of the latest execution of this objects Execute() * function, ie. the time when this objects state or output was last * updated according to the input parameters. */ long ExecuteTime; /** Flag for pending updates. * This field is set to 1 if Update() discovers a changed input object. * ExecuteIfNecessary() then evaluates this flag in addition to this objects * modification time and calls Exedcute() even if only this flag is set. * UpdateExecuteTime() then finally resets this field to 0. */ int ExecutePending; }; //@} } // namespace cmtk #endif // #ifndef __cmtkPipelineObject_h_included_ cmtk-3.3.1/libs/Pipeline/cmtkPlane.cxx000066400000000000000000000047101276303427400176260ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2010 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 2398 $ // // $LastChangedDate: 2010-10-05 14:54:37 -0700 (Tue, 05 Oct 2010) $ // // $LastChangedBy: torstenrohlfing $ // */ #include #include namespace cmtk { /** \addtogroup Pipeline */ //@{ Plane::Plane() { Dims[0] = Dims[1] = 0; Spacing[0] = Spacing[1] = 1; Origin[0] = Origin[1] = Origin[2] = 0; DirectionX[0] = 1; DirectionY[1] = 1; DirectionX[1] = DirectionX[2] = DirectionY[0] = DirectionY[2] = 0; } void Plane::CopyStructure( const Plane *plane ) { this->SetDims( plane->GetDims() ); this->SetSpacing( plane->GetSpacing() ); this->SetOrigin( plane->GetOrigin() ); this->SetDirectionX( plane->GetDirectionX() ); this->SetDirectionY( plane->GetDirectionY() ); } void Plane::Project( Vector3D& p, const Vector3D& q ) const { Vector3D v( q ); v[0] -= Origin[0]; v[1] -= Origin[1]; v[2] -= Origin[2]; p[0] = ( v[0] * DirectionX[0] + v[1] * DirectionX[1] + v[2] * DirectionX[2] ) / ( MathUtil::Square( DirectionX[0] ) + MathUtil::Square( DirectionX[1] ) + MathUtil::Square( DirectionX[2] ) ); p[1] = ( v[0] * DirectionY[0] + v[1] * DirectionY[1] + v[2] * DirectionY[2] ) / ( MathUtil::Square( DirectionY[0] ) + MathUtil::Square( DirectionY[1] ) + MathUtil::Square( DirectionY[2] ) ); p[2] = 0; } void Plane::ProjectPixel( const Vector3D& v, unsigned int& i, unsigned int& j ) const { Vector3D q(v), p; this->Project( p, q ); i = MathUtil::Round( p[0] / Spacing[0] ); j = MathUtil::Round( p[1] / Spacing[1] ); } } // namespace cmtk cmtk-3.3.1/libs/Pipeline/cmtkPlane.h000066400000000000000000000063071276303427400172570ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2011 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 2752 $ // // $LastChangedDate: 2011-01-17 11:33:31 -0800 (Mon, 17 Jan 2011) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkPlane_h_included_ #define __cmtkPlane_h_included_ #include #include #include #include #include namespace cmtk { /** \addtogroup Pipeline */ //@{ /** Class for 2D planes, that is uniform point meshes. */ class Plane : public PipelineObject { public: /// Create new object. static Plane* New() { return new Plane; } /// Dimensions array. igsClassParameter2Array(unsigned int,Dims); /// Spacing (ie. pixel size) array. igsClassParameter2Array(Types::Coordinate,Spacing); /// Origin of image in 3D space. igsClassParameter3Array(Types::Coordinate,Origin); /// Direction of image's x-axis in 3D space. igsClassParameter3Array(Types::Coordinate,DirectionX); /// Direction of image's y-axis in 3D space. igsClassParameter3Array(Types::Coordinate,DirectionY); /** Copy the structure of another Plane object. * This function copies dimensions, pixel size, and spatial location of * a given object. */ void CopyStructure( const Plane *plane ); /// Return number of pixels in this object. virtual unsigned int GetNumPixels() const { return Dims[0]*Dims[1]; } /// Return 3D coordinate of a particular pixel. void GetPixelLocation( Vector3D& v, const unsigned int x, const unsigned int y ) const { for ( int dim = 0; dim<3; ++dim ) v[dim] = Origin[dim] + x * DirectionX[dim] * Spacing[0] + y * DirectionY[dim] * Spacing[1]; } /** Project 3D coordinate onto plane. *\param p Projected coordinate. *\param q Original coordinate. */ void Project( Vector3D& p, const Vector3D& q ) const; /** Project 3D coordinate onto image plane pixels. *\param v Original coordinate. *\param i Index of projected pixel in x direction. *\param j Index of projected pixel in y direction. */ void ProjectPixel( const Vector3D& v, unsigned int& i, unsigned int& j ) const; protected: /** Default constructor. * Set all plane fields to safe values. */ Plane(); /// Destructor. virtual ~Plane() {}; }; //@} } // namespace cmtk #endif // #ifndef __cmtkPlane_h_included_ cmtk-3.3.1/libs/Pipeline/cmtkRGB.h000066400000000000000000000036271276303427400166340ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2010 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 2398 $ // // $LastChangedDate: 2010-10-05 14:54:37 -0700 (Tue, 05 Oct 2010) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkRGB_h_included_ #define __cmtkRGB_h_included_ #include namespace cmtk { /** \addtogroup Pipeline */ //@{ /** Red, green, and blue components of one pixel. */ typedef struct { #ifdef WORDS_BIGENDIAN /// Red byte R; /// Green byte G; /// Blue byte B; #else byte B; byte G; byte R; #endif } RGB; /** RGB components plus transparency (alpha value). */ class RGBA { public: #ifdef WORDS_BIGENDIAN /** Transparency. * Opacity is linear between 0 (fully transparent) and 255 (fully opaque). */ byte Alpha; /// Red byte R; /// Green byte G; /// Blue byte B; #else byte B; byte G; byte R; byte Alpha; #endif /// Assignment operator. void operator =( const RGB& rgb ) { R = rgb.R; G = rgb.G; B = rgb.B; } }; //@} } // namespace cmtk #endif // #ifndef __cmtkRGB_h_included_ cmtk-3.3.1/libs/Pipeline/cmtkRenderer.cxx000066400000000000000000000035551276303427400203430ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2010 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 2398 $ // // $LastChangedDate: 2010-10-05 14:54:37 -0700 (Tue, 05 Oct 2010) $ // // $LastChangedBy: torstenrohlfing $ // */ #include namespace cmtk { /** \addtogroup Pipeline */ //@{ Renderer::Renderer() { Input = NULL; Active = true; RenderPending = false; } Renderer::~Renderer() { if ( Input != NULL ) Input->Delete(); } void Renderer::SetInput( ImageRGB *const input ) { ReplaceObject( Input, input ); }; long Renderer::Update() { if ( this->IsActive() ) this->CheckInputForUpdate( Input ); return this->Superclass::Update(); } void Renderer::Render() { // Is this renderer being updated already, ie. do we have a recursion here? // If no: go through it. if ( ! RenderPending ) { RenderPending = true; // Fake modification to make sure we WILL update ourselves. this->UpdateModifiedTime(); this->Update(); RenderPending = false; } } } // namespace cmtk cmtk-3.3.1/libs/Pipeline/cmtkRenderer.h000066400000000000000000000063521276303427400177660ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2010 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 2752 $ // // $LastChangedDate: 2011-01-17 11:33:31 -0800 (Mon, 17 Jan 2011) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkRenderer_h_included_ #define __cmtkRenderer_h_included_ #include #include namespace cmtk { /** \addtogroup Pipeline */ //@{ /** General renderer template class. * This class provides a virtual "Render()" function as a common access path * for client code. It also provides an ImageRGB input object representing * the actual image to be rendered. */ class Renderer : public PipelineObject { public: /// Calls to this function should make derived objects update their display. virtual void Render(); /// Set image to display. void SetInput( ImageRGB *const input ); /// The actual Update() function. virtual long Update(); /** Make this renderer active. *\see Active */ virtual void SetActive() {Active = true; } /** Make this renderer inactive. *\see Active */ virtual void SetInactive() { Active = false; } /** Query activity state of this renderer. *\see Active */ virtual int IsActive() const { return Active; } protected: /// Default constructor. Renderer(); /// Destructor. ~Renderer(); /// Image to be displayed. ImageRGB* Input; private: /** Active flag. * If this flag is set, the renderer is active. It will display its * associated image if Render() is called. If this flag is not * set, calls to Render() will result in this object setting its * output to size 0. */ bool Active; /** Recursion flag. * This flag is used by the Render() method to check whether this viewer is * being rendered already. This is necessary, as checking ancestors in the * pipeline can lead to calls back to the environment, Tcl for example, for * status output. That, in turn can cause calls to the renderer again. * * So while this object is in its Render() method, this flag is set to * 'true'. * If Render() is then called again, the function simply returns without * actually updating anything, thus avoiding recursion. */ bool RenderPending; /// Convenience declaration for calls to parent class' functions. typedef PipelineObject Superclass; }; //@} } // namespace cmtk #endif // #ifndef __cmtkRenderer_h_included_ cmtk-3.3.1/libs/Pipeline/cmtkSource.h000066400000000000000000000043251276303427400174560ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // Copyright 2004-2009 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 2398 $ // // $LastChangedDate: 2010-10-05 14:54:37 -0700 (Tue, 05 Oct 2010) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkSource_h_included_ #define __cmtkSource_h_included_ #include #include namespace cmtk { /** \addtogroup Pipeline */ //@{ /** General data source object. * This class provides common fields and acces functions for data source * objects. The output object type is defined by the template parameter "O". */ template class Source : /// This is a pipeline object. public PipelineObject { protected: /// Default constructor. Source() { Output = NULL; } /** Destructor. * Unregister from the output object. */ virtual ~Source() { if ( Output ) Output->Unregister( this ); } public: /** Get output object. * If the output object does not yet exist, a new objet is created. The * current Source object is then registered as the new output object's * primary owner. */ virtual O *GetOutput() { if ( Output == NULL ) { Output = O::New(); Output->Register( this ); } return Output; } protected: /// Pointer to the actual output object. O *Output; }; //@} } // namespace cmtk #endif // #ifndef __cmtkSource_h_included_ cmtk-3.3.1/libs/Pipeline/doxygen.h000066400000000000000000000026621276303427400170160ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2011 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 2734 $ // // $LastChangedDate: 2011-01-14 10:23:45 -0800 (Fri, 14 Jan 2011) $ // // $LastChangedBy: torstenrohlfing $ // */ /** \defgroup Pipeline cmtkPipeline Library * This library provides pipeline-based processing similar to the paradigms * used by VTK and, to lesser extent, ITK. *\deprecated This library is deprecated and should not be relied on for * future development. It is currently only maintained to the extent * necessary to keep the "triplanar" viewer program functional. */ cmtk-3.3.1/libs/Qt/000077500000000000000000000000001276303427400140015ustar00rootroot00000000000000cmtk-3.3.1/libs/Qt/CMakeLists.txt000066400000000000000000000050641276303427400165460ustar00rootroot00000000000000## ## Copyright 1997-2009 Torsten Rohlfing ## ## Copyright 2004-2011 SRI International ## ## This file is part of the Computational Morphometry Toolkit. ## ## http://www.nitrc.org/projects/cmtk/ ## ## The Computational Morphometry Toolkit is free software: you can ## redistribute it and/or modify it under the terms of the GNU General Public ## License as published by the Free Software Foundation, either version 3 of ## the License, or (at your option) any later version. ## ## The Computational Morphometry Toolkit is distributed in the hope that it ## will be useful, but WITHOUT ANY WARRANTY; without even the implied ## warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License along ## with the Computational Morphometry Toolkit. If not, see ## . ## ## $Revision: 3161 $ ## ## $LastChangedDate: 2011-04-18 14:37:45 -0700 (Mon, 18 Apr 2011) $ ## ## $LastChangedBy: torstenrohlfing $ ## # Sources of non-templated classes. SET(cmtkQt_SRCS cmtkQtIcons.cxx cmtkQtImageOperators.cxx cmtkQtProgress.cxx cmtkQtRenderImageRGB.cxx cmtkQtScrollRenderView.cxx cmtkQtSliderEntry.cxx cmtkQtTriplanarViewerBatchMode.cxx cmtkQtTriplanarViewer.cxx cmtkQtTriplanarWindow.cxx cmtkQtTriplanarWindowViewActions.cxx cmtkQtWindowLevelControls.cxx cmtkQtWindowLevelDialog.cxx ) SET(cmtkQt_MOC_SRCS cmtkQGraphicsPixmapItemEvents.h cmtkQtImageOperators.h cmtkQtRenderImageRGB.h cmtkQtScrollRenderView.h cmtkQtSliderEntry.h cmtkQtTriplanarViewer.h cmtkQtTriplanarWindow.h cmtkQtWindowLevelControls.h cmtkQtWindowLevelDialog.h ) #------------------------------- # Generate Moc Files #------------------------------- IF(QT_WRAP_CPP) QT_WRAP_CPP(cmtkQt cmtkQt_SRCS ${cmtkQt_MOC_SRCS} ) ENDIF(QT_WRAP_CPP) ADD_LIBRARY(cmtkQt ${cmtkQt_SRCS}) TARGET_LINK_LIBRARIES(cmtkQt cmtkPipeline cmtkIO cmtkBase cmtkSystem cmtkNumerics ${QT_LIBRARIES}) IF(CMTK_LIBRARY_PROPERTIES) SET_TARGET_PROPERTIES(cmtkQt PROPERTIES ${CMTK_LIBRARY_PROPERTIES}) ENDIF(CMTK_LIBRARY_PROPERTIES) INSTALL(TARGETS cmtkQt RUNTIME DESTINATION ${CMTK_INSTALL_BIN_DIR} COMPONENT runtime LIBRARY DESTINATION ${CMTK_INSTALL_LIB_DIR} COMPONENT runtime ARCHIVE DESTINATION ${CMTK_INSTALL_LIB_DIR} COMPONENT libraries) FILE(GLOB files_h "${CMAKE_CURRENT_SOURCE_DIR}/*.h") FILE(GLOB files_txx "${CMAKE_CURRENT_SOURCE_DIR}/*.txx") INSTALL(FILES ${files_h} ${files_txx} DESTINATION ${CMTK_INSTALL_INCLUDE_DIR}/Qt COMPONENT headers) cmtk-3.3.1/libs/Qt/cmtkQGraphicsPixmapItemEvents.h000066400000000000000000000043451276303427400221030ustar00rootroot00000000000000/* // // Copyright 2010 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 2263 $ // // $LastChangedDate: 2010-08-19 14:20:04 -0700 (Thu, 19 Aug 2010) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkQGraphicsPixmapItemEvents_h_included_ #define __cmtkQGraphicsPixmapItemEvents_h_included_ #include #include #include #include namespace cmtk { /// Class that derives from Qt's pixmap graphics item and signals events it receives. class QGraphicsPixmapItemEvents : public QObject, public QGraphicsPixmapItem { Q_OBJECT public: /// This class. typedef QGraphicsPixmapItemEvents Self; /// Parent class. typedef QGraphicsPixmapItem Superclass; /// Default constructor. QGraphicsPixmapItemEvents( QGraphicsItem* parent = 0 ) : QGraphicsPixmapItem ( parent ) {} /// Constructor. QGraphicsPixmapItemEvents( const QPixmap& pixmap, QGraphicsItem* parent = 0 ) : QGraphicsPixmapItem ( pixmap, parent ) {} signals: /// Signal that is sent when "mouse press" event is received. void mousePressed( QGraphicsSceneMouseEvent* event ); protected: /// Catch mouse press event. virtual void mousePressEvent( QGraphicsSceneMouseEvent* event ) { emit( mousePressed( event ) ); Superclass::mousePressEvent( event ); } }; } // namespace cmtk #endif // #ifndef __cmtkQGraphicsPixmapItemEvents_h_included_ cmtk-3.3.1/libs/Qt/cmtkQtIcons.cxx000066400000000000000000000023641276303427400167710ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // Copyright 2004-2009 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 2022 $ // // $LastChangedDate: 2010-07-21 15:26:03 -0700 (Wed, 21 Jul 2010) $ // // $LastChangedBy: torstenrohlfing $ // */ #include "cmtkQtIcons.h" #include "xpm/icon_main_window.xpm" //Added by qt3to4: #include QPixmap cmtk::QtIcons::WindowIcon() { return QPixmap( icon_main_window_xpm ); } cmtk-3.3.1/libs/Qt/cmtkQtIcons.h000066400000000000000000000026711276303427400164170ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // Copyright 2004-2009 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 11 $ // // $LastChangedDate: 2009-05-30 11:30:08 -0700 (Sat, 30 May 2009) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkQtIcons_h_included_ #define __cmtkQtIcons_h_included_ #include #include namespace cmtk { /** \addtogroup Qt */ //@{ /// Collection of general-purpose icons for Qt applications. class QtIcons { public: /// Generic IGL window icon. static QPixmap WindowIcon(); }; //@} } // namespace cmtk #endif // #ifndef __cmtkQtIcons_h_included_ cmtk-3.3.1/libs/Qt/cmtkQtImageOperators.cxx000066400000000000000000000112141276303427400206310ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2010 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 2398 $ // // $LastChangedDate: 2010-10-05 14:54:37 -0700 (Tue, 05 Oct 2010) $ // // $LastChangedBy: torstenrohlfing $ // */ #include "cmtkQtImageOperators.h" #include #include #include #include #include #include namespace cmtk { /** \addtogroup Qt */ //@{ QMenu* QtImageOperators::CreatePopupMenu() { QMenu* operatorsMenu = new QMenu; operatorsMenu->setTitle( "&Operators" ); operatorsMenu->addAction( "&Median Filter...", this, SLOT( slotOperatorMedian() ) ); operatorsMenu->addAction( "&Histogram Equalization...", this, SLOT( slotOperatorHistEq() ) ); operatorsMenu->addAction( "&Sobel Edge Filter", this, SLOT( slotOperatorSobel() ) ); operatorsMenu->addSeparator(); QMenu* algOperatorsMenu = operatorsMenu->addMenu( "&Algebraic" ); algOperatorsMenu->addAction( "&abs()", this, SLOT( slotOperatorAbs() ) ); algOperatorsMenu->addAction( "&log()", this, SLOT( slotOperatorLog() ) ); algOperatorsMenu->addAction( "&exp()", this, SLOT( slotOperatorExp() ) ); return operatorsMenu; } void QtImageOperators::slotOperatorMedian() { if ( this->StudyDataValid() ) { bool ok; int radius = QInputDialog::getInt( this->Parent, "Median Filter", "Neighborhood radius:", 1, 1, 5, 1, &ok ); if ( ok ) { // user entered something and pressed OK if ( this->ProgressInstance ) this->ProgressInstance->SetProgressWidgetMode( QtProgress::PROGRESS_DIALOG ); (*(this->CurrentStudy))->GetVolume()->SetData( DataGridFilter( (*(this->CurrentStudy))->GetVolume() ).GetDataMedianFiltered( radius ) ); emit dataChanged( *(this->CurrentStudy) ); } else { // user pressed Cancel } } } void QtImageOperators::slotOperatorSobel() { if ( this->StudyDataValid() ) { if ( this->ProgressInstance ) this->ProgressInstance->SetProgressWidgetMode( QtProgress::PROGRESS_BAR ); (*(this->CurrentStudy))->GetVolume()->SetData( DataGridFilter( (*(this->CurrentStudy))->GetVolume() ).GetDataSobelFiltered() ); emit dataChanged( *(this->CurrentStudy) ); } } void QtImageOperators::slotOperatorHistEq() { if ( this->StudyDataValid() ) { if ( this->ProgressInstance ) this->ProgressInstance->SetProgressWidgetMode( QtProgress::PROGRESS_BAR ); bool ok; int bins = QInputDialog::getInt( this->Parent, "Histogram Equalization", "Number of Histogram Bins:", 256, 2, 256, 1, &ok ); if ( ok ) { // user entered something and pressed OK if ( this->ProgressInstance ) this->ProgressInstance->SetProgressWidgetMode( QtProgress::PROGRESS_DIALOG ); (*(this->CurrentStudy))->GetVolume()->GetData()->ApplyFunctionObject( TypedArrayFunctionHistogramEqualization( (*(*(this->CurrentStudy))->GetVolume()->GetData()), bins ) ); emit dataChanged( *(this->CurrentStudy) ); } else { // user pressed Cancel } } } void QtImageOperators::slotOperatorAbs() { if ( this->StudyDataValid() ) { (*(this->CurrentStudy))->GetVolume()->GetData()->ApplyFunctionDouble( cmtk::Wrappers::Abs ); emit dataChanged( *(this->CurrentStudy) ); } } void QtImageOperators::slotOperatorLog() { if ( this->StudyDataValid() ) { (*(this->CurrentStudy))->GetVolume()->GetData()->ApplyFunctionDouble( cmtk::Wrappers::Log ); emit dataChanged( *(this->CurrentStudy) ); } } void QtImageOperators::slotOperatorExp() { if ( this->StudyDataValid() ) { (*(this->CurrentStudy))->GetVolume()->GetData()->ApplyFunctionDouble( cmtk::Wrappers::Exp ); emit dataChanged( *(this->CurrentStudy) ); } } } // namespace cmtk cmtk-3.3.1/libs/Qt/cmtkQtImageOperators.h000066400000000000000000000054251276303427400202650ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2010 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 2398 $ // // $LastChangedDate: 2010-10-05 14:54:37 -0700 (Tue, 05 Oct 2010) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkQtImageOperators_h_included_ #define __cmtkQtImageOperators_h_included_ #include #include #include #include #include #include namespace cmtk { /** \addtogroup Qt */ //@{ /// A collection of 3D image operators with Qt menu. class QtImageOperators : /// Inherit from QObject for event handling etc. public QObject { Q_OBJECT public slots: /// Slot to open selected type of fusion window. void slotOperatorMedian(); void slotOperatorSobel(); void slotOperatorHistEq(); void slotOperatorAbs(); void slotOperatorLog(); void slotOperatorExp(); signals: /// This signal is sent when the image data has been changed. void dataChanged( Study::SmartPtr& ); public: /// Constructor. QtImageOperators ( Study::SmartPtr* currentStudy, QWidget *const parent = NULL, QtProgress *const progressInstance = NULL ) : Parent( parent ), CurrentStudy( currentStudy ), ProgressInstance( progressInstance ) {}; /// Create and return popup menu that makes operators available. QMenu* CreatePopupMenu(); private: /** The parent widget. * This is for modal dialogs that may be opened for some operations. */ QWidget* Parent; /// Pointer to an object with the current study pointer. Study::SmartPtr* CurrentStudy; /// Optional instance of a Qt progress indicator. QtProgress* ProgressInstance; /// Check whether study and volume data are all valid. bool StudyDataValid() const { return (*(this->CurrentStudy)) && (*(this->CurrentStudy))->GetVolume() && (*(this->CurrentStudy))->GetVolume()->GetData(); } }; //@} } // namespace cmtk #endif // #ifndef __cmtkQtImageOperators_h_included_ cmtk-3.3.1/libs/Qt/cmtkQtProgress.cxx000066400000000000000000000054161276303427400175230ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // Copyright 2004-2009 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 2398 $ // // $LastChangedDate: 2010-10-05 14:54:37 -0700 (Tue, 05 Oct 2010) $ // // $LastChangedBy: torstenrohlfing $ // */ #include #include #include namespace cmtk { /** \addtogroup Qt */ //@{ QtProgress::QtProgress ( QWidget *const parentWindow ) { ParentWindow = parentWindow; ProgressBar = NULL; ProgressDialog = NULL; this->m_ProgressWidgetMode = PROGRESS_DIALOG; } void QtProgress ::BeginVirtual( const double start, const double end, const double increment, const std::string& taskName ) { this->Superclass::BeginVirtual( start, end, increment, taskName ); if ( this->IsTopLevel() ) { if ( ProgressBar ) { ProgressBar->setRange( 0, 100 ); ProgressBar->show(); } if ( ! ProgressDialog ) ProgressDialog = new QProgressDialog( taskName.c_str(), "Cancel", 0, 100, ParentWindow, Qt::Dialog ); ProgressDialog->setWindowModality(Qt::WindowModal); ProgressDialog->setModal( true ); ProgressDialog->setMinimumDuration( 100 ); ProgressDialog->show(); ProgressDialog->setRange( 0, 100 ); qApp->processEvents(); } Progress::SetProgressInstance( this ); } Progress::ResultEnum QtProgress::UpdateProgress() { const int percent = static_cast( 100 * this->GetFractionComplete() ); if ( ProgressBar ) ProgressBar->setValue( percent ); if ( ProgressDialog ) ProgressDialog->setValue( percent ); qApp->processEvents(); Progress::ResultEnum result = Progress::OK; if ( ProgressDialog ) if ( ProgressDialog->wasCanceled() ) result = Progress::INTERRUPT; return result; } void QtProgress::DoneVirtual() { if ( this->IsTopLevel() ) { if ( ProgressBar ) ProgressBar->reset(); if ( ProgressDialog ) ProgressDialog->hide(); } } } // namespace cmtk cmtk-3.3.1/libs/Qt/cmtkQtProgress.h000066400000000000000000000055721276303427400171530ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // Copyright 2004-2009 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 2398 $ // // $LastChangedDate: 2010-10-05 14:54:37 -0700 (Tue, 05 Oct 2010) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkQtProgress_h_included_ #define __cmtkQtProgress_h_included_ #include #include #include #include #include #include namespace cmtk { /** \addtogroup Qt */ //@{ /** Class for interface of progress meter to Qt. */ class QtProgress : /// Inherit from internal progress class. public Progress { public: /// This class. typedef QtProgress Self; /// Parent class. typedef Progress Superclass; /// Constructor. QtProgress( QWidget *const parentWindow ); /// Set the embedded progress bar. void SetProgressBar( QProgressBar *const progressBar ) { ProgressBar = progressBar; } /// This member function initialises the Qt progress indicator. virtual void BeginVirtual( const double start, const double end, const double increment, const std::string& taskName = std::string("") ); /// This member function sets the Qt progress indicator. virtual Progress::ResultEnum UpdateProgress(); /// This member function deletes the Qt progress indicator. virtual void DoneVirtual(); /// Progress indicator mode. typedef enum { /// Use a modal progress dialog. PROGRESS_DIALOG, /// Use a progress bar within another window. PROGRESS_BAR } ProgressWidgetMode; /// Set progress indicator mode. void SetProgressWidgetMode( ProgressWidgetMode progressWidgetMode ) { this->m_ProgressWidgetMode = progressWidgetMode; } private: /// The progress window parent widget. QWidget* ParentWindow; /// The progress bar widget. QProgressBar* ProgressBar; /// Progress dialog widget. QProgressDialog* ProgressDialog; /// Progress widget mode. ProgressWidgetMode m_ProgressWidgetMode; }; //@} } // namespace cmtk #endif // #ifndef __cmtkQtProgress_h_included_ cmtk-3.3.1/libs/Qt/cmtkQtRenderImageRGB.cxx000066400000000000000000000123601276303427400204300ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2012 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4422 $ // // $LastChangedDate: 2012-06-05 15:06:15 -0700 (Tue, 05 Jun 2012) $ // // $LastChangedBy: torstenrohlfing $ // */ #include "cmtkQtRenderImageRGB.h" #include #include #include #include #include namespace cmtk { /** \addtogroup Qt */ //@{ QtRenderImageRGB::QtRenderImageRGB ( QWidget *const parent, Qt::WFlags f ) : QWidget( parent, f ), ZoomFactorPercent( 100 ), FlipX( false ), FlipY( false ), ImageAspectMode( AspectNone ), CrosshairMode( false ), Image() { CrosshairPosition[0] = CrosshairPosition[1] = 0; this->setBaseSize( 512, 512 ); // this->setMouseTracking( true ); this->setCursor( QCursor( Qt::CrossCursor ) ); } QtRenderImageRGB::~QtRenderImageRGB() { } void QtRenderImageRGB::paintEvent( QPaintEvent *const ) { // update modified time to force rendering this->UpdateModifiedTime(); this->Update(); this->RenderTo( this ); this->UpdateExecuteTime(); } void QtRenderImageRGB::Execute() { if ( Input == NULL ) return; } void QtRenderImageRGB::RenderTo( QPaintDevice *pd ) { if ( ! Input ) return; if ( Input->GetAlphaChannel() != IMAGE_RGBA ) { return; } unsigned char* imageDataRGB = (unsigned char*) Input->GetDataPtr(); if ( imageDataRGB == NULL ) return; unsigned int width, height; Input->GetDims( width, height ); this->setFixedSize( ZoomFactorPercent * width / 100, ZoomFactorPercent * height / 100 ); Image = QImage( width, height, QImage::Format_RGB32 ); memcpy( Image.bits(), imageDataRGB, width * height * 4 ); if ( FlipX || FlipY ) Image = Image.mirrored( FlipX, FlipY ); if ( ZoomFactorPercent != 100 ) { Image = Image.scaled( width * ZoomFactorPercent / 100, height * ZoomFactorPercent / 100 ); } QPainter painter( pd ); painter.drawImage( 0, 0, Image ); if ( CrosshairMode ) this->DrawCrosshair( painter, width, height ); } void QtRenderImageRGB::DrawCrosshair ( QPainter &painter, const unsigned int width, const unsigned int height ) const { int crosshairX = ( FlipX ) ? width-1-CrosshairPosition[0] : CrosshairPosition[0]; crosshairX = static_cast( (0.5+crosshairX) * ZoomFactorPercent / 100 ); int crosshairY = ( FlipY ) ? height-1-CrosshairPosition[1] : CrosshairPosition[1]; crosshairY = static_cast( (0.5+crosshairY) * ZoomFactorPercent / 100 ); const int realWidth = static_cast( 1.0 * width * ZoomFactorPercent / 100 ); const int realHeight = static_cast( 1.0 * height * ZoomFactorPercent / 100 ); painter.setPen( CrosshairColors[0] ); painter.drawLine( 0, crosshairY, realWidth-1, crosshairY ); painter.setPen( CrosshairColors[1] ); painter.drawLine( crosshairX, 0, crosshairX, realHeight-1 ); } QPixmap QtRenderImageRGB::GetPixmap() { if ( Input == NULL ) return QPixmap(); QPixmap capture( ZoomFactorPercent * Input->GetDims( AXIS_X ) / 100, ZoomFactorPercent * Input->GetDims( AXIS_Y ) / 100 ); this->RenderTo( &capture ); return capture; } /// React to mouse clicks (generate a signal). void QtRenderImageRGB ::mousePressEvent( QMouseEvent *e ) { unsigned int scaledX = (e->x()-static_cast(ZoomFactorPercent/200)) * 100 / ZoomFactorPercent; if ( Input && this->FlipX ) { scaledX = Input->GetDims( AXIS_X ) - 1 - scaledX; } unsigned int scaledY = (e->y()-static_cast(ZoomFactorPercent/200)) * 100 / ZoomFactorPercent; if ( Input && this->FlipY ) { scaledY = Input->GetDims( AXIS_Y ) - 1 - scaledY; } emit signalMousePressed( e->button(), scaledX, scaledY ); Vector3D v; Input->GetPixelLocation( v, scaledX, scaledY ); emit signalMouse3D( e->button(), v ); e->accept(); } void QtRenderImageRGB ::mouseMoveEvent( QMouseEvent *e ) { unsigned int scaledX = (e->x()-static_cast(ZoomFactorPercent/200)) * 100 / ZoomFactorPercent; if ( Input && this->FlipX ) { scaledX = Input->GetDims( AXIS_X ) - 1 - scaledX; } unsigned int scaledY = (e->y()-static_cast(ZoomFactorPercent/200)) * 100 / ZoomFactorPercent; if ( Input && this->FlipY ) { scaledY = Input->GetDims( AXIS_Y ) - 1 - scaledY; } emit signalMousePressed( e->button(), scaledX, scaledY ); Vector3D v; Input->GetPixelLocation( v, scaledX, scaledY ); emit signalMouse3D( e->button(), v ); e->accept(); } } // namespace cmtk cmtk-3.3.1/libs/Qt/cmtkQtRenderImageRGB.h000066400000000000000000000073461276303427400200650ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2010 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 2356 $ // // $LastChangedDate: 2010-09-24 11:54:25 -0700 (Fri, 24 Sep 2010) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkQtRenderImageRGB_h_included_ #define __cmtkQtRenderImageRGB_h_included_ #include #include #include #include #include #include #include #include #include #include #include namespace cmtk { /** \addtogroup Qt */ //@{ /** Class to render RGB images in Qt. */ class QtRenderImageRGB : /// Inherit from QImage for Qt display features. public QWidget, /// Inherit from Renderer pipeline object. public Renderer { Q_OBJECT /// Zoom factor. igsClassParameter(unsigned int,ZoomFactorPercent); /// Flip image in x-direction. igsClassParameter(bool,FlipX); /// Flip image in y-direction. igsClassParameter(bool,FlipY); /// Aspect mode constants. typedef enum { /// No aspect control; render as is. AspectNone, /// Use x-axis as aspect master. AspectX, /// Use y-axis as aspect master. AspectY, /// Automatically select aspect axis. AspectAuto } AspectMode; /// Flip image in y-direction. igsClassParameter(AspectMode,ImageAspectMode); /// Crosshair mode. igsClassParameter(bool,CrosshairMode); /// Crosshair position. igsClassParameter2Array(unsigned int,CrosshairPosition); /// Crosshair colors. igsClassParameter2Array(QColor,CrosshairColors); public: /// Constructor. QtRenderImageRGB( QWidget *const parent = 0, Qt::WFlags f = 0 ); /// Destructor. virtual ~QtRenderImageRGB(); /// The actual renderer function. virtual void Execute(); /// Return currently displayed image. QPixmap GetPixmap(); /// Render to given paint device. void RenderTo( QPaintDevice *pd ); /// Render this image. virtual void Render() { this->Renderer::Render(); this->update(); } signals: /// This signal is emitted when a mouse button is pressed on the widget. void signalMousePressed( Qt::MouseButton button, int x, int y ); /// This signal is emitted when a mouse button is pressed on the widget. void signalMouse3D( Qt::MouseButton button, const Vector3D& v ); protected: /// Repaint widget. virtual void paintEvent( QPaintEvent *const ); /// React to mouse clicks (generate a signal). virtual void mousePressEvent( QMouseEvent *e ); /// React to mouse dragging (generate a signal). virtual void mouseMoveEvent( QMouseEvent *e ); private: /// Intermediate QImage object for painting. QImage Image; /// Draw crosshair. void DrawCrosshair( QPainter &painter, const unsigned int width, const unsigned int height ) const; }; //@} } // namespace cmtk #endif // #ifndef __cmtkQtRenderImageRGB_h_included_ cmtk-3.3.1/libs/Qt/cmtkQtScrollRenderView.cxx000066400000000000000000000106431276303427400211460ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2010 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 2363 $ // // $LastChangedDate: 2010-09-27 11:11:35 -0700 (Mon, 27 Sep 2010) $ // // $LastChangedBy: torsten_at_home $ // */ #include "cmtkQtScrollRenderView.h" #include #include #include #include namespace cmtk { /** \addtogroup Qt */ //@{ QtScrollRenderView::QtScrollRenderView ( QWidget *parentWidget, const QString& title ) : QGroupBox( parentWidget ), RenderImage( NULL ) { if ( ! parentWidget ) qFatal( "No parent widget in QtScrollRenderView constructor." ); if ( title != QString::null ) { this->setAlignment ( Qt::AlignLeft ); this->setTitle( title ); } ScrollView = new QScrollArea( this ); //, "ScrollView", Qt::WResizeNoErase|Qt::WStaticContents ); RenderImage = new QtRenderImageRGB( this ); ScrollView->setWidget( RenderImage ); ScrollView->setFrameStyle( QFrame::NoFrame ); // export viewer's mouse signal QObject::connect( RenderImage,SIGNAL(signalMousePressed( Qt::MouseButton, int, int )), SIGNAL(signalMousePressed( Qt::MouseButton, int, int )) ); QObject::connect( RenderImage, SIGNAL(signalMouse3D( Qt::MouseButton, const Vector3D& )), SIGNAL(signalMouse3D( Qt::MouseButton, const Vector3D& )) ); // lock minimum size so we can display 256x256 with not scrollbars. RenderImage->setMinimumSize( 256, 256 ); this->m_SliderGroupBox = new QGroupBox( this ); this->m_SliderGroupBox->hide(); QGridLayout* sliderBoxLayout = new QGridLayout( this->m_SliderGroupBox ); sliderBoxLayout->setContentsMargins( 0,0,0,0 ); this->ImageIndexSlider = new QSlider( this->m_SliderGroupBox ); this->ImageIndexSlider->setOrientation( Qt::Horizontal ); this->ImageIndexSlider->setDisabled( true ); sliderBoxLayout->addWidget( this->ImageIndexSlider, 0, 1 ); this->m_LabelL = new QLabel( this->m_SliderGroupBox ); sliderBoxLayout->addWidget( this->m_LabelL, 0, 0 ); this->m_LabelR = new QLabel( this->m_SliderGroupBox ); sliderBoxLayout->addWidget( this->m_LabelR, 0, 2 ); QVBoxLayout *vbox = new QVBoxLayout; vbox->setContentsMargins( 0,0,0,0 ); vbox->addWidget( this->ScrollView ); vbox->addWidget( this->m_SliderGroupBox ); vbox->setSpacing( 0 ); this->setLayout(vbox); // export slider's signal QObject::connect( ImageIndexSlider, SIGNAL( valueChanged( int ) ), SIGNAL( indexChanged( int ) ) ); } QtScrollRenderView::~QtScrollRenderView() { } void QtScrollRenderView::slotConnectImage( ImageRGB *const image ) { if ( RenderImage ) { RenderImage->SetInput( image ); } else { qWarning( "RenderImage is NULL in QtScrollRenderView::ConnectRenderView." ); } } void QtScrollRenderView::slotRender() { if ( RenderImage ) { RenderImage->Render(); } else { qWarning( "RenderImage is NULL in QtScrollRenderView::Render." ); } } void QtScrollRenderView::slotSetNumberOfSlices( unsigned int nSlices ) { if ( nSlices ) { ImageIndexSlider->setEnabled( true ); ImageIndexSlider->setMinimum( 0 ); ImageIndexSlider->setMaximum( nSlices - 1 ); if ( ImageIndexSlider->value() < 0 || ImageIndexSlider->value() >= (int)nSlices ) { ImageIndexSlider->setValue( nSlices / 2 ); } this->ImageIndexSlider->setDisabled( false ); } else { ImageIndexSlider->setDisabled( true ); } } void QtScrollRenderView::slotSetSlice( unsigned int slice ) { if ( slice <= static_cast(ImageIndexSlider->maximum()) ) { ImageIndexSlider->setValue( slice ); } } } // namespace cmtk cmtk-3.3.1/libs/Qt/cmtkQtScrollRenderView.h000066400000000000000000000100351276303427400205660ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // Copyright 2004-2009 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 2398 $ // // $LastChangedDate: 2010-10-05 14:54:37 -0700 (Tue, 05 Oct 2010) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkQtScrollRenderView_h_included_ #define __cmtkQtScrollRenderView_h_included_ #include #include #include #include #include #include #include #include namespace cmtk { /** \addtogroup Qt */ //@{ /// Widget that renders an RGB image in a scrolled viewport. class QtScrollRenderView : /// This is essentially a vertical layout of scroll view and index slider. public QGroupBox { Q_OBJECT public: /** Constructor. * This object will be child of the given parent widget. */ QtScrollRenderView( QWidget *parentWidget, const QString &title = QString::null ); /// Destructor. virtual ~QtScrollRenderView(); /// Show the optional image index slider at the bottom of the widget. void ShowSlider() { this->m_SliderGroupBox->show(); } /// Hide the optional image index slider at the bottom of the widget. void HideSlider() { this->m_SliderGroupBox->hide(); } /// Set left slider label. void SetSliderLabelL( const QString& left ) { if ( this->m_LabelL ) { if ( left.isNull() ) { this->m_LabelL->hide(); } else { this->m_LabelL->setText( left ); this->m_LabelL->show(); } } } /// Set right slider label. void SetSliderLabelR( const QString& right ) { if ( this->m_LabelR ) { if ( right.isNull() ) { this->m_LabelR->hide(); } else { this->m_LabelR->setText( right ); this->m_LabelR->show(); } } } /// Get render image object. QtRenderImageRGB* GetRenderImage() { return RenderImage; } /// Get constant render image object. const QtRenderImageRGB* GetRenderImage() const { return RenderImage; } signals: /// This signals a change in the index slider's value. void indexChanged( int ); /// This signal is emitted when a mouse button is pressed on the viewer. void signalMousePressed( Qt::MouseButton button, int x, int y ); /// This signal is emitted when a mouse button is pressed on the viewer. void signalMouse3D( Qt::MouseButton button, const Vector3D& v ); public slots: /// Connect render view to an RGB image object. void slotConnectImage( ImageRGB *const image ); /// Update rendering. void slotRender(); /// Set number of slices for the image slider. void slotSetNumberOfSlices( unsigned int nSlices ); /// Set to given slice. void slotSetSlice( unsigned int slice ); public: /// Get current slice index. unsigned int GetSlice() const { return ImageIndexSlider->value(); } private: /// The scrolled viewport. QScrollArea* ScrollView; /// The actual renderer, if we're rendering to an image object. QtRenderImageRGB* RenderImage; /// Image index slider. QSlider* ImageIndexSlider; /// Left slider label. QLabel* m_LabelL; /// Right slider label. QLabel* m_LabelR; /// Slider layout. QGroupBox* m_SliderGroupBox; }; //@} } // namespace cmtk #endif // #ifndef __cmtkQtScrollRenderView_h_included_ cmtk-3.3.1/libs/Qt/cmtkQtSliderEntry.cxx000066400000000000000000000130321276303427400201540ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2010 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 2398 $ // // $LastChangedDate: 2010-10-05 14:54:37 -0700 (Tue, 05 Oct 2010) $ // // $LastChangedBy: torstenrohlfing $ // */ #include "cmtkQtSliderEntry.h" #include #include #include #include #include #include namespace cmtk { /** \addtogroup Qt */ //@{ QtSliderEntry::QtSliderEntry( QWidget* parent ) : QWidget( parent ) { QFont font = this->font(); font.setPointSize( 2 * font.pointSize() / 4 ); Layout = new QGridLayout( this ); Layout->setColumnStretch( 0, 1 ); Layout->setColumnStretch( 1, 1 ); Layout->setColumnStretch( 2, 0 ); Layout->setColumnStretch( 3, 0 ); Slider = new QSlider( Qt::Horizontal, this ); QObject::connect( Slider, SIGNAL( valueChanged( int ) ), this, SLOT( slotSliderValueChanged( int ) ) ); // Layout->addWidget( Slider, 1, 1, 0, 1 ); Layout->addWidget( Slider, 1, 0, 1, 2 ); Edit = new QLineEdit( this ); Edit->setFixedWidth( 100 ); Validator = new QDoubleValidator( Edit ); Edit->setValidator( Validator ); QObject::connect( Edit, SIGNAL( returnPressed() ), this, SLOT( slotEditReturnPressed() ) ); Layout->addWidget( Edit, 1, 3 ); TitleLabel = new QLabel( this ); TitleLabel->hide(); MinLabel = new QLabel( this ); MinLabel->setFont( font ); MinLabel->hide(); MaxLabel = new QLabel( this ); MaxLabel->setFont( font ); MaxLabel->setAlignment( Qt::AlignRight ); MaxLabel->hide(); Precision = 0; PrecisionFactor = 1; } double QtSliderEntry::GetValue() const { return Edit->text().toDouble(); } double QtSliderEntry::GetMinValue() const { return 1.0 * Slider->minimum() / PrecisionFactor; } double QtSliderEntry::GetMaxValue() const { return 1.0 * Slider->minimum() / PrecisionFactor; } void QtSliderEntry::slotSetRange( double rangeFrom, double rangeTo ) { const double rangeWidth = rangeTo - rangeFrom; if ( rangeWidth > 0 ) { const int autoPrecision = static_cast( (log( 0.001 ) + log( rangeWidth )) / log( 0.1 ) ); this->slotSetPrecision( std::max( this->Precision, autoPrecision ) ); } Slider->setRange( static_cast( rangeFrom * PrecisionFactor ), static_cast( rangeTo * PrecisionFactor ) ); Validator->setRange( rangeFrom - 10 * rangeWidth, rangeTo + 10 * rangeWidth, Precision ); MinLabel->setNum( rangeFrom ); MaxLabel->setNum( rangeTo ); } void QtSliderEntry::slotSetPrecision( int precision ) { Precision = precision; PrecisionFactor = static_cast( pow( 10.0, precision ) ); Validator->setDecimals( Precision ); } void QtSliderEntry::slotSetTitle( const QString& title ) { TitleLabel->setText( title ); // Layout->addWidget( TitleLabel, 0, 0, 0, 2 ); Layout->addWidget( TitleLabel, 0, 0, 1, 3 ); TitleLabel->show(); } void QtSliderEntry::slotSetMinMaxLabels( const QString& minLabel, const QString& maxLabel ) { if ( minLabel != QString::null ) { MinLabel->setText( minLabel ); } else { MinLabel->setNum( Validator->bottom() ); } Layout->addWidget( MinLabel, 2, 0 ); MinLabel->show(); if ( maxLabel != QString::null ) { MaxLabel->setText( maxLabel ); } else { MaxLabel->setNum( Validator->top() ); } Layout->addWidget( MaxLabel, 2, 1 ); MaxLabel->show(); } void QtSliderEntry::slotEditReturnPressed() { double value = Edit->text().toDouble(); int valueSlider = static_cast( value * PrecisionFactor ); if ( valueSlider < Slider->minimum() ) this->slotSetRange( value, Slider->maximum() / PrecisionFactor ); if ( valueSlider > Slider->maximum() ) this->slotSetRange( Slider->minimum() / PrecisionFactor, value ); Slider->setValue( valueSlider ); emit valueChanged( value ); } void QtSliderEntry::slotSliderValueChanged( int value ) { double realValue = 1.0 * value / PrecisionFactor; QString valueString; Edit->setText( valueString.setNum( realValue, 'f', Precision ) ); emit valueChanged( realValue ); } void QtSliderEntry::slotCenter() { Slider->setValue( (Slider->minimum() + Slider->maximum()) / 2 ); // valueChanged signal should be emitted indirectly by slotSliderValueChanged } void QtSliderEntry::slotSetValue( const double value ) { QString valueString; Edit->setText( valueString.setNum( value, 'f', Precision ) ); int valueSlider = static_cast( value * PrecisionFactor ); if ( valueSlider < Slider->minimum() ) this->slotSetRange( value, Slider->maximum() / PrecisionFactor ); if ( valueSlider > Slider->maximum() ) this->slotSetRange( Slider->minimum() / PrecisionFactor, value ); Slider->setValue( valueSlider ); emit valueChanged( value ); } } // namespace cmtk cmtk-3.3.1/libs/Qt/cmtkQtSliderEntry.h000066400000000000000000000062021276303427400176020ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // Copyright 2004-2009 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 970 $ // // $LastChangedDate: 2009-12-03 17:34:37 -0800 (Thu, 03 Dec 2009) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkQtSliderEntry_h_included_ #define __cmtkQtSliderEntry_h_included_ #include #include #include #include #include #include #include #include namespace cmtk { /** \addtogroup Qt */ //@{ /** Widget that combines a slider with a numerical entry field and labels. */ class QtSliderEntry : /// We use a vertical group box as the base class. public QWidget { Q_OBJECT // we use slots and signals. public: /// Constructor. QtSliderEntry( QWidget* parent ); /// Get value. double GetValue() const; /// Get minimum value. double GetMinValue() const; /// Get maximum value. double GetMaxValue() const; signals: /// Emitted when value changes. void valueChanged( double value ); public slots: /// Set title label. void slotSetTitle( const QString& title ); /// Set min/max labels. void slotSetMinMaxLabels( const QString& minLabel, const QString& maxLabel ); /// Set value range. void slotSetRange( double rangeFrom, double rangeTo ); /// Set number of digits. void slotSetPrecision( int precision ); /// Set value. void slotSetValue( const double value ); /// Set to center position. void slotCenter(); private slots: /// Called when "Return" is pressed in the line edit field. void slotEditReturnPressed(); /// Called when line edit value changes. void slotSliderValueChanged( int value ); private: /// Number of decimal digits. uint Precision; /// Factor to convert between integer and true float representation. uint PrecisionFactor; /// Layout for children. QGridLayout* Layout; /// The slider object. QSlider* Slider; /// The entry field. QLineEdit* Edit; /// The entry validator object. QDoubleValidator* Validator; /// The label for the widget title. QLabel* TitleLabel; /// The label for the slider's minimum value. QLabel* MinLabel; /// The label for the slider's maximum value. QLabel* MaxLabel; }; //@} } // namespace cmtk #endif // #ifndef __cmtkQtSliderEntry_h_included_ cmtk-3.3.1/libs/Qt/cmtkQtTriplanarViewer.cxx000066400000000000000000000133141276303427400210310ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2010 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4878 $ // // $LastChangedDate: 2013-09-24 15:48:06 -0700 (Tue, 24 Sep 2013) $ // // $LastChangedBy: torstenrohlfing $ // */ #include "cmtkQtTriplanarViewer.h" #include #include #include #include #include #include #include #include #include namespace cmtk { /** \addtogroup Qt */ //@{ QtTriplanarViewer::QtTriplanarViewer() : WindowLevel( NULL ) { this->setWindowTitle( "Triplanar Image Viewer" ); QMenu* StudyMenu = new QMenu(); StudyMenu->setTitle( "&Study" ); StudyMenu->addAction( "&Load...", this, SLOT( slotLoadFile() ) ); StudyMenu->addAction( "&Reload Data...", this, SLOT( slotReloadData() ) ); StudyMenu->addSeparator(); StudyMenu->addAction( "&Save" ); StudyMenu->addAction( "Save &as..." ); StudyMenu->addAction( "&Export landmarks..." ); StudyMenu->addSeparator(); StudyMenu->addAction( "&Quit", qApp, SLOT( quit() ) ); QtImageOperators* imageOperators = new QtImageOperators( &this->m_Study, this, NULL /*progressInstance*/ ); QObject::connect( imageOperators, SIGNAL( dataChanged( Study::SmartPtr& ) ), this, SLOT( slotDataChanged( Study::SmartPtr& ) ) ); // insert "Study" as first menu. MenuBar->insertMenu( this->ViewMenu->menuAction(), StudyMenu ); // insert "Operators" after "View". MenuBar->addMenu( imageOperators->CreatePopupMenu() ); MenuBar->show(); this->m_ImagesTab = new QWidget( this->m_ControlsTab ); this->m_ControlsTab->addTab( this->m_ImagesTab, "Images" ); this->m_ControlsTab->setTabEnabled( this->m_ControlsTab->indexOf( this->m_ImagesTab ), false ); this->m_StudiesListBox = new QListWidget( this->m_ImagesTab ); this->m_StudiesListBox->setSelectionMode( QAbstractItemView::SingleSelection ); // this->m_StudiesListBox->setColumnMode( QListWidget::FixedNumber ); QObject::connect( this->m_StudiesListBox, SIGNAL( currentTextChanged( const QString& ) ), this, SLOT( slotSwitchStudy( const QString& ) ) ); QVBoxLayout* studiesLayout = new QVBoxLayout( this->m_ImagesTab ); studiesLayout->setContentsMargins( 5, 5, 5, 5 ); studiesLayout->setSpacing( 5 ); studiesLayout->addWidget( this->m_StudiesListBox ); QPushButton* copyColormapButton = new QPushButton( this->m_ImagesTab ); copyColormapButton->setText( "Copy Colormap to Other Images" ); studiesLayout->addWidget( copyColormapButton ); QObject::connect( copyColormapButton, SIGNAL( clicked() ), this, SLOT( slotCopyColormapToOtherImages() ) ); } void QtTriplanarViewer::slotAddStudy( const char* fname ) { Study::SmartPtr newStudy( new Study( fname ) ); this->m_StudiesListBox->addItem( QString( newStudy->GetFileSystemPath().c_str() ) ); this->m_Studies.push_back( newStudy ); this->m_ControlsTab->setTabEnabled( this->m_ControlsTab->indexOf( this->m_ImagesTab ), this->m_Studies.size() > 1 ); this->slotSwitchToStudy( newStudy ); this->slotCenter(); } void QtTriplanarViewer::slotLoadFile() { #ifdef CMTK_BUILD_NRRD QString path = QFileDialog::getOpenFileName( this, "Load File", QString(), "All image files (*.hdr *.nii *.nii.gz *.nrrd *.nhdr *.pic);; NIfTI / Analyze (*.hdr *.nii *.nii.gz);; Nrrd (*.nhdr *.nrrd);; BIORAD (*.pic)" ); #else QString path = QFileDialog::getOpenFileName( this, "Load File", QString(), "All image files (*.hdr *.nii *.nii.gz *.pic);; NIfTI / Analyze (*.hdr *.nii *.nii.gz);; BIORAD (*.pic)" ); #endif if ( ! (path.isEmpty() || path.isNull() ) ) { Study::SmartPtr newStudy( new Study( path.toLocal8Bit().constData() ) ); this->m_Studies.push_back( newStudy ); this->m_ControlsTab->setTabEnabled( this->m_ControlsTab->indexOf( this->m_ImagesTab ), this->m_Studies.size() > 1 ); this->m_StudiesListBox->addItem( QString( newStudy->GetFileSystemPath().c_str() ) ); this->m_StudiesListBox->setCurrentItem( this->m_StudiesListBox->item( this->m_StudiesListBox->count()-1 ) ); this->slotSwitchToStudy( newStudy ); this->slotCenter(); } } void QtTriplanarViewer::slotReloadData() { if ( this->m_Study ) { this->m_Study->ReadVolume( true /* reload */, AnatomicalOrientation::ORIENTATION_STANDARD ); } } void QtTriplanarViewer::slotSwitchStudy( const QString& study ) { for ( size_t idx = 0; idx < this->m_Studies.size(); ++idx ) { if ( study.toLocal8Bit().constData() == this->m_Studies[idx]->GetFileSystemPath() ) { this->slotSwitchToStudyInternal( this->m_Studies[idx] ); return; } } } void QtTriplanarViewer::slotCopyColormapToOtherImages() { if ( this->m_Study ) { for ( size_t i = 0; i < this->m_Studies.size(); ++i ) { if ( this->m_Studies[i] != this->m_Study ) { this->m_Studies[i]->CopyColormap( this->m_Study ); } } } } } // namespace cmtk cmtk-3.3.1/libs/Qt/cmtkQtTriplanarViewer.h000066400000000000000000000047051276303427400204620ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2010 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 2398 $ // // $LastChangedDate: 2010-10-05 14:54:37 -0700 (Tue, 05 Oct 2010) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkQtTriplanarViewer_h_included_ #define __cmtkQtTriplanarViewer_h_included_ #include #include #include #include namespace cmtk { /** \addtogroup Qt */ //@{ /** Stand-alone triplanar image viewer. */ class QtTriplanarViewer : /// Inherit from triplanar viewer widget. public QtTriplanarWindow { Q_OBJECT // we're using slots public: /// Constructor. QtTriplanarViewer(); /// Virtual destructor. virtual ~QtTriplanarViewer() {}; /// Execute in batch mode. virtual int ExecuteBatchMode( const int argc, char* argv[] ); public slots: /// Add study by filesystem path. void slotAddStudy( const char* fname ); /// Load image from file. void slotLoadFile(); /// Load image from file. void slotReloadData(); /// Copy current image colormap to all other images. void slotCopyColormapToOtherImages(); private: /// Window/Level dialog. QtWindowLevelDialog* WindowLevel; /// Vector of loaded studies. std::vector m_Studies; /// Tab for the images list. QWidget* m_ImagesTab; /// List box with loaded studies' names. QListWidget* m_StudiesListBox; private slots: /// Study was double-clicked in listbox. void slotSwitchStudy( const QString & study ); }; //@} } // namespace cmtk #endif // #ifndef __cmtkQtTriplanarViewer_h_included_ cmtk-3.3.1/libs/Qt/cmtkQtTriplanarViewerBatchMode.cxx000066400000000000000000000055261276303427400226060ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2010 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 2022 $ // // $LastChangedDate: 2010-07-21 15:26:03 -0700 (Wed, 21 Jul 2010) $ // // $LastChangedBy: torstenrohlfing $ // */ #include "cmtkQtTriplanarViewer.h" namespace cmtk { /** \addtogroup Qt */ //@{ int QtTriplanarViewer ::ExecuteBatchMode( const int argc, char* argv[] ) { this->m_BatchMode = true; for ( int i = 0; i < argc; ++i ) { if ( !strcmp( argv[i], "load" ) ) { this->slotAddStudy( argv[++i] ); } else if ( !strcmp( argv[i], "goto-pixel" ) ) { this->slotGoToPixel( argv[++i] ); } else if ( !strcmp( argv[i], "goto-location" ) ) { this->slotGoToLocation( argv[++i] ); } else if ( !strcmp( argv[i], "colormap" ) ) { this->slotSetColormap( argv[++i] ); } else if ( !strcmp( argv[i], "window-level" ) ) { this->slotSetWindowLevel( argv[++i] ); } else if ( !strcmp( argv[i], "zoom" ) ) { this->slotSetZoom( atoi( argv[++i] ) ); } else if ( !strcmp( argv[i], "crosshair" ) ) { const char* chOnOff = argv[++i]; this->slotSetCrosshairMode( ! strcmp( chOnOff, "on" ) || ! strcmp( chOnOff, "yes" ) || ! strcmp( chOnOff, "true" ) ); } else if ( !strcmp( argv[i], "checkerboard" ) ) { const char* chOnOff = argv[++i]; this->slotSetCheckerboardMode( ! strcmp( chOnOff, "on" ) || ! strcmp( chOnOff, "yes" ) || ! strcmp( chOnOff, "true" ) ); } else if ( !strcmp( argv[i], "export-axial" ) ) { this->slotExportImage( argv[++i], 1 ); } else if ( !strcmp( argv[i], "export-coronal" ) ) { this->slotExportImage( argv[++i], 2 ); } else if ( !strcmp( argv[i], "export-sagittal" ) ) { this->slotExportImage( argv[++i], 3 ); } else if ( !strcmp( argv[i], "export-panel" ) ) { this->slotExportImage( argv[++i], 4 ); } } return 0; } } // namespace cmtk cmtk-3.3.1/libs/Qt/cmtkQtTriplanarWindow.cxx000066400000000000000000000777601276303427400210560ustar00rootroot00000000000000/* // // Copyright 1997-2010 Torsten Rohlfing // // Copyright 2004-2013 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4918 $ // // $LastChangedDate: 2013-10-03 13:01:32 -0700 (Thu, 03 Oct 2013) $ // // $LastChangedBy: torstenrohlfing $ // */ #include "cmtkQtTriplanarWindow.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace cmtk { /** \addtogroup Qt */ //@{ QtTriplanarWindow::QtTriplanarWindow() : QWidget( NULL ), m_Study( NULL ), m_ZoomFactor( 100 ), m_BatchMode( false ) { this->setWindowIcon( QtIcons::WindowIcon() ); this->m_ZoomActions = new QActionGroup( this ); this->m_ZoomActions->setExclusive( true ); QAction* action; MenuBar = new QMenuBar( this ); this->ViewMenu = MenuBar->addMenu( "&View" ); action = ViewMenu->addAction( "25%", this, SLOT( slotView25() ) ); action->setCheckable( true ); this->m_ZoomActions->addAction( action ); action = ViewMenu->addAction( "33%", this, SLOT( slotView33() ) ); action->setCheckable( true ); this->m_ZoomActions->addAction( action ); action = ViewMenu->addAction( "50%", this, SLOT( slotView50() ) ); action->setCheckable( true ); this->m_ZoomActions->addAction( action ); ViewMenu->addSeparator(); action = ViewMenu->addAction( "100%", this, SLOT( slotView100() ) ); action->setCheckable( true ); action->setChecked( true ); this->m_ZoomActions->addAction( action ); ViewMenu->addSeparator(); action = ViewMenu->addAction( "200%", this, SLOT( slotView200() ) ); action->setCheckable( true ); this->m_ZoomActions->addAction( action ); action = ViewMenu->addAction( "300%", this, SLOT( slotView300() ) ); action->setCheckable( true ); this->m_ZoomActions->addAction( action ); action = ViewMenu->addAction( "400%", this, SLOT( slotView400() ) ); action->setCheckable( true ); this->m_ZoomActions->addAction( action ); action = ViewMenu->addAction( "500%", this, SLOT( slotView500() ) ); action->setCheckable( true ); this->m_ZoomActions->addAction( action ); ViewMenu->addSeparator(); (this->m_InterpolateAction = ViewMenu->addAction( "&Interpolation", this, SLOT( slotViewInterpolation() ) ))->setCheckable( true ); this->m_InterpolateAction->setChecked( false ); (this->m_CrosshairAction = ViewMenu->addAction( "&Crosshair", this, SLOT( slotViewCrosshair() ) ))->setCheckable( true ); this->m_CrosshairAction->setChecked( true ); (this->m_CheckerboxAction = ViewMenu->addAction( "C&heckerbox", this, SLOT( slotViewCheckerbox() ) ))->setCheckable( true ); this->m_CheckerboxAction->setChecked( true ); this->ExportMenu = MenuBar->addMenu( "&Export" ); ExportMenu->addAction( "&Axial" )->setData( QVariant( 1 ) ); ExportMenu->addAction( "&Coronal" )->setData( QVariant( 2 ) ); ExportMenu->addAction( "&Sagittal" )->setData( QVariant( 3 ) ); ExportMenu->addSeparator(); ExportMenu->addAction( "&Panel")->setData( QVariant( 4 ) ); QObject::connect( ExportMenu, SIGNAL( triggered( QAction* ) ), this, SLOT( slotExportMenuCmd( QAction* ) ) ); MenuBar->show(); StatusBar = new QStatusBar( this ); StatusBar->show(); GridIndexInfo = new QLabel( StatusBar ); GridIndex[0] = GridIndex[1] = GridIndex[2] = 0; StatusBar->addWidget( GridIndexInfo, 1 ); GridLayout = new QGridLayout( this ); GridLayout->setMenuBar( MenuBar ); GridLayout->addWidget( StatusBar, 2, 0, 1, 2 ); ScrollRenderViewAx = new QtScrollRenderView( this, "Axial" ); ScrollRenderViewAx->SetSliderLabelL( "I" ); ScrollRenderViewAx->SetSliderLabelR( "S" ); ScrollRenderViewAx->GetRenderImage()->SetFlipX( true ); ScrollRenderViewAx->GetRenderImage()->SetFlipY( true ); ScrollRenderViewAx->ShowSlider(); ScrollRenderViewAx->GetRenderImage()->SetCrosshairMode( this->m_CrosshairAction->isChecked() ); ScrollRenderViewAx->GetRenderImage()->SetCrosshairColors( QColor( 255,0,0 ), QColor( 0,255,0 ) ); GridLayout->addWidget( ScrollRenderViewAx, 1, 0 ); ScrollRenderViewSa = new QtScrollRenderView( this, "Sagittal" ); ScrollRenderViewSa->ShowSlider(); ScrollRenderViewSa->SetSliderLabelL( "L" ); ScrollRenderViewSa->SetSliderLabelR( "R" ); ScrollRenderViewSa->GetRenderImage()->SetCrosshairMode( this->m_CrosshairAction->isChecked() ); ScrollRenderViewSa->GetRenderImage()->SetCrosshairColors( QColor( 0,255,0 ), QColor( 0,0,255 ) ); GridLayout->addWidget( ScrollRenderViewSa, 0, 1 ); ScrollRenderViewCo = new QtScrollRenderView( this, "Coronal" ); ScrollRenderViewCo->ShowSlider(); ScrollRenderViewCo->SetSliderLabelL( "P" ); ScrollRenderViewCo->SetSliderLabelR( "A" ); ScrollRenderViewCo->GetRenderImage()->SetFlipX( true ); ScrollRenderViewCo->GetRenderImage()->SetCrosshairMode( this->m_CrosshairAction->isChecked() ); ScrollRenderViewCo->GetRenderImage()->SetCrosshairColors( QColor( 255,0,0 ), QColor( 0,0,255 ) ); GridLayout->addWidget( ScrollRenderViewCo, 0, 0 ); this->m_ControlsTab = new QTabWidget( this ); this->m_ControlsTab->setContentsMargins( 10, 10, 10, 10 ); GridLayout->addWidget( this->m_ControlsTab, 1, 1 ); QWidget *landmarksTab = new QWidget( this->m_ControlsTab ); this->m_ControlsTab->addTab( landmarksTab, "Landmarks" ); LandmarksLayout = new QGridLayout( landmarksTab ); LocationEntryX = new QLineEdit( landmarksTab ); LocationValidatorX = new QDoubleValidator( LocationEntryX ); LocationValidatorX->setDecimals( 6 ); LocationEntryX->setValidator( LocationValidatorX ); LandmarksLayout->addWidget( LocationEntryX, 0, 0 ); QObject::connect( LocationEntryX, SIGNAL( returnPressed() ), this, SLOT( slotGoToLocation() ) ); LocationEntryY = new QLineEdit( landmarksTab ); LocationValidatorY = new QDoubleValidator( LocationEntryY ); LocationEntryY->setValidator( LocationValidatorY ); LocationValidatorY->setDecimals( 6 ); LandmarksLayout->addWidget( LocationEntryY, 0, 1 ); QObject::connect( LocationEntryY, SIGNAL( returnPressed() ), this, SLOT( slotGoToLocation() ) ); LocationEntryZ = new QLineEdit( landmarksTab ); LocationValidatorZ = new QDoubleValidator( LocationEntryZ ); LocationEntryZ->setValidator( LocationValidatorZ ); LocationValidatorZ->setDecimals( 6 ); LandmarksLayout->addWidget( LocationEntryZ, 0, 2 ); QObject::connect( LocationEntryZ, SIGNAL( returnPressed() ), this, SLOT( slotGoToLocation() ) ); CenterButton = new QPushButton( landmarksTab ); CenterButton->setText( "Center" ); LandmarksLayout->addWidget( CenterButton, 1, 1 ); QObject::connect( CenterButton, SIGNAL( clicked() ), this, SLOT( slotCenter() ) ); GoToLocationButton = new QPushButton( landmarksTab ); GoToLocationButton->setText( "Go To" ); LandmarksLayout->addWidget( GoToLocationButton, 1, 2 ); QObject::connect( GoToLocationButton, SIGNAL( clicked() ), this, SLOT( slotGoToLocation() ) ); LandmarkBox = new QComboBox( landmarksTab ); LandmarksLayout->addWidget( LandmarkBox, 2, 0, 1, 2 ); LandmarkBox->setEnabled( false ); QObject::connect( LandmarkBox, SIGNAL( currentIndexChanged(int) ), this, SLOT( slotGoToLandmark() ) ); GoToLandmarkButton = new QPushButton( landmarksTab ); GoToLandmarkButton->setText( "Go To" ); LandmarksLayout->addWidget( GoToLandmarkButton, 3, 0 ); QObject::connect( GoToLandmarkButton, SIGNAL( clicked() ), this, SLOT( slotGoToLandmark() ) ); GoToLandmarkButton->setEnabled( false ); DeleteLandmarkButton = new QPushButton( landmarksTab ); DeleteLandmarkButton->setText( "Delete" ); LandmarksLayout->addWidget( DeleteLandmarkButton, 3, 1 ); QObject::connect( DeleteLandmarkButton, SIGNAL( clicked() ), this, SLOT( slotDeleteLandmark() ) ); DeleteLandmarkButton->setEnabled( false ); AddLandmarkButton = new QPushButton( landmarksTab ); AddLandmarkButton->setText( "Add..." ); LandmarksLayout->addWidget( AddLandmarkButton, 3, 2 ); QObject::connect( AddLandmarkButton, SIGNAL( clicked() ), this, SLOT( slotAddLandmark() ) ); ExportLandmarksButton = new QPushButton( landmarksTab ); ExportLandmarksButton->setText( "Export Landmarks..." ); LandmarksLayout->addWidget( ExportLandmarksButton, 5, 0 ); QObject::connect( ExportLandmarksButton, SIGNAL( clicked() ), this, SLOT( slotExportLandmarks() ) ); ExportLandmarksButton->setEnabled( false ); ImportLandmarksButton = new QPushButton( landmarksTab ); ImportLandmarksButton->setText( "Import Landmarks..." ); LandmarksLayout->addWidget( ImportLandmarksButton, 5, 2 ); QObject::connect( ImportLandmarksButton, SIGNAL( clicked() ), this, SLOT( slotImportLandmarks() ) ); ImportLandmarksButton->setEnabled( true ); // create the window/level tab WindowLevelControls = new QtWindowLevelControls( this->m_ControlsTab ); QObject::connect( WindowLevelControls, SIGNAL( colormap( Study::SmartPtr& ) ), this, SLOT( slotColormapChanged( Study::SmartPtr& ) ) ); this->m_ControlsTab->addTab( WindowLevelControls, "Window/Level" ); // create the visualization pipeline this->m_Colormap = Colormap::New(); this->m_Colormap->SetStandardColormap( PALETTE_GRAY ); this->m_Colormap->SetDataRange( 0, 2000 ); // set up axial viewer PipelineImageAx = Image::New(); ImageToImageRGBAx = ImageToImageRGB::New(); ImageToImageRGBAx->SetAlphaMode( ImageToImageRGB::AlphaModeConst ); ImageToImageRGBAx->SetInput( PipelineImageAx ); ImageToImageRGBAx->SetColormap( this->m_Colormap ); ScrollRenderViewAx->slotConnectImage( ImageToImageRGBAx->GetOutput() ); QObject::connect( ScrollRenderViewAx, SIGNAL( indexChanged( int ) ), this, SLOT( slotSwitchImageAx( int ) ) ); QObject::connect( ScrollRenderViewAx, SIGNAL( signalMouse3D( Qt::MouseButton, const Vector3D& ) ), this, SLOT( slotMouseAx( Qt::MouseButton, const Vector3D& ) ) ); // set up sagittal viewer PipelineImageSa = Image::New(); ImageToImageRGBSa = ImageToImageRGB::New(); ImageToImageRGBSa->SetAlphaMode( ImageToImageRGB::AlphaModeConst ); ImageToImageRGBSa->SetInput( PipelineImageSa ); ImageToImageRGBSa->SetColormap( this->m_Colormap ); ScrollRenderViewSa->slotConnectImage( ImageToImageRGBSa->GetOutput() ); PipelineImageCo = Image::New(); QObject::connect( ScrollRenderViewSa, SIGNAL( indexChanged( int ) ), this, SLOT( slotSwitchImageSa( int ) ) ); QObject::connect( ScrollRenderViewSa, SIGNAL( signalMouse3D( Qt::MouseButton, const Vector3D& ) ), this, SLOT( slotMouseSa( Qt::MouseButton, const Vector3D& ) ) ); // set up coronal viewer ImageToImageRGBCo = ImageToImageRGB::New(); ImageToImageRGBCo->SetAlphaMode( ImageToImageRGB::AlphaModeConst ); ImageToImageRGBCo->SetInput( PipelineImageCo ); ImageToImageRGBCo->SetColormap( this->m_Colormap ); ScrollRenderViewCo->slotConnectImage( ImageToImageRGBCo->GetOutput() ); QObject::connect( ScrollRenderViewCo, SIGNAL( indexChanged( int ) ), this, SLOT( slotSwitchImageCo( int ) ) ); QObject::connect( ScrollRenderViewCo, SIGNAL( signalMouse3D( Qt::MouseButton, const Vector3D& ) ), this, SLOT( slotMouseCo( Qt::MouseButton, const Vector3D& ) ) ); this->m_ProgressReporter = new QtProgress( this ); } void QtTriplanarWindow::slotSetZoom( const int zoomPercent ) { this->m_ZoomFactor = zoomPercent; ScrollRenderViewAx->GetRenderImage()->SetZoomFactorPercent( zoomPercent ); ScrollRenderViewCo->GetRenderImage()->SetZoomFactorPercent( zoomPercent ); ScrollRenderViewSa->GetRenderImage()->SetZoomFactorPercent( zoomPercent ); this->slotRenderAll(); } void QtTriplanarWindow::slotSetCheckerboardMode( const bool mode ) { this->m_CheckerboxAction->setChecked( mode ); this->slotViewCheckerbox(); } void QtTriplanarWindow::slotSetCrosshairMode( const bool mode ) { this->m_CrosshairAction->setChecked( mode ); this->slotViewCrosshair(); } void QtTriplanarWindow::slotSetInterpolateMode( const bool mode ) { this->m_InterpolateAction->setChecked( mode ); this->slotViewInterpolation(); } void QtTriplanarWindow::slotExportMenuCmd( QAction* action ) { const int mode = action->data().toInt(); QString title( "Choose filename" ); switch ( mode ) { case 1: // Axial image. title = "Axial image export"; break; case 2: // Coronal image. title = "Coronal image export"; break; case 3: // Sagittal. title = "Sagittal image export"; break; case 4: title = "Panel image export"; break; } QString filename( "image.png" ); filename = QFileDialog::getSaveFileName( this, title, filename, "Portable Network Graphic (*.png);; Tagged Image File Format (*.tif);; Portable Pixmap (*.ppm *.pgm);; JPEG (*.jpg)" ); if ( !filename.isEmpty() ) { this->slotExportImage( filename, mode ); } } void QtTriplanarWindow::slotExportImage( const QString& filename, const int command ) { QPixmap pixmap; switch ( command ) { case 1: // Axial image. pixmap = ScrollRenderViewAx->GetRenderImage()->GetPixmap(); break; case 2: // Coronal image. pixmap = ScrollRenderViewCo->GetRenderImage()->GetPixmap(); break; case 3: // Sagittal. pixmap = ScrollRenderViewSa->GetRenderImage()->GetPixmap(); break; case 4: { // Panel. QPixmap pixmapAx = ScrollRenderViewAx->GetRenderImage()->GetPixmap(); QPixmap pixmapSa = ScrollRenderViewSa->GetRenderImage()->GetPixmap(); QPixmap pixmapCo = ScrollRenderViewCo->GetRenderImage()->GetPixmap(); pixmap = QPixmap( pixmapCo.width() + pixmapSa.width(), pixmapCo.height() + pixmapAx.height() ); QPainter painter( &pixmap ); painter.drawPixmap( 0, 0, pixmapCo.width(), pixmapCo.height(), pixmapCo ); painter.drawPixmap( pixmapCo.width(), 0, pixmapSa.width(), pixmapSa.height(), pixmapSa ); painter.drawPixmap( 0, pixmapCo.height(), pixmapAx.width(), pixmapAx.height(), pixmapAx ); painter.fillRect( pixmapCo.width(), pixmapCo.height(), pixmapSa.width(), pixmapAx.height(), Qt::black ); break; } } QString format = filename.section( ".", -1 ).toUpper(); if ( format.isEmpty() ) format = "PNG"; if ( !pixmap.save( filename, format.toLatin1() ) ) { if ( this->m_BatchMode ) std::cerr << "WARNING: saving file failed." << std::endl; else QMessageBox::warning( this, "Save failed", "Error saving file" ); } } void QtTriplanarWindow::slotRenderAll() { ScrollRenderViewAx->slotRender(); ScrollRenderViewCo->slotRender(); ScrollRenderViewSa->slotRender(); } void QtTriplanarWindow::slotSwitchToStudy( Study::SmartPtr& study ) { this->m_Study = study; if ( this->m_Study ) { qApp->setOverrideCursor( Qt::WaitCursor ); this->m_Study->ReadVolume( true /* reload */, AnatomicalOrientation::ORIENTATION_STANDARD ); qApp->restoreOverrideCursor(); if ( !this->m_BatchMode ) { while ( ! this->m_Study->GetVolume() ) { int button = QMessageBox::warning ( NULL, "Error", "Could not read image data for this study.", QMessageBox::Retry, QMessageBox::Abort ); if ( button == QMessageBox::Abort ) break; } } if ( this->m_Study->GetVolume() ) { this->SetStudy( this->m_Study ); this->WindowLevelControls->slotSetStudy( this->m_Study ); this->slotCenter(); this->slotColormapChanged( this->m_Study ); this->UpdateDialog(); this->show(); } else { if ( this->m_BatchMode ) StdErr << "ERROR: could not read image " << this->m_Study->GetFileSystemPath() << "\n"; } // if study has landmarks, put them into combo box. LandmarkBox->clear(); const LandmarkList* ll = this->m_Study->GetLandmarkList(); if ( ll ) { LandmarkList::const_iterator ll_it = ll->begin(); while ( ll_it != ll->end() ) { LandmarkBox->addItem( ll_it->m_Name.c_str() ); ++ll_it; } } LandmarkBox->setEnabled( LandmarkBox->count() ); GoToLandmarkButton->setEnabled( LandmarkBox->count() ); DeleteLandmarkButton->setEnabled( LandmarkBox->count() ); ExportLandmarksButton->setEnabled( LandmarkBox->count() ); } } void QtTriplanarWindow::slotSwitchToStudyInternal( Study::SmartPtr& study ) { this->m_Study = study; if ( this->m_Study ) { this->m_Study->ReadVolume( false /* reload */, AnatomicalOrientation::ORIENTATION_STANDARD ); while ( ! this->m_Study->GetVolume() ) { int button = QMessageBox::warning ( NULL, "Error", "Could not read image data for this study.", QMessageBox::Retry, QMessageBox::Abort ); if ( button == QMessageBox::Abort ) break; } if ( this->m_Study->GetVolume() ) { this->SetStudy( this->m_Study ); this->WindowLevelControls->slotSetStudy( this->m_Study ); this->slotSwitchImageAx( ScrollRenderViewAx->GetSlice() ); this->slotSwitchImageSa( ScrollRenderViewSa->GetSlice() ); this->slotSwitchImageCo( ScrollRenderViewCo->GetSlice() ); this->UpdateDialog(); this->show(); } } } void QtTriplanarWindow::UpdateDialog() { if ( this->m_Study ) { const UniformVolume *volume = this->m_Study->GetVolume(); if ( volume ) { VolumeDims = volume->GetDims(); ScrollRenderViewAx->slotSetNumberOfSlices( VolumeDims[AXIS_Z] ); ScrollRenderViewSa->slotSetNumberOfSlices( VolumeDims[AXIS_X] ); ScrollRenderViewCo->slotSetNumberOfSlices( VolumeDims[AXIS_Y] ); LocationValidatorX->setBottom( 0 ); LocationValidatorX->setTop( volume->m_Size[AXIS_X] ); LocationValidatorY->setBottom( 0 ); LocationValidatorY->setTop( volume->m_Size[AXIS_Y] ); LocationValidatorZ->setBottom( 0 ); LocationValidatorZ->setTop( volume->m_Size[AXIS_Z] ); } else { qWarning( "QtTriplanarWindow::UpdateDialog called with no image data loaded.\n" ); } if ( this->m_CrosshairAction->isChecked() ) { this->slotRenderAll(); } QString caption; this->setWindowTitle( (std::string( "CMTK Triplanar Viewer: " ) + this->m_Study->GetName()).c_str() ); this->show(); } } void QtTriplanarWindow::slotDataChanged( Study::SmartPtr& study ) { if ( study != this->m_Study ) return; this->slotGoToLocation(); } void QtTriplanarWindow::slotSwitchImageAx( int imageIndex ) { const UniformVolume *volume = this->m_Study->GetVolume(); if ( volume ) { ScalarImage::SmartPtr sliceImage( volume->GetOrthoSlice( AXIS_Z, imageIndex ) ); if ( sliceImage ) { if ( ! this->m_CheckerboxAction->isChecked() ) sliceImage->GetPixelData()->ReplacePaddingData( 0.0 ); sliceImage->AdjustToIsotropic( volume->GetMinDelta(), this->m_InterpolateAction->isChecked() ); PipelineImageAx->SetFromScalarImage( *sliceImage ); } sliceImage = ScalarImage::SmartPtr::Null(); LocationEntryZ->setText( QString::number( volume->GetPlaneCoord( AXIS_Z, imageIndex ) ) ); GridIndex[2] = imageIndex; this->UpdateGridInfo(); if ( this->m_CrosshairAction->isChecked() ) { this->slotGoToLocation(); } else { ScrollRenderViewAx->slotRender(); } } else { qWarning( "QtTriplanarWindow::slotSwitchImageAx called with no image data loaded.\n" ); } } void QtTriplanarWindow::slotSwitchImageSa( int imageIndex ) { const UniformVolume *volume = this->m_Study->GetVolume(); if ( volume ) { ScalarImage::SmartPtr sliceImage = volume->GetOrthoSlice( AXIS_X, imageIndex ); if ( sliceImage ) { if ( ! this->m_CheckerboxAction->isChecked() ) sliceImage->GetPixelData()->ReplacePaddingData( 0.0 ); sliceImage->Mirror( false /* horizontal */, true /* vertical */ ); sliceImage->AdjustToIsotropic( volume->GetMinDelta(), this->m_InterpolateAction->isChecked() ); PipelineImageSa->SetFromScalarImage( *sliceImage ); } LocationEntryX->setText( QString::number( volume->GetPlaneCoord( AXIS_X, imageIndex ) ) ); GridIndex[0] = imageIndex; this->UpdateGridInfo(); if ( this->m_CrosshairAction->isChecked() ) { this->slotGoToLocation(); } else { ScrollRenderViewSa->slotRender(); } } else { qWarning( "QtTriplanarWindow::slotSwitchImageSa called with no image data loaded.\n" ); } } void QtTriplanarWindow::slotSwitchImageCo( int imageIndex ) { const UniformVolume *volume = this->m_Study->GetVolume(); if ( volume ) { ScalarImage::SmartPtr sliceImage = volume->GetOrthoSlice( AXIS_Y, imageIndex ); if ( sliceImage ) { if ( ! this->m_CheckerboxAction->isChecked() ) sliceImage->GetPixelData()->ReplacePaddingData( 0.0 ); sliceImage->Mirror( false /* horizontal */, true /* vertical */ ); sliceImage->AdjustToIsotropic( volume->GetMinDelta(), this->m_InterpolateAction->isChecked() ); PipelineImageCo->SetFromScalarImage( *sliceImage ); } LocationEntryY->setText( QString::number( volume->GetPlaneCoord( AXIS_Y, imageIndex ) ) ); GridIndex[1] = imageIndex; this->UpdateGridInfo(); if ( this->m_CrosshairAction->isChecked() ) { this->slotGoToLocation(); } else { ScrollRenderViewCo->slotRender(); } } else { qWarning( "QtTriplanarWindow::slotSwitchImageCo called with no image data loaded.\n" ); } } void QtTriplanarWindow ::slotGoToPixel( const QString& xyz ) { int x, y, z; if ( 3 != sscanf( xyz.toLatin1(), "%10d,%10d,%10d", &x, &y, &z ) ) { qWarning( "QtTriplanarWindow::slotGoToPixel needs pixel index as 'x,y,z'.\n" ); } else { this->slotSwitchImageSa( x ); this->slotSwitchImageCo( y ); this->slotSwitchImageAx( z ); } } void QtTriplanarWindow::slotGoToLocation( const QString& xyz ) { float v[3]; if ( 3 != sscanf( xyz.toLatin1(), "%15f,%15f,%15f", v, v+1, v+2 ) ) { qWarning( "QtTriplanarWindow::slotGoToLocation needs 3D coordinate as 'x,y,z'.\n" ); } else { this->slotMouse3D( Qt::LeftButton, UniformVolume::CoordinateVectorType::FromPointer( v ) ); } } void QtTriplanarWindow ::slotSetColormap( const QString& cmap ) { for ( unsigned int colormapIndex = 0; Colormap::StandardColormaps[colormapIndex]; ++colormapIndex ) { if ( cmap == QString( Colormap::StandardColormaps[colormapIndex] ) ) { this->m_Colormap->SetStandardColormap( colormapIndex ); this->slotRenderAll(); break; } } } void QtTriplanarWindow ::slotSetWindowLevel( const QString& wl ) { float window, level; if ( 2 != sscanf( wl.toLatin1(), "%15f:%15f", &window, &level ) ) { qWarning( "QtTriplanarWindow::slotSetWindowLevel needs 'window:level'.\n" ); } else { this->m_Colormap->SetDataRange( level-0.5*window, level+0.5*window ); this->slotRenderAll(); } } void QtTriplanarWindow ::slotMouse3D( Qt::MouseButton, const Vector3D& v ) { // if we don't have a study yet, simply ignore. if ( ! this->m_Study ) return; const UniformVolume *volume = this->m_Study->GetVolume(); unsigned int i=0, j=0; PipelineImageAx->ProjectPixel( v, i, j ); ScrollRenderViewAx->GetRenderImage()->SetCrosshairPosition( i, j ); PipelineImageSa->ProjectPixel( v, i, j ); ScrollRenderViewSa->GetRenderImage()->SetCrosshairPosition( i, j ); PipelineImageCo->ProjectPixel( v, i, j ); ScrollRenderViewCo->GetRenderImage()->SetCrosshairPosition( i, j ); if ( volume ) { const unsigned int sliceSa = volume->GetClosestCoordIndex( AXIS_X, v[AXIS_X] ); ScrollRenderViewSa->slotSetSlice( sliceSa ); ScrollRenderViewSa->slotRender(); const unsigned int sliceCo = volume->GetClosestCoordIndex( AXIS_Y, v[AXIS_Y] ); ScrollRenderViewCo->slotSetSlice( sliceCo ); ScrollRenderViewCo->slotRender(); const unsigned int sliceAx = volume->GetClosestCoordIndex( AXIS_Z, v[AXIS_Z] ); ScrollRenderViewAx->slotSetSlice( sliceAx ); ScrollRenderViewAx->slotRender(); } } void QtTriplanarWindow ::slotMouseAx( Qt::MouseButton, const Vector3D& v ) { // if we don't have a study yet, simply ignore. if ( ! this->m_Study ) return; const UniformVolume *volume = this->m_Study->GetVolume(); unsigned int i=0, j=0; PipelineImageAx->ProjectPixel( v, i, j ); ScrollRenderViewAx->GetRenderImage()->SetCrosshairPosition( i, j ); if ( volume ) { const unsigned int sliceSa = volume->GetClosestCoordIndex( AXIS_X, v[AXIS_X] ); ScrollRenderViewSa->slotSetSlice( sliceSa ); ScrollRenderViewSa->slotRender(); const unsigned int sliceCo = volume->GetClosestCoordIndex( AXIS_Y, v[AXIS_Y] ); ScrollRenderViewCo->slotSetSlice( sliceCo ); ScrollRenderViewCo->slotRender(); } } void QtTriplanarWindow ::slotMouseSa( Qt::MouseButton, const Vector3D& v ) { // if we don't have a study yet, simply ignore. if ( ! this->m_Study ) return; const UniformVolume *volume = this->m_Study->GetVolume(); unsigned int i=0, j=0; PipelineImageSa->ProjectPixel( v, i, j ); ScrollRenderViewSa->GetRenderImage()->SetCrosshairPosition( i, j ); if ( volume ) { const unsigned int sliceAx = volume->GetClosestCoordIndex( AXIS_Z, v[AXIS_Z] ); ScrollRenderViewAx->slotSetSlice( sliceAx ); ScrollRenderViewAx->slotRender(); const unsigned int sliceCo = volume->GetClosestCoordIndex( AXIS_Y, v[AXIS_Y] ); ScrollRenderViewCo->slotSetSlice( sliceCo ); ScrollRenderViewCo->slotRender(); } } void QtTriplanarWindow ::slotMouseCo( Qt::MouseButton, const Vector3D& v ) { // if we don't have a study yet, simply ignore. if ( ! this->m_Study ) return; const UniformVolume *volume = this->m_Study->GetVolume(); unsigned int i=0, j=0; PipelineImageCo->ProjectPixel( v, i, j ); ScrollRenderViewCo->GetRenderImage()->SetCrosshairPosition( i, j ); if ( volume ) { const unsigned int sliceAx = volume->GetClosestCoordIndex( AXIS_Z, v[AXIS_Z] ); ScrollRenderViewAx->slotSetSlice( sliceAx ); ScrollRenderViewAx->slotRender(); const unsigned int sliceSa = volume->GetClosestCoordIndex( AXIS_X, v[AXIS_X] ); ScrollRenderViewSa->slotSetSlice( sliceSa ); ScrollRenderViewSa->slotRender(); } } void QtTriplanarWindow::slotCenter() { const UniformVolume *volume = this->m_Study->GetVolume(); if ( ! volume ) return; // Pretend there was a button event at the given location this->slotMouse3D( Qt::LeftButton, volume->GetCenterCropRegion() ); } void QtTriplanarWindow::slotGoToLocation() { const UniformVolume *volume = this->m_Study->GetVolume(); if ( ! volume ) return; // Pretend there was a button event at the given location const double location[3] = { LocationEntryX->text().toDouble(), LocationEntryY->text().toDouble(), LocationEntryZ->text().toDouble() }; this->slotMouse3D( Qt::LeftButton, UniformVolume::CoordinateVectorType::FromPointer( location ) ); } void QtTriplanarWindow::slotGoToLandmark() { if ( ! this->m_Study ) return; const LandmarkList *ll = this->m_Study->GetLandmarkList(); if ( ! ll ) return; LandmarkList::ConstIterator lm = ll->FindByName( LandmarkBox->currentText().toStdString() ); if ( lm != ll->end() ) { this->slotMouse3D( Qt::LeftButton, lm->m_Location ); } } void QtTriplanarWindow::slotDeleteLandmark() { } void QtTriplanarWindow::slotExportLandmarks() { if ( ! this->m_Study ) return; LandmarkList::SmartPtr ll = this->m_Study->GetLandmarkList(); if ( ! ll ) return; QString path = QFileDialog::getSaveFileName( this, "Save Landmarks File" ); if ( ! path.isEmpty() ) { std::ofstream stream( path.toLatin1().constData() ); if ( stream.good() ) { for ( LandmarkList::ConstIterator it = ll->begin(); it != ll->end(); ++it ) { stream << it->m_Location[0] << "\t" << it->m_Location[1] << "\t" << it->m_Location[2] << "\t" << it->m_Name << std::endl; } stream.close(); } else { QMessageBox::critical( NULL, "Error", "Could not open file for writing.", QMessageBox::Ok, Qt::NoButton, Qt::NoButton ); } } } void QtTriplanarWindow::slotImportLandmarks() { if ( ! this->m_Study ) return; LandmarkList::SmartPtr ll = this->m_Study->GetLandmarkList(); if ( ! ll ) { ll = LandmarkList::SmartPtr( new LandmarkList ); this->m_Study->SetLandmarkList( ll ); } QString path = QFileDialog::getOpenFileName( this, "Open Landmarks File", QString(), "All Files (*.*)" ); if ( ! path.isEmpty() ) { std::ifstream stream( path.toLatin1().constData() ); unsigned int cnt = 0; if ( stream.good() ) { while ( ! stream.eof() ) { Landmark::SpaceVectorType xyz; stream >> xyz[0] >> xyz[1] >> xyz[2]; char name[128]; stream.getline( name, 128, '\n' ); if ( ! strlen(name) ) { sprintf( name, "LM-%04d", cnt++ ); } ll->push_back( Landmark( name, xyz ) ); LandmarkBox->addItem( name ); } LandmarkList::ConstIterator lm = ll->begin(); if ( lm != ll->end() ) { this->LandmarkBox->setCurrentIndex( this->LandmarkBox->findText( lm->m_Name.c_str() ) ); this->slotMouse3D( Qt::LeftButton, lm->m_Location ); } LandmarkBox->setEnabled( true ); GoToLandmarkButton->setEnabled( true ); DeleteLandmarkButton->setEnabled( true ); ExportLandmarksButton->setEnabled( true ); stream.close(); } else { QMessageBox::critical( NULL, "Error", "Could not open file for reading.", QMessageBox::Ok, Qt::NoButton, Qt::NoButton ); } } } void QtTriplanarWindow::slotAddLandmark() { if ( ! this->m_Study ) return; LandmarkList::SmartPtr ll = this->m_Study->GetLandmarkList(); if ( ! ll ) { ll = LandmarkList::SmartPtr( new LandmarkList ); this->m_Study->SetLandmarkList( ll ); } bool ok; QString name = QInputDialog::getText( this, "Add New Landmark", "Enter new landmark name:", QLineEdit::Normal, QString::null, &ok ); if ( ok && !name.isEmpty() ) { Types::Coordinate location[3] = { LocationEntryX->text().toDouble(), LocationEntryY->text().toDouble(), LocationEntryZ->text().toDouble() }; ll->push_back( Landmark( name.toStdString(), Landmark::SpaceVectorType::FromPointer( location ) ) ); LandmarkBox->addItem( name ); LandmarkBox->setCurrentIndex( this->LandmarkBox->findText( name ) ); LandmarkBox->setEnabled( true ); GoToLandmarkButton->setEnabled( true ); DeleteLandmarkButton->setEnabled( true ); ExportLandmarksButton->setEnabled( true ); } } void QtTriplanarWindow::slotColormapChanged( Study::SmartPtr& study ) { if ( this->m_Study && (this->m_Study == study) ) { this->m_Colormap->SetFromStudy( this->m_Study.GetPtr() ); this->slotRenderAll(); } } void QtTriplanarWindow::UpdateGridInfo() { if ( ! (this->m_Study && this->m_Study->GetVolume()) ) return; QString str = "OUTSIDE"; const UniformVolume* volume = this->m_Study->GetVolume(); if ( volume->IndexIsInRange( GridIndex[0], GridIndex[1], GridIndex[2] ) ) { const FixedVector<3,float> v = volume->IndexToPhysical( GridIndex ); Types::DataItem value; if ( volume->GetDataAt( value, GridIndex[0], GridIndex[1], GridIndex[2] ) ) str.sprintf( "Pixel Index: [%d,%d,%d] RAS: [%g,%g,%g] Value: %g", GridIndex[0], GridIndex[1], GridIndex[2], v[0], v[1], v[2], value ); else str.sprintf( "Pixel Index: [%d,%d,%d] RAS: [%g,%g,%g]", GridIndex[0], GridIndex[1], GridIndex[2], v[0], v[1], v[2] ); } GridIndexInfo->setText( str ); } } // namespace cmtk cmtk-3.3.1/libs/Qt/cmtkQtTriplanarWindow.h000066400000000000000000000146601276303427400204710ustar00rootroot00000000000000/* // // Copyright 1997-2010 Torsten Rohlfing // // Copyright 2004-2010 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 2398 $ // // $LastChangedDate: 2010-10-05 14:54:37 -0700 (Tue, 05 Oct 2010) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkQtTriplanarWindow_h_included_ #define __cmtkQtTriplanarWindow_h_included_ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace cmtk { /** \addtogroup Qt */ //@{ /** Triplanar image viewer window. */ class QtTriplanarWindow : /// Inherit from Qt widget. public QWidget { Q_OBJECT // we're using slots public: /// Constructor. QtTriplanarWindow(); protected: /// The Study object we're working on. cmtkGetSetMacro(Study::SmartPtr,Study); /// Use (linear) interpolation when rendering slices. QAction* m_InterpolateAction; /// Use (linear) interpolation when rendering slices. QAction* m_CrosshairAction; /// Fill Null data areas with a checkerbox pattern QAction* m_CheckerboxAction; /// Zoom factor in percent. int m_ZoomFactor; /// Update dialog after study change. void UpdateDialog(); /// Batch mode flag: if set, no dialog boxes are shown. bool m_BatchMode; /// Action group for zoom factor adjustment. QActionGroup* m_ZoomActions; public slots: void slotDataChanged( Study::SmartPtr& study ); void slotColormapChanged( Study::SmartPtr& study ); void slotSwitchToStudy( Study::SmartPtr& study ); void slotSwitchToStudyInternal( Study::SmartPtr& study ); protected slots: void slotView25(); void slotView33(); void slotView50(); void slotView100(); void slotView200(); void slotView300(); void slotView400(); void slotView500(); void slotViewInterpolation(); void slotViewCrosshair(); void slotViewCheckerbox(); void slotExportMenuCmd( QAction* ); void slotRenderAll(); /// Batch mode slots void slotSetColormap( const QString& cmap ); void slotSetWindowLevel( const QString& wl ); void slotGoToPixel( const QString& xyz ); void slotGoToLocation( const QString& xyz ); void slotExportImage( const QString& filename, const int command ); void slotSetInterpolateMode( const bool mode ); void slotSetCrosshairMode( const bool mode ); void slotSetCheckerboardMode( const bool mode ); void slotSetZoom( const int zoomPercent ); /// Switch image in axial viewer. void slotSwitchImageAx( int imageIndex ); void slotSwitchImageSa( int imageIndex ); void slotSwitchImageCo( int imageIndex ); /// Three-dimensional mouse event. void slotMouse3D( Qt::MouseButton, const Vector3D& ); /// Three-dimensional mouse event from axial slice. void slotMouseAx( Qt::MouseButton, const Vector3D& ); /// Three-dimensional mouse event from sagittal slice. void slotMouseSa( Qt::MouseButton, const Vector3D& ); /// Three-dimensional mouse event from coronal slice. void slotMouseCo( Qt::MouseButton, const Vector3D& ); /// This slot is called when the "Center" button is clicked. void slotCenter(); /// This slot is called when the "Go To Location" button is clicked. void slotGoToLocation(); /// This slot is called when the "Go To Landmark" button is clicked. void slotGoToLandmark(); /// This slot is called when the "Delete Landmark" button is clicked. void slotDeleteLandmark(); /// This slot is called when the "Add Landmark" button is clicked. void slotAddLandmark(); /// This slot is called when the "Export Landmarks" button is clicked. void slotExportLandmarks(); /// This slot is called when the "Import Landmarks" button is clicked. void slotImportLandmarks(); protected: /// The "View" menu. QMenu* ViewMenu; /// The "Export" menu. QMenu* ExportMenu; /// Store volume dimensions here for convenient access. DataGrid::IndexType VolumeDims; /// The scrolled view we display an image in. QtScrollRenderView* ScrollRenderViewAx; QtScrollRenderView* ScrollRenderViewSa; QtScrollRenderView* ScrollRenderViewCo; Image* PipelineImageAx; Image* PipelineImageSa; Image* PipelineImageCo; Colormap* m_Colormap; ImageToImageRGB* ImageToImageRGBAx; ImageToImageRGB* ImageToImageRGBSa; ImageToImageRGB* ImageToImageRGBCo; QMenuBar* MenuBar; QGridLayout* GridLayout; QStatusBar* StatusBar; QGridLayout* LandmarksLayout; QLineEdit* LocationEntryX; QLineEdit* LocationEntryY; QLineEdit* LocationEntryZ; QDoubleValidator* LocationValidatorX; QDoubleValidator* LocationValidatorY; QDoubleValidator* LocationValidatorZ; QPushButton* GoToLocationButton; QPushButton* CenterButton; QPushButton* GoToLandmarkButton; QPushButton* AddLandmarkButton; QPushButton* DeleteLandmarkButton; QPushButton* ExportLandmarksButton; QPushButton* ImportLandmarksButton; QComboBox* LandmarkBox; QtWindowLevelControls* WindowLevelControls; QTabWidget* m_ControlsTab; QtProgress* m_ProgressReporter; private: /// The pixel grid index of the current location. DataGrid::IndexType GridIndex; /// Status bar output of grid coordinate. QLabel* GridIndexInfo; /// Update status bar. void UpdateGridInfo(); }; //@} } // namespace cmtk #endif // #ifndef __cmtkQtTriplanarWindow_h_included_ cmtk-3.3.1/libs/Qt/cmtkQtTriplanarWindowViewActions.cxx000066400000000000000000000042511276303427400232130ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2010 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 5806 $ // // $LastChangedDate: 2009-05-29 13:36:00 -0700 (Fri, 29 May 2009) $ // // $LastChangedBy: torsten $ // */ #include "cmtkQtTriplanarWindow.h" namespace cmtk { void QtTriplanarWindow::slotView25() { this->slotSetZoom( 25 ); } void QtTriplanarWindow::slotView33() { this->slotSetZoom( 33 ); } void QtTriplanarWindow::slotView50() { this->slotSetZoom( 50 ); } void QtTriplanarWindow::slotView100() { this->slotSetZoom( 100 ); } void QtTriplanarWindow::slotView200() { this->slotSetZoom( 200 ); } void QtTriplanarWindow::slotView300() { this->slotSetZoom( 300 ); } void QtTriplanarWindow::slotView400() { this->slotSetZoom( 400 ); } void QtTriplanarWindow::slotView500() { this->slotSetZoom( 500 ); } void QtTriplanarWindow::slotViewInterpolation() { this->slotRenderAll(); } void QtTriplanarWindow::slotViewCrosshair() { const bool mode = this->m_CrosshairAction->isChecked(); ScrollRenderViewAx->GetRenderImage()->SetCrosshairMode( mode ); ScrollRenderViewCo->GetRenderImage()->SetCrosshairMode( mode ); ScrollRenderViewSa->GetRenderImage()->SetCrosshairMode( mode ); this->slotRenderAll(); } void QtTriplanarWindow::slotViewCheckerbox() { this->slotRenderAll(); } } // namespace cmtk cmtk-3.3.1/libs/Qt/cmtkQtWindowLevelControls.cxx000066400000000000000000000127361276303427400217050ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2010, 2013 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4882 $ // // $LastChangedDate: 2013-09-27 15:16:36 -0700 (Fri, 27 Sep 2013) $ // // $LastChangedBy: torstenrohlfing $ // */ #include "cmtkQtWindowLevelControls.h" #include #include namespace cmtk { /** \addtogroup Qt */ //@{ QtWindowLevelControls::QtWindowLevelControls ( QWidget *const myParent ) : QWidget( myParent ), m_Study( NULL ), RangeFrom( 0.0 ), RangeTo( 1.0 ), RangeWidth( 1.0 ) { Layout = new QVBoxLayout( this ); Layout->setContentsMargins( 5, 5, 5, 5 ); QComboBox* colormapBox = new QComboBox( this ); Layout->addWidget( colormapBox ); for ( unsigned int colormapIndex = 0; Colormap::StandardColormaps[colormapIndex]; ++colormapIndex ) { colormapBox->addItem( Colormap::StandardColormaps[colormapIndex] ); } QObject::connect( colormapBox, SIGNAL( activated( int ) ), this, SLOT( slotSelectColormap( int ) ) ); BlackWindowSlider = new QtSliderEntry( this ); QObject::connect( BlackWindowSlider, SIGNAL( valueChanged( double ) ), this, SLOT( slotControlsChanged() ) ); BlackWindowSlider->slotSetTitle( "Black" ); BlackWindowSlider->slotSetMinMaxLabels( QString::null, QString::null ); Layout->addWidget( BlackWindowSlider ); WhiteLevelSlider = new QtSliderEntry( this ); QObject::connect( WhiteLevelSlider, SIGNAL( valueChanged( double ) ), this, SLOT( slotControlsChanged() ) ); WhiteLevelSlider->slotSetTitle( "White" ); WhiteLevelSlider->slotSetMinMaxLabels( QString::null, QString::null ); Layout->addWidget( WhiteLevelSlider ); WindowLevelCheckBox = new QCheckBox( "Window/Level", this ); QObject::connect( WindowLevelCheckBox, SIGNAL( stateChanged( int ) ), this, SLOT( slotSwitchModeWL( int ) ) ); Layout->addWidget( WindowLevelCheckBox ); GammaSlider = new QtSliderEntry( this ); GammaSlider->slotSetPrecision( 1 ); GammaSlider->slotSetRange( 0.1, 10 ); GammaSlider->slotSetValue( 1 ); GammaSlider->slotSetTitle( "Gamma Value" ); GammaSlider->slotSetMinMaxLabels( QString::null, QString::null ); QObject::connect( GammaSlider, SIGNAL( valueChanged( double ) ), this, SLOT( slotControlsChanged() ) ); Layout->addWidget( GammaSlider ); Layout->addItem( new QSpacerItem( 0, 0, QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding ) ); } void QtWindowLevelControls::slotSetStudy( Study::SmartPtr& study ) { this->m_Study = study; RangeFrom = this->m_Study->GetMinimumValue(); RangeTo = this->m_Study->GetMaximumValue(); RangeWidth = RangeTo - RangeFrom; this->slotSwitchModeWL( WindowLevelCheckBox->isChecked() ); } void QtWindowLevelControls::slotSwitchModeWL( int modeWindowLevel ) { if ( !this->m_Study ) return; const float black = this->m_Study->GetBlack(); const float white = this->m_Study->GetWhite(); unsigned int precision = 0; if ( RangeWidth > 0 ) { precision = static_cast( std::max( 0.0, (log( 1.0 / 256 ) + log( RangeWidth )) / log(0.1) ) ); } WhiteLevelSlider->slotSetPrecision( precision ); BlackWindowSlider->slotSetPrecision( precision ); if ( modeWindowLevel ) { BlackWindowSlider->slotSetRange( 0, RangeTo - RangeFrom ); BlackWindowSlider->slotSetValue( white - black ); BlackWindowSlider->slotSetTitle( "Window" ); WhiteLevelSlider->slotSetRange( RangeFrom, RangeTo ); WhiteLevelSlider->slotSetValue( (white + black) / 2 ); WhiteLevelSlider->slotSetTitle( "Level" ); } else { BlackWindowSlider->slotSetRange( RangeFrom, RangeTo ); BlackWindowSlider->slotSetValue( black ); BlackWindowSlider->slotSetTitle( "Black" ); WhiteLevelSlider->slotSetRange( RangeFrom, RangeTo ); WhiteLevelSlider->slotSetValue( white ); WhiteLevelSlider->slotSetTitle( "White" ); } } void QtWindowLevelControls::slotControlsChanged() { if ( !this->m_Study ) return; float black, white; if ( WindowLevelCheckBox->isChecked() ) { black = WhiteLevelSlider->GetValue() - BlackWindowSlider->GetValue() / 2; white = WhiteLevelSlider->GetValue() + BlackWindowSlider->GetValue() / 2; } else { black = BlackWindowSlider->GetValue(); white = WhiteLevelSlider->GetValue(); } const float gamma = GammaSlider->GetValue(); this->m_Study->SetBlack( black ); this->m_Study->SetWhite( white ); this->m_Study->SetGamma( gamma ); emit colormap( this->m_Study ); } void QtWindowLevelControls::slotSelectColormap( int colormapIndex ) { if ( this->m_Study ) { this->m_Study->SetStandardColormap( colormapIndex ); emit colormap( this->m_Study ); } } } // namespace cmtk cmtk-3.3.1/libs/Qt/cmtkQtWindowLevelControls.h000066400000000000000000000056451276303427400213330ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2010 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 2398 $ // // $LastChangedDate: 2010-10-05 14:54:37 -0700 (Tue, 05 Oct 2010) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkQtWindowLevelControls_h_included_ #define __cmtkQtWindowLevelControls_h_included_ #include #include #include #include #include #include #include namespace cmtk { /** \addtogroup Qt */ //@{ /// Widget for a group box with Window/Level controls. class QtWindowLevelControls : /// Inherit from Qt's group box. public QWidget { Q_OBJECT // we're using signals and slots signals: /// This signal is emitted when the controls change. void colormap( Study::SmartPtr& ); public slots: /// This signal tells the controls that the study object has changed. void slotSetStudy( Study::SmartPtr& study ); private slots: /// This slot is called when the Window/Level mode is changed. void slotSwitchModeWL( int ); /// This slot is called by the UI widgets when their values change. void slotControlsChanged(); /// This slot is called when the user picks a new colormap. void slotSelectColormap( int colormapIndex ); public: /// Constructor. QtWindowLevelControls( QWidget *const parent ); private: /// The study object that we're working on. Study::SmartPtr m_Study; /// Layout of this widget. QVBoxLayout* Layout; /// The top slider in the UI. QtSliderEntry* BlackWindowSlider; /// The bottom slider in the UI. QtSliderEntry* WhiteLevelSlider; /// The gamma slider in the UI. QtSliderEntry* GammaSlider; /// The checkbox that switches between Window/Level and Black/White mode. QCheckBox* WindowLevelCheckBox; /// The smallest value in the image. float RangeFrom; /// The largest value in the image. float RangeTo; /// The dominant width of the range (standard deviation or total width). float RangeWidth; }; //@} } // namespace cmtk #endif // #ifndef __cmtkQtWindowLevelControls_h_included_ cmtk-3.3.1/libs/Qt/cmtkQtWindowLevelDialog.cxx000066400000000000000000000035051276303427400212730ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2010 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 2398 $ // // $LastChangedDate: 2010-10-05 14:54:37 -0700 (Tue, 05 Oct 2010) $ // // $LastChangedBy: torstenrohlfing $ // */ #include "cmtkQtWindowLevelDialog.h" #include #include #include namespace cmtk { /** \addtogroup Qt */ //@{ QtWindowLevelDialog::QtWindowLevelDialog ( QWidget* parent, bool modal, Qt::WFlags f ) : QDialog( parent, f ) { this->setModal( modal ); this->setWindowIcon( QtIcons::WindowIcon() ); this->setWindowTitle( "Window/Level Control" ); QBoxLayout* layout = new QVBoxLayout( this ); Controls = new QtWindowLevelControls( this ); QObject::connect( Controls, SIGNAL( colormap( Study::SmartPtr& ) ), SIGNAL( colormapChanged( Study::SmartPtr& ) ) ); layout->addWidget( Controls ); } void QtWindowLevelDialog::slotSetStudy( Study::SmartPtr& study ) { Controls->slotSetStudy( study ); } } // namespace cmtk cmtk-3.3.1/libs/Qt/cmtkQtWindowLevelDialog.h000066400000000000000000000037441276303427400207250ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // Copyright 2004-2009 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 2398 $ // // $LastChangedDate: 2010-10-05 14:54:37 -0700 (Tue, 05 Oct 2010) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkQtWindowLevelDialog_h_included_ #define __cmtkQtWindowLevelDialog_h_included_ #include #include #include namespace cmtk { /** \addtogroup Qt */ //@{ /// Dialog with WIndow/Level and Colormap controls. class QtWindowLevelDialog : /// Inherit from Qt dialog. public QDialog { Q_OBJECT public: /// Constructor. QtWindowLevelDialog( QWidget* parent = 0, bool modal = FALSE, Qt::WFlags f = 0 ); /// Virtual destructor. virtual ~QtWindowLevelDialog() {} public slots: /// Set study object. void slotSetStudy( Study::SmartPtr& study ); signals: /// This signal is emitted when the colormap of the study has changed. void colormapChanged( Study::SmartPtr& ); private: /// The Window/Level user interface component. QtWindowLevelControls* Controls; }; //@} } // namespace cmtk #endif // #ifndef __cmtkQtWindowLevelDialog_h_included_ cmtk-3.3.1/libs/Qt/doxygen.h000066400000000000000000000022751276303427400156350ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2011 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 2734 $ // // $LastChangedDate: 2011-01-14 10:23:45 -0800 (Fri, 14 Jan 2011) $ // // $LastChangedBy: torstenrohlfing $ // */ /** \defgroup Qt cmtkQt Library * This library provides classes to interface lower-level CMTK classes with the Qt toolkit. */ cmtk-3.3.1/libs/Qt/xpm/000077500000000000000000000000001276303427400146055ustar00rootroot00000000000000cmtk-3.3.1/libs/Qt/xpm/icon_main_window.xpm000066400000000000000000000043651276303427400206660ustar00rootroot00000000000000/* XPM */ static const char * icon_main_window_xpm[] = { "16 16 100 2", " c #FFFFFF", ". c #E0E0E0", "+ c #A4A4A4", "@ c #7C7C7C", "# c #7E7E7E", "$ c #787878", "% c #7B7B7B", "& c #616161", "* c #707070", "= c #898989", "- c #ECECEC", "; c #A3A3A3", "> c #878787", ", c #A9A9A9", "' c #B3B3B3", ") c #C8C8C8", "! c #AFAFAF", "~ c #C1C1C1", "{ c #B9B9B9", "] c #ABABAB", "^ c #7D7D7D", "/ c #A0A0A0", "( c #EDEDED", "_ c #E1E1E1", ": c #D7D7D7", "< c #C2C2C2", "[ c #C3C3C3", "} c #BBBBBB", "| c #D6D6D6", "1 c #BABABA", "2 c #E9E9E9", "3 c #B5B5B5", "4 c #7F7F7F", "5 c #727272", "6 c #8C8C8C", "7 c #EAEAEA", "8 c #6F6F6F", "9 c #737373", "0 c #9D9D9D", "a c #F8F8F8", "b c #F9F9F9", "c c #0F0F0F", "d c #292929", "e c #818181", "f c #4A4A4A", "g c #9E9E9E", "h c #131313", "i c #000000", "j c #CBCBCB", "k c #999999", "l c #FCFCFC", "m c #F6F6F6", "n c #ACACAC", "o c #6E6E6E", "p c #606060", "q c #0E0E0E", "r c #525252", "s c #4B4B4B", "t c #333333", "u c #313131", "v c #F4F4F4", "w c #5C5C5C", "x c #282828", "y c #383838", "z c #3B3B3B", "A c #9F9F9F", "B c #2D2D2D", "C c #1B1B1B", "D c #2A2A2A", "E c #949494", "F c #F7F7F7", "G c #6B6B6B", "H c #595959", "I c #777777", "J c #979797", "K c #5E5E5E", "L c #848484", "M c #7A7A7A", "N c #A6A6A6", "O c #8D8D8D", "P c #AAAAAA", "Q c #B1B1B1", "R c #969696", "S c #B2B2B2", "T c #DADADA", "U c #CFCFCF", "V c #B8B8B8", "W c #BCBCBC", "X c #CDCDCD", "Y c #9A9A9A", "Z c #BDBDBD", "` c #A8A8A8", " . c #DEDEDE", ".. c #DDDDDD", "+. c #B7B7B7", "@. c #797979", "#. c #808080", "$. c #919191", "%. c #E4E4E4", "&. c #E7E7E7", " ", " ", " . + @ # $ % & * = . ", " - ; > , ' ) ! ~ { ] + ^ / ( ", "_ ' : < : ~ : [ : } : | 1 : 1 2 ", "3 ) 4 5 6 7 % 8 8 8 9 0 a 8 $ [ ", "b c d e f g h i j ! i i k i c l ", "m n o p q r h i s t i u ( i c ", "v w x y z A B C l D C E C D ", "F G @ H 0 I I J K 0 L / % 0 M b ", "N / / O 6 P / e J Q R Q P e e S ", "T / U V U W U W U 3 U X ' U ] . ", " 7 Y > , Z ) ! < W ; ` @ R .X ", " ..+.O O @.e p #.$... %.&.", " ", " "}; cmtk-3.3.1/libs/Recon/000077500000000000000000000000001276303427400144635ustar00rootroot00000000000000cmtk-3.3.1/libs/Recon/CMakeLists.txt000066400000000000000000000036071276303427400172310ustar00rootroot00000000000000## ## Copyright 1997-2009 Torsten Rohlfing ## ## Copyright 2004-2010 SRI International ## ## This file is part of the Computational Morphometry Toolkit. ## ## http://www.nitrc.org/projects/cmtk/ ## ## The Computational Morphometry Toolkit is free software: you can ## redistribute it and/or modify it under the terms of the GNU General Public ## License as published by the Free Software Foundation, either version 3 of ## the License, or (at your option) any later version. ## ## The Computational Morphometry Toolkit is distributed in the hope that it ## will be useful, but WITHOUT ANY WARRANTY; without even the implied ## warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License along ## with the Computational Morphometry Toolkit. If not, see ## . ## ## $Revision: 3161 $ ## ## $LastChangedDate: 2011-04-18 14:37:45 -0700 (Mon, 18 Apr 2011) $ ## ## $LastChangedBy: torstenrohlfing $ ## # Sources of non-templated classes. SET(cmtkRecon_SRCS cmtkInverseInterpolationVolumeReconstructionBase.cxx cmtkVolumeInjectionReconstruction.cxx ) ADD_LIBRARY(cmtkRecon ${cmtkRecon_SRCS}) TARGET_LINK_LIBRARIES(cmtkRecon cmtkRegistration cmtkBase cmtkSystem cmtkNumerics ) IF(CMTK_LIBRARY_PROPERTIES) SET_TARGET_PROPERTIES(cmtkRecon PROPERTIES ${CMTK_LIBRARY_PROPERTIES}) ENDIF(CMTK_LIBRARY_PROPERTIES) INSTALL(TARGETS cmtkRecon RUNTIME DESTINATION ${CMTK_INSTALL_BIN_DIR} COMPONENT runtime LIBRARY DESTINATION ${CMTK_INSTALL_LIB_DIR} COMPONENT runtime ARCHIVE DESTINATION ${CMTK_INSTALL_LIB_DIR} COMPONENT libraries) FILE(GLOB files_h "${CMAKE_CURRENT_SOURCE_DIR}/*.h") FILE(GLOB files_txx "${CMAKE_CURRENT_SOURCE_DIR}/*.txx") INSTALL(FILES ${files_h} ${files_txx} DESTINATION ${CMTK_INSTALL_INCLUDE_DIR}/Recon COMPONENT headers) cmtk-3.3.1/libs/Recon/cmtkDeblurringVolumeReconstruction.h000066400000000000000000000130001276303427400237340ustar00rootroot00000000000000/* // // Copyright 2016 Google, Inc. // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2011 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 5402 $ // // $LastChangedDate: 2016-01-14 20:53:49 -0800 (Thu, 14 Jan 2016) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkDeblurringVolumeReconstruction_h_included_ #define __cmtkDeblurringVolumeReconstruction_h_included_ #include #include #include #include #include #include "Numerics/ap.h" #include namespace cmtk { /** \addtogroup Recon */ //@{ /** Class for volume reconstruction using joint iterative deblurring. * *\author Torsten Rohlfing and Michael P. Hasak */ template class DeblurringVolumeReconstruction : /// Inherit from non-templated base class. public InverseInterpolationVolumeReconstructionBase { public: /// This class. typedef DeblurringVolumeReconstruction Self; /// Superclass. typedef InverseInterpolationVolumeReconstructionBase Superclass; /** Constructor for interleaved image motion correction. * Take original image. Set interleaved image count and stacking axis. Construct separate 3D image stacks for interleaved passes. Allocate corrected image. *\param originalImage Smart pointer to the original image with motion artifacts. *\param numberOfPasses The number of interleaved passes, i.e., the number of pass images that comprise the final image. *\param interleaveAxis Between-slice axis of the interleaved acquisition. *\param psfScale Optional spatial scale factor for the PSF. Values larger than 1 increase PSF size, smaller than one decrease it. */ DeblurringVolumeReconstruction( const UniformVolume* originalImage, const Types::GridIndexType numberOfPasses, const int interleaveAxis, const Types::Coordinate psfScale = 1.0 ) : InverseInterpolationVolumeReconstructionBase( originalImage, numberOfPasses, interleaveAxis ) { this->m_FunctionAndGradient = new typename Self::FunctionAndGradient( this ); cmtk::FixedVector<3,cmtk::Types::Coordinate> scale( originalImage->m_Delta ); scale *= psfScale; scale[interleaveAxis] *= numberOfPasses; for ( size_t i = 0; i < numberOfPasses; ++i ) { typename TPSF::SmartPtr psf( new TPSF( scale ) ); this->m_PassImagePSF.push_back( psf ); } } /** Constructor for general volume reconstruction from multiple acquired images. */ DeblurringVolumeReconstruction( const UniformVolume* reconstructionGrid, std::vector& images, const Vector3D& psfSize ) : InverseInterpolationVolumeReconstructionBase( reconstructionGrid, images ) { this->m_FunctionAndGradient = new typename Self::FunctionAndGradient( this ); for ( size_t i = 0; i < images.size(); ++i ) { const Vector3D scale( psfSize ); typename TPSF::SmartPtr psf( new TPSF( scale ) ); this->m_PassImagePSF.push_back( psf ); } } private: /// Scale factors for acquired images point spread functions. std::vector m_PassImagePSF; /// Blur corrected image into pass images. void Blur( const ap::real_1d_array& reconstructedPixelArray ); /// Compute gradient of approximation error w.r.t. corrected image pixels. void ComputeErrorGradientImage( ap::real_1d_array& g ); /** Get a bounding box of the transformed pass-image pixel neighborhood. * (Transformed from pass-image space to corrected-image space) */ void GetBoundingBoxOfXformedPassNeighborhood( Types::GridIndexType* region, const UniformVolume* correctedImage, const Vector3D& currentPassVoxel, const TPSF* psf, const AffineXform* passToCorrectedXform, const DataGrid::IndexType& correctedImageDims ) const; /// Glue class for function and gradient evaluation. class FunctionAndGradient : /// Inherit from virtual base class. public ap::FunctionAndGradient { public: /// Function class type. typedef DeblurringVolumeReconstruction FunctionType; /// Constructor. FunctionAndGradient( FunctionType* function ) { this->m_Function = function; } /// Evaluate function and gradient. virtual void Evaluate( const ap::real_1d_array& x, ap::real_value_type& f, ap::real_1d_array& g ); private: /// Pointer to actual function class. FunctionType* m_Function; }; /// Give function and gradient evaluator private access. friend class Self::FunctionAndGradient; }; #include "cmtkDeblurringVolumeReconstruction.txx" //@} } // namespace cmtk #endif // #ifndef __cmtkDeblurringVolumeReconstruction_h_included_ cmtk-3.3.1/libs/Recon/cmtkDeblurringVolumeReconstruction.txx000066400000000000000000000311001276303427400243310ustar00rootroot00000000000000/* // // Copyright 2016 Google, Inc. // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2012 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 5402 $ // // $LastChangedDate: 2016-01-14 20:53:49 -0800 (Thu, 14 Jan 2016) $ // // $LastChangedBy: torstenrohlfing $ // */ #include #include #include #undef CMTK_USE_GCD #ifdef CMTK_USE_GCD # include #endif template void DeblurringVolumeReconstruction ::Blur( const ap::real_1d_array& reconstructedPixelArray ) { this->m_InterpolatedPassImages.clear(); const UniformVolume* correctedImage = this->m_CorrectedImage; const DataGrid::IndexType& correctedImageDims = correctedImage->GetDims(); for ( Types::GridIndexType pass = 0; pass < this->m_NumberOfPasses; ++pass ) { const UniformVolume* passImage = this->m_OriginalPassImages[pass]; UniformVolume::SmartPtr result( passImage->CloneGrid() ); result->CreateDataArray( TYPE_FLOAT, true/*setToZero*/ ); const DataGrid::IndexType& passImageDims = passImage->GetDims(); const Types::GridIndexType passImageDimsX = passImageDims[0], passImageDimsY = passImageDims[1], passImageDimsZ = passImageDims[2]; const Types::GridIndexType passImageDimsXY = passImageDimsX*passImageDimsY; const Types::GridIndexType passImageDimsXYZ = passImageDimsXY*passImageDimsZ; const TPSF* passImagePSF = this->m_PassImagePSF[pass]; const AffineXform* correctedToPassXform = AffineXform::SmartPtr::DynamicCastFrom( this->m_TransformationsToPassImages[pass] ); const AffineXform* passToCorrectedXform = AffineXform::SmartPtr::DynamicCastFrom( this->m_TransformationsToPassImages[pass] )->GetInverse(); #ifdef CMTK_USE_GCD const size_t stride = passImageDimsXYZ / (2 * Threads::GetNumberOfProcessors() ); dispatch_apply( passImageDimsXYZ / stride, dispatch_get_global_queue(0, 0), ^(size_t i ) { const size_t last = std::min( stride * (i+1), passImageDimsXYZ ); for ( size_t offset = i * stride; offset < last; ++offset ) #else #pragma omp parallel for for ( Types::GridIndexType offset = 0; offset < static_cast( passImageDimsXYZ ); ++offset ) #endif { const Types::GridIndexType curPassVox[3] = { (offset % passImageDimsXY) % passImageDimsX, (offset % passImageDimsXY) / passImageDimsX, (offset / passImageDimsXY) }; Types::DataItem blurredData = 0; Types::DataItem totalWeight = 0; /* Get a bounding box for the transformed neighborhood (pass- to corrected-image) */ const UniformVolume::CoordinateVectorType curPassVec3D = passImage->GetGridLocation( curPassVox[0], curPassVox[1], curPassVox[2] ); Types::GridIndexType corrBoundingBox[6]; this->GetBoundingBoxOfXformedPassNeighborhood( corrBoundingBox, correctedImage, curPassVec3D, passImagePSF, passToCorrectedXform, correctedImageDims ); /* Iterate through the bounding box of the blur-weights matrix, * assembling the weighted sum of the neighbors, and the sum of weights */ Types::DataItem data; for (Types::GridIndexType k = corrBoundingBox[2]; k <= corrBoundingBox[5]; ++k) { for (Types::GridIndexType j = corrBoundingBox[1]; j <= corrBoundingBox[4]; ++j) { for (Types::GridIndexType i = corrBoundingBox[0]; i <= corrBoundingBox[3]; ++i) { const UniformVolume::CoordinateVectorType curNeighbVec3D = correctedToPassXform->Apply( correctedImage->GetGridLocation( i, j, k ) ); Types::Coordinate from[3], to[3]; Types::GridIndexType neighborPassVox[3]; if ( passImage->FindVoxel( curNeighbVec3D, neighborPassVox, from, to ) ) { Types::Coordinate weightIJK = 1; for ( int dim = 0; dim < 3; ++dim ) { const Types::Coordinate displacement = curNeighbVec3D[dim] - curPassVec3D[dim]; weightIJK *= passImagePSF->GetWeight( dim, displacement ); } totalWeight += weightIJK; data = reconstructedPixelArray( 1+correctedImage->GetOffsetFromIndex( i, j, k ) ); blurredData += data * weightIJK; } } } } /* Assign the blurred value to the result */ if ( totalWeight == 0 ) result->GetData()->SetPaddingAt( offset ); else result->SetDataAt( blurredData / totalWeight, offset ); } this->m_InterpolatedPassImages.push_back( result ); } #ifdef CMTK_USE_GCD } ); #endif } template void DeblurringVolumeReconstruction ::ComputeErrorGradientImage( ap::real_1d_array& g ) { const UniformVolume* correctedImage = this->m_CorrectedImage; const size_t numberOfPixels = correctedImage->GetNumberOfPixels(); for ( size_t i = 1; i <= numberOfPixels; ++i ) g(i) = 0; const DataGrid::IndexType& correctedImageDims = correctedImage->GetDims(); const Types::GridIndexType correctedImageDimsX = correctedImageDims[0], correctedImageDimsY = correctedImageDims[1]; const Types::GridIndexType correctedImageDimsXY = correctedImageDimsX*correctedImageDimsY; for ( Types::GridIndexType pass = 0; pass < this->m_NumberOfPasses; ++pass ) { const Types::Coordinate passImageWeight = this->m_PassWeights[pass]; if ( passImageWeight > 0 ) { const UniformVolume* differencePassImage = this->m_DifferencePassImages[pass]; const UniformVolume* blurredPassImage = this->m_InterpolatedPassImages[pass]; const TPSF* passImagePSF = this->m_PassImagePSF[pass]; const AffineXform* transformationToPassImage = AffineXform::SmartPtr::DynamicCastFrom( this->m_TransformationsToPassImages[pass] ); #pragma omp parallel for for ( Types::GridIndexType offset = 0; offset < static_cast( numberOfPixels ); ++offset ) { const Types::GridIndexType corrCenterVox[3] = { (offset % correctedImageDimsXY) % correctedImageDimsX, (offset % correctedImageDimsXY) / correctedImageDimsX, (offset / correctedImageDimsXY) }; const UniformVolume::CoordinateVectorType curCenterVec3D = transformationToPassImage->Apply( correctedImage->GetGridLocation( corrCenterVox[0], corrCenterVox[1], corrCenterVox[2] ) ); /* compute neighborhood in blurred (pass) image from which blurred pixels * are affected by current corrected image pixel */ Types::GridIndexType passRegionCorners[2][3]; for ( int dim = 0; dim < 3; ++dim ) { passRegionCorners[0][dim] = blurredPassImage->GetCoordIndex( dim, curCenterVec3D[dim] - passImagePSF->GetTruncationRadius( dim ) ); passRegionCorners[1][dim] = blurredPassImage->GetCoordIndex( dim, curCenterVec3D[dim] + passImagePSF->GetTruncationRadius( dim ) ); } /* Iterate through the neighborhood in the blurred (pass) image, * assembling a value for the gradient at this offset of the corrected * image */ Types::DataItem gradientData = 0; for (Types::GridIndexType k = passRegionCorners[0][2]; k <= passRegionCorners[1][2]; ++k) { for (Types::GridIndexType j = passRegionCorners[0][1]; j <= passRegionCorners[1][1]; ++j) { for (Types::GridIndexType i = passRegionCorners[0][0]; i <= passRegionCorners[1][0]; ++i) { Types::DataItem differenceData; // if differenceData==0, we can skip this, too if ( differencePassImage->GetDataAt( differenceData, i, j, k ) && (differenceData !=0) ) { Types::DataItem weight = 0; const UniformVolume::CoordinateVectorType passNeighbVec3D = blurredPassImage->GetGridLocation( i, j, k ); weight = passImageWeight; for ( int dim = 0; dim < 3; ++dim ) { const Types::Coordinate displacement = curCenterVec3D[dim] - passNeighbVec3D[dim]; weight *= passImagePSF->GetWeight( dim, displacement ); } gradientData += weight * differenceData; } } } } if ( this->m_FourthOrderError ) g(offset+1) += 4 * (gradientData*gradientData*gradientData) / numberOfPixels; else g(offset+1) += 2 * gradientData / numberOfPixels; } } } } template void DeblurringVolumeReconstruction ::GetBoundingBoxOfXformedPassNeighborhood ( Types::GridIndexType* correctedImageBoundingBox, const UniformVolume* correctedImage, const Vector3D& currentPassVoxel, const TPSF* psf, const AffineXform* passToCorrectedXform, const DataGrid::IndexType& correctedImageDims ) const { /* Compute the blurring neighborhood in the pass image */ Types::Coordinate corners[2][3]; for ( int dim = 0; dim < 3; ++dim ) { const Types::Coordinate radius = psf->GetTruncationRadius( dim ); corners[0][dim] = currentPassVoxel[dim] - radius; corners[1][dim] = currentPassVoxel[dim] + radius; } /* Make corner vectors out of that 2-D array and put them in a 1-D array, * transforming each to the space of the corrected image. */ Vector3D corners3D[8]; int neighborIdx = 0; for ( int a = 0; a < 2 ; ++a ) { for ( int b = 0; b < 2 ; ++b ) { for ( int c = 0; c < 2; ++c, ++neighborIdx ) { corners3D[neighborIdx][0] = corners[a][0]; corners3D[neighborIdx][1] = corners[b][1]; corners3D[neighborIdx][2] = corners[c][2]; corners3D[neighborIdx] = passToCorrectedXform->Apply( corners3D[neighborIdx] ); } } } /* Compute the bounding box of that transformed region */ Vector3D bboxMin = corners3D[0]; Vector3D bboxMax = corners3D[0]; for ( int m = 1; m < 8; ++m ) { for ( int o = 0; o < 3; ++o ) { if ( corners3D[m][o] < bboxMin[o] ) { bboxMin[o] = corners3D[m][o]; } else if ( corners3D[m][o] > bboxMax[o] ) { bboxMax[o] = corners3D[m][o]; } } } /* Put the voxel indices corresponding to the corners of that bounding box * into correctedImageBoundingBox */ correctedImage->GetVoxelIndexNoBounds( bboxMin, correctedImageBoundingBox+0 ); correctedImage->GetVoxelIndexNoBounds( bboxMax, correctedImageBoundingBox+3 ); /* Clip bounding box (now correctedImageBoundingBox) against corrected image boundaries */ for ( int dim = 0; dim < 3; ++dim ) { correctedImageBoundingBox[dim] = std::max( correctedImageBoundingBox[dim], 0 ); // increment upper indexes by one to compensate for floating point truncation in pixel index lookup. correctedImageBoundingBox[dim+3] = std::min( correctedImageBoundingBox[dim+3]+1, correctedImageDims[dim]-1 ); } } template void DeblurringVolumeReconstruction ::FunctionAndGradient ::Evaluate( const ap::real_1d_array& x, ap::real_value_type& f, ap::real_1d_array& g ) { this->m_Function->Blur( x ); this->m_Function->ComputeApproximationError(); this->m_Function->ComputeErrorGradientImage( g ); const ap::real_value_type msd = f = this->m_Function->GetMeanSquaredError(); ap::real_value_type lnorm = 0; if ( this->m_Function->m_ConstraintWeightLNorm > 0 ) { f += this->m_Function->m_ConstraintWeightLNorm * (lnorm = this->m_Function->ComputeCorrectedImageLaplacianNorm( x )); this->m_Function->AddLaplacianGradientImage( g, x, this->m_Function->m_ConstraintWeightLNorm ); } if ( this->m_Function->GetMaximumError() <= this->m_Function->m_LowestMaxError ) { this->m_Function->m_LowestMaxError = this->m_Function->GetMaximumError(); const Types::GridIndexType numberOfPixels = this->m_Function->m_CorrectedImage->GetNumberOfPixels(); for ( Types::GridIndexType i = 1; i <= numberOfPixels; ++i ) this->m_Function->m_CorrectedImage->SetDataAt( x(i), i-1 ); this->m_Function->m_LowestMaxErrorImage = UniformVolume::SmartPtr( this->m_Function->m_CorrectedImage->Clone( true /*copyData*/ ) ); } StdOut << "f " << f << " MSD " << msd << " MAX " << this->m_Function->GetMaximumError() << " KLD " << this->m_Function->GetOriginalToCorrectedImageKLD( x ) << " LNORM " << lnorm << "\n"; } cmtk-3.3.1/libs/Recon/cmtkInverseInterpolationVolumeReconstruction.h000066400000000000000000000121571276303427400260360ustar00rootroot00000000000000/* // // Copyright 2016 Google, Inc. // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2010 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 5401 $ // // $LastChangedDate: 2016-01-14 19:47:47 -0800 (Thu, 14 Jan 2016) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkInverseInterpolationVolumeReconstruction_h_included_ #define __cmtkInverseInterpolationVolumeReconstruction_h_included_ #include #include #include #include #include #include #include "Numerics/ap.h" #include namespace cmtk { /** \addtogroup Recon */ //@{ /** Class for volume reconstruction using inverse interpolation. *\author Torsten Rohlfing */ template class InverseInterpolationVolumeReconstruction : /// Inherit from non-templated base class. public InverseInterpolationVolumeReconstructionBase { public: /// This class. typedef InverseInterpolationVolumeReconstruction Self; /// Superclass. typedef InverseInterpolationVolumeReconstructionBase Superclass; /** Constructor for interleaved image motion correction. * Take original image. Set interleaved image count and stacking axis. Construct separate 3D image stacks for interleaved passes. Allocate corrected image. *\param originalImage Smart pointer to the original image with motion artifacts. *\param numberOfPasses The number of interleaved passes, i.e., the number of pass images that comprise the final image. *\param interleaveAxis Between-slice axis of the interleaved acquisition. */ InverseInterpolationVolumeReconstruction( const UniformVolume* originalImage, const Types::GridIndexType numberOfPasses, const int interleaveAxis ) : InverseInterpolationVolumeReconstructionBase( originalImage, numberOfPasses, interleaveAxis ) { this->m_FunctionAndGradient = new typename Self::FunctionAndGradient( this ); } /** Constructor for general volume reconstruction from multiple acquired images. */ InverseInterpolationVolumeReconstruction( const UniformVolume* reconstructionGrid, std::vector& images ) : InverseInterpolationVolumeReconstructionBase( reconstructionGrid, images ) { this->m_FunctionAndGradient = new typename Self::FunctionAndGradient( this ); } /// Destructor: delete function evaluator object. virtual ~InverseInterpolationVolumeReconstruction() { delete this->m_FunctionAndGradient; } private: /// Interpolates subimages from the existing corrected image. void Interpolation( const ap::real_1d_array& reconstructedPixelArray ); /// Compute gradient of approximation error w.r.t. corrected image pixels. void ComputeErrorGradientImage( ap::real_1d_array& g ); /// Get pass image region that contains all pixels dependent on currently considered corrected image pixel. void GetPassImageDependentPixelRegion ( Types::GridIndexType* region, const UniformVolume* correctedImage, const Types::GridIndexType* currentCorrectedGridPoint, const UniformVolume* passImage, const AffineXform* transformationToPassImage, const DataGrid::IndexType& passImageDims ); /// Glue class for function and gradient evaluation. class FunctionAndGradient : /// Inherit from virtual base class. public ap::FunctionAndGradient { public: /// Function class type. typedef InverseInterpolationVolumeReconstruction FunctionType; /// Constructor. FunctionAndGradient( FunctionType* function ) { this->m_Function = function; } /// Evaluate function and gradient. virtual void Evaluate( const ap::real_1d_array& x, ap::real_value_type& f, ap::real_1d_array& g ); /// Get notified when L-BFGS-B goes into next iteration. virtual void NextIteration( const int iteration ) { Progress::SetProgress( iteration ); } private: /// Pointer to actual function class. FunctionType* m_Function; }; /// Give function and gradient evaluator private access. friend class Self::FunctionAndGradient; }; //@} } // namespace cmtk #include "cmtkInverseInterpolationVolumeReconstruction.txx" #endif // #ifndef __cmtkInverseInterpolationVolumeReconstruction_h_included_ cmtk-3.3.1/libs/Recon/cmtkInverseInterpolationVolumeReconstruction.txx000066400000000000000000000326511276303427400264330ustar00rootroot00000000000000/* // // Copyright 2016 Google, Inc. // // Copyright 1997-2010 Torsten Rohlfing // // Copyright 2004-2013 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 5402 $ // // $LastChangedDate: 2016-01-14 20:53:49 -0800 (Thu, 14 Jan 2016) $ // // $LastChangedBy: torstenrohlfing $ // */ #include #include #include #include namespace cmtk { template void InverseInterpolationVolumeReconstruction ::Interpolation( const ap::real_1d_array& reconstructedPixelArray ) { this->m_InterpolatedPassImages.clear(); for ( Types::GridIndexType pass = 0; pass < this->m_NumberOfPasses; ++pass ) { const UniformVolume* passImage = this->m_OriginalPassImages[pass]; UniformVolume::SmartPtr result( passImage->CloneGrid() ); result->CreateDataArray( TYPE_FLOAT, true/*setToZero*/ ); const DataGrid::IndexType& passImageDims = passImage->GetDims(); const Types::GridIndexType passImageDimsX = passImageDims[0], passImageDimsY = passImageDims[1], passImageDimsZ = passImageDims[2]; const DataGrid::IndexType& correctedImageDims = this->m_CorrectedImage->GetDims(); const Types::GridIndexType correctedImageDimsX = correctedImageDims[0], correctedImageDimsY = correctedImageDims[1], correctedImageDimsZ = correctedImageDims[2]; const AffineXform* inversePassXform = dynamic_cast( this->m_TransformationsToPassImages[pass].GetPtr() ); if ( ! inversePassXform ) { StdErr << "ERROR: dynamic_cast to AffineXform ptr failed in InverseInterpolationVolumeReconstruction::Interpolation"; throw ExitException( 1 ); } const AffineXform* passXform = inversePassXform->GetInverse(); #pragma omp parallel for for ( Types::GridIndexType x = 0; x < passImageDimsX; ++x ) { Types::GridIndexType correctedImageGridPoint[3]; Types::Coordinate from[3], to[3]; for ( Types::GridIndexType y = 0; y < passImageDimsY; ++y ) { for ( Types::GridIndexType z = 0; z < passImageDimsZ; ++z ) { Types::DataItem interpolatedData = 0; Types::Coordinate totalWeight = 0; const UniformVolume::CoordinateVectorType v = passXform->Apply( passImage->GetGridLocation( x, y, z ) ); if ( this->m_CorrectedImage->FindVoxel( v, correctedImageGridPoint, from, to ) ) { const Types::GridIndexType xx = correctedImageGridPoint[0] + 1 - TInterpolator::RegionSizeLeftRight; const Types::GridIndexType yy = correctedImageGridPoint[1] + 1 - TInterpolator::RegionSizeLeftRight; const Types::GridIndexType zz = correctedImageGridPoint[2] + 1 - TInterpolator::RegionSizeLeftRight; Types::DataItem data; Types::Coordinate difference[3]; Types::Coordinate interpolationWeights[3][2 * TInterpolator::RegionSizeLeftRight]; for ( int n = 0; n < 3; ++n ) { difference[n] = (v[n] - from[n]) / (to[n] - from[n]); for ( Types::GridIndexType m = 1-TInterpolator::RegionSizeLeftRight; m <= TInterpolator::RegionSizeLeftRight; ++m ) { interpolationWeights[n][m+TInterpolator::RegionSizeLeftRight-1] = TInterpolator::GetWeight(m, difference[n]); } } const Types::GridIndexType iMin = std::max( 0, -xx ); const Types::GridIndexType iMax = std::min( 2 * TInterpolator::RegionSizeLeftRight, correctedImageDimsX - xx ); const Types::GridIndexType jMin = std::max( 0, -yy ); const Types::GridIndexType jMax = std::min( 2 * TInterpolator::RegionSizeLeftRight, correctedImageDimsY - yy ); const Types::GridIndexType kMin = std::max( 0, -zz ); const Types::GridIndexType kMax = std::min( 2 * TInterpolator::RegionSizeLeftRight, correctedImageDimsZ - zz ); for ( Types::GridIndexType k = kMin; k < kMax; ++k ) { for ( Types::GridIndexType j = jMin; j < jMax; ++j ) { const Types::Coordinate weightJK = interpolationWeights[1][j] * interpolationWeights[2][k]; for ( Types::GridIndexType i = iMin; i < iMax; ++i ) { const Types::Coordinate weightIJK = interpolationWeights[0][i] * weightJK; data = reconstructedPixelArray( 1+this->m_CorrectedImage->GetOffsetFromIndex( xx + i, yy + j, zz + k ) ); interpolatedData = interpolatedData + data * weightIJK; totalWeight += weightIJK; } } } } if ( totalWeight == 0 ) result->GetData()->SetPaddingAt( result->GetOffsetFromIndex( x, y, z ) ); else result->SetDataAt( interpolatedData / totalWeight, x, y, z ); } } } this->m_InterpolatedPassImages.push_back( result ); } } template void InverseInterpolationVolumeReconstruction ::ComputeErrorGradientImage( ap::real_1d_array& g ) { const UniformVolume* correctedImage = this->m_CorrectedImage; const size_t numberOfPixels = correctedImage->GetNumberOfPixels(); for ( size_t i = 1; i <= numberOfPixels; ++i ) g(i) = 0; const DataGrid::IndexType& correctedImageDims = correctedImage->GetDims(); const Types::GridIndexType correctedImageDimsX = correctedImageDims[0], correctedImageDimsY = correctedImageDims[1]; const Types::GridIndexType correctedImageDimsXY = correctedImageDimsX*correctedImageDimsY; for ( Types::GridIndexType pass = 0; pass < this->m_NumberOfPasses; ++pass ) { const UniformVolume* differencePassImage = this->m_DifferencePassImages[pass]; const UniformVolume* interpolatedPassImage = this->m_InterpolatedPassImages[pass]; const AffineXform* transformationToPassImage = dynamic_cast( this->m_TransformationsToPassImages[pass].GetPtr() ); if ( ! transformationToPassImage ) { StdErr << "ERROR: dynamic_cast to AffineXform ptr failed in InverseInterpolationVolumeReconstruction::ComputeErrorGradientImage"; throw ExitException( 1 ); } const AffineXform* inverseTransformationToPassImage = transformationToPassImage->GetInverse(); const DataGrid::IndexType& passImageDims = this->m_InterpolatedPassImages[pass]->GetDims(); const Types::Coordinate passImageWeight = this->m_PassWeights[pass]; if ( passImageWeight > 0 ) { #pragma omp parallel for for ( Types::GridIndexType offset = 0; offset < static_cast( numberOfPixels ); ++offset ) { const Types::GridIndexType correctedImageCurrentGridPoint[3] = { (offset % correctedImageDimsXY) % correctedImageDimsX, (offset % correctedImageDimsXY) / correctedImageDimsX, (offset / correctedImageDimsXY) }; Types::GridIndexType passImageDependentRegion[6]; this->GetPassImageDependentPixelRegion( passImageDependentRegion, correctedImage, correctedImageCurrentGridPoint, interpolatedPassImage, transformationToPassImage, passImageDims ); Types::DataItem gradientData = 0; Types::Coordinate from[3], to[3]; for (Types::GridIndexType k = passImageDependentRegion[2]; k <= passImageDependentRegion[5]; ++k) { for (Types::GridIndexType j = passImageDependentRegion[1]; j <= passImageDependentRegion[4]; ++j) { for (Types::GridIndexType i = passImageDependentRegion[0]; i <= passImageDependentRegion[3]; ++i) { Types::DataItem differenceData; if ( differencePassImage->GetDataAt( differenceData, i, j, k ) && (differenceData !=0) ) // if differenceData==0, we can skip this, too { Types::DataItem weight = 0; const UniformVolume::CoordinateVectorType v = inverseTransformationToPassImage->Apply( interpolatedPassImage->GetGridLocation( i, j, k ) ); Types::GridIndexType correctedImageGridPoint[3]; if ( correctedImage->FindVoxel( v, correctedImageGridPoint, from, to ) ) { const Types::GridIndexType correctedImageDifference[3] = { correctedImageCurrentGridPoint[0] - correctedImageGridPoint[0], correctedImageCurrentGridPoint[1] - correctedImageGridPoint[1], correctedImageCurrentGridPoint[2] - correctedImageGridPoint[2], }; if ( correctedImageDifference[0] > -TInterpolator::RegionSizeLeftRight && correctedImageDifference[1] > -TInterpolator::RegionSizeLeftRight && correctedImageDifference[2] > -TInterpolator::RegionSizeLeftRight && correctedImageDifference[0] <= TInterpolator::RegionSizeLeftRight && correctedImageDifference[1] <= TInterpolator::RegionSizeLeftRight && correctedImageDifference[2] <= TInterpolator::RegionSizeLeftRight ) { weight = passImageWeight; for ( int n = 0; n < 3; ++n ) { const Types::Coordinate relative = (v[n] - from[n]) / (to[n] - from[n]); weight *= TInterpolator::GetWeight( correctedImageDifference[n], relative ); } } } gradientData += weight * differenceData; } } } } if ( this->m_FourthOrderError ) g(offset+1) += 4 * (gradientData*gradientData*gradientData) / numberOfPixels; else g(offset+1) += 2 * gradientData / numberOfPixels; } } } } template void InverseInterpolationVolumeReconstruction ::GetPassImageDependentPixelRegion ( Types::GridIndexType* region, const UniformVolume* correctedImage, const Types::GridIndexType* currentCorrectedGridPoint, const UniformVolume* passImage, const AffineXform* transformationToPassImage, const DataGrid::IndexType& passImageDims ) { // compute neighborhood in corrected image from which interpolated pixels are affected by current corrected image pixel Types::GridIndexType corners[2][3]; for ( int dim = 0; dim < 3; ++dim ) { corners[0][dim] = std::max( currentCorrectedGridPoint[dim] - TInterpolator::RegionSizeLeftRight, 0 ); corners[1][dim] = std::min( currentCorrectedGridPoint[dim] + TInterpolator::RegionSizeLeftRight, correctedImage->GetDims()[dim]-1 ); } UniformVolume::CoordinateVectorType corners3D[8]; int neighborIdx = 0; for ( int a = 0; a < 2 ; ++a ) { for ( int b = 0; b < 2 ; ++b ) { for ( int c = 0; c < 2; ++c, ++neighborIdx ) { corners3D[neighborIdx] = transformationToPassImage->Apply( correctedImage->GetGridLocation( corners[a][0], corners[b][1], corners[c][2] ) ); } } } // now get bounding box of transformed region that is aligned with pass image Vector3D bboxMin = corners3D[0]; Vector3D bboxMax = corners3D[0]; for ( int m = 1; m < 8; ++m ) { for ( int o = 0; o < 3; ++o ) { if ( corners3D[m][o] < bboxMin[o] ) { bboxMin[o] = corners3D[m][o]; } else if ( corners3D[m][o] > bboxMax[o] ) { bboxMax[o] = corners3D[m][o]; } } } // get pass image pixels indexes corresponding to bounding box passImage->GetVoxelIndexNoBounds( bboxMin, region+0 ); passImage->GetVoxelIndexNoBounds( bboxMax, region+3 ); // clip bounding box against pass image boundaries for ( int dim = 0; dim < 3; ++dim ) { region[dim] = std::max( region[dim], 0 ); // increment upper indexes by one to compensate for floating point truncation in pixel index lookup. region[dim+3] = std::min( region[dim+3]+1, passImageDims[dim]-1 ); } } template void InverseInterpolationVolumeReconstruction ::FunctionAndGradient ::Evaluate( const ap::real_1d_array& x, ap::real_value_type& f, ap::real_1d_array& g ) { this->m_Function->Interpolation( x ); this->m_Function->ComputeApproximationError(); this->m_Function->ComputeErrorGradientImage( g ); const ap::real_value_type msd = f = this->m_Function->GetMeanSquaredError(); ap::real_value_type lnorm = 0; if ( this->m_Function->m_ConstraintWeightLNorm > 0 ) { f += this->m_Function->m_ConstraintWeightLNorm * (lnorm = this->m_Function->ComputeCorrectedImageLaplacianNorm( x )); this->m_Function->AddLaplacianGradientImage( g, x, this->m_Function->m_ConstraintWeightLNorm ); } if ( this->m_Function->GetMaximumError() <= this->m_Function->m_LowestMaxError ) { this->m_Function->m_LowestMaxError = this->m_Function->GetMaximumError(); const Types::GridIndexType numberOfPixels = this->m_Function->m_CorrectedImage->GetNumberOfPixels(); for ( Types::GridIndexType i = 1; i <= numberOfPixels; ++i ) this->m_Function->m_CorrectedImage->SetDataAt( x(i), i-1 ); this->m_Function->m_LowestMaxErrorImage = UniformVolume::SmartPtr( this->m_Function->m_CorrectedImage->Clone( true /*copyData*/ ) ); } StdOut << "f " << f << " MSD " << msd << " MAX " << this->m_Function->GetMaximumError() << " KLD " << this->m_Function->GetOriginalToCorrectedImageKLD( x ) << " LNORM " << lnorm << "\n"; } } // namespace cmtk cmtk-3.3.1/libs/Recon/cmtkInverseInterpolationVolumeReconstructionBase.cxx000066400000000000000000000122761276303427400272060ustar00rootroot00000000000000/* // // Copyright 2016 Google, Inc. // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2010, 2013 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 5401 $ // // $LastChangedDate: 2016-01-14 19:47:47 -0800 (Thu, 14 Jan 2016) $ // // $LastChangedBy: torstenrohlfing $ // */ #include #include "cmtkInverseInterpolationVolumeReconstructionBase.h" #include #include namespace cmtk { InverseInterpolationVolumeReconstructionBase ::InverseInterpolationVolumeReconstructionBase( const UniformVolume* originalImage, const Types::GridIndexType interleaveFactor, const int interleaveAxis ) : VolumeInjectionReconstruction( originalImage, interleaveFactor, interleaveAxis ), m_RegionalIntensityTruncation( true ), m_LowestMaxError( 1e12 ), m_FourthOrderError( false ), m_ConstraintWeightLNorm( 0.0 ), m_MeanSquaredError( 0.0 ), m_MaximumError( 0.0 ), m_FunctionAndGradient( NULL ) { } InverseInterpolationVolumeReconstructionBase ::InverseInterpolationVolumeReconstructionBase( const UniformVolume* reconstructionGrid, std::vector& images ) : VolumeInjectionReconstruction( reconstructionGrid, images ), m_RegionalIntensityTruncation( true ), m_LowestMaxError( 1e12 ), m_FourthOrderError( false ), m_ConstraintWeightLNorm( 0.0 ), m_MeanSquaredError( 0.0 ), m_MaximumError( 0.0 ), m_FunctionAndGradient( NULL ) { } double InverseInterpolationVolumeReconstructionBase ::ComputeApproximationError() { this->m_MeanSquaredError = 0; this->m_MaximumError = 0; this->m_DifferencePassImages.clear(); double squaredError = 0; size_t totalNumberOfPixels = 0; for ( int pass = 0; pass < this->m_NumberOfPasses; ++pass ) { UniformVolume::SmartPtr differencePassImage( this->m_InterpolatedPassImages[pass]->CloneGrid()); differencePassImage->CreateDataArray( TYPE_FLOAT, true/*setToZero*/ ); const int numberOfPixels = this->m_InterpolatedPassImages[pass]->GetNumberOfPixels(); for ( int idx = 0; idx < numberOfPixels; ++idx ) { Types::DataItem originalData, interpolatedData; if ( !this->m_OriginalPassImages[pass]->GetDataAt( originalData, idx ) ) originalData = 0.0; if ( this->m_InterpolatedPassImages[pass]->GetDataAt( interpolatedData, idx ) ) { const double difference = interpolatedData - originalData; differencePassImage->SetDataAt( difference, idx ); if ( this->m_FourthOrderError ) squaredError += difference*difference*difference*difference; else squaredError += difference*difference; this->m_MaximumError = std::max( fabs(difference), this->m_MaximumError ); ++totalNumberOfPixels; } else { differencePassImage->GetData()->SetPaddingAt( idx ); } } this->m_DifferencePassImages.push_back( differencePassImage ); } if ( totalNumberOfPixels ) this->m_MeanSquaredError = squaredError / totalNumberOfPixels; else this->m_MeanSquaredError = 0.0; return this->m_MeanSquaredError; } void InverseInterpolationVolumeReconstructionBase ::Optimize( const int numberOfIterations ) { const int numberOfPixels = this->m_CorrectedImage->GetNumberOfPixels(); ap::real_1d_array x; x.setbounds( 1, numberOfPixels ); for ( int i = 1; i <= numberOfPixels; ++i ) { x(i) = this->m_CorrectedImage->GetDataAt( i-1 ); } const int nbdAll = this->m_RegionalIntensityTruncation ? 2 : 0; ap::integer_1d_array nbd; nbd.setbounds( 1, numberOfPixels ); for ( int i = 1; i <= numberOfPixels; ++i ) { nbd(i) = nbdAll; if ( this->m_NeighorhoodMinPixelValues(i) > this->m_NeighorhoodMaxPixelValues(i) ) { this->m_NeighorhoodMinPixelValues(i) = this->m_OriginalImageRange.m_LowerBound; this->m_NeighorhoodMaxPixelValues(i) = this->m_OriginalImageRange.m_UpperBound; } } Progress::Begin( 0, numberOfIterations, 1, "Inverse Interpolation" ); int info; ap::lbfgsbminimize( this->m_FunctionAndGradient, numberOfPixels, 5, x, 1e-10 /*epsg*/, 1e-10 /*epsf*/, 1e-10 /*epsx*/, numberOfIterations, nbd, this->m_NeighorhoodMinPixelValues, this->m_NeighorhoodMaxPixelValues, info ); Progress::Done(); if ( info < 0 ) StdErr << "ERROR: lbfgsbminimize returned status code " << info << "\n"; else for ( int i = 1; i <= numberOfPixels; ++i ) this->m_CorrectedImage->SetDataAt( x(i), i-1 ); } } // namespace cmtk cmtk-3.3.1/libs/Recon/cmtkInverseInterpolationVolumeReconstructionBase.h000066400000000000000000000135601276303427400266300ustar00rootroot00000000000000/* // // Copyright 2016 Google, Inc. // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2010 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 5401 $ // // $LastChangedDate: 2016-01-14 19:47:47 -0800 (Thu, 14 Jan 2016) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkInverseInterpolationVolumeReconstructionBase_h_included_ #define __cmtkInverseInterpolationVolumeReconstructionBase_h_included_ #include #include #include #include #include #include "Numerics/ap.h" #include "Numerics/lbfgsb.h" #include namespace cmtk { /** \addtogroup Recon */ //@{ /** Base class for volume reconstruction using inverse interpolation. * * This class is specifically designed to use the L-BFFS-B Optimizer from alglib.net. * *\see http://www.alglib.net/optimization/lbfgsb.php * *\author Torsten Rohlfing */ class InverseInterpolationVolumeReconstructionBase : /// Inherit from volume injection class. public VolumeInjectionReconstruction { public: /// This class. typedef InverseInterpolationVolumeReconstructionBase Self; /// Parent class. typedef VolumeInjectionReconstruction Superclass; /** Constructor for interleaved image motion correction. * Take original image. Set interleaved image count and stacking axis. Construct separate 3D image stacks for interleaved passes. Allocate corrected image. *\param originalImage Smart pointer to the original image with motion artifacts. *\param numberOfPasses The number of interleaved passes, i.e., the number of pass images that comprise the final image. *\param interleaveAxis Between-slice axis of the interleaved acquisition. */ InverseInterpolationVolumeReconstructionBase( const UniformVolume* originalImage, const Types::GridIndexType numberOfPasses, const int interleaveAxis ); /** Constructor for general volume reconstruction from multiple acquired images. */ InverseInterpolationVolumeReconstructionBase( const UniformVolume* reconstructionGrid, std::vector& images ); /// Virtual destructor stub. virtual ~InverseInterpolationVolumeReconstructionBase() {} /** Compute approximation error. * This function computed the difference images between the original and the interpolated pass images, * and it also computes from these the mean squared and the maximum approximation errors. * *\return The approximation error, i.e., the sum of squared differences of the intensities * in the original pass images and the corresponding intensities in the images interpolated * from the current estimated corrected image. */ double ComputeApproximationError(); /// Returns the reconstructed image wioth the lowest maximum error. UniformVolume::SmartPtr& GetLowestMaxErrorImage() { return this->m_LowestMaxErrorImage; } /// Set flag for regional intensity trunctation. void SetUseRegionalIntensityTruncation( const bool flag = true ) { this->m_RegionalIntensityTruncation = flag; } /// Set fourth order error flag. void SetUseFourthOrderError( const bool flag = true ) { this->m_FourthOrderError = flag; } /// Set L-norm constraint weight. void SetConstraintWeightLNorm( const double weight ) { this->m_ConstraintWeightLNorm = weight; } /// Optimize approximation error. void Optimize( const int numberOfIterations ); /// Return MSD error. double GetMeanSquaredError() const { return this->m_MeanSquaredError; } /// Return maximum error. double GetMaximumError() const { return this->m_MaximumError; } protected: /// Flag for regional pixel intensity truncation. bool m_RegionalIntensityTruncation; /// Corrected image with lowest maximum error. UniformVolume::SmartPtr m_LowestMaxErrorImage; /// Current lowest maximum error. double m_LowestMaxError; /// Interpolated (from current estimate of correcting image) pass images. std::vector m_InterpolatedPassImages; /// Difference Subimages between original and interpolated Subimages, used for error- and gradientcalculation. std::vector m_DifferencePassImages; /** Flag for fourth order error optimization. * If this flag is set, the error optimized is the fourth order error, otherwise the second order (square) error. */ bool m_FourthOrderError; /** Constraint weight for Laplacian norm regularizer. * Regularization is turned off if weight is less than, or equal to, zero. */ double m_ConstraintWeightLNorm; /// Mean squared error of the approximation, i.e., MSD between interpolated and actual pass images. double m_MeanSquaredError; /// Maximum error of the approximation, i.e., maximum difference between interpolated and actual pass images. double m_MaximumError; /// Function and gradient evaluator object. ap::FunctionAndGradient* m_FunctionAndGradient; }; //@} } // namespace cmtk #endif // #ifndef __cmtkInverseInterpolationVolumeReconstructionBase_h_included_ cmtk-3.3.1/libs/Recon/cmtkPointSpreadFunctionBox.h000066400000000000000000000041101276303427400221160ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // Copyright 2004-2009 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 1635 $ // // $LastChangedDate: 2010-05-10 16:02:23 -0700 (Mon, 10 May 2010) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkPointSpreadFunctionBox_h_included_ #define __cmtkPointSpreadFunctionBox_h_included_ #include namespace cmtk { /** \addtogroup Recon */ //@{ /// Point spread functions for iterative deblurring. namespace PointSpreadFunctions { /// Box point-spread function. class Box { public: /// This class. typedef Box Self; /// Smart pointer. typedef SmartPointer SmartPtr; /// Constructor. Box( const Vector3D& pixelSize ) { this->m_Radius = 0.5 * pixelSize; } /// Get truncation radius. Types::Coordinate GetTruncationRadius( const int dim ) const { return this->m_Radius[dim]; } /// Get the weight for a neighbor based on its radius from the kernel center. Types::Coordinate GetWeight( const int dim, const Types::Coordinate r ) const { if ( fabs( r ) <= this->m_Radius[dim] ) { return 1.0; } return 0.0; } private: /// Kernel radius. Vector3D m_Radius; }; } // namespace PointSpreadFunctions //@} } // namespace cmtk #endif cmtk-3.3.1/libs/Recon/cmtkPointSpreadFunctionGaussian.h000066400000000000000000000042721276303427400231510ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2010 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 1635 $ // // $LastChangedDate: 2010-05-10 16:02:23 -0700 (Mon, 10 May 2010) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkPointSpreadFunctionGaussian_h_included_ #define __cmtkPointSpreadFunctionGaussian_h_included_ #include namespace cmtk { /** \addtogroup Recon */ //@{ namespace PointSpreadFunctions { /// Gaussian point-spread function. class Gaussian { public: /// This class. typedef Gaussian Self; /// Smart pointer. typedef SmartPointer SmartPtr; /// Constructor. Gaussian( const Vector3D& pixelSize ) { this->m_Radius = 0.5 * pixelSize; } /// Get truncation radius. Types::Coordinate GetTruncationRadius( const int dim ) const { return 4 * this->m_Radius[dim]; } /// Get the weight for a neighbor based on its radius from the kernel center. Types::Coordinate GetWeight( const int dim, const Types::Coordinate r ) const { Types::Coordinate rAbsRel = fabs( r / this->m_Radius[dim] ); if ( rAbsRel <= 4 ) { rAbsRel *= (2.3548 / 2.0); // go from HWHM to FWHM to sigma return exp( -rAbsRel*rAbsRel ); } return 0.0; } private: /// Kernel radius. Vector3D m_Radius; }; } // namespace PointSpreadFunctions //@} } // namespace cmtk #endif cmtk-3.3.1/libs/Recon/cmtkVolumeInjectionReconstruction.cxx000066400000000000000000000546471276303427400241620ustar00rootroot00000000000000/* // // Copyright 2016 Google, Inc. // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2011, 2013 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 5400 $ // // $LastChangedDate: 2016-01-14 19:42:40 -0800 (Thu, 14 Jan 2016) $ // // $LastChangedBy: torstenrohlfing $ // */ #include "cmtkVolumeInjectionReconstruction.h" #include #include #include #include #include #include #ifdef CMTK_USE_GCD # include #endif namespace cmtk { /** \addtogroup Registration */ //@{ VolumeInjectionReconstruction ::VolumeInjectionReconstruction( const UniformVolume* originalImage, const Types::GridIndexType interleaveFactor, const int interleaveAxis ) : m_NumberOfPasses( interleaveFactor ), m_PassWeights( interleaveFactor ), m_OriginalImageRange( 0, 0 ), m_OriginalImageHistogram(), m_CorrectedImageHistogram() { this->m_OriginalImageHistogram = Histogram::SmartPtr( new Histogram( Self::NumberOfHistogramBins ) ); this->m_CorrectedImageHistogram = Histogram::SmartPtr( new Histogram( Self::NumberOfHistogramBins ) ); const TypedArray* originalData = originalImage->GetData(); this->SetupHistogramKernels( originalData ); this->m_CorrectedImage = UniformVolume::SmartPtr( originalImage->CloneGrid() ); this->m_CorrectedImage->CreateDataArray( TYPE_FLOAT ); // split original image into subvolumes and store these. this->m_OriginalPassImages.clear(); for ( int pass = 0; pass < this->m_NumberOfPasses; ++pass ) { UniformVolume::SmartPtr passImage( originalImage->GetInterleavedSubVolume( interleaveAxis, this->m_NumberOfPasses, pass ) ); this->m_OriginalPassImages.push_back( passImage ); } std::fill( m_PassWeights.begin(), m_PassWeights.end(), 1.0 ); this->m_TransformationsToPassImages.clear(); for ( int pass = 0; pass < this->m_NumberOfPasses; ++pass ) { this->m_TransformationsToPassImages.push_back( AffineXform::SmartPtr( new AffineXform ) ); } } VolumeInjectionReconstruction ::VolumeInjectionReconstruction( const UniformVolume* reconstructionGrid, std::vector& images ) : m_NumberOfPasses( images.size() ), m_PassWeights( images.size() ), m_OriginalImageRange( 0, 0 ), m_OriginalImageHistogram( new Histogram( Self::NumberOfHistogramBins ) ), m_CorrectedImageHistogram( new Histogram( Self::NumberOfHistogramBins ) ) { const TypedArray* originalData = reconstructionGrid->GetData(); if ( !originalData ) originalData = images[0]->GetData(); this->SetupHistogramKernels( originalData ); this->m_CorrectedImage = UniformVolume::SmartPtr( reconstructionGrid->CloneGrid() ); this->m_CorrectedImage->CreateDataArray( TYPE_FLOAT ); this->m_OriginalPassImages = images; std::fill( m_PassWeights.begin(), m_PassWeights.end(), 1.0 ); this->m_TransformationsToPassImages.clear(); for ( int pass = 0; pass < this->m_NumberOfPasses; ++pass ) { this->m_TransformationsToPassImages.push_back( AffineXform::SmartPtr( new AffineXform ) ); } } int VolumeInjectionReconstruction ::GuessInterleaveAxis ( const UniformVolume* image, const int defaultAxis ) { if ( (image->m_Dims[0] == image->m_Dims[1]) && (image->m_Dims[1] != image->m_Dims[2]) ) return 2; if ( (image->m_Dims[0] == image->m_Dims[2]) && (image->m_Dims[1] != image->m_Dims[2]) ) return 1; if ( (image->m_Dims[1] == image->m_Dims[2]) && (image->m_Dims[1] != image->m_Dims[0]) ) return 0; if ( (image->m_Delta[0] == image->m_Delta[1]) && (image->m_Delta[1] != image->m_Delta[2]) ) return 2; if ( (image->m_Delta[0] == image->m_Delta[2]) && (image->m_Delta[1] != image->m_Delta[2]) ) return 1; if ( (image->m_Delta[1] == image->m_Delta[2]) && (image->m_Delta[1] != image->m_Delta[0]) ) return 0; return defaultAxis; } void VolumeInjectionReconstruction ::SetupHistogramKernels( const TypedArray* originalData ) { this->m_OriginalImageRange = originalData->GetRange(); this->m_CorrectedImageHistogram->SetRange( this->m_OriginalImageRange ); this->m_OriginalImageHistogram->SetRange( this->m_OriginalImageRange ); originalData->GetEntropy( *this->m_OriginalImageHistogram, true /*fractional*/ ); const HistogramType::BinType noiseSigma = TypedArrayNoiseEstimatorNaiveGaussian( *originalData, Self::NumberOfHistogramBins ).GetNoiseLevelSigma(); const HistogramType::BinType kernelSigma = Self::NumberOfHistogramBins * noiseSigma / this->m_OriginalImageRange.Width(); size_t kernelRadius = static_cast( 1 + 2 * kernelSigma ); // We now make sure kernel radius is large enough to cover any gaps in the original image histogram. // This is to avoid infinite KL divergence values size_t runLengthZeroes = 1; for ( size_t i = 0; i < Self::NumberOfHistogramBins; ++i ) { if ( (*this->m_OriginalImageHistogram)[i] == 0 ) { ++runLengthZeroes; kernelRadius = std::max( kernelRadius, runLengthZeroes ); } else { runLengthZeroes = 0; } } // Create Gaussian kernel using the previously determined sigma and cutoff radius. this->m_OriginalImageIntensityNoiseKernel.resize( kernelRadius ); if ( kernelRadius > 1 ) { const HistogramType::BinType normFactor = 1.0/(sqrt(2*M_PI) * kernelSigma); for ( size_t i = 0; i < kernelRadius; ++i ) { this->m_OriginalImageIntensityNoiseKernel[i] = static_cast( normFactor * exp( -MathUtil::Square( 1.0 * i / kernelSigma ) / 2 ) ); } } else { this->m_OriginalImageIntensityNoiseKernel[0] = static_cast( 1 ); } // Finally, get original image histogram again, this time using the previously determined kernel. originalData->GetEntropy( *this->m_OriginalImageHistogram, &this->m_OriginalImageIntensityNoiseKernel[0], this->m_OriginalImageIntensityNoiseKernel.size() ); } void VolumeInjectionReconstruction ::ComputeTransformationsToPassImages( const int registrationMetric ) { this->m_TransformationsToPassImages.clear(); UniformVolume::SmartPtr registerToImage = this->m_ReferenceImage ? this->m_ReferenceImage : this->m_OriginalPassImages[0]; for ( int pass = 0; pass < this->m_NumberOfPasses; ++pass ) { if ( registerToImage == this->m_OriginalPassImages[pass] ) { // set identity transformation if current subvolume is used as the reference. this->m_TransformationsToPassImages.push_back( AffineXform::SmartPtr( new AffineXform ) ); } else { // run the registration AffineRegistration ar; ar.SetVolume_1( registerToImage ); ar.SetVolume_2( this->m_OriginalPassImages[pass] ); ar.AddNumberDOFs( 6 ); ar.SetInitialAlignCenters( false ); ar.SetNoSwitch( true ); ar.SetMetric( registrationMetric ); ar.SetExploration( 4 * this->m_CorrectedImage->GetMaxDelta() ); ar.SetAccuracy( .1 * this->m_CorrectedImage->GetMinDelta() ); ar.SetSampling( 2 * this->m_CorrectedImage->GetMaxDelta() ); ar.Register(); this->m_TransformationsToPassImages.push_back( ar.GetTransformation() ); } } } void VolumeInjectionReconstruction ::VolumeInjectionAnisotropic( const Types::Coordinate kernelSigmaFactor, const Types::Coordinate kernelRadiusFactor ) { const Types::Coordinate minusOneOverTwoSigmaSquare = -1 / (2 * kernelSigmaFactor*kernelSigmaFactor); UniformVolume::SmartPtr& correctedImage = this->m_CorrectedImage; TypedArray::SmartPtr correctedImageData = correctedImage->GetData(); const Types::GridIndexType correctedImageNumPixels = correctedImage->GetNumberOfPixels(); const Types::Coordinate correctedDelta[3] = { correctedImage->m_Delta[0], correctedImage->m_Delta[1], correctedImage->m_Delta[2] }; this->m_NeighorhoodMaxPixelValues.setbounds( 1, correctedImageNumPixels ); this->m_NeighorhoodMinPixelValues.setbounds( 1, correctedImageNumPixels ); for ( Types::GridIndexType i = 1; i <= correctedImageNumPixels; ++i ) { this->m_NeighorhoodMaxPixelValues(i) = this->m_OriginalImageRange.m_LowerBound; this->m_NeighorhoodMinPixelValues(i) = this->m_OriginalImageRange.m_UpperBound; } Progress::Begin( 0, correctedImageNumPixels, 1e5, "Anisotropic Volume Injection" ); // The following is currently broken due to Apple bug: // http://forums.macrumors.com/showthread.php?t=952857 // http://lists.apple.com/archives/perfoptimization-dev/2009/Sep/msg00043.html //#ifdef CMTK_USE_GCD // const cmtk::Threads::Stride stride( correctedImageNumPixels ); // dispatch_apply( stride.NBlocks(), dispatch_get_global_queue(0, 0), ^(size_t b) // { for ( size_t correctedPx = stride.From( b ); correctedPx < stride.To( b ); ++correctedPx ) //#else #pragma omp parallel for schedule(dynamic) for ( Types::GridIndexType correctedPx = 0; correctedPx < static_cast( correctedImageNumPixels ); ++correctedPx ) //#endif { if ( (correctedPx % ((Types::GridIndexType)1e5)) == 0 ) Progress::SetProgress( correctedPx ); ap::real_value_type sum = 0; ap::real_value_type weight = 0; const UniformVolume::CoordinateVectorType vCorrected = correctedImage->GetGridLocation( correctedPx ); for ( int pass = 0; pass < this->m_NumberOfPasses; ++pass ) { const ap::real_value_type passImageWeight = this->m_PassWeights[pass]; if ( passImageWeight > 0 ) { const UniformVolume* passImage = this->m_OriginalPassImages[pass]; const DataGrid::IndexType& passImageDims = passImage->GetDims(); const Xform* passImageXform = this->m_TransformationsToPassImages[pass]; const UniformVolume::CoordinateVectorType vPass = passImageXform->Apply( vCorrected ); Types::GridIndexType passGridPosition[3]; passImage->GetVoxelIndexNoBounds( vPass, passGridPosition ); Types::GridIndexType passGridFrom[3], passGridTo[3]; for ( int n = 0; n < 3; ++n ) { passGridFrom[n] = std::max( passGridPosition[n] - static_cast( kernelRadiusFactor ), 0 ); passGridTo[n] = std::min( passGridPosition[n] + static_cast( kernelRadiusFactor ) + 1, passImageDims[n] ); } UniformVolume::CoordinateVectorType u, delta; for ( Types::GridIndexType k = passGridFrom[2]; k < passGridTo[2]; ++k ) { u[2] = passImage->GetPlaneCoord( AXIS_Z, k ); delta[2] = (u[2] - vPass[2]) / correctedDelta[2]; for ( Types::GridIndexType j = passGridFrom[1]; j < passGridTo[1]; ++j ) { u[1] = passImage->GetPlaneCoord( AXIS_Y, j ); delta[1] = (u[1] - vPass[1]) / correctedDelta[1]; for ( Types::GridIndexType i = passGridFrom[0]; i < passGridTo[0]; ++i ) { u[0] = passImage->GetPlaneCoord( AXIS_X, i ); delta[0] = (u[0] - vPass[0]) / correctedDelta[0]; Types::DataItem passImageData; if ( passImage->GetDataAt( passImageData, i, j, k ) ) { const Types::Coordinate mahalanobis = delta.RootSumOfSquares(); if ( mahalanobis <= kernelRadiusFactor ) { const ap::real_value_type kernelWeightPixel = passImageWeight * exp( mahalanobis*mahalanobis * minusOneOverTwoSigmaSquare ); sum += passImageData * kernelWeightPixel; weight += kernelWeightPixel; if ( passImageData < this->m_NeighorhoodMinPixelValues(correctedPx+1) ) this->m_NeighorhoodMinPixelValues(correctedPx+1) = passImageData; if ( passImageData > this->m_NeighorhoodMaxPixelValues(correctedPx+1) ) this->m_NeighorhoodMaxPixelValues(correctedPx+1) = passImageData; } } } } } } } if ( weight > 0 ) correctedImageData->Set( static_cast( sum / weight ), correctedPx ); else correctedImageData->SetPaddingAt( correctedPx ); } //#ifdef CMTK_USE_GCD // }); //#endif Progress::Done(); } void VolumeInjectionReconstruction ::VolumeInjectionIsotropic( const Types::Coordinate kernelSigma, const Types::Coordinate kernelRadius ) { const Types::GridIndexType correctedImageNumPixels = this->m_CorrectedImage->GetNumberOfPixels(); const DataGrid::IndexType& splattedImageDims = this->m_CorrectedImage->GetDims(); this->m_CorrectedImage->GetData()->ClearArray(); this->m_NeighorhoodMaxPixelValues.setbounds( 1, correctedImageNumPixels ); this->m_NeighorhoodMinPixelValues.setbounds( 1, correctedImageNumPixels ); for ( Types::GridIndexType i = 1; i <= correctedImageNumPixels; ++i ) { this->m_NeighorhoodMaxPixelValues(i) = this->m_OriginalImageRange.m_LowerBound; this->m_NeighorhoodMinPixelValues(i) = this->m_OriginalImageRange.m_UpperBound; } const Types::GridIndexType kernelRadiusIndex[3] = { 1 + static_cast( kernelRadius / this->m_CorrectedImage->m_Delta[0] ), 1 + static_cast( kernelRadius / this->m_CorrectedImage->m_Delta[1] ), 1 + static_cast( kernelRadius / this->m_CorrectedImage->m_Delta[2] ) }; const Types::Coordinate kernelRadiusSquare = kernelRadius * kernelRadius; const Types::Coordinate minusOneOverTwoSigmaSquare = -1 / (2 * kernelSigma*kernelSigma); std::vector kernelWeights( correctedImageNumPixels ); std::fill( kernelWeights.begin(), kernelWeights.end(), 0 ); std::vector splattedImage( correctedImageNumPixels ); std::fill( splattedImage.begin(), splattedImage.end(), 0 ); Progress::Begin( 0, this->m_NumberOfPasses, 1, "Isotropic Volume Injection" ); for ( int pass = 0; pass < this->m_NumberOfPasses; ++pass ) { Progress::SetProgress( pass ); const ap::real_value_type passImageWeight = this->m_PassWeights[pass]; if ( passImageWeight > 0 ) { const UniformVolume* passImage = this->m_OriginalPassImages[pass]; AffineXform::SmartPtr affinePassImageXform = AffineXform::SmartPtr::DynamicCastFrom( this->m_TransformationsToPassImages[pass] ); const AffineXform* passImageXformInverse = (affinePassImageXform) ? affinePassImageXform->GetInverse() : static_cast( NULL ); const Types::GridIndexType passImageNumPixels = passImage->GetNumberOfPixels(); for ( Types::GridIndexType offset = 0; offset < passImageNumPixels; ++offset ) { Types::DataItem passImageData; if ( passImage->GetDataAt( passImageData, offset ) ) { Types::GridIndexType x, y, z; passImage->GetIndexFromOffset( offset, x, y, z ); UniformVolume::CoordinateVectorType v = passImage->GetGridLocation( x, y, z ); if ( passImageXformInverse ) { v = passImageXformInverse->Apply( v ); } else { if ( ! this->m_TransformationsToPassImages[pass]->ApplyInverse( v, v ) ) { StdErr << "ERROR: failed to apply inverse transformation in VolumeInjectionReconstruction::VolumeInjectionIsotropic\n"; throw ExitException( 1 ); } } Types::GridIndexType targetGridPosition[3]; if ( this->m_CorrectedImage->FindVoxel( v, targetGridPosition ) ) { // check if neighbours are outside - if yes, set new neighbour ranges Types::GridIndexType targetGridFrom[3], targetGridTo[3]; for ( int n = 0; n < 3; ++n ) { targetGridFrom[n] = std::max( targetGridPosition[n] - kernelRadiusIndex[n], 0 ); targetGridTo[n] = std::min( targetGridPosition[n] + kernelRadiusIndex[n] + 1, splattedImageDims[n] ); } UniformVolume::CoordinateVectorType u; for ( Types::GridIndexType k = targetGridFrom[2]; k < targetGridTo[2]; ++k ) { u[2] = this->m_CorrectedImage->GetPlaneCoord( AXIS_Z, k ); for ( Types::GridIndexType j = targetGridFrom[1]; j < targetGridTo[1]; ++j ) { u[1] = this->m_CorrectedImage->GetPlaneCoord( AXIS_Y, j ); Types::GridIndexType splattedImageOffset = this->m_CorrectedImage->GetOffsetFromIndex( targetGridFrom[0], j, k ); for ( Types::GridIndexType i = targetGridFrom[0]; i < targetGridTo[0]; ++i, ++splattedImageOffset ) { u[0] = this->m_CorrectedImage->GetPlaneCoord( AXIS_X, i ); const Types::Coordinate distanceSquare = ( u-v ).SumOfSquares(); if ( distanceSquare <= kernelRadiusSquare ) { const ap::real_value_type kernelWeightPixel = passImageWeight * exp( distanceSquare * minusOneOverTwoSigmaSquare ); splattedImage[splattedImageOffset] += passImageData * kernelWeightPixel; kernelWeights[splattedImageOffset] += kernelWeightPixel; if ( passImageData < this->m_NeighorhoodMinPixelValues(splattedImageOffset+1) ) this->m_NeighorhoodMinPixelValues(splattedImageOffset+1) = passImageData; if ( passImageData > this->m_NeighorhoodMaxPixelValues(splattedImageOffset+1) ) this->m_NeighorhoodMaxPixelValues(splattedImageOffset+1) = passImageData; } } } } } } } } } #pragma omp parallel for for ( Types::GridIndexType idx = 0; idx < static_cast( correctedImageNumPixels ); ++idx ) { const Types::DataItem kernelWeightPixel = static_cast( kernelWeights[idx] ); if ( kernelWeightPixel > 0 ) // check if pixel is a neighbour { this->m_CorrectedImage->SetDataAt( static_cast( splattedImage[idx] / kernelWeightPixel ), idx ); // Set normalized data on grid } } Progress::Done(); } UniformVolume::SmartPtr& VolumeInjectionReconstruction ::GetCorrectedImage() { return this->m_CorrectedImage; } void VolumeInjectionReconstruction ::SetReferenceImage( UniformVolume::SmartPtr& referenceImage ) { this->m_ReferenceImage = referenceImage; } ap::real_value_type VolumeInjectionReconstruction ::GetOriginalToCorrectedImageKLD( const ap::real_1d_array& x ) { this->m_CorrectedImageHistogram->Reset(); for ( int i = x.getlowbound(); i <= x.gethighbound(); ++i ) this->m_CorrectedImageHistogram->AddWeightedSymmetricKernel ( this->m_CorrectedImageHistogram->ValueToBin( static_cast( x(i) ) ), this->m_OriginalImageIntensityNoiseKernel.size(), &this->m_OriginalImageIntensityNoiseKernel[0] ); const ap::real_value_type kld = this->m_CorrectedImageHistogram->GetKullbackLeiblerDivergence( *this->m_OriginalImageHistogram ); return kld; } ap::real_value_type VolumeInjectionReconstruction ::ComputeCorrectedImageLaplacianNorm( const ap::real_1d_array& correctedImagePixels ) { const UniformVolume* correctedImage = this->m_CorrectedImage; const Types::GridIndexType correctedImageNumPixels = correctedImage->GetNumberOfPixels(); this->m_CorrectedImageLaplacians.resize( correctedImageNumPixels ); const DataGrid::IndexType& correctedImageDims = correctedImage->GetDims(); const Types::GridIndexType nextI = 1; const Types::GridIndexType nextJ = nextI * correctedImageDims[0]; const Types::GridIndexType nextK = nextJ * correctedImageDims[1]; ap::real_value_type lnorm = 0; #pragma omp parallel for reduction(+:lnorm) for ( Types::GridIndexType idx = 1; idx <= static_cast( correctedImageNumPixels ); ++idx ) { Types::GridIndexType x, y, z; correctedImage->GetIndexFromOffset( idx-1, x, y, z ); const Types::GridIndexType xm = (x>0) ? idx-nextI : idx+nextI; const Types::GridIndexType ym = (y>0) ? idx-nextJ : idx+nextJ; const Types::GridIndexType zm = (z>0) ? idx-nextK : idx+nextK; const Types::GridIndexType xp = (x+1 < correctedImageDims[0]) ? idx+nextI : idx-nextI; const Types::GridIndexType yp = (y+1 < correctedImageDims[1]) ? idx+nextJ : idx-nextJ; const Types::GridIndexType zp = (z+1 < correctedImageDims[2]) ? idx+nextK : idx-nextK; const ap::real_value_type l = correctedImagePixels( xm ) + correctedImagePixels( xp ) + correctedImagePixels( ym ) + correctedImagePixels( yp ) + correctedImagePixels( zm ) + correctedImagePixels( zp ) - 6 * correctedImagePixels( idx ); this->m_CorrectedImageLaplacians[idx-1] = l; lnorm += l*l; } if ( correctedImageNumPixels ) lnorm /= correctedImageNumPixels; return lnorm; } void VolumeInjectionReconstruction ::AddLaplacianGradientImage( ap::real_1d_array& g, const ap::real_1d_array&, const ap::real_value_type weight ) const { const UniformVolume* correctedImage = this->m_CorrectedImage; const Types::GridIndexType correctedImageNumPixels = correctedImage->GetNumberOfPixels(); const DataGrid::IndexType& correctedImageDims = correctedImage->GetDims(); const Types::GridIndexType nextI = 1; const Types::GridIndexType nextJ = nextI * correctedImageDims[0]; const Types::GridIndexType nextK = nextJ * correctedImageDims[1]; #pragma omp parallel for for ( Types::GridIndexType idx = 0; idx < static_cast( correctedImageNumPixels ); ++idx ) { Types::GridIndexType x, y, z; correctedImage->GetIndexFromOffset( idx, x, y, z ); const Types::GridIndexType xm = (x>0) ? idx-nextI : idx+nextI; const Types::GridIndexType ym = (y>0) ? idx-nextJ : idx+nextJ; const Types::GridIndexType zm = (z>0) ? idx-nextK : idx+nextK; const Types::GridIndexType xp = (x+1 < correctedImageDims[0]) ? idx+nextI : idx-nextI; const Types::GridIndexType yp = (y+1 < correctedImageDims[1]) ? idx+nextJ : idx-nextJ; const Types::GridIndexType zp = (z+1 < correctedImageDims[2]) ? idx+nextK : idx-nextK; g(idx+1) += (2 * weight / correctedImageNumPixels) * ( this->m_CorrectedImageLaplacians[xm] + this->m_CorrectedImageLaplacians[xp] + this->m_CorrectedImageLaplacians[ym] + this->m_CorrectedImageLaplacians[yp] + this->m_CorrectedImageLaplacians[zm] + this->m_CorrectedImageLaplacians[zp] - 6 * this->m_CorrectedImageLaplacians[idx] ); } } } // namespace cmtk cmtk-3.3.1/libs/Recon/cmtkVolumeInjectionReconstruction.h000066400000000000000000000215721276303427400235760ustar00rootroot00000000000000/* // // Copyright 2016 Google, Inc. // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2011 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 5400 $ // // $LastChangedDate: 2016-01-14 19:42:40 -0800 (Thu, 14 Jan 2016) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkVolumeInjectionReconstruction_h_included_ #define __cmtkVolumeInjectionReconstruction_h_included_ #include #include #include #include #include #include "Numerics/ap.h" #include namespace cmtk { /** \addtogroup Recon */ //@{ /** Class for volume reconstruction using volume injection. * * This class implements some side effects that are not strictly part of the volume * injection algorithm, but which supply functionality to the derived * igsInverseInterpolationVolumeReconstructionBase class. These include computation * of regional minimum/maximum intensity ranges, between-pass registration, and * KLD metric computation. * *\author Torsten Rohlfing */ class VolumeInjectionReconstruction { public: /// This class. typedef VolumeInjectionReconstruction Self; /** Constructor from single interleaved image. * Take original image. Set interleaved image count and stacking axis. Construct separate 3D image stacks for interleaved passes. Allocate corrected image. *\param originalImage Smart pointer to the original image with motion artifacts. *\param numberOfPasses The number of interleaved passes, i.e., the number of pass images that comprise the final image. *\param interleaveAxis Between-slice axis of the interleaved acquisition. */ VolumeInjectionReconstruction( const UniformVolume* originalImage, const Types::GridIndexType numberOfPasses, const int interleaveAxis ); /** Constructor for general volume reconstruction from multiple acquired images. */ VolumeInjectionReconstruction( const UniformVolume* reconstructionGrid, std::vector& images ); /// Virtual destructor stub. virtual ~VolumeInjectionReconstruction() {} /** Static helper function: guess interleaved axis. * Basically, we assume that images are acquired as interleaved stacks of * square 2D images with pixel size different from inter-slice spacing. * So we guess that the interleaved axis is the one that does not match the * other two axes' image dimensions, and if all three are the same, the * one that doesn't match their spacing (delta). */ static int GuessInterleaveAxis( const UniformVolume* image /*!< The interleaved image.*/, const int defaultAxis = 2 /*!< In case all guessing fails, this is the default axis we return.*/ ); /** Compute transformations between the reference image grid and the original pass images. * The resulting transformations are stored in the m_TransformationsToPassImages vector. * If a high-resolution reference image is set in m_ReferenceImage, then all subimages are registered to it. * Otherwise, the 0th subimage is used as the reference and the remaining subimages are registered to it. * In this case, the transformation for the 0th subimage is set as the identity transformation. *\param registrationMetric Similarity metric for registration of the passes to the reference image. */ void ComputeTransformationsToPassImages( const int registrationMetric = 0 ); /// Set transformations to pass images externally (e.g., imported from disk). void SetTransformationsToPassImages( std::vector& transformations ) { this->m_TransformationsToPassImages = transformations; } /// Get transformation to one pass image. Xform::SmartPtr& GetTransformationToPassImage( const size_t passIdx ) { if ( passIdx < this->m_TransformationsToPassImages.size() ) return this->m_TransformationsToPassImages[passIdx]; else return Xform::SmartPtr::Null(); } /// Get transformation to one pass image. std::vector& GetTransformationsToPassImages() { return this->m_TransformationsToPassImages; } /// Create initial approximation using isotropic volume injection. void VolumeInjectionIsotropic( const Types::Coordinate kernelSigma /*!< Gaussian kernel sigma (standard deviation) parameter*/, const Types::Coordinate kernelRadius /*!< Gaussian kernel cut-off radius.*/ ); /// Create initial approximation using anisotropic volume injection. void VolumeInjectionAnisotropic( const Types::Coordinate kernelSigmaFactor /*!< Gaussian kernel sigma (standard deviation) factor (multiple of per-dimension pass image spacing)*/, const Types::Coordinate kernelRadiusFactor /*!< Gaussian kernel cut-off radius factor (multiple of per-dimension pass image spacing)*/ ); /// Returns the corrected image. UniformVolume::SmartPtr& GetCorrectedImage(); /// Set optional separate reference image for motion parameter estimation. void SetReferenceImage( UniformVolume::SmartPtr& referenceImage ); /** Set pass weight. * Each pass weight should be between 0 and 1. If the weight for a pass is zero, then that * pass is effectively excluded from the reconstruction. This can be useful if one of the * passes shows severe within-pass motion artifacts that would otherwise disturb the * across-pass correction. * * By default, all pass weights are set to 1, i.e., all passes contribute equally. */ void SetPassWeight( const size_t pass, const Types::Coordinate weight ) { this->m_PassWeights[pass] = weight; } /// Get Kullback-Leibler Divergence between intensity distributions in original and corrected image. ap::real_value_type GetOriginalToCorrectedImageKLD( const ap::real_1d_array& x ); protected: /// Number of interleaved passes. Types::GridIndexType m_NumberOfPasses; /// Relative weights of the passes in the correction; can be used to underweight or even exclude passes. std::vector m_PassWeights; /// Original volume pixel intensity range. Types::DataItemRange m_OriginalImageRange; /// Original pass images. std::vector m_OriginalPassImages; /// Histogram type. typedef Histogram HistogramType; /// Original image histogram. HistogramType::SmartPtr m_OriginalImageHistogram; /// Corrected image histogram. HistogramType::SmartPtr m_CorrectedImageHistogram; /// Original image intensity noise kernel. std::vector m_OriginalImageIntensityNoiseKernel; /// Optional high-resolution non-interleaved reference image. UniformVolume::SmartPtr m_ReferenceImage; /// Affine transformations that map FROM the corrected image TO each of the subimages. std::vector m_TransformationsToPassImages; /// Developing corrected image. UniformVolume::SmartPtr m_CorrectedImage; /// Corrected image Laplacian. std::vector m_CorrectedImageLaplacians; /** Compute norm of the corrected image Laplacian. * Side effect: this function first computes the Laplacian image, which is stored in * m_CorrectedImageLaplacian for use in the AddLaplacianGradientImage function. */ ap::real_value_type ComputeCorrectedImageLaplacianNorm( const ap::real_1d_array& correctedImagePixels /*!< Current vector of corrected image pixels.*/ ); /// Add weighted gradient image of Laplacian to already computed cost function gradient. void AddLaplacianGradientImage( ap::real_1d_array& g, const ap::real_1d_array& correctedImagePixels, const ap::real_value_type weight ) const; /// Maximum neighborhood pixel values in the corrected image. ap::real_1d_array m_NeighorhoodMaxPixelValues; /// Minimum neighborhood pixel values in the corrected image. ap::real_1d_array m_NeighorhoodMinPixelValues; private: /// Number of histogram bins for image entropy estimation. static const unsigned int NumberOfHistogramBins = 64; /// Setup kernels and histograms for image entropy estimation. void SetupHistogramKernels( const TypedArray* originalData ); }; //@} } // namespace cmtk #endif // #ifndef __cmtkVolumeInjectionReconstruction_h_included_ cmtk-3.3.1/libs/Recon/doxygen.h000066400000000000000000000023411276303427400163110ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2011 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 2734 $ // // $LastChangedDate: 2011-01-14 10:23:45 -0800 (Fri, 14 Jan 2011) $ // // $LastChangedBy: torstenrohlfing $ // */ /** \defgroup Recon cmtkRecon Library * This library provides classes for volume reconstruction by volume injection, * inverse interpolation, and deblurring. */ cmtk-3.3.1/libs/Registration/000077500000000000000000000000001276303427400160675ustar00rootroot00000000000000cmtk-3.3.1/libs/Registration/CMakeLists.txt000066400000000000000000000107561276303427400206400ustar00rootroot00000000000000## ## Copyright 1997-2009 Torsten Rohlfing ## ## Copyright 2004-2011 SRI International ## ## This file is part of the Computational Morphometry Toolkit. ## ## http://www.nitrc.org/projects/cmtk/ ## ## The Computational Morphometry Toolkit is free software: you can ## redistribute it and/or modify it under the terms of the GNU General Public ## License as published by the Free Software Foundation, either version 3 of ## the License, or (at your option) any later version. ## ## The Computational Morphometry Toolkit is distributed in the hope that it ## will be useful, but WITHOUT ANY WARRANTY; without even the implied ## warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License along ## with the Computational Morphometry Toolkit. If not, see ## . ## ## $Revision: 4409 $ ## ## $LastChangedDate: 2012-06-01 15:12:13 -0700 (Fri, 01 Jun 2012) $ ## ## $LastChangedBy: torstenrohlfing $ ## # Sources of non-templated classes. SET(cmtkRegistration_SRCS cmtkAffineRegistrationCommandLine.cxx cmtkAffineRegistration.cxx cmtkBestDirectionOptimizer.cxx cmtkBestNeighbourOptimizer.cxx cmtkCongealingFunctional.cxx cmtkEchoPlanarUnwarpFunctional.cxx cmtkElasticRegistrationCommandLine.cxx cmtkElasticRegistration.cxx cmtkGroupwiseRegistrationFunctionalAffineInitializer.cxx cmtkGroupwiseRegistrationFunctionalBase.cxx cmtkGroupwiseRegistrationFunctionalXformTemplate.cxx cmtkGroupwiseRegistrationFunctionalXformTemplate_Affine_IO.cxx cmtkGroupwiseRegistrationFunctionalXformTemplate_SplineWarp.cxx cmtkGroupwiseRegistrationFunctionalXformTemplate_SplineWarp_IO.cxx cmtkGroupwiseRegistrationFunctionalXformTemplateBase.cxx cmtkGroupwiseRegistrationOutput.cxx cmtkGroupwiseRegistrationRMIFunctional.cxx cmtkHausdorffDistance.cxx cmtkImagePairRegistration.cxx cmtkImagePairRegistrationFunctional.cxx cmtkImagePairRegistrationImagePreprocessor.cxx cmtkImagePairAffineRegistration.cxx cmtkImagePairAffineRegistrationCommandLine.cxx cmtkImagePairAffineRegistrationFunctional.cxx cmtkImagePairNonrigidRegistration.cxx cmtkImagePairNonrigidRegistrationCommandLine.cxx cmtkImagePairNonrigidRegistrationFunctional.cxx cmtkImagePairSimilarityJointHistogram.cxx cmtkImagePairSimilarityMeasure.cxx cmtkImagePairSimilarityMeasureCR.cxx cmtkImagePairSimilarityMeasureMSD.cxx cmtkImagePairSimilarityMeasureNCC.cxx cmtkImagePairSimilarityMeasureRMS.cxx cmtkImagePairSymmetricAffineRegistrationFunctional.cxx cmtkImagePairSymmetricNonrigidRegistrationFunctional.cxx cmtkImageSymmetryPlaneCommandLineBase.cxx cmtkImageSymmetryPlaneFunctional.cxx cmtkImageSymmetryPlaneFunctionalBase.cxx cmtkMakeInitialAffineTransformation.cxx cmtkMultiChannelRegistrationFunctionalBase.cxx cmtkProtocolCallback.cxx cmtkReformatVolume.cxx cmtkRegistrationCallback.cxx cmtkRegistrationJointHistogram.cxx cmtkSplineWarpCongealingFunctional.cxx cmtkSplineWarpCongealingFunctionalStaticThreadStorage.cxx cmtkSplineWarpGroupwiseRegistrationRMIFunctional.cxx cmtkSymmetricElasticFunctional.cxx cmtkSymmetryPlaneFunctional.cxx cmtkTypedArraySimilarity.cxx cmtkTypedArraySimilarityMemory.cxx cmtkTypedArraySimilarityRMI.cxx cmtkVoxelMatchingAffineFunctional.cxx cmtkVoxelMatchingCorrRatio.cxx cmtkVoxelMatchingCrossCorrelation.cxx cmtkVoxelMatchingElasticFunctional.cxx cmtkVoxelMatchingFunctional.cxx cmtkVoxelMatchingMeanSquaredDifference.cxx cmtkVoxelMatchingMetric.cxx cmtkVoxelMatchingMetric_Type.cxx cmtkVoxelRegistration.cxx cmtkVoxelRegistrationImagePreprocessor.cxx ) IF(CMTK_USE_SQLITE) SET(cmtkRegistration_SRCS ${cmtkRegistration_SRCS} cmtkImageXformDB.cxx) ENDIF(CMTK_USE_SQLITE) ADD_LIBRARY(cmtkRegistration ${cmtkRegistration_SRCS}) TARGET_LINK_LIBRARIES(cmtkRegistration cmtkIO cmtkBase cmtkSystem cmtkNumerics ) IF(CMTK_LIBRARY_PROPERTIES) SET_TARGET_PROPERTIES(cmtkRegistration PROPERTIES ${CMTK_LIBRARY_PROPERTIES}) ENDIF(CMTK_LIBRARY_PROPERTIES) INSTALL(TARGETS cmtkRegistration RUNTIME DESTINATION ${CMTK_INSTALL_BIN_DIR} COMPONENT runtime LIBRARY DESTINATION ${CMTK_INSTALL_LIB_DIR} COMPONENT runtime ARCHIVE DESTINATION ${CMTK_INSTALL_LIB_DIR} COMPONENT libraries) FILE(GLOB files_h "${CMAKE_CURRENT_SOURCE_DIR}/*.h") FILE(GLOB files_txx "${CMAKE_CURRENT_SOURCE_DIR}/*.txx") INSTALL(FILES ${files_h} ${files_txx} DESTINATION ${CMTK_INSTALL_INCLUDE_DIR}/Registration COMPONENT headers) cmtk-3.3.1/libs/Registration/cmtkAffineCongealingFunctional.h000066400000000000000000000035671276303427400243340ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2011 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 2735 $ // // $LastChangedDate: 2011-01-14 10:34:12 -0800 (Fri, 14 Jan 2011) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkAffineCongealingFunctional_h_included_ #define __cmtkAffineCongealingFunctional_h_included_ #include #include #include namespace cmtk { /** \addtogroup Registration */ //@{ /** Functional for affine congealing. * This functional evaluates Lilla Zollei's entropy criterion for massively * groupwise image registration. * *\section refs References * * [1] L . Zöllei, E. Learned-Miller, E. Grimson, W.M. Wells III: "Efficient * Population Registration of 3D Data", ICCV 2005, Computer Vision for * Biomedical Image Applications; Beijing, China */ typedef CongealingFunctional AffineCongealingFunctional; //@} } // namespace cmtk #endif // #ifndef __cmtkAffineCongealingFunctional_h_included_ cmtk-3.3.1/libs/Registration/cmtkAffineGroupwiseRegistrationRMIFunctional.h000066400000000000000000000032151276303427400272030ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2011 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 2745 $ // // $LastChangedDate: 2011-01-14 14:22:47 -0800 (Fri, 14 Jan 2011) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkAffineGroupwiseRegistrationRMIFunctional_h_included_ #define __cmtkAffineGroupwiseRegistrationRMIFunctional_h_included_ #include #include #include namespace cmtk { /** \addtogroup Registration */ //@{ /** Functional for affine groupwise registration using "RMI" metric. */ typedef GroupwiseRegistrationRMIFunctional AffineGroupwiseRegistrationRMIFunctional; //@} } // namespace cmtk #endif // #ifndef __cmtkAffineGroupwiseRegistrationRMIFunctional_h_included_ cmtk-3.3.1/libs/Registration/cmtkAffineMultiChannelRegistrationFunctional.h000066400000000000000000000205771276303427400272440ustar00rootroot00000000000000/* // // Copyright 2016 Google, Inc. // // Copyright 1997-2010 Torsten Rohlfing // // Copyright 2004-2011 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 5403 $ // // $LastChangedDate: 2016-01-14 20:59:04 -0800 (Thu, 14 Jan 2016) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkAffineMultiChannelRegistrationFunctional_h_included_ #define __cmtkAffineMultiChannelRegistrationFunctional_h_included_ #include #include #include #include #include #include #include namespace cmtk { /** \addtogroup Registration */ //@{ /** Forward declaration of class template. */ template class SplineWarpMultiChannelRegistrationFunctional; /** Class for affine multi-channel registration functional. */ template class AffineMultiChannelRegistrationFunctional : /** Inherit from multi-channel registration functional base class. */ public TemplateMultiChannelRegistrationFunctional { public: /** This class. */ typedef AffineMultiChannelRegistrationFunctional Self; /** Smart pointer. */ typedef SmartPointer SmartPtr; /** This class. */ typedef TemplateMultiChannelRegistrationFunctional Superclass; /** Default constructor. */ AffineMultiChannelRegistrationFunctional() : m_NumberOfThreads( Threads::GetNumberOfThreads() ) { } /** Initialize transformation. */ void InitTransformation( const bool alignCenters ); /** Compute functional value with volume clipping using multi-threading. * This function iterates over all voxels of the reference image that - after * applying the current coordinate transformation - are located inside the * mode image. This set of voxels is determined on-the-fly by an extension of * Liang and Barsky's "Parameterized Line-Clipping" technique. * * From the resulting sequence of reference/floating voxel pairs, the * selected voxel-based similarity measure (metric) is computed. *\return The computed similarity measure as returned by the "Metric" * subobject. *\see VolumeClipping */ virtual typename Self::ReturnType Evaluate(); private: /** Volume clipping object. */ VolumeClipping m_VolumeClipper; /** Perform clipping/cropping in z-direction. * This function computes the intersection of reference and floating data in * z-direction. It determines the range of indices of those planes in the * reference that intersect the floating. This is the range over which to * for-loop during metric computation. *\param clipper A volume clipping object with clipping boundaries and grid * orientation set. *\param origin Starting point of the reference volume. *\param start Upon return, this reference is set to the index of first plane * in the reference that intersects the floating. *\param end Upon return, this reference is set to one plus the index of the * last plane in the reference that intersects the floating. *\return true if there is an intersection of reference and floating, false if there * isn't. The range of indices returned in "start" and "end" is only * guaranteed to be valid if 1 is the return value. */ bool ClipZ ( const VolumeClipping& clipper, const Vector3D& origin, Types::GridIndexType& start, Types::GridIndexType& end ) const; /** Perform clipping/cropping in x-direction. * This function computes the intersection of reference and floating data in * x-direction. It determines the range of indices of those voxels in the * current reference row that intersect the floating image. This is the range * over which to for-loop during metric computation. * * Compared to ClipZ and ClipY, this step has to operate very exact as there * is no further level that would reduce remaining invalid voxels. Therefore, * clipper.ClipX() is called with an extended initial range of indices and an * explicitly open upper bound. * * This is necessary to discriminate inside-boundary from on-boundary voxels. * For the right, upper and back boundary, on-boundary voxels are already * outside the allowed range as the upper boundaries of the volume are open * in terms of interpolation. *\param clipper A volume clipping object with clipping boundaries and grid * orientation set. *\param origin Starting point of the current row in the reference volume. *\param start Upon return, this reference is set to the index of first voxel * in the reference that intersects the floating image. *\param end Upon return, this reference is set to one plus the index of the * last voxel in the reference that intersects the floating image. *\return true if there is an intersection of the current reference row and * the floating, false if there isn't. The range of indices returned in "start" * and "end" is only guaranteed to be valid if true is the return value. */ bool ClipX( const VolumeClipping& clipper, const Vector3D& origin, Types::GridIndexType& start, Types::GridIndexType &end ) const; /** Perform clipping/cropping in y-direction. * This function computes the intersection of reference and floating data in * y-direction. It determines the range of indices of those rows in the * current reference plane that intersect the floating image. This is the * range over which to for-loop during metric computation. *\param clipper A volume clipping object with clipping boundaries and grid * orientation set. *\param origin Starting point of the current plane in the reference volume. *\param start Upon return, this reference is set to the index of first row * in the reference that intersects the floating image. *\param end Upon return, this reference is set to one plus the index of the * last row in the reference that intersects the floating image. *\return true if there is an intersection of the current reference plane and * the floating, false if there isn't. The range of indices returned in "start" * and "end" is only guaranteed to be valid if true is the return value. */ bool ClipY( const VolumeClipping& clipper, const Vector3D& origin, Types::GridIndexType& start, Types::GridIndexType& end ) const; /** Number of parallel threads. */ const size_t m_NumberOfThreads; /** Parameters for threaded metric computation. */ class EvaluateThreadParameters : /// Inherit from generic thread parameters. public ThreadParameters { public: /** Pointer to transformed reference axes arrays. */ const TransformedVolumeAxes* m_TransformedAxes; /** First clipped pixel index in z direction. */ Types::GridIndexType m_StartZ; /** Last clipped pixel index in z direction plus one. */ Types::GridIndexType m_EndZ; }; /** Thread function for metric computation. */ static void EvaluateThreadFunction( void* args, const size_t taskIdx, const size_t taskCnt, const size_t, const size_t ); #ifdef CMTK_USE_SMP /** Mutex lock for shared metric data object. */ MutexLock m_MetricDataMutex; #endif /** Make spline functional friend to access this class' channel image vectors. */ template friend class SplineWarpMultiChannelRegistrationFunctional; }; //@} } // namespace cmtk #include "cmtkAffineMultiChannelRegistrationFunctional.txx" #endif // #ifndef __cmtkAffineMultiChannelRegistrationFunctional_h_included_ cmtk-3.3.1/libs/Registration/cmtkAffineMultiChannelRegistrationFunctional.txx000066400000000000000000000241131276303427400276260ustar00rootroot00000000000000/* // // Copyright 2016 Google, Inc. // // Copyright 1997-2010 Torsten Rohlfing // // Copyright 2004-2011 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 5403 $ // // $LastChangedDate: 2016-01-14 20:59:04 -0800 (Thu, 14 Jan 2016) $ // // $LastChangedBy: torstenrohlfing $ // */ #include namespace cmtk { /** \addtogroup Registration */ //@{ template void AffineMultiChannelRegistrationFunctional ::InitTransformation( const bool alignCenters ) { this->m_Transformation.MakeIdentityXform(); if ( alignCenters ) { if ( this->m_ReferenceChannels.size() == 0 || this->m_FloatingChannels.size() == 0 ) { StdErr << "ERROR: must set at least one reference and one floating channel image before calling\n" << " AffineMultiChannelRegistrationFunctional::InitTransformation()\n"; exit( 1 ); } Vector3D deltaCenter = ( this->m_ReferenceChannels[0]->GetCenterCropRegion() - this->m_FloatingChannels[0]->GetCenterCropRegion() ); this->m_Transformation.SetXlate( deltaCenter.begin() ); } Vector3D center = this->m_ReferenceChannels[0]->GetCenterCropRegion(); this->m_Transformation.ChangeCenter( center ); } template typename AffineMultiChannelRegistrationFunctional::ReturnType AffineMultiChannelRegistrationFunctional ::Evaluate() { this->m_MetricData.Init( this ); const TransformedVolumeAxes transformedAxes( *this->m_ReferenceChannels[0], &this->m_Transformation ); const DataGrid::IndexType& dims = this->m_ReferenceDims; const Types::GridIndexType dimsX = dims[0], dimsY = dims[1], dimsZ = dims[2]; this->m_VolumeClipper.SetDeltaX( transformedAxes[0][dimsX-1] - transformedAxes[0][0] ); this->m_VolumeClipper.SetDeltaY( transformedAxes[1][dimsY-1] - transformedAxes[1][0] ); this->m_VolumeClipper.SetDeltaZ( transformedAxes[2][dimsZ-1] - transformedAxes[2][0] ); this->m_VolumeClipper.SetClippingBoundaries( this->m_FloatingCropRegion ); Types::GridIndexType startZ, endZ; if ( this->ClipZ( this->m_VolumeClipper, transformedAxes[2][0], startZ, endZ ) ) { startZ = std::max( startZ, this->m_ReferenceCropRegion.From()[2] ); endZ = std::min( endZ, this->m_ReferenceCropRegion.To()[2] ); ThreadPool& threadPool = ThreadPool::GetGlobalThreadPool(); const size_t numberOfThreads = threadPool.GetNumberOfThreads(); const size_t numberOfTasks = 4 * numberOfThreads - 3; std::vector threadParams( numberOfTasks ); for ( size_t thread = 0; thread < numberOfTasks; ++thread ) { threadParams[thread].thisObject = this; threadParams[thread].m_TransformedAxes = &transformedAxes; threadParams[thread].m_StartZ = startZ; threadParams[thread].m_EndZ = endZ; } threadPool.Run( Self::EvaluateThreadFunction, threadParams ); } return this->GetMetric( this->m_MetricData ); } template void AffineMultiChannelRegistrationFunctional ::EvaluateThreadFunction( void* args, const size_t taskIdx, const size_t taskCnt, const size_t, const size_t ) { typename Self::EvaluateThreadParameters* params = static_cast( args ); Self* This = params->thisObject; const Self* constThis = This; typename Self::MetricData metricData; metricData.Init( This ); const Vector3D *transformedAxesX = (*params->m_TransformedAxes)[0]; const Vector3D *transformedAxesY = (*params->m_TransformedAxes)[1]; const Vector3D *transformedAxesZ = (*params->m_TransformedAxes)[2]; const DataGrid::IndexType& dims = constThis->m_ReferenceDims; const Types::GridIndexType dimsX = dims[0], dimsY = dims[1]; Vector3D pFloating, rowStart; // Loop over all remaining planes for ( Types::GridIndexType pZ = params->m_StartZ + taskIdx; pZ < params->m_EndZ; pZ += taskCnt ) { // Offset of current reference voxel Types::GridIndexType r = pZ * dimsX * dimsY; Vector3D planeStart = transformedAxesZ[pZ]; Types::GridIndexType startY, endY; if ( constThis->ClipY( constThis->m_VolumeClipper, planeStart, startY, endY ) ) { startY = std::max( startY, constThis->m_ReferenceCropRegion.From()[1] ); endY = std::min( endY, constThis->m_ReferenceCropRegion.To()[1] ); r += startY * dimsX; // Loop over all remaining rows for ( Types::GridIndexType pY = startY; pYClipX( constThis->m_VolumeClipper, rowStart, startX, endX ) ) { startX = std::max( startX, constThis->m_ReferenceCropRegion.From()[0] ); endX = std::min( endX, constThis->m_ReferenceCropRegion.To()[0] ); r += startX; // Loop over all remaining voxels in current row for ( Types::GridIndexType pX = startX; pXContinueMetric( metricData, r, pFloating ); } r += (dimsX-endX); } else { r += dimsX; } } r += (dimsY-endY) * dimsX; } else { r += dimsY * dimsX; } } #ifdef CMTK_USE_SMP This->m_MetricDataMutex.Lock(); #endif This->m_MetricData += metricData; #ifdef CMTK_USE_SMP This->m_MetricDataMutex.Unlock(); #endif } template bool AffineMultiChannelRegistrationFunctional ::ClipZ ( const VolumeClipping& clipper, const Vector3D& origin, Types::GridIndexType& start, Types::GridIndexType& end ) const { // perform clipping Types::Coordinate fromFactor, toFactor; if (! clipper.ClipZ( fromFactor, toFactor, origin ) ) return false; // there is an intersection: Look up the corresponding grid indices start = static_cast( (this->m_ReferenceDims[2]-1)*fromFactor ); end = 1+std::min( (Types::GridIndexType)(this->m_ReferenceDims[2]-1), (Types::GridIndexType)(1 + ((this->m_ReferenceDims[2]-1)*toFactor) ) ); // finally, apply cropping boundaries of the reference volume start = std::max( start, this->m_ReferenceCropRegion.From()[2] ); end = std::min( end, this->m_ReferenceCropRegion.To()[2] ); // return true iff index range is non-empty. return (start < end ); } template bool AffineMultiChannelRegistrationFunctional ::ClipX ( const VolumeClipping& clipper, const Vector3D& origin, Types::GridIndexType& start, Types::GridIndexType& end ) const { // perform clipping Types::Coordinate fromFactor, toFactor; if ( ! clipper.ClipX( fromFactor, toFactor, origin, 0, 2, false, true ) ) return false; fromFactor = std::min( 1.0, fromFactor ); // there is an intersection: Look up the corresponding grid indices start = std::max( 0, (Types::GridIndexType)((this->m_ReferenceDims[0]-1)*fromFactor)-1 ); while ( ( start*this->m_ReferenceChannels[0]->m_Delta[0] < fromFactor*this->m_ReferenceSize[0]) && ( start < this->m_ReferenceDims[0] ) ) ++start; if ( (toFactor > 1.0) || (start == this->m_ReferenceDims[0]) ) { end = this->m_ReferenceDims[0]; } else { end = std::min( this->m_ReferenceDims[0]-2, (Types::GridIndexType)(1 + (this->m_ReferenceDims[0]-1)*toFactor)); while ( end*this->m_ReferenceChannels[0]->m_Delta[0] > toFactor*this->m_ReferenceSize[0] ) // 'if' not sufficient! --end; ++end; // otherwise end=1+min(...) and ...[0][end-1] above!! } // finally, apply cropping boundaries of the reference volume start = std::max( start, this->m_ReferenceCropRegion.From()[0] ); end = std::min( end, this->m_ReferenceCropRegion.To()[0] ); // return true iff index range is non-empty. return (start < end ); } template bool AffineMultiChannelRegistrationFunctional ::ClipY ( const VolumeClipping& clipper, const Vector3D& origin, Types::GridIndexType& start, Types::GridIndexType& end ) const { // perform clipping Types::Coordinate fromFactor, toFactor; if ( !clipper.ClipY( fromFactor, toFactor, origin ) ) return false; // there is an intersection: Look up the corresponding grid indices start = static_cast( (this->m_ReferenceDims[1]-1)*fromFactor ); if ( toFactor > 1.0 ) { end = this->m_ReferenceDims[1]; } else { end = 1+std::min( this->m_ReferenceDims[1]-1, (Types::GridIndexType)(1+(this->m_ReferenceDims[1]-1)*toFactor ) ); } // finally, apply cropping boundaries of the reference volume start = std::max( start, this->m_ReferenceCropRegion.From()[1] ); end = std::min( end, this->m_ReferenceCropRegion.To()[1] ); // return true iff index range is non-empty. return (start < end ); } } // namespace cmtk cmtk-3.3.1/libs/Registration/cmtkAffineRegistration.cxx000066400000000000000000000154451276303427400232660ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2011 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4426 $ // // $LastChangedDate: 2012-06-12 10:30:56 -0700 (Tue, 12 Jun 2012) $ // // $LastChangedBy: torstenrohlfing $ // */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace cmtk { /** \addtogroup Registration */ //@{ AffineRegistration::AffineRegistration () : m_MatchFltToRefHistogram( false ) { this->m_InitialAlignCenters = false; this->m_NoSwitch = false; } AffineRegistration::~AffineRegistration () { } CallbackResult AffineRegistration::InitRegistration () { CallbackResult result = this->Superclass::InitRegistration(); if ( result != CALLBACK_OK ) return result; if ( this->m_NoSwitch || (this->m_Volume_1->AverageVoxelVolume() >= this->m_Volume_2->AverageVoxelVolume()) ) { this->m_ReferenceVolume = this->m_Volume_1; this->m_FloatingVolume = this->m_Volume_2; SwitchVolumes = false; } else { this->m_ReferenceVolume = this->m_Volume_2; this->m_FloatingVolume = this->m_Volume_1; SwitchVolumes = true; } if ( this->m_MatchFltToRefHistogram ) { this->GetVolume_2()->GetData()->ApplyFunctionObject( TypedArrayFunctionHistogramMatching( *(this->GetVolume_2()->GetData()), *(this->GetVolume_1()->GetData()) ) ); } AffineXform::SmartPtr affineXform; if ( this->m_InitialTransformation ) { if ( SwitchVolumes ^ this->m_InitialXformIsInverse ) { affineXform = AffineXform::SmartPtr( this->m_InitialTransformation->MakeInverse() ); } else { affineXform = AffineXform::SmartPtr( this->m_InitialTransformation ); } } else { affineXform = AffineXform::SmartPtr( new AffineXform ); } if ( this->m_InitialAlignCenters ) { Vector3D deltaCenter = ( this->m_FloatingVolume->GetCenterCropRegion() - this->m_ReferenceVolume->GetCenterCropRegion() ); affineXform->SetXlate( deltaCenter.begin() ); } this->m_Xform = affineXform; Vector3D center = this->m_ReferenceVolume->GetCenterCropRegion(); affineXform->ChangeCenter( center ); if ( this->m_UseOriginalData ) { Functional::SmartPtr newFunctional( VoxelMatchingAffineFunctional::Create( this->m_Metric, this->m_ReferenceVolume, this->m_FloatingVolume, affineXform ) ); FunctionalStack.push( newFunctional ); } Types::Coordinate currSampling = std::max( this->m_Sampling, 2 * std::min( this->m_ReferenceVolume->GetMinDelta(), this->m_FloatingVolume->GetMinDelta())); double coarsest = CoarsestResolution; if ( coarsest <= 0 ) coarsest = this->m_Exploration; UniformVolume::SmartPtr currRef( this->m_ReferenceVolume ); UniformVolume::SmartPtr currFlt( this->m_FloatingVolume ); for ( ; (currSampling<=coarsest); currSampling *= 2 ) { UniformVolume::SmartPtr nextRef( currRef->GetResampled( currSampling ) ); UniformVolume::SmartPtr nextFlt( currFlt->GetResampled( currSampling ) ); Functional::SmartPtr newFunctional( VoxelMatchingAffineFunctional::Create( this->m_Metric, nextRef, nextFlt, affineXform ) ); FunctionalStack.push( newFunctional ); currRef = nextRef; currFlt = nextFlt; } this->m_Optimizer = Optimizer::SmartPtr( new BestNeighbourOptimizer( OptimizerStepFactor ) ); this->m_Optimizer->SetCallback( this->m_Callback ); // default to rigid transformation if ( NumberDOFs.empty() ) NumberDOFs.push_back( 6 ); // push guard elements NumberDOFs.push_back( -1 ); NumberDOFsFinal.push_back( -1 ); // intialize iterator. NumberDOFsIterator = NumberDOFs.begin(); return CALLBACK_OK; } void AffineRegistration::EnterResolution ( CoordinateVector::SmartPtr& v, Functional::SmartPtr& f, const int level, const int total ) { if ( *NumberDOFsIterator < 0 ) { if ( (level == total) && (NumberDOFsFinal.size()>1) ) NumberDOFsIterator = NumberDOFsFinal.begin(); else NumberDOFsIterator = NumberDOFs.begin(); } AffineXform::SmartPtr affineXform = AffineXform::SmartPtr::DynamicCastFrom( this->m_Xform ); if ( affineXform ) { int numberDOFs = std::min( 12, *NumberDOFsIterator ); affineXform->SetNumberDOFs( numberDOFs ); if ( this->m_Callback ) { char buffer[64]; snprintf( buffer, sizeof( buffer ), "Setting Number DOFs to %d.", numberDOFs ); this->m_Callback->Comment( buffer ); } } this->Superclass::EnterResolution( v, f, level, total ); } int AffineRegistration::DoneResolution ( CoordinateVector::SmartPtr& v, Functional::SmartPtr& f, const int level, const int total ) { this->Superclass::DoneResolution( v, f, level, total ); NumberDOFsIterator++; return (*NumberDOFsIterator < 0); } AffineXform::SmartPtr AffineRegistration::GetTransformation() const { AffineXform::SmartPtr affineXform = AffineXform::SmartPtr::DynamicCastFrom( this->m_Xform ); if ( affineXform && SwitchVolumes ) { return affineXform->GetInverse(); } else { return affineXform; } } const UniformVolume::SmartPtr AffineRegistration::GetReformattedFloatingImage( Interpolators::InterpolationEnum interpolator ) const { ReformatVolume reformat; reformat.SetInterpolation( interpolator ); reformat.SetReferenceVolume( this->m_Volume_1 ); reformat.SetFloatingVolume( this->m_Volume_2 ); AffineXform::SmartPtr affineXform( this->GetTransformation() ); reformat.SetAffineXform( affineXform ); return reformat.PlainReformat(); } } // namespace cmtk cmtk-3.3.1/libs/Registration/cmtkAffineRegistration.h000066400000000000000000000115321276303427400227040ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2010 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 2752 $ // // $LastChangedDate: 2011-01-17 11:33:31 -0800 (Mon, 17 Jan 2011) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkAffineRegistration_h_included_ #define __cmtkAffineRegistration_h_included_ #include #include #include #include #include namespace cmtk { /** \addtogroup Registration */ //@{ /** Class for affine multi-resolution voxel registration. */ class AffineRegistration : /// Inherit general voxel registration interface and functionality. public VoxelRegistration { protected: /** Flag for initial alignment of volume centers. */ cmtkGetSetMacroDefault(bool,InitialAlignCenters,true); /// Flag whether to adjust floating image histogram to match reference image. cmtkGetSetMacro(bool,MatchFltToRefHistogram); /// If set, this flag prevent automatic switching of model and reference. cmtkGetSetMacro(bool,NoSwitch); /** Numbers of degrees of freedom. * This list contains the numbers of degrees of freedom for every resolution * level. Registration is repeated with the same data as many times as there * are entries in this list. If the derived classes do not set any entries, * InitRegistration() will push a "6" into the list, resulting in an affine * registration. */ std::vector NumberDOFs; /** Numbers of degrees of freedom for the final resolution level. * Just as "NumberDOFs", this list defines the sequence of numbers of degrees * of freedom for the finest resolution level. */ std::vector NumberDOFsFinal; /** Initialize registration. * This function is called by Register before any other operations. It can * be overloaded to open status dialog windows, etc. Derived implementations * should call their base class' InitRegistration first. *\return Overriding functions should return a value other than * CALLBACK_OK if the registration is to be interrupted. */ virtual CallbackResult InitRegistration (); /** Enter a resolution level. * This function mainly determines the next effective number of degrees of * freedom of the optimization. It sets the transformation object accordingly * and writes a comment to the "Callback" object. Afterwards, the inherited * EnterResolution() function is called. */ virtual void EnterResolution( CoordinateVector::SmartPtr& v, Functional::SmartPtr& f, const int level, const int total ); /** Finish resolution level. * This function determines whether there are any remaining numbers in the * effective NumberDOFs list. If so, the list iterator is advanced and 0 is * returned, indicating that the current resolution level is to be repeated. * With no number left, 1 is returned indicating that the current level has * been completed. */ virtual int DoneResolution( CoordinateVector::SmartPtr& v, Functional::SmartPtr& f, const int level, const int total ); public: /** Default constructor. */ AffineRegistration (); /** Destructor. */ virtual ~AffineRegistration (); /// Return final transformation. AffineXform::SmartPtr GetTransformation() const; /// Get reformatted floating image. const UniformVolume::SmartPtr GetReformattedFloatingImage( Interpolators::InterpolationEnum interpolator = Interpolators::LINEAR ) const; /// Add a number to the general list of numbers of DOFs. void AddNumberDOFs( const int numberDOFs ) { NumberDOFs.push_back( numberDOFs ); } /// Add a number to the list of numbers of DOFs for the last level. void AddNumberDOFsFinal( const int numberDOFs ) { NumberDOFsFinal.push_back( numberDOFs ); } private: /// Convenience definition. typedef VoxelRegistration Superclass; /// Iterator for NumberDOFs and NumberDOFsFinal std::vector::iterator NumberDOFsIterator; }; //@} } // namespace cmtk #endif // __cmtkAffineRegistration_h_included_ cmtk-3.3.1/libs/Registration/cmtkAffineRegistrationCommandLine.cxx000066400000000000000000000514011276303427400253650ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2013 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 5316 $ // // $LastChangedDate: 2014-04-11 13:43:18 -0700 (Fri, 11 Apr 2014) $ // // $LastChangedBy: torstenrohlfing $ // */ #include "cmtkAffineRegistrationCommandLine.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef CMTK_USE_SQLITE # include #endif #ifdef HAVE_SYS_TYPES_H # include #endif #ifdef HAVE_SYS_STAT_H # include #endif #ifdef HAVE_SYS_UTSNAME_H # include #endif #include #include #include #ifdef _MSC_VER # include #endif namespace cmtk { /** \addtogroup Registration */ //@{ AffineRegistrationCommandLine ::AffineRegistrationCommandLine ( const int argc, const char* argv[] ) { this->m_Metric = 0; this->m_AutoMultiLevels = 0; CoarsestResolution = -1; this->m_Exploration = 8; this->m_Accuracy = 0.1; this->m_Sampling = 1.0; InitXlate = 0; this->m_NoSwitch = 0; std::string InitialStudylist; std::string clArg1; // input studylist or reference image std::string clArg2; // empty or floating image try { CommandLine cl( CommandLine::PROPS_XML ); cl.SetProgramInfo( CommandLine::PRG_TITLE, "Rigid and affine registration" ); cl.SetProgramInfo( CommandLine::PRG_DESCR, "This program performs rigid and affine image registration using multi-resolution optimization of voxel-based image similarity measures." ); cl.SetProgramInfo( CommandLine::PRG_CATEG, "CMTK.Registration" ); typedef CommandLine::Key Key; cl.BeginGroup( "Automation", "Automation Options" ); cl.AddOption( Key( "auto-multi-levels" ), &this->m_AutoMultiLevels, "Automatic optimization and resolution parameter generation for levels" ); cl.BeginGroup( "Optimization", "Optimization settings" ); cl.AddOption( Key( 'e', "exploration" ), &this->m_Exploration, "Exploration [initial optimizer step size]" ); cl.AddOption( Key( 'a', "accuracy" ), &this->m_Accuracy, "Accuracy [final optimizer step size]" ); cl.AddOption( Key( 'f', "stepfactor" ), &this->OptimizerStepFactor, "Factor for search step size reduction. Must be > 0.0 and < 1.0" ); cl.AddOption( Key( "delta-f-threshold" ), &this->m_DeltaFThreshold, "Optional threshold to terminate optimization (level) if relative change of target function drops below this value." ); cl.EndGroup(); cl.BeginGroup( "Resolution", "Image resolution parameters" ); cl.AddOption( Key( 's', "sampling" ), &this->m_Sampling, "Image sampling (finest resampled image resolution)" ); cl.AddOption( Key( "coarsest" ), &this->CoarsestResolution, "Upper limit for image sampling in multiresolution hierarchy" ); cl.AddSwitch( Key( "omit-original-data" ), &this->m_UseOriginalData, false, "Do not use original data in full resolution, omit final stage in multiresolution hierarchy, thus reducing computation time." ); cl.EndGroup(); cl.BeginGroup( "Transformation", "Transformation parameters" ); cl.AddVector( Key( "dofs" ), this->NumberDOFs, "Add number of degrees of freedom [can be repeated]" ); cl.AddVector( Key( "dofs-final" ), this->NumberDOFsFinal, "Add number of degrees of freedom for final level only [can be repeated]" ); cl.AddSwitch( Key( 'n', "no-switch" ), &this->m_NoSwitch, true, "Do not auto-switch reference and floating image for improved computational performance" ); cl.AddSwitch( Key( 'i', "initxlate" ), &InitXlate, true, "Initialized transformation by translating floating image FOV center onto reference image FOV center" ); cl.AddOption( Key( "initial" ), &InitialStudylist, "Initialize transformation from given path" )->SetProperties( CommandLine::PROPS_XFORM ); cl.AddSwitch( Key( "initial-is-inverse" ), &this->m_InitialXformIsInverse, true, "Invert initial transformation before initializing registration" ); cl.EndGroup(); cl.BeginGroup( "Image data", "Image data" ); CommandLine::EnumGroup::SmartPtr metricGroup = cl.AddEnum( "registration-metric", &this->m_Metric, "Registration metric for motion estimation by image-to-image registration." ); metricGroup->AddSwitch( Key( "nmi" ), 0, "Normalized Mutual Information metric" ); metricGroup->AddSwitch( Key( "mi" ), 1, "Standard Mutual Information metric" ); metricGroup->AddSwitch( Key( "cr" ), 2, "Correlation Ratio metric" ); metricGroup->AddSwitch( Key( "msd" ), 4, "Mean Squared Difference metric" ); metricGroup->AddSwitch( Key( "ncc" ), 5, "Normalized Cross Correlation metric" ); cl.AddSwitch( Key( "match-histograms" ), &this->m_MatchFltToRefHistogram, true, "Match floating image histogram to reference image histogram." ); this->m_PreprocessorRef.AttachToCommandLine( cl ); this->m_PreprocessorFlt.AttachToCommandLine( cl ); cl.BeginGroup( "Output", "Output parameters" )->SetProperties( CommandLine::PROPS_NOXML ); cl.AddOption( Key( 'o', "outlist" ), &this->Studylist, "Output path for final transformation" ); cl.AddOption( Key( "out-matrix" ), &this->OutMatrixName, "Output path for final transformation in matrix format" ); cl.AddOption( Key( "out-parameters" ), &this->OutParametersName, "Output path for final transformation in plain parameter list format" ); cl.AddOption( Key( 'p', "protocol" ), &this->Protocol, "Optimization protocol output file name" ); cl.AddOption( Key( 't', "time" ), &this->Time, "Computation time statistics output file name" ); cl.EndGroup(); cl.BeginGroup( "SlicerImport", "Import Results into Slicer" ); cl.AddOption( Key( "out-itk" ), &this->m_OutputPathITK, "Output path for final transformation in ITK format" ) ->SetProperties( CommandLine::PROPS_XFORM | CommandLine::PROPS_OUTPUT ) ->SetAttribute( "reference", "FloatingImage" ); cl.AddOption( Key( "write-reformatted" ), &this->m_ReformattedImagePath, "Write reformatted floating image." )->SetProperties( CommandLine::PROPS_IMAGE | CommandLine::PROPS_OUTPUT ); cl.EndGroup(); #ifdef CMTK_USE_SQLITE cl.BeginGroup( "Database", "Image/Transformation Database" ); cl.AddOption( Key( "db" ), &this->m_UpdateDB, "Path to image/transformation database that should be updated with the new registration and/or reformatted image." ); cl.EndGroup(); #endif cl.AddParameter( &clArg1, "ReferenceImage", "Reference (fixed) image path" )->SetProperties( CommandLine::PROPS_IMAGE ); cl.AddParameter( &clArg2, "FloatingImage", "Floating (moving) image path" )->SetProperties( CommandLine::PROPS_IMAGE | CommandLine::PROPS_OPTIONAL ); cl.Parse( argc, argv ); } catch ( const CommandLine::Exception& ex ) { StdErr << ex << "\n"; throw cmtk::ExitException( 1 ); } if ( (OptimizerStepFactor <= 0) || (OptimizerStepFactor >= 1) ) { StdErr << "ERROR: step factor value " << OptimizerStepFactor << " is invalid. Must be in range (0..1)\n"; throw cmtk::ExitException( 1 ); } // check for supported numbers of degrees of freedom const std::set supportedDOFs = AffineXform::GetSupportedDOFs(); for ( std::vector::iterator it = this->NumberDOFs.begin(); it != this->NumberDOFs.end(); ++it ) { if ( *it == 603 ) // fix legacy value *it = 3303; if ( supportedDOFs.find( *it ) == supportedDOFs.end() ) { StdErr << "ERROR: DOF number " << *it << " is not supported.\n"; throw cmtk::ExitException( 1 ); } } // check for supported numbers of degrees of freedom for ( std::vector::iterator it = this->NumberDOFsFinal.begin(); it != this->NumberDOFsFinal.end(); ++it ) { if ( *it == 603 ) // fix legacy value *it = 3303; if ( supportedDOFs.find( *it ) == supportedDOFs.end() ) { StdErr << "ERROR: DOF number " << *it << " is not supported.\n"; throw cmtk::ExitException( 1 ); } } this->SetInitialTransformation( AffineXform::SmartPtr( new AffineXform() ) ); if ( ! clArg2.empty() ) { Study1 = clArg1; Study2 = clArg2; } else { this->m_InitialXformPath = clArg1; if ( ! InitialStudylist.empty() ) { StdErr << "WARNING: transformation of input studylist will be overriden by transformation provided with '--initial'.\n"; } DebugOutput( 1 ) << "Reading input studylist " << this->m_InitialXformPath << ".\n"; ClassStreamInput typedStream( MountPoints::Translate(this->m_InitialXformPath), "registration" ); if ( ! typedStream.IsValid() ) { StdErr << "ERROR: could not open studylist archive " << this->m_InitialXformPath << ".\n"; throw cmtk::ExitException( 1 ); } typedStream.Seek ( "registration" ); Study1 = typedStream.ReadStdString( "reference_study" ); Study2 = typedStream.ReadStdString( "floating_study" ); if ( ! Study2.empty() ) { AffineXform::SmartPtr affineXform; typedStream >> affineXform; this->SetInitialTransformation( affineXform ); } else { // legacy studylists have inverse transformation in them Study2 = typedStream.ReadStdString( "model_study" ); AffineXform::SmartPtr affineXform; typedStream >> affineXform; try { this->SetInitialTransformation( affineXform->GetInverse() ); } catch ( const AffineXform::MatrixType::SingularMatrixException& ) { StdErr << "ERROR: singular matrix read from initialization file\n"; throw ExitException( 1 ); } } typedStream.Close(); } if ( Study1.empty() ) { StdErr << "ERROR: reference image path resolved to NULL.\n"; throw cmtk::ExitException( 1 ); } if ( Study2.empty() ) { StdErr << "ERROR: floating image path resolved to NULL.\n"; throw cmtk::ExitException( 1 ); } UniformVolume::SmartPtr volume( VolumeIO::ReadOriented( this->Study1 ) ); if ( !volume ) { StdErr << "ERROR: volume " << this->Study1 << " could not be read\n"; throw cmtk::ExitException( 1 ); } this->SetVolume_1( UniformVolume::SmartPtr( this->m_PreprocessorRef.GetProcessedImage( volume ) ) ); volume = UniformVolume::SmartPtr( VolumeIO::ReadOriented( this->Study2 ) ); if ( !volume ) { StdErr << "ERROR: volume " << this->Study2 << " could not be read\n"; throw cmtk::ExitException( 1 ); } this->SetVolume_2( UniformVolume::SmartPtr( this->m_PreprocessorFlt.GetProcessedImage( volume ) ) ); if ( ! InitialStudylist.empty() ) { this->m_InitialXformPath = InitialStudylist; Xform::SmartPtr xform( XformIO::Read( InitialStudylist ) ); if ( ! xform ) { StdErr << "ERROR: could not read transformation from " << InitialStudylist << "\n"; throw cmtk::ExitException( 1 ); } AffineXform::SmartPtr affine( AffineXform::SmartPtr::DynamicCastFrom( xform ) ); if ( ! affine ) { StdErr << "ERROR: transformation " << InitialStudylist << " is not affine.\n"; throw cmtk::ExitException( 1 ); } if ( affine->GetMetaInfo( META_SPACE ) != AnatomicalOrientation::ORIENTATION_STANDARD ) { try { TransformChangeFromSpaceAffine toStandardSpace( *affine, *(this->m_Volume_1), *(this->m_Volume_2) ); *affine = toStandardSpace.GetTransformation(); affine->SetMetaInfo( META_SPACE, AnatomicalOrientation::ORIENTATION_STANDARD ); } catch ( const AffineXform::MatrixType::SingularMatrixException& ) { StdErr << "ERROR: singular matrix cannot be inverted to change transformation to standard space in AffineRegistrationCommandLine constructor.\n"; throw ExitException( 1 ); } } this->SetInitialTransformation( affine ); } if ( InitXlate ) { if ( ! this->m_InitialXformPath.empty() ) { StdErr << "WARNING: Initial transformation was taken from studylist. Switch --initxlate / -i will be ignored.\n"; } else { this->SetInitialAlignCenters(); } } if ( this->m_AutoMultiLevels > 0 ) { const Types::Coordinate minDelta = std::min( this->m_Volume_1->GetMinDelta(), this->m_Volume_2->GetMinDelta() ); const Types::Coordinate maxDelta = std::max( this->m_Volume_1->GetMaxDelta(), this->m_Volume_2->GetMaxDelta() ); this->m_Accuracy = 0.1 * minDelta; this->m_Sampling = maxDelta; this->m_Exploration = maxDelta * (1<<(this->m_AutoMultiLevels-1)); } if ( !Protocol.empty() ) { RegistrationCallback::SmartPtr callback( new ProtocolCallback( Protocol ) ); this->SetCallback( callback ); } } CallbackResult AffineRegistrationCommandLine::InitRegistration () { CallbackResult Result = AffineRegistration::InitRegistration(); return Result; } void AffineRegistrationCommandLine::OutputResultMatrix( const std::string& matrixName ) const { const AffineXform::MatrixType& matrix = this->GetTransformation()->Matrix; FILE* mfile = fopen( matrixName.c_str(), "w" ); if ( mfile ) { for ( int i = 0; i < 4; ++i ) { fprintf( mfile, "%e\t%e\t%e\t%e\n", static_cast( matrix[0][i] ), static_cast( matrix[1][i] ), static_cast( matrix[2][i] ), static_cast( matrix[3][i] ) ); } fclose( mfile ); } } void AffineRegistrationCommandLine::OutputResultParameters ( const std::string& paramsName, const CoordinateVector& v ) const { FILE* pfile = fopen( paramsName.c_str(), "w" ); if ( pfile ) { for ( unsigned int idx=0; idx < v.Dim; ++idx ) fprintf( pfile, "#%u: %f\n", idx, v.Elements[idx] ); fclose( pfile ); } } void AffineRegistrationCommandLine::OutputResultList( const std::string& studyList ) const { ClassStreamOutput classStream( studyList, "studylist", ClassStreamOutput::MODE_WRITE ); if ( !classStream.IsValid() ) return; classStream.Begin( "studylist" ); classStream.WriteInt( "num_sources", 2 ); classStream.End(); classStream.Begin( "source" ); classStream.WriteString( "studyname", CompressedStream::GetBaseName( Study1 ) ); classStream.End(); classStream.Begin( "source" ); classStream.WriteString( "studyname", CompressedStream::GetBaseName( Study2 ) ); classStream.End(); classStream.Close(); classStream.Open( studyList, "registration", ClassStreamOutput::MODE_WRITE ); classStream.Begin( "registration" ); classStream.WriteString( "reference_study", CompressedStream::GetBaseName( Study1 ) ); classStream.WriteString( "floating_study", CompressedStream::GetBaseName( Study2 ) ); classStream << *(this->GetTransformation()); classStream.End(); classStream.Close(); classStream.Open( studyList, "settings", ClassStreamOutput::MODE_WRITE ); classStream.WriteDouble( "exploration", this->m_Exploration ); classStream.WriteDouble( "accuracy", this->m_Accuracy ); classStream.WriteDouble( "min_sampling", this->m_Sampling ); classStream.WriteDouble( "coarsest_resolution", CoarsestResolution ); classStream.WriteInt( "metric", this->m_Metric ); classStream.WriteDouble( "optimizer_step_factor", OptimizerStepFactor ); classStream.WriteBool( "no_switch", this->m_NoSwitch ); this->m_PreprocessorRef.WriteSettings( classStream ); this->m_PreprocessorFlt.WriteSettings( classStream ); classStream.Close(); classStream.Open( studyList, "statistics", ClassStreamOutput::MODE_WRITE ); classStream.WriteDouble( "time", this->GetTotalElapsedTime() ); classStream.WriteDouble( "walltime", this->GetTotalElapsedWalltime() ); #ifdef CMTK_USE_PTHREADS classStream.WriteDouble( "thread_time", this->GetThreadTotalElapsedTime() ); #endif #ifndef _MSC_VER struct utsname name; if ( uname( &name ) >= 0 ) { classStream.WriteString( "host", name.nodename ); classStream.WriteString( "system", name.sysname ); } #endif classStream.Close(); } void AffineRegistrationCommandLine::OutputResult ( const CoordinateVector* v, const CallbackResult irq ) { DebugOutput( 1 ) << "Resulting transformation parameters: \n"; for ( unsigned int idx=0; idxDim; ++idx ) DebugOutput( 1 ).GetStream().printf( "#%ud: %f\n", idx, v->Elements[idx] ); if ( !this->OutMatrixName.empty() ) { if ( irq != CALLBACK_OK ) this->OutputResultMatrix( this->OutMatrixName + "-partial" ); else this->OutputResultMatrix( this->OutMatrixName ); } if ( !this->OutParametersName.empty() ) { if ( irq != CALLBACK_OK ) this->OutputResultParameters( this->OutParametersName + "-partial", *v ); else this->OutputResultParameters( this->OutParametersName, *v ); } if ( !this->Studylist.empty() ) { if ( irq != CALLBACK_OK ) this->OutputResultList( this->Studylist + "-partial" ); else this->OutputResultList( this->Studylist ); } if ( !this->m_OutputPathITK.empty() ) { try { TransformChangeToSpaceAffine toNative( *(this->GetTransformation()), *(this->m_Volume_1), *(this->m_Volume_2), AnatomicalOrientationBase::SPACE_ITK ); if ( irq != CALLBACK_OK ) AffineXformITKIO::Write( this->m_OutputPathITK + "-partial", toNative.GetTransformation() ); else AffineXformITKIO::Write( this->m_OutputPathITK, toNative.GetTransformation() ); } catch ( const AffineXform::MatrixType::SingularMatrixException& ) { StdErr << "ERROR: singular matrix cannot be inverted to change transformation to standard space in AffineRegistrationCommandLine::OutputResult\n"; throw ExitException( 1 ); } } if ( !this->m_ReformattedImagePath.empty() ) { if ( irq != CALLBACK_OK ) VolumeIO::Write( *(this->GetReformattedFloatingImage()), this->m_ReformattedImagePath + "-partial" ); else VolumeIO::Write( *(this->GetReformattedFloatingImage()), this->m_ReformattedImagePath ); } #ifdef CMTK_USE_SQLITE if ( (irq == CALLBACK_OK) && !this->m_UpdateDB.empty() ) { try { ImageXformDB db( this->m_UpdateDB ); if ( !this->m_ReformattedImagePath.empty() ) { db.AddImage( this->m_ReformattedImagePath, this->m_ReferenceVolume->GetMetaInfo( META_FS_PATH ) ); } if ( !this->Studylist.empty() ) { if ( ! this->m_InitialXformPath.empty() ) { db.AddRefinedXform( this->Studylist, true /*invertible*/, this->m_InitialXformPath, this->m_InitialXformIsInverse ); } else { db.AddImagePairXform( this->Studylist, true /*invertible*/, this->m_ReferenceVolume->GetMetaInfo( META_FS_PATH ), this->m_FloatingVolume->GetMetaInfo( META_FS_PATH ) ); } } } catch ( const ImageXformDB::Exception& ex ) { StdErr << "DB ERROR: " << ex.what() << " on database " << this->m_UpdateDB << "\n"; } } #endif } void AffineRegistrationCommandLine::EnterResolution ( CoordinateVector::SmartPtr& v, Functional::SmartPtr& f, const int index, const int total ) { DebugOutput( 1 ).GetStream().printf( "Entering resolution level %d out of %d...\n", index, total ); this->Superclass::EnterResolution( v, f, index, total ); } CallbackResult AffineRegistrationCommandLine::Register () { const double baselineTime = Timers::GetTimeProcess(); CallbackResult Result = Superclass::Register(); const int elapsed = static_cast( Timers::GetTimeProcess() - baselineTime ); if ( !this->Time.empty() ) { FILE *tfp = fopen( this->Time.c_str(), "w" ); if ( tfp ) { fprintf( tfp, "%d\n", elapsed ); fclose( tfp ); } else { StdErr << "Could not open time file " << this->Time << "\n"; } } return Result; } } // namespace cmtk cmtk-3.3.1/libs/Registration/cmtkAffineRegistrationCommandLine.h000066400000000000000000000132621276303427400250150ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2011, 2013 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4836 $ // // $LastChangedDate: 2013-09-12 16:07:28 -0700 (Thu, 12 Sep 2013) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkAffineRegistrationCommandLine_h_included_ #define __cmtkAffineRegistrationCommandLine_h_included_ #include #include namespace cmtk { /** \addtogroup Registration */ //@{ /** Class for command line-controlled affine registration. *\author T. Rohlfing */ class AffineRegistrationCommandLine : /// Inherit generic affine registration. public AffineRegistration { public: /// This class. typedef AffineRegistrationCommandLine Self; /// Smart pointer. typedef SmartPointer SmartPtr; /// Convenience typedef. typedef AffineRegistration Superclass; /** Constructor. *\param argc Number of command line arguments; this should be the argc * parameter of the main() function. *\param argv Array of command line arguments; this should be the argv * parameter of the main() function. *\exception ConstructorFailed This exception is thrown if there where * invalid or unknown options or missing required parameters. In all these * cases, an information text describing the known options will have been * written to the standard error stream before throwing the exception. */ AffineRegistrationCommandLine ( const int argc, const char *argv [] ); /** Perform registration. */ virtual CallbackResult Register (); protected: /** Initialize registration. * So far, this function has no effect other than calling the equivalent * inherited function. */ virtual CallbackResult InitRegistration(); /** Output registration result. * This function write the transformation that was found to a studylist * archive with the name provided by command line arguments. The result is * also printed to stderr in parameter list form. *\param v The vector of resulting transformation parameters. *\param irq The interrupt status - this allows the output function to determine whether computation finished or was interrupted. */ virtual void OutputResult ( const CoordinateVector* v, const CallbackResult irq ); /** Enter resolution level. * An information is printed to stderr and to the protocol file if one is * written. */ virtual void EnterResolution( CoordinateVector::SmartPtr&, Functional::SmartPtr&, const int, const int ); private: /** Path of the actual input transformation, if any. * If two input transformations are specified, i.e., one as the input studylist and one via * the "--initial" command line switch, then this variable holds the path of the transformation * that was actually used (the one specified with "--initial"). This is used when the optional * image/transformation database is updated. */ std::string m_InitialXformPath; /// Number of levels for automatic parameter generation. unsigned int m_AutoMultiLevels; /// Path for reformatted floating image. std::string m_ReformattedImagePath; /** Name of output studylist. * This is defined by the -o or --outlist command line option. */ std::string Studylist; /** Name of the output matrix file. * This is defined by the "--out-matrix" command line argument. */ std::string OutMatrixName; /** Name of the output parameter file. * This is defined by the "--out-params" command line argument. */ std::string OutParametersName; /// Name of output transformation file in ITK format. std::string m_OutputPathITK; /** Name of first study to be registered. * This is given as the first non-option command line paramter. */ std::string Study1; /** Name of second study to be registered. * This is given as the second non-option command line paramter. */ std::string Study2; #ifdef CMTK_USE_SQLITE /// Database to update after registration completes. std::string m_UpdateDB; #endif /** Name of protocol output file. * This is defined by the -p or --protocol command line option. */ std::string Protocol; /** Name of elapsed time output file. * This is defined by the -t or --time command line option. */ std::string Time; /** Flag for initial center-of-mass translation. * This defaults to 'no' and is set to 'yes' by -i or --initxlate command * line switch. */ bool InitXlate; /// Output result as matrix (text) file. void OutputResultMatrix( const std::string& matrixName ) const; /// Output result (and statistics) as studylist archive. void OutputResultParameters( const std::string& paramsName, const CoordinateVector& v ) const; /// Output result (and statistics) as studylist archive. void OutputResultList( const std::string& studyList ) const; }; //@} } // namespace cmtk #endif // #ifndef _COMMANDLINEVOXELREGISTRATION_H_ cmtk-3.3.1/libs/Registration/cmtkBestDirectionOptimizer.cxx000066400000000000000000000127461276303427400241450ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2011 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 3376 $ // // $LastChangedDate: 2011-08-18 13:43:00 -0700 (Thu, 18 Aug 2011) $ // // $LastChangedBy: torstenrohlfing $ // */ #include "cmtkBestDirectionOptimizer.h" #include #include #include #include #include namespace cmtk { /** \addtogroup Registration */ //@{ CallbackResult BestDirectionOptimizer::Optimize ( CoordinateVector& v, const Self::ParameterType exploration, const Self::ParameterType accuracy ) { this->m_LastOptimizeChangedParameters = false; const int Dim = this->GetSearchSpaceDimension(); const Self::ParameterType real_accuracy = std::min( exploration, accuracy ); int numOfSteps = 1+static_cast(log(real_accuracy/exploration)/log(StepFactor)); Self::ParameterType step = real_accuracy * pow( StepFactor, 1-numOfSteps ); CoordinateVector directionVector( v.Dim, 0.0 ); Progress::Begin( 0, numOfSteps, 1, "Multi-resolution optimization" ); CallbackResult irq = CALLBACK_OK; for ( int stepIdx = 0; (stepIdx < numOfSteps) && (irq == CALLBACK_OK); ++stepIdx, step *= StepFactor ) { Progress::SetProgress( stepIdx ); char comment[128]; snprintf( comment, sizeof( comment ), "Setting step size to %4g [mm]", step ); this->CallbackComment( comment ); DebugOutput( 1 ) << comment << "\n"; bool update = true; int levelRepeatCounter = this->m_RepeatLevelCount; while ( update && ( irq == CALLBACK_OK ) ) { update = false; Self::ReturnType current = this->EvaluateWithGradient( v, directionVector, step ); irq = this->CallbackExecuteWithData( v, current ); const Self::ReturnType previous = current; // Daniel Rueckert is supposedly using Euclid's norm here, but we found this // to be less efficient AND accurate. Makes no sense anyway. const Self::ParameterType vectorLength = ( this->m_UseMaxNorm ) ? directionVector.MaxNorm() : directionVector.EuclidNorm(); if ( vectorLength > 0 ) { const Self::ParameterType stepLength = step / vectorLength; // is there a minimum threshold for gradient components? if so, // filter out (set to zero) all components below this threshold. if ( this->m_DirectionThreshold < 0 ) { #pragma omp parallel for for ( int idx=0; idxGetParamStep(idx) ); } else { #pragma omp parallel for for ( int idx=0; idx ( vectorLength * this->m_DirectionThreshold ) ) { directionVector[idx] *= (stepLength * this->GetParamStep(idx) ); } else { directionVector[idx] = 0; } } CoordinateVector vNext( v ); vNext += directionVector; Self::ReturnType next = this->Evaluate( vNext ); while ( next > current ) { if ( ( irq = this->CallbackExecute() ) != CALLBACK_OK ) break; current = next; update = true; this->m_LastOptimizeChangedParameters = true; vNext += directionVector; next = this->Evaluate( vNext ); } vNext -= directionVector; if ( update ) v = vNext; directionVector *= 0.5; // Forward-Backward search for ( int dirStepIndex = 0; dirStepIndex < numOfSteps; ++dirStepIndex ) { vNext += directionVector; Self::ReturnType nextUp = this->Evaluate( vNext ); ( vNext = v ) -= directionVector; Self::ReturnType nextDown = this->Evaluate( vNext ); if ((nextUp > current) || (nextDown > current)) { // Here, as we demand strict ">", we prefer smaller steps. if ( nextUp > nextDown ) { current = nextUp; v += directionVector; } else { current = nextDown; v -= directionVector; } vNext = v; if ( this->m_AggressiveMode ) { update = true; this->m_LastOptimizeChangedParameters = true; } } directionVector *= 0.5; } } irq = this->CallbackExecuteWithData( v, current ); DebugOutput( 5 ) << current << "\n"; #ifdef CMTK_BUILD_DEMO if ( update ) this->m_Functional->SnapshotAt( v ); #endif if ( (fabs(previous-current) / (fabs(previous)+fabs(current)) ) < this->m_DeltaFThreshold ) update = false; if ( this->m_AggressiveMode ) { if ( update ) { levelRepeatCounter = this->m_RepeatLevelCount; } else { --levelRepeatCounter; update = (levelRepeatCounter > 0) && this->m_Functional->Wiggle(); } } } } Progress::Done(); return irq; } } // namespace cmtk cmtk-3.3.1/libs/Registration/cmtkBestDirectionOptimizer.h000066400000000000000000000070011276303427400235560ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2010 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 2398 $ // // $LastChangedDate: 2010-10-05 14:54:37 -0700 (Tue, 05 Oct 2010) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkBestDirectionOptimizer_h_included_ #define __cmtkBestDirectionOptimizer_h_included_ #include #include namespace cmtk { /** \addtogroup Registration */ //@{ /** Optimizer derived from BestNeighbourOptimizer. */ class BestDirectionOptimizer : /// Inherit generic optimizer features. public Optimizer { public: /// This class. typedef BestDirectionOptimizer Self; /// Superclass. typedef Optimizer Superclass; /// Flag whether to use maximum (1) or Euclid (0) for normalization. cmtkGetSetMacroDefault(bool,UseMaxNorm,true); /** Threshold for direction components. * Before searching in a certain directions, all components below this * fraction of the maximum absolute component are set to zero. This is * done to prevent changes in irrelevant parameters and thus support local * recomputation when optimizing elastic deformation parameters for example. * set this flag to 0 to disable thresholding; set it to 1 to remove all but * the most significant components. */ cmtkGetSetMacroDefault(Self::ParameterType,DirectionThreshold,-1); /** Number of repetitions of each search level, even if previously unsuccessful. * This is one by default, so whenever no successful update was made, the level * is finished. Setting this to values larger than 1 only makes sense if the * optimized functional is changing over time, e.g., due to probabilistic * effects. */ cmtkGetSetMacro(int,RepeatLevelCount); /** Agressive mode. * If this flag is set, the optimization is continued at one level as long as * there is an improvement to the target function at any step level. Otherwise, * steps in the binary seach phase are not considered and search terminates * earlier. */ cmtkGetSetMacro(bool,AggressiveMode); /// Constructor. BestDirectionOptimizer ( const Self::ParameterType stepFactor = 0.5, const Self::ParameterType = 0.1 ) { StepFactor = stepFactor; this->m_UseMaxNorm = true; this->m_DirectionThreshold = -1; this->m_RepeatLevelCount = 1; this->m_AggressiveMode = false; }; /// Optimize functional. virtual CallbackResult Optimize( CoordinateVector&, const Self::ParameterType = 1, const Self::ParameterType = 0 ); private: /// Factor by which the step size is reduced after each pass. Self::ParameterType StepFactor; }; //@} } // namespace cmtk #endif // #ifndef __cmtkBestDirectionOptimizer_h_included_ cmtk-3.3.1/libs/Registration/cmtkBestNeighbourOptimizer.cxx000066400000000000000000000100011276303427400241250ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2011 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 2999 $ // // $LastChangedDate: 2011-03-15 16:13:20 -0700 (Tue, 15 Mar 2011) $ // // $LastChangedBy: torstenrohlfing $ // */ #include "cmtkBestNeighbourOptimizer.h" #include #include #include #include #include namespace cmtk { /** \addtogroup Registration */ //@{ CallbackResult BestNeighbourOptimizer::Optimize ( CoordinateVector& v, const Self::ParameterType exploration, const Self::ParameterType accuracy ) { this->m_LastOptimizeChangedParameters = false; int Dim = this->GetSearchSpaceDimension(); Self::ReturnType optimum = this->Evaluate( v ); CoordinateVector optimumV(v); int optimumDim = -1; Types::Coordinate optimumDir = 0; const Self::ParameterType real_accuracy = std::min( exploration, accuracy ); int numOfSteps = 1+static_cast(log(real_accuracy/exploration)/log(StepFactor)); Self::ParameterType step = real_accuracy * pow( StepFactor, 1-numOfSteps ); std::vector stepScaleVector( Dim ); for ( int idx=0; idxGetParamStep( idx ); SearchTrace searchTrace ( Dim ); Progress::Begin( 0, numOfSteps, 1, "Multi-resolution optimization" ); CallbackResult irq = this->CallbackExecuteWithData( v, optimum ); for ( int stepIdx = 0; (stepIdx < numOfSteps) && ( irq == CALLBACK_OK ); ++stepIdx, step *= StepFactor ) { Progress::SetProgress( stepIdx ); char comment[128]; snprintf( comment, sizeof( comment ), "Setting step size to %4g [mm]", step ); this->CallbackComment( comment ); int update = 1; while ( update && ( irq == CALLBACK_OK ) ) { update = 0; const Self::ReturnType previous = optimum; for ( int dim = 0; dim < Dim; ++dim ) { double next; const Self::ParameterType vOld = v[dim]; for ( int direction = -1; direction <= 1; direction += 2 ) { if ( (irq = this->CallbackExecute()) ) break; v[dim] = vOld + direction * step * stepScaleVector[dim]; if ( !searchTrace.Get( next, dim, step ) ) next = this->Evaluate( v ); if ( next > optimum ) { optimum = next; optimumV = v; update = 1; optimumDim = dim; optimumDir = direction * step; } } v[dim] = vOld; } if (update) { #ifdef CMTK_BUILD_DEMO this->m_Functional->SnapshotAt( optimumV ); #endif v = optimumV; searchTrace.Move( optimumDim, optimumDir ); irq = this->CallbackExecuteWithData( v, optimum ); this->m_LastOptimizeChangedParameters = true; DebugOutput( 5 ) << optimum << "\n"; // query functional for new parameter steppings if the respective // optimizer flag is set. if ( this->m_UpdateStepScaleVector ) for ( int idx=0; idxGetParamStep( idx ); } if ( (fabs(previous-optimum) / (fabs(previous)+fabs(optimum)) ) < this->m_DeltaFThreshold ) update = false; } } Progress::Done(); this->SetFinalValue( optimum ); return irq; } } // namespace cmtk cmtk-3.3.1/libs/Registration/cmtkBestNeighbourOptimizer.h000066400000000000000000000056621276303427400235730ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2010 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 2752 $ // // $LastChangedDate: 2011-01-17 11:33:31 -0800 (Mon, 17 Jan 2011) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkBestNeighbourOptimizer_h_included_ #define __cmtkBestNeighbourOptimizer_h_included_ #include #include namespace cmtk { /** \addtogroup Registration */ //@{ /** Best-neighbour-search optimizer. * This class implements a search technique introduced by Studholme et al. * By modifying each parameter of the search space by a certain step up- and * downwards, all "neighbours" of the current parameter vector are visited. For * each of these, the target function (functional) is evaluated. The search * then continues from the parameter vector producing the maximum value. If no * further improvement is possible, the step size is decreased by a given * factor until it reaches a lower bound. */ class BestNeighbourOptimizer : /// Inherit generic optimizer features. public Optimizer { public: /// This class. typedef BestNeighbourOptimizer Self; /// Superclass. typedef Optimizer Superclass; /** Constructor. * Hand functional and callback to parent class and initialize local * variables. *\param stepFactor Factor by which the search step size is decreased. */ BestNeighbourOptimizer ( const Self::ParameterType stepFactor = 0.5 ) { StepFactor = stepFactor; }; /** Perform the optimization. */ virtual CallbackResult Optimize( CoordinateVector&, const Self::ParameterType = 1, const Self::ParameterType = 0 ); private: /** Search step factor. * This variable determines the factor by which to decrease the search step * size if no further improvement is possible at a certain resolution. * Reasonable values are in the range 0 < StepFactor < 1. For most cases, * a value of 0.5 has been found to provide optimum accuracy and performance. */ Self::ParameterType StepFactor; }; //@} } // namespace cmtk #endif // #ifndef __cmtkBestNeighbourOptimizer_h_included_ cmtk-3.3.1/libs/Registration/cmtkCongealingFunctional.cxx000066400000000000000000000323131276303427400235650ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2011, 2013 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4907 $ // // $LastChangedDate: 2013-10-01 11:49:29 -0700 (Tue, 01 Oct 2013) $ // // $LastChangedBy: torstenrohlfing $ // */ #include "cmtkCongealingFunctional.h" #include #include namespace cmtk { /** \addtogroup Registration */ //@{ template CongealingFunctional::CongealingFunctional() : m_NeedsUpdateStandardDeviationByPixel( true ) { this->SetNumberOfHistogramBins( this->m_HistogramBins ); } template CongealingFunctional::~CongealingFunctional() { for ( size_t idx = 0; idx < this->m_HistogramKernel.size(); ++idx ) if ( this->m_HistogramKernel[idx] ) Memory::ArrayC::Delete( this->m_HistogramKernel[idx] ); this->m_HistogramKernel.clear(); } template void CongealingFunctional ::SetNumberOfHistogramBins( const size_t numberOfHistogramBins ) { this->m_HistogramBins = numberOfHistogramBins; this->m_HistogramKernelRadiusMax = this->m_HistogramBins / 2; this->CreateGaussianKernels(); this->Superclass::SetNumberOfHistogramBins( numberOfHistogramBins ); } template void CongealingFunctional::CreateGaussianKernels() { for ( size_t idx = 0; idx < this->m_HistogramKernel.size(); ++idx ) if ( this->m_HistogramKernel[idx] ) Memory::ArrayC::Delete( this->m_HistogramKernel[idx] ); this->m_HistogramKernel.resize( this->m_HistogramKernelRadiusMax+1 ); this->m_HistogramKernelRadius.resize( this->m_HistogramKernelRadiusMax+1 ); for ( size_t idx = 0; idx <= this->m_HistogramKernelRadiusMax; ++idx ) { const size_t radius = idx + 1; const double sigma = idx; this->m_HistogramKernelRadius[idx] = radius; this->m_HistogramKernel[idx] = Memory::ArrayC::Allocate( radius ); if ( idx < 1.0 ) { this->m_HistogramKernel[idx][0] = cmtk::ScaleHistogramValueTrait::Scale( 1.0 ); for ( size_t i = 1; i < radius; ++i ) this->m_HistogramKernel[idx][i] = cmtk::ScaleHistogramValueTrait::Scale( 0.0 ); } else { const double normFactor = 1.0/(sqrt(2*M_PI) * sigma); for ( size_t i = 0; i < radius; ++i ) { this->m_HistogramKernel[idx][i] = cmtk::ScaleHistogramValueTrait::Scale( normFactor * exp( -MathUtil::Square( 1.0 * i / sigma ) / 2 ) ); } } } } template void CongealingFunctional::SetTemplateGrid ( UniformVolume::SmartPtr& templateGrid, const int downsample, const bool useTemplateData ) { this->Superclass::SetTemplateGrid( templateGrid, downsample, useTemplateData ); this->m_NeedsUpdateStandardDeviationByPixel = true; } template typename CongealingFunctional::ReturnType CongealingFunctional::Evaluate() { if ( this->m_NeedsUpdateStandardDeviationByPixel ) this->UpdateStandardDeviationByPixel(); double entropy = 0; unsigned int count = 0; this->m_ThreadHistograms.resize( this->m_NumberOfThreads ); std::vector params( this->m_NumberOfTasks ); for ( size_t idx = 0; idx < this->m_NumberOfTasks; ++idx ) params[idx].thisObject = this; ThreadPool& threadPool = ThreadPool::GetGlobalThreadPool(); if ( this->m_ProbabilisticSamples.size() ) threadPool.Run( Self::EvaluateProbabilisticThread, params ); else threadPool.Run( Self::EvaluateThread, params ); // gather partial entropies from tasks for ( size_t task = 0; task < this->m_NumberOfTasks; ++task ) { entropy += params[task].m_Entropy; count += params[task].m_Count; } if ( count ) return static_cast( entropy / count ); else return -FLT_MAX; } template void CongealingFunctional::EvaluateThread ( void *const args, const size_t taskIdx, const size_t taskCnt, const size_t threadIdx, const size_t ) { EvaluateThreadParameters* threadParameters = static_cast( args ); Self* This = threadParameters->thisObject; const Self* ThisConst = threadParameters->thisObject; HistogramType& histogram = This->m_ThreadHistograms[threadIdx]; histogram.Resize( ThisConst->m_HistogramBins + 2 * ThisConst->m_HistogramKernelRadiusMax, false /*reset*/ ); double entropy = 0; unsigned int count = 0; const size_t numberOfPixels = ThisConst->m_TemplateNumberOfPixels; const size_t pixelsPerThread = 1+(numberOfPixels / taskCnt); const size_t pixelFrom = taskIdx * pixelsPerThread; const size_t pixelTo = std::min( numberOfPixels, pixelFrom + pixelsPerThread ); const size_t imagesFrom = ThisConst->m_ActiveImagesFrom; const size_t imagesTo = ThisConst->m_ActiveImagesTo; const byte paddingValue = ThisConst->m_PaddingValue; for ( size_t ofs = pixelFrom; ofs < pixelTo; ++ofs ) { histogram.Reset(); const size_t kernelIdx = ThisConst->m_StandardDeviationByPixel[ofs]; const size_t kernelRadius = ThisConst->m_HistogramKernelRadius[kernelIdx]; const HistogramBinType* kernel = ThisConst->m_HistogramKernel[kernelIdx]; bool fullCount = true; if ( ThisConst->m_UseTemplateData ) { const byte templateValue = ThisConst->m_TemplateData[ofs]; if ( (fullCount = (templateValue != paddingValue )) ) { histogram.AddWeightedSymmetricKernel( templateValue, kernelRadius, kernel ); } } for ( size_t idx = imagesFrom; (idx < imagesTo) && fullCount; ++idx ) { const byte value = ThisConst->m_Data[idx][ofs]; if ( value != paddingValue ) { histogram.AddWeightedSymmetricKernel( value, kernelRadius, kernel ); } else { fullCount = false; } } if ( fullCount ) { entropy -= histogram.GetEntropy(); ++count; } } threadParameters->m_Entropy = entropy; threadParameters->m_Count = count; } template void CongealingFunctional::EvaluateProbabilisticThread ( void *const args, const size_t taskIdx, const size_t taskCnt, const size_t threadIdx, const size_t ) { EvaluateThreadParameters* threadParameters = static_cast( args ); Self* This = threadParameters->thisObject; const Self* ThisConst = threadParameters->thisObject; HistogramType& histogram = This->m_ThreadHistograms[threadIdx]; histogram.Resize( ThisConst->m_HistogramBins + 2 * ThisConst->m_HistogramKernelRadiusMax, false /*reset*/ ); double entropy = 0; unsigned int count = 0; const size_t imagesFrom = ThisConst->m_ActiveImagesFrom; const size_t imagesTo = ThisConst->m_ActiveImagesTo; const byte paddingValue = ThisConst->m_PaddingValue; const size_t numberOfSamples = ThisConst->m_ProbabilisticSamples.size(); const size_t samplesPerThread = numberOfSamples / taskCnt; const size_t sampleFrom = taskIdx * samplesPerThread; const size_t sampleTo = std::min( numberOfSamples, sampleFrom + samplesPerThread ); for ( size_t sample = sampleFrom; sample < sampleTo; ++sample ) { histogram.Reset(); bool fullCount = true; const size_t kernelIdx = ThisConst->m_StandardDeviationByPixel[sample]; const size_t kernelRadius = ThisConst->m_HistogramKernelRadius[kernelIdx]; const HistogramBinType* kernel = ThisConst->m_HistogramKernel[kernelIdx]; if ( ThisConst->m_UseTemplateData ) { const byte templateValue = ThisConst->m_TemplateData[sample]; if ( (fullCount = (templateValue != paddingValue)) ) { histogram.AddWeightedSymmetricKernel( templateValue, kernelRadius, kernel ); } } for ( size_t idx = imagesFrom; (idx < imagesTo) && fullCount; ++idx ) { const byte value = ThisConst->m_Data[idx][sample]; if ( value != paddingValue ) { histogram.AddWeightedSymmetricKernel( value, kernelRadius, kernel ); } else { fullCount = false; } } if ( fullCount ) { entropy -= histogram.GetEntropy(); ++count; } else { } } threadParameters->m_Entropy = entropy; threadParameters->m_Count = count; } template bool CongealingFunctional ::Wiggle() { bool wiggle = this->Superclass::Wiggle(); if ( wiggle ) { this->m_NeedsUpdateStandardDeviationByPixel = true; } return wiggle; } template void CongealingFunctional ::UpdateStandardDeviationByPixel() { if ( this->m_ProbabilisticSamples.size() ) { const size_t numberOfSamples = this->m_ProbabilisticSamples.size(); this->m_StandardDeviationByPixel.resize( numberOfSamples ); } else { const size_t numberOfPixels = this->m_TemplateNumberOfPixels; this->m_StandardDeviationByPixel.resize( numberOfPixels ); } std::vector params( this->m_NumberOfTasks ); for ( size_t idx = 0; idx < this->m_NumberOfTasks; ++idx ) params[idx].thisObject = this; ThreadPool& threadPool = ThreadPool::GetGlobalThreadPool(); threadPool.Run( Self::UpdateStandardDeviationByPixelThreadFunc, params ); this->m_NeedsUpdateStandardDeviationByPixel = false; } template void CongealingFunctional ::UpdateStandardDeviationByPixelThreadFunc ( void *const args, const size_t taskIdx, const size_t taskCnt, const size_t, const size_t ) { ThreadParametersType* taskParameters = static_cast( args ); Self* This = taskParameters->thisObject; const Self* ThisConst = taskParameters->thisObject; const size_t imagesFrom = ThisConst->m_ActiveImagesFrom; const size_t imagesTo = ThisConst->m_ActiveImagesTo; const byte paddingValue = ThisConst->m_PaddingValue; if ( ThisConst->m_ProbabilisticSamples.size() ) { const size_t numberOfSamples = ThisConst->m_ProbabilisticSamples.size(); const size_t samplesPerTask = 1 + (numberOfSamples / taskCnt ); const size_t sampleFrom = taskIdx * samplesPerTask; const size_t sampleTo = std::min( numberOfSamples, sampleFrom + samplesPerTask ); for ( size_t smpl = sampleFrom; smpl < sampleTo; ++smpl ) { double sum = 0, sumsq = 0; unsigned int count = 0; if ( ThisConst->m_UseTemplateData ) { const byte templateValue = ThisConst->m_TemplateData[smpl]; if ( templateValue != paddingValue ) { sum += templateValue; sumsq += templateValue * templateValue; ++count; } } for ( size_t idx = imagesFrom; idx < imagesTo; ++idx ) { const byte value = ThisConst->m_Data[idx][smpl]; if ( value != paddingValue ) { const double data = static_cast( value ); sum += data; sumsq += data * data; ++count; } } if ( count > 1 ) // count>0 not enough for unbiased estimate of sdev below { const double mu = sum / count; const byte sdev = std::min( ThisConst->m_HistogramKernelRadiusMax, (byte)(sqrt(( count * mu * mu - 2 * mu * sum + sumsq ) / (count-1)) ) ); This->m_StandardDeviationByPixel[smpl] = sdev; } else { This->m_StandardDeviationByPixel[smpl] = 0; } } } else { const size_t numberOfPixels = ThisConst->m_TemplateNumberOfPixels; const size_t pixelsPerTask = 1 + (numberOfPixels / taskCnt); const size_t pixelFrom = taskIdx * pixelsPerTask; const size_t pixelTo = std::min( numberOfPixels, pixelFrom + pixelsPerTask ); for ( size_t px = pixelFrom; px < pixelTo; ++px ) { double sum = 0, sumsq = 0; unsigned int count = 0; if ( ThisConst->m_UseTemplateData ) { const byte templateValue = ThisConst->m_TemplateData[px]; if ( templateValue != paddingValue ) { sum += templateValue; sumsq += templateValue * templateValue; ++count; } } for ( size_t idx = imagesFrom; idx < imagesTo; ++idx ) { const byte value = ThisConst->m_Data[idx][px]; if ( value != paddingValue ) { const double data = static_cast( value ); sum += data; sumsq += data * data; ++count; } } if ( count > 1 ) { const double mu = sum / count; const byte sdev = std::min( ThisConst->m_HistogramKernelRadiusMax, (byte)(sqrt(( count * mu * mu - 2 * mu * sum + sumsq ) / (count-1)) ) ); This->m_StandardDeviationByPixel[px] = sdev; } else { This->m_StandardDeviationByPixel[px] = 0; } } } } //@} } // namespace cmtk #include #include template class cmtk::CongealingFunctional; template class cmtk::CongealingFunctional; cmtk-3.3.1/libs/Registration/cmtkCongealingFunctional.h000066400000000000000000000132671276303427400232210ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2011 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 3608 $ // // $LastChangedDate: 2011-12-02 10:47:18 -0800 (Fri, 02 Dec 2011) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkCongealingFunctional_h_included_ #define __cmtkCongealingFunctional_h_included_ #include #include #include #include #include #include #include #include namespace cmtk { /** \addtogroup Registration */ //@{ /** Functional base class for groupwise congealing registration. * This functional evaluates Lilla Zollei's entropy criterion for massively * groupwise image registration. * *\section ref References * * [1] L . Zoellei, E. Learned-Miller, E. Grimson, W.M. Wells III: "Efficient * Population Registration of 3D Data", ICCV 2005, Computer Vision for * Biomedical Image Applications; Beijing, China */ template class CongealingFunctional : /** Inherit from template congealing base class. */ public GroupwiseRegistrationFunctionalXformTemplate { public: /// Type of parent class. typedef GroupwiseRegistrationFunctionalXformTemplate Superclass; /// Type of this class. typedef CongealingFunctional Self; /// Smart pointer. typedef SmartPointer SmartPtr; /// Transformation type. typedef TXform XformType; /// Smart pointer to transformation type. typedef typename XformType::SmartPtr XformPointer; /// Base type for histogram bins. typedef unsigned int HistogramBinType; /// Histogram type. typedef Histogram HistogramType; /// Constructor. CongealingFunctional(); /// Destructor. virtual ~CongealingFunctional(); /// Set number of histogram bins. virtual void SetNumberOfHistogramBins( const size_t numberOfHistogramBins ); /** Set template grid. */ virtual void SetTemplateGrid( UniformVolume::SmartPtr& templateGrid /*!< The template grid that defines size and resolution for the implicit registration template. */, const int downsample = 1 /*!< Grid downsampling factor */, const bool useTemplateData = false /*!< Flag to use template pixel data, not just grid, in registration */ ); /// Evaluate functional with currently set parameters. virtual typename Self::ReturnType Evaluate(); protected: /// Standard deviation over all images by pixel. std::vector m_StandardDeviationByPixel; /// Update standard deviation by pixel. virtual void UpdateStandardDeviationByPixel(); /// Flag whether standard deviations by pixel need updating. bool m_NeedsUpdateStandardDeviationByPixel; /** Create Gaussian kernels for samples in histogram. */ void CreateGaussianKernels(); /** Histogram sample kernels. * Each element is a pointer to an array that holds the elements of one side * of a discretely sampled symmetric kernel the represents samples with * uncertainty in a histogram. Element [0] of each such array is the central * element. */ std::vector m_HistogramKernel; /** Radius of histogram sample kernel. * Each element here is the number of elements in the corresponding * HistogramKernel array. */ std::vector m_HistogramKernelRadius; /** Histograms for computation threads. */ std::vector m_ThreadHistograms; /// Update probabilistic sample table.. virtual bool Wiggle(); private: /// Thread parameters with no further data. typedef ThreadParameters ThreadParametersType; /// Thread function to update standard dedviations by pixel. static void UpdateStandardDeviationByPixelThreadFunc( void *const args, const size_t taskIdx, const size_t taskCnt, const size_t, const size_t ); /// Thread parameter for entropy evaluation. class EvaluateThreadParameters : /// Inherit from generic thread parameter class. public ThreadParametersType { public: /// Upon return from the thread function, this holds the partial entropy. double m_Entropy; /** Upon return from the thread function, this holds the number of * pixels with full image count, i.e., pixels that are within all * target images. */ unsigned int m_Count; }; /// Evaluate functional with currently set parameters. static void EvaluateThread( void *const args, const size_t taskIdx, const size_t taskCnt, const size_t threadIdx, const size_t threadCnt ); /// Evaluate functional with currently set parameters with probabilistic sampling. static void EvaluateProbabilisticThread( void *const args, const size_t taskIdx, const size_t taskCnt, const size_t threadIdx, const size_t threadCnt ); }; //@} } // namespace cmtk #endif // #ifndef __cmtkCongealingFunctional_h_included_ cmtk-3.3.1/libs/Registration/cmtkEchoPlanarUnwarpFunctional.cxx000066400000000000000000000523661276303427400247420ustar00rootroot00000000000000/* // // Copyright 2011-2013 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4882 $ // // $LastChangedDate: 2013-09-27 15:16:36 -0700 (Fri, 27 Sep 2013) $ // // $LastChangedBy: torstenrohlfing $ // */ #include "cmtkEchoPlanarUnwarpFunctional.h" #include #include #include #include #include #include #include #include #include const int cmtk::EchoPlanarUnwarpFunctional::InterpolationKernelRadius = 2; cmtk::EchoPlanarUnwarpFunctional::EchoPlanarUnwarpFunctional ( UniformVolume::SmartConstPtr& imageFwd, UniformVolume::SmartConstPtr& imageRev, const byte phaseEncodeDirection, const bool initShiftCentersOfMass ) : m_ImageGrid( imageFwd->CloneGrid() ), m_ImageFwd( imageFwd ), m_ImageRev( imageRev ), m_SmoothImageFwd( imageFwd ), m_SmoothImageRev( imageRev ), m_PhaseEncodeDirection( phaseEncodeDirection ), m_SmoothnessConstraintWeight( 0.0 ), m_FoldingConstraintWeight( 0.0 ) { if ( ! this->m_ImageFwd->GridMatches( *(this->m_ImageRev) ) ) { StdErr << "ERROR: forward and reverse-encoded image must have same grids.\n"; throw ExitException( 1 ); } this->m_Deformation.setbounds( 1, this->m_ImageGrid->GetNumberOfPixels() ); if ( initShiftCentersOfMass ) { this->InitShiftCentersOfMass(); } else { for ( size_t i = 1; i < 1+this->m_ImageGrid->GetNumberOfPixels(); ++i ) this->m_Deformation(i) = 0.0; } this->m_UnwarpImageFwd.resize( this->m_ImageGrid->GetNumberOfPixels() ); this->m_UnwarpImageRev.resize( this->m_ImageGrid->GetNumberOfPixels() ); this->m_CorrectedImageFwd.resize( this->m_ImageGrid->GetNumberOfPixels() ); this->m_CorrectedImageRev.resize( this->m_ImageGrid->GetNumberOfPixels() ); // determine "readout" direction, or rather, direction other than phase encoding with the maximum number of pixels DataGrid::IndexType dims = this->m_ImageGrid->GetDims(); dims[this->m_PhaseEncodeDirection] = 0; this->m_ReadoutDirection = dims.MaxIndex(); } void cmtk::EchoPlanarUnwarpFunctional::InitShiftCentersOfMass() { DebugOutput( 9 ) << "Initializing by shifting rows according to centers of mass.\n"; const DataGrid::RegionType wholeImageRegion = this->m_ImageGrid->GetWholeImageRegion(); DataGrid::RegionType faceRegion = wholeImageRegion; faceRegion.To()[this->m_PhaseEncodeDirection] = faceRegion.From()[this->m_PhaseEncodeDirection]+1; for ( RegionIndexIterator it( faceRegion ); it != it.end(); ++it ) { // for each row, compute the two centers of mass double totalMassFwd = 0, centerOfMassFwd = 0, totalMassRev = 0, centerOfMassRev = 0; DataGrid::IndexType idx = it.Index(); for ( idx[this->m_PhaseEncodeDirection] = wholeImageRegion.From()[this->m_PhaseEncodeDirection]; idx[this->m_PhaseEncodeDirection] < wholeImageRegion.To()[this->m_PhaseEncodeDirection]; ++idx[this->m_PhaseEncodeDirection] ) { const Types::DataItem valueFwd = this->m_ImageFwd->GetDataAt( this->m_ImageFwd->GetOffsetFromIndex( idx ) ); totalMassFwd += valueFwd; centerOfMassFwd += idx[this->m_PhaseEncodeDirection] * valueFwd; const Types::DataItem valueRev = this->m_ImageRev->GetDataAt( this->m_ImageRev->GetOffsetFromIndex( idx ) ); totalMassRev += valueRev; centerOfMassRev += idx[this->m_PhaseEncodeDirection] * valueRev; } if ( (centerOfMassFwd > 0) && (centerOfMassRev > 0) ) { centerOfMassFwd /= totalMassFwd; centerOfMassRev /= totalMassRev; const double delta = (centerOfMassFwd - centerOfMassRev) / 2; for ( idx[this->m_PhaseEncodeDirection] = wholeImageRegion.From()[this->m_PhaseEncodeDirection]; idx[this->m_PhaseEncodeDirection] < wholeImageRegion.To()[this->m_PhaseEncodeDirection]; ++idx[this->m_PhaseEncodeDirection] ) { this->m_Deformation( 1 + this->m_ImageFwd->GetOffsetFromIndex( idx ) ) = delta; } } else { // for all-zero rows, we cannot shift COMs because there isn't any, so set init deformation to zero for ( idx[this->m_PhaseEncodeDirection] = wholeImageRegion.From()[this->m_PhaseEncodeDirection]; idx[this->m_PhaseEncodeDirection] < wholeImageRegion.To()[this->m_PhaseEncodeDirection]; ++idx[this->m_PhaseEncodeDirection] ) { this->m_Deformation( 1 + this->m_ImageFwd->GetOffsetFromIndex( idx ) ) = 0.0; } } } } void cmtk::EchoPlanarUnwarpFunctional::SetSmoothingKernelWidth( const Units::GaussianSigma& sigma, const Types::Coordinate maxError ) { if ( sigma.Value() > 0 ) { { UniformVolumeGaussianFilter filterFwd( this->m_ImageFwd ); UniformVolume::SmartPtr smooth = UniformVolume::SmartPtr( this->m_ImageGrid->CloneGrid() ); smooth->SetData( filterFwd.GetFiltered1D( this->m_PhaseEncodeDirection, sigma, maxError ) ); this->m_SmoothImageFwd = smooth; } { UniformVolumeGaussianFilter filterRev( this->m_ImageRev ); UniformVolume::SmartPtr smooth = UniformVolume::SmartPtr( this->m_ImageGrid->CloneGrid() ); smooth->SetData( filterRev.GetFiltered1D( this->m_PhaseEncodeDirection, sigma, maxError ) ); this->m_SmoothImageRev = smooth; } } else { this->m_SmoothImageFwd = this->m_ImageFwd; this->m_SmoothImageRev = this->m_ImageRev; } } cmtk::UniformVolume::SmartPtr cmtk::EchoPlanarUnwarpFunctional::GetCorrectedImage( const int direction ) const { UniformVolume::SmartPtr correctedImage( this->m_ImageFwd->CloneGrid() ); const std::vector& srcImage = ( direction > 0 ) ? this->m_CorrectedImageFwd : this->m_CorrectedImageRev; correctedImage->CreateDataArray( TYPE_FLOAT ); for ( size_t px = 0; px < this->m_ImageFwd->GetNumberOfPixels(); ++px ) { correctedImage->SetDataAt( srcImage[px], px ); } return correctedImage; } cmtk::UniformVolume::SmartPtr cmtk::EchoPlanarUnwarpFunctional::GetJacobianMap( const int direction ) const { UniformVolume::SmartPtr jacobianImage( this->m_ImageGrid->CloneGrid() ); jacobianImage->CreateDataArray( TYPE_DOUBLE ); const DataGrid::RegionType wholeImageRegion = this->m_ImageGrid->GetWholeImageRegion(); #ifndef _OPENMP const DataGrid::RegionType region = wholeImageRegion; #else // _OPENMP const int sliceFrom = wholeImageRegion.From()[this->m_ReadoutDirection]; const int sliceTo = wholeImageRegion.To()[this->m_ReadoutDirection]; #pragma omp parallel for for ( int slice = sliceFrom; slice < sliceTo; ++slice ) { DataGrid::RegionType region = wholeImageRegion; region.From()[this->m_ReadoutDirection] = slice; region.To()[this->m_ReadoutDirection] = slice+1; #endif for ( RegionIndexIterator it( region ); it != it.end(); ++it ) { jacobianImage->SetDataAt( 1 + direction * this->GetPartialJacobian( this->m_Deformation, it.Index() ), this->m_ImageGrid->GetOffsetFromIndex( it.Index() ) ); } #ifdef _OPENMP } #endif return jacobianImage; } cmtk::DeformationField::SmartPtr cmtk::EchoPlanarUnwarpFunctional::GetDeformationField( const int direction ) const { cmtk::DeformationField::SmartPtr dfield( new DeformationField( this->m_ImageGrid ) ); const Types::Coordinate delta = direction * this->m_ImageGrid->Deltas()[this->m_PhaseEncodeDirection]; const size_t nPixels = this->m_ImageGrid->GetNumberOfPixels(); size_t offset = 0; for ( size_t px = 0; px < nPixels; ++px, offset += 3 ) { dfield->m_Parameters[offset+0] = dfield->m_Parameters[offset+1] = dfield->m_Parameters[offset+2] = 0; dfield->m_Parameters[offset+this->m_PhaseEncodeDirection] = delta * this->m_Deformation(px+1); } return dfield; } void cmtk::EchoPlanarUnwarpFunctional::MakeGradientImage( const ap::real_1d_array& u, const int direction, const UniformVolume& sourceImage, std::vector& gradientImageData ) { DebugOutput( 9 ) << "Making gradient image\n"; gradientImageData.resize( sourceImage.GetNumberOfPixels() ); const DataGrid::RegionType wholeImageRegion = sourceImage.GetWholeImageRegion(); #ifndef _OPENMP const DataGrid::RegionType region = wholeImageRegion; #else // _OPENMP const int sliceFrom = wholeImageRegion.From()[this->m_ReadoutDirection]; const int sliceTo = wholeImageRegion.To()[this->m_ReadoutDirection]; #pragma omp parallel for for ( int slice = sliceFrom; slice < sliceTo; ++slice ) { DataGrid::RegionType region = wholeImageRegion; region.From()[this->m_ReadoutDirection] = slice; region.To()[this->m_ReadoutDirection] = slice+1; #endif for ( RegionIndexIterator it( region ); it != it.end(); ++it ) { DataGrid::IndexType idx = it.Index(); const size_t i = sourceImage.GetOffsetFromIndex( idx ); // apply deformation const Types::Coordinate shift = direction * u(1+i) + idx[this->m_PhaseEncodeDirection]; Types::Coordinate position = shift + 1; idx[this->m_PhaseEncodeDirection] = static_cast( floor( position ) ); gradientImageData[i] = this->Interpolate1D( sourceImage, idx, position - idx[this->m_PhaseEncodeDirection] ); position = shift - 1; idx[this->m_PhaseEncodeDirection] = static_cast( floor( position ) ); gradientImageData[i] -= this->Interpolate1D( sourceImage, idx, position - idx[this->m_PhaseEncodeDirection] ); // apply Jacobian - this is needed implicitly in cost function gradient gradientImageData[i] *= 0.5 * (1 + direction * this->GetPartialJacobian( u, it.Index() )); } #ifdef _OPENMP } #endif } cmtk::Types::DataItem cmtk::EchoPlanarUnwarpFunctional::Interpolate1D( const UniformVolume& sourceImage, const FixedVector<3,int>& baseIdx, const Types::Coordinate relative ) const { FixedVector<3,int> idx = baseIdx; const int maxIdx = sourceImage.m_Dims[this->m_PhaseEncodeDirection] - 1; const int iFrom = -std::min( Self::InterpolationKernelRadius, idx[this->m_PhaseEncodeDirection] ); const int iTo = std::min( Self::InterpolationKernelRadius, maxIdx - idx[this->m_PhaseEncodeDirection] ); idx[this->m_PhaseEncodeDirection] += iFrom; Types::DataItem value = 0; Types::Coordinate total = 0; for ( int i = iFrom; i < iTo; ++i, ++idx[this->m_PhaseEncodeDirection] ) { const Types::Coordinate weight = Interpolators::CosineSinc::GetWeight( i, relative ); value += weight * sourceImage.GetDataAt( sourceImage.GetOffsetFromIndex( idx ) ); total += weight; } if ( total > 0 ) return static_cast( value / total ); else return 0; } void cmtk::EchoPlanarUnwarpFunctional::ComputeDeformedImage( const ap::real_1d_array& u, int direction, const UniformVolume& sourceImage, std::vector& targetUnwarpData, std::vector& targetCorrectedData ) { DebugOutput( 9 ) << "Computing deformed image\n"; const DataGrid::RegionType wholeImageRegion = sourceImage.GetWholeImageRegion(); #ifndef _OPENMP const DataGrid::RegionType region = wholeImageRegion; #else // _OPENMP const int sliceFrom = wholeImageRegion.From()[this->m_ReadoutDirection]; const int sliceTo = wholeImageRegion.To()[this->m_ReadoutDirection]; #pragma omp parallel for for ( int slice = sliceFrom; slice < sliceTo; ++slice ) { DataGrid::RegionType region = wholeImageRegion; region.From()[this->m_ReadoutDirection] = slice; region.To()[this->m_ReadoutDirection] = slice+1; #endif for ( RegionIndexIterator it( region ); it != it.end(); ++it ) { DataGrid::IndexType idx = it.Index(); const size_t i = sourceImage.GetOffsetFromIndex( idx ); // now compute deformed position for interpolation const Types::Coordinate position = direction * u(1+i) + idx[this->m_PhaseEncodeDirection]; idx[this->m_PhaseEncodeDirection] = static_cast( floor( position ) ); targetUnwarpData[i] = this->Interpolate1D( sourceImage, idx, position - idx[this->m_PhaseEncodeDirection] ); targetCorrectedData[i] = targetUnwarpData[i] * (1 + direction * this->GetPartialJacobian( u, it.Index() )); } #ifdef _OPENMP } #endif } cmtk::Types::Coordinate cmtk::EchoPlanarUnwarpFunctional::GetPartialJacobian( const ap::real_1d_array& u, const FixedVector<3,int>& baseIdx ) const { size_t offset = this->m_ImageGrid->GetOffsetFromIndex( baseIdx ); if ( (baseIdx[this->m_PhaseEncodeDirection] > 0) && (baseIdx[this->m_PhaseEncodeDirection] < this->m_ImageGrid->m_Dims[this->m_PhaseEncodeDirection]-1) ) { return 0.5 * ( u( 1 + offset + this->m_ImageGrid->m_GridIncrements[this->m_PhaseEncodeDirection] ) - u( 1 + offset - this->m_ImageGrid->m_GridIncrements[this->m_PhaseEncodeDirection] ) ); } return 0; } void cmtk::EchoPlanarUnwarpFunctional::Optimize( const int numberOfIterations, const Units::GaussianSigma& smoothMax, const Units::GaussianSigma& smoothMin, const Units::GaussianSigma& smoothDiff ) { const int numberOfPixels = this->m_ImageGrid->GetNumberOfPixels(); ap::integer_1d_array nbd; nbd.setbounds( 1, numberOfPixels ); for ( int i = 1; i <= numberOfPixels; ++i ) { nbd(i) = 0; } // dummy array for unused constraints ap::real_1d_array dummy; for ( Units::GaussianSigma smoothness = smoothMax; ! (smoothness < smoothMin); smoothness = smoothness - smoothDiff ) { DebugOutput( 4 ) << "Setting image smoothing kernel sigma=" << smoothness.Value() << "\n"; this->SetSmoothingKernelWidth( smoothness ); Progress::Begin( 0, numberOfIterations, 1, "EPI Unwarping" ); int info; Self::FunctionAndGradient functionAndGradient( this ); ap::lbfgsbminimize( &(functionAndGradient), numberOfPixels, 5, this->m_Deformation, 1e-10 /*epsg*/, 1e-10 /*epsf*/, 1e-10 /*epsx*/, numberOfIterations, nbd, dummy, dummy, info ); Progress::Done(); if ( info < 0 ) StdErr << "ERROR: lbfgsbminimize returned status code " << info << "\n"; } // update corrected images using unsmoothed images with final deformation this->ComputeDeformedImage( this->m_Deformation, +1, *(this->m_ImageFwd), this->m_UnwarpImageFwd, this->m_CorrectedImageFwd ); this->ComputeDeformedImage( this->m_Deformation, -1, *(this->m_ImageRev), this->m_UnwarpImageRev, this->m_CorrectedImageRev ); } void cmtk::EchoPlanarUnwarpFunctional ::FunctionAndGradient ::Evaluate( const ap::real_1d_array& x, ap::real_value_type& f, ap::real_1d_array& g ) { FunctionType& function = *(this->m_Function); const int phaseEncodeDirection = function.m_PhaseEncodeDirection; // reset gradient vector for ( int i = g.getlowbound(); i <= g.gethighbound(); ++i ) g(i) = 0.0; const UniformVolume& sourceImage = *(function.m_ImageGrid); const DataGrid::RegionType wholeImageRegion = sourceImage.GetWholeImageRegion(); function.ComputeDeformedImage( x, +1, *(function.m_SmoothImageFwd), function.m_UnwarpImageFwd, function.m_CorrectedImageFwd ); function.ComputeDeformedImage( x, -1, *(function.m_SmoothImageRev), function.m_UnwarpImageRev, function.m_CorrectedImageRev ); function.MakeGradientImage( x, +1, *(function.m_SmoothImageFwd), function.m_GradientImageFwd ); function.MakeGradientImage( x, -1, *(function.m_SmoothImageRev), function.m_GradientImageRev ); // initialize gradient vector with derivative of image differences DataGrid::RegionType insideRegion = wholeImageRegion; insideRegion.From()[phaseEncodeDirection] += 1; insideRegion.To()[phaseEncodeDirection] -= 1; size_t insideRegionSize = insideRegion.Size(); const size_t nPixels = function.m_ImageGrid->GetNumberOfPixels(); // precompute composite images for MSD gradient std::vector diffImage( nPixels ); std::vector compositeImage( nPixels ); #pragma omp parallel for for ( int px = 0; px < static_cast( nPixels ); ++px ) { diffImage[px] = function.m_CorrectedImageFwd[px] - function.m_CorrectedImageRev[px]; compositeImage[px] = diffImage[px] * ( function.m_UnwarpImageFwd[px] + function.m_UnwarpImageRev[px] ); } double msd = 0; #ifndef _OPENMP const DataGrid::RegionType region = insideRegion; { #else // _OPENMP const int sliceFrom = wholeImageRegion.From()[function.m_ReadoutDirection]; const int sliceTo = wholeImageRegion.To()[function.m_ReadoutDirection]; #pragma omp parallel for reduction(+:msd) for ( int slice = sliceFrom; slice < sliceTo; ++slice ) { DataGrid::RegionType region = insideRegion; region.From()[function.m_ReadoutDirection] = slice; region.To()[function.m_ReadoutDirection] = slice+1; #endif for ( RegionIndexIterator it( region ); it != it.end(); ++it ) { // difference term derivative DataGrid::IndexType idx = it.Index(); size_t px = sourceImage.GetOffsetFromIndex( idx ); const size_t pxg = 1+px; const Types::Coordinate diff = diffImage[px]; msd += diff * diff; g(pxg) = 2.0 * diff * (function.m_GradientImageFwd[px] + function.m_GradientImageRev[px]); // need "+" between gradient terms because we computed dI/dx, not dI/du, and -du==dx // add gradient terms for Jacobians idx[phaseEncodeDirection] -= 1; px = sourceImage.GetOffsetFromIndex( idx ); g(pxg) += compositeImage[px]; idx[phaseEncodeDirection] += 2; px = sourceImage.GetOffsetFromIndex( idx ); g(pxg) -= compositeImage[px]; g(pxg) /= insideRegionSize; } } f = (msd /= insideRegionSize); // smoothness constraint and its derivative const ap::real_value_type lambda2 = function.m_SmoothnessConstraintWeight; double smooth = 0; if ( lambda2 > 0 ) { for ( int dim = 0; dim < 3; ++dim ) { insideRegion = wholeImageRegion; insideRegion.From()[dim] += 1; insideRegionSize = insideRegion.Size(); #ifndef _OPENMP const DataGrid::RegionType region = insideRegion; { #else // _OPENMP const int sliceInsideFrom = insideRegion.From()[function.m_ReadoutDirection]; const int sliceInsideTo = insideRegion.To()[function.m_ReadoutDirection]; #pragma omp parallel for reduction(+:smooth) for ( int sliceInside = sliceInsideFrom; sliceInside < sliceInsideTo; ++sliceInside ) { DataGrid::RegionType region = insideRegion; region.From()[function.m_ReadoutDirection] = sliceInside; region.To()[function.m_ReadoutDirection] = sliceInside+1; #endif for ( RegionIndexIterator it( region ); it != it.end(); ++it ) { const size_t ofs = 1 + sourceImage.GetOffsetFromIndex( it.Index() ); const ap::real_value_type diff = x( ofs ) - x( ofs - sourceImage.m_GridIncrements[dim] ); // increment smoothness term smooth += diff * diff; // increment relevant gradient elements const ap::real_value_type delta = 2 * lambda2 * diff / insideRegionSize; g( ofs ) += delta; g( ofs - sourceImage.m_GridIncrements[dim] ) -= delta; } } } f += lambda2 * (smooth /= insideRegionSize); } // folding prevention constraint double fold = 0; const ap::real_value_type lambda3 = function.m_FoldingConstraintWeight; if ( lambda3 > 0 ) { insideRegion = wholeImageRegion; insideRegion.From()[phaseEncodeDirection] += 1; insideRegionSize = insideRegion.Size(); #ifndef _OPENMP const DataGrid::RegionType region = insideRegion; #else // _OPENMP const int sliceInsideFrom = insideRegion.From()[function.m_ReadoutDirection]; const int sliceInsideTo = insideRegion.To()[function.m_ReadoutDirection]; #pragma omp parallel for reduction(+:fold) for ( int sliceInside = sliceInsideFrom; sliceInside < sliceInsideTo; ++sliceInside ) { DataGrid::RegionType region = insideRegion; region.From()[function.m_ReadoutDirection] = sliceInside; region.To()[function.m_ReadoutDirection] = sliceInside+1; #endif for ( RegionIndexIterator it( region ); it != it.end(); ++it ) { const size_t ofs = 1 + sourceImage.GetOffsetFromIndex( it.Index() ); const ap::real_value_type diff = x( ofs ) - x( ofs - sourceImage.m_GridIncrements[phaseEncodeDirection] ); fold += diff*diff; // increment relevant gradient elements const ap::real_value_type delta = 2 * lambda3 * diff / insideRegionSize; g( ofs ) += delta; g( ofs - sourceImage.m_GridIncrements[phaseEncodeDirection] ) -= delta; } } f += lambda3 * (fold /= insideRegionSize); #ifdef _OPENMP } #endif DebugOutput( 5 ) << "f " << f << " msd " << msd << " smooth " << smooth << " fold " << fold << "\n"; } cmtk-3.3.1/libs/Registration/cmtkEchoPlanarUnwarpFunctional.h000066400000000000000000000211401276303427400243510ustar00rootroot00000000000000/* // // Copyright 2011, 2012 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4052 $ // // $LastChangedDate: 2012-03-21 10:13:42 -0700 (Wed, 21 Mar 2012) $ // // $LastChangedBy: torsten_at_home $ // */ #ifndef __cmtkEchoPlanarUnwarpFunctional_h_included_ #define __cmtkEchoPlanarUnwarpFunctional_h_included_ #include #include #include #include #include #include namespace cmtk { /** \addtogroup Registration */ //@{ /** Class for computing a const function for unwarping Echo Planar Images. *\see D. Holland, J. M. Kuperman, and A. M. Dale, "Efficient correction of inhomogeneous static magnetic field-induced distortion in Echo Planar Imaging," NeuroImage, vol. 50, no. 1, pp. 175-183, 2010. */ class EchoPlanarUnwarpFunctional { public: /// This class. typedef EchoPlanarUnwarpFunctional Self; /// Sinc image interpolation kernel radius. static const int InterpolationKernelRadius; /// Constructor. EchoPlanarUnwarpFunctional( UniformVolume::SmartConstPtr& imageFwd /*!< "Forward direction" EPI */, UniformVolume::SmartConstPtr& imageRev /*!< "Reverse" direction EPI */, const byte phaseEncodeDirection /*!< Phase encoding direction (image coordinate axis) */, const bool initShiftCentersOfMass = false /*!< When set, initialize by shifting the centers of mass of each row to match */ ); /** Set image smoothing kernel width and create smoothed images. *\note Smoothing is applied only in phase encoding direction because the deformation field is restricted to this direction, and the optimization can thus not * get "caught" in local minima in the other two directions. */ void SetSmoothingKernelWidth ( const Units::GaussianSigma& sigma, /*!< Kernel parameter "sigma" (standard deviation). Setting kernel width to "0" simply uses original images. */ const Types::Coordinate maxError = 1e-5 /*!< Maximum approximation error: the kernel is truncated when it falls below this threshold */ ); /// Return either first or second corrected image. UniformVolume::SmartPtr GetCorrectedImage( const int direction /*!< Select forward (+1) vs. reverse encoding (-1) image */ ) const; /// Return either first or second Jacobian intensity correction factor map. UniformVolume::SmartPtr GetJacobianMap( const int direction /*!< Select forward (+1) vs. reverse encoding (-1) map */ ) const; /// Get forward or reverse deformation field. DeformationField::SmartPtr GetDeformationField( const int direction /*!< Select forward (+1) vs. reverse encoding (-1) deformation field */ ) const; /// Optimize unwarping deformation using L-BFGS optimizer. void Optimize( const int numberOfIterations, const Units::GaussianSigma& smoothMax, const Units::GaussianSigma& smoothMin, const Units::GaussianSigma& smoothDiff ); /** Set smoothness constraint weight. * This is lambda_2 in Eq. (9) in Holland et al. NIMG 2010. */ void SetSmoothnessConstraintWeight( const Types::Coordinate weight ) { this->m_SmoothnessConstraintWeight = weight; } /** Set folding constraint weight. * This is based on the log() of the local 1D Jacobians of the deformation field. */ void SetFoldingConstraintWeight( const Types::Coordinate weight ) { this->m_FoldingConstraintWeight = weight; } private: /// Unwarped image grid. UniformVolume::SmartPtr m_ImageGrid; /// "Forward" phase encoding image. UniformVolume::SmartConstPtr m_ImageFwd; /// "Reverse" phase encoding image. UniformVolume::SmartConstPtr m_ImageRev; /// Smoothed "forward" phase encoding image. UniformVolume::SmartConstPtr m_SmoothImageFwd; /// Smoothed "reverse" phase encoding image. UniformVolume::SmartConstPtr m_SmoothImageRev; /// Phase encoding direction. byte m_PhaseEncodeDirection; /** Readout direction. * This is a bit misnamed - really this is the coordinate dimension, excluding the phase encoding direction, * with the maximum number of pixels. The purpose of this is for a maximally efficient partition for SMP * computation. So while the name may not always fit the determined value, and there may be cases when this is * selected as the slice direction, it does serve its purpose. */ byte m_ReadoutDirection; /// Smoothness constraint weight. Types::Coordinate m_SmoothnessConstraintWeight; /// Folding constraint weight. Types::Coordinate m_FoldingConstraintWeight; /// 1D deformation map along phase encoding direction. ap::real_1d_array m_Deformation; /// Initialize deformation by shifting each row's center of mass. void InitShiftCentersOfMass(); /// 1D intensity gradient of "forward" image. std::vector m_GradientImageFwd; /// 1D intensity gradient of "reverse" image. std::vector m_GradientImageRev; /// Deformed "forward" image. std::vector m_UnwarpImageFwd; /// Deformed "reverse" image. std::vector m_UnwarpImageRev; /// Deformed and intensity-corrected "forward" image. std::vector m_CorrectedImageFwd; /// Deformed and intensity-corrected "reverse" image. std::vector m_CorrectedImageRev; /// Compute 1D intensity gradient image. void MakeGradientImage( const ap::real_1d_array& x /*!< Current deformation parameter vector.*/, const int direction /*!< +1 = forward image, -1 = reverse image */, const UniformVolume& sourceImage /*!< Image to compute gradient for.*/, std::vector& gradientImageData /*!< Reference to data array for computed gradient data.*/ ); /// Compute deformed image. void ComputeDeformedImage( const ap::real_1d_array& x /*!< Current deformation parameter vector.*/, int direction /*!< Deformation direction - 1 computes unwarped "forward" image, -1 computed unwarped "reverse" image.*/, const UniformVolume& sourceImage /*!< Undeformed input image.*/, std::vector& targetUnwarpData /*!< Reference to deformed output image data.*/, std::vector& targetCorrectedData /*!< Reference to deformed and intensity-corrected output image data.*/ ); /// 1D sinc interpolation Types::DataItem Interpolate1D( const UniformVolume& sourceImage /*!< Image to interpolate from */, const FixedVector<3,int>& baseIdx /*!< Grid base index - this is the grid cell where the interpolation kernel is anchored. */, const Types::Coordinate relative /*!< Relative position of interpolation location in grid cell, relative to phase encoding direction. */ ) const; /** Get partial image deformation Jacobian. *\return The forward image Jacobian can be computed from this as Jfwd = 1+Jpartial, the reverse Jacobian as Jrev = 1-Jpartial. */ Types::Coordinate GetPartialJacobian( const ap::real_1d_array& x /*!< Current deformation parameter vector.*/, const FixedVector<3,int>& baseIdx /*!< Grid base index - this is the grid cell where the differential operator stencil is anchored. */ ) const; /// Glue class for function and gradient evaluation. class FunctionAndGradient : /// Inherit from virtual base class. public ap::FunctionAndGradient { public: /// Function class type. typedef EchoPlanarUnwarpFunctional FunctionType; /// Constructor. FunctionAndGradient( FunctionType* function ) { this->m_Function = function; } /// Evaluate function and gradient. virtual void Evaluate( const ap::real_1d_array& x, ap::real_value_type& f, ap::real_1d_array& g ); private: /// Pointer to actual function class. FunctionType* m_Function; }; /// Give function and gradient evaluator private access. friend class Self::FunctionAndGradient; }; //@} } // namespace cmtk #endif // #ifndef __cmtkEchoPlanarUnwarpFunctional_h_included_ cmtk-3.3.1/libs/Registration/cmtkElasticRegistration.cxx000066400000000000000000000310201276303427400234450ustar00rootroot00000000000000/* // // Copyright 1997-2010 Torsten Rohlfing // // Copyright 2004-2013 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4816 $ // // $LastChangedDate: 2013-09-10 10:06:43 -0700 (Tue, 10 Sep 2013) $ // // $LastChangedBy: torstenrohlfing $ // */ #include "cmtkElasticRegistration.h" #include #include #include #include #include #include #include #include #include #include namespace cmtk { /** \addtogroup Registration */ //@{ ElasticRegistration::ElasticRegistration () : VoxelRegistration(), InitialWarpXform( NULL ), InverseWarpXform( NULL ), ForceSwitchVolumes( false ), m_MatchFltToRefHistogram( false ), m_ExactGridSpacing( false ), m_RigidityConstraintMap( NULL ), m_InverseConsistencyWeight( 0.0 ), m_RelaxToUnfold( false ), m_ForceOutsideFlag( false ), m_ForceOutsideValue( 0.0 ) { this->m_GridSpacing = 10; RestrictToAxes = NULL; this->m_RefineGrid = 0; RefinedGridAtLevel = -1; RefineGridCount = 0; this->m_DelayRefineGrid = 0; RefineDelayed = false; IgnoreEdge = 0; this->m_FastMode = false; this->m_AdaptiveFixParameters = 1; this->m_AdaptiveFixThreshFactor = 0.5; this->m_JacobianConstraintWeight = 0; this->m_RigidityConstraintWeight = 0; this->m_GridEnergyWeight = 0; this->m_RelaxWeight = -1; this->m_InverseConsistencyWeight = 0.0; RelaxationStep = false; } CallbackResult ElasticRegistration::InitRegistration () { this->m_ReferenceVolume = this->m_Volume_1; this->m_FloatingVolume = this->m_Volume_2; if ( this->m_MatchFltToRefHistogram ) { this->GetVolume_2()->GetData()->ApplyFunctionObject( TypedArrayFunctionHistogramMatching( *(this->GetVolume_2()->GetData()), *(this->GetVolume_1()->GetData()) ) ); } AffineXform::SmartPtr affineXform = this->m_InitialTransformation; AffineXform::SmartPtr initialInverse = AffineXform::SmartPtr::DynamicCastFrom( this->m_InitialTransformation->GetInverse() ); affineXform->ChangeCenter( this->m_FloatingVolume->GetCenterCropRegion() ); Types::Coordinate currSampling = std::max( this->m_Sampling, 2 * std::min( this->m_ReferenceVolume->GetMinDelta(), this->m_FloatingVolume->GetMinDelta())); // If no initial transformation exists, create one from the defined // parameters. if ( InitialWarpXform ) { // If we have an initial transformation from somewhere, use that. // This will override all registration parameters otherwise defined, // for example grid spacing and deformation type. InitialWarpXform->SetIgnoreEdge( IgnoreEdge ); InitialWarpXform->SetFastMode( this->m_FastMode ); // MIPSpro needs explicit. this->m_Xform = Xform::SmartPtr::DynamicCastFrom( InitialWarpXform ); } else { SplineWarpXform::SmartPtr warpXform( this->MakeWarpXform( this->m_ReferenceVolume->m_Size, affineXform ) ); if ( this->m_InverseConsistencyWeight > 0 ) InverseWarpXform = SplineWarpXform::SmartPtr( this->MakeWarpXform( this->m_FloatingVolume->m_Size, initialInverse ) ); // MIPSpro needs explicit: this->m_Xform = Xform::SmartPtr::DynamicCastFrom( warpXform ); } if ( this->m_UseOriginalData ) { Functional::SmartPtr nextFunctional( this->MakeFunctional( this->m_ReferenceVolume, this->m_FloatingVolume, this->m_RigidityConstraintMap ) ); FunctionalStack.push( nextFunctional ); } if ( this->m_Exploration <= 0 ) { const SplineWarpXform* warp = SplineWarpXform::SmartPtr::DynamicCastFrom( this->m_Xform ); this->m_Exploration = 0.25 * std::max( warp->m_Spacing[0], std::max( warp->m_Spacing[1], warp->m_Spacing[2] ) ); } if ( this->CoarsestResolution <= 0 ) this->CoarsestResolution = this->m_Exploration; UniformVolume::SmartPtr currRef( this->m_ReferenceVolume ); UniformVolume::SmartPtr currFlt( this->m_FloatingVolume ); for ( ;(currSampling<=this->CoarsestResolution); currSampling *= 2 ) { UniformVolume::SmartPtr nextRef( currRef->GetResampled( currSampling ) ); UniformVolume::SmartPtr nextFlt( currFlt->GetResampled( currSampling ) ); UniformVolume::SmartPtr nextRigidityMap; if ( this->m_RigidityConstraintMap ) { nextRigidityMap = UniformVolume::SmartPtr( this->m_RigidityConstraintMap->GetResampled( currSampling ) ); } Functional::SmartPtr nextFunctional( this->MakeFunctional( nextRef, nextFlt, nextRigidityMap ) ); FunctionalStack.push( nextFunctional ); currRef = nextRef; currFlt = nextFlt; } switch ( this->m_Algorithm ) { case 0: this->m_Optimizer = Optimizer::SmartPtr( new BestNeighbourOptimizer( OptimizerStepFactor ) ); break; case 1: case 2: this->m_Optimizer = Optimizer::SmartPtr( NULL ); break; case 3: { BestDirectionOptimizer *optimizer = new BestDirectionOptimizer( OptimizerStepFactor ); optimizer->SetUseMaxNorm( UseMaxNorm ); this->m_Optimizer = Optimizer::SmartPtr( optimizer ); break; } } this->m_Optimizer->SetCallback( this->m_Callback ); return this->Superclass::InitRegistration(); } const SplineWarpXform::SmartPtr ElasticRegistration::MakeWarpXform ( const Vector3D& size, const AffineXform* initialAffine ) const { SplineWarpXform::SmartPtr warpXform( new SplineWarpXform( size, this->m_GridSpacing, initialAffine, this->m_ExactGridSpacing ) ); warpXform->SetIgnoreEdge( this->IgnoreEdge ); warpXform->SetFastMode( this->m_FastMode ); warpXform->SetParametersActive(); return warpXform; } Functional* ElasticRegistration::MakeFunctional ( UniformVolume::SmartPtr& refVolume, UniformVolume::SmartPtr& fltVolume, UniformVolume::SmartPtr& rigidityMap ) const { if ( this->m_InverseConsistencyWeight > 0 ) { SymmetricElasticFunctional *newFunctional = CreateSymmetricElasticFunctional( this->m_Metric, refVolume, fltVolume ); newFunctional->SetInverseConsistencyWeight( this->m_InverseConsistencyWeight ); newFunctional->SetAdaptiveFixParameters( this->m_AdaptiveFixParameters ); newFunctional->SetAdaptiveFixThreshFactor( this->m_AdaptiveFixThreshFactor ); newFunctional->SetJacobianConstraintWeight( this->m_JacobianConstraintWeight ); newFunctional->SetRigidityConstraintWeight( this->m_RigidityConstraintWeight ); newFunctional->SetGridEnergyWeight( this->m_GridEnergyWeight ); // newFunctional->SetForceOutside( this->m_ForceOutsideFlag, this->m_ForceOutsideValue ); return newFunctional; } else { VoxelMatchingElasticFunctional *newFunctional = CreateElasticFunctional( this->m_Metric, refVolume, fltVolume ); newFunctional->SetAdaptiveFixParameters( this->m_AdaptiveFixParameters ); newFunctional->SetAdaptiveFixThreshFactor( this->m_AdaptiveFixThreshFactor ); newFunctional->SetJacobianConstraintWeight( this->m_JacobianConstraintWeight ); newFunctional->SetRigidityConstraintWeight( this->m_RigidityConstraintWeight ); newFunctional->SetForceOutside( this->m_ForceOutsideFlag, this->m_ForceOutsideValue ); newFunctional->SetActiveCoordinates( this->RestrictToAxes ); if ( rigidityMap ) { newFunctional->SetRigidityConstraintMap( rigidityMap ); } newFunctional->SetGridEnergyWeight( this->m_GridEnergyWeight ); return newFunctional; } } void ElasticRegistration::EnterResolution ( CoordinateVector::SmartPtr& v, Functional::SmartPtr& functional, const int idx, const int total ) { SplineWarpXform::SmartPtr warpXform = SplineWarpXform::SmartPtr::DynamicCastFrom( this->m_Xform ); float effGridEnergyWeight = this->m_GridEnergyWeight; float effJacobianConstraintWeight = this->m_JacobianConstraintWeight; float effRigidityConstraintWeight = this->m_RigidityConstraintWeight; float effInverseConsistencyWeight = this->m_InverseConsistencyWeight; if ( (this->m_RelaxWeight > 0) && !this->RelaxationStep ) { effGridEnergyWeight *= this->m_RelaxWeight; effJacobianConstraintWeight *= this->m_RelaxWeight; effRigidityConstraintWeight *= this->m_RelaxWeight; effInverseConsistencyWeight *= this->m_RelaxWeight; } // handle simple elastic functional SmartPointer elasticFunctional = VoxelMatchingElasticFunctional::SmartPtr::DynamicCastFrom( functional ); if ( elasticFunctional ) { elasticFunctional->SetWarpXform( warpXform ); if ( this->m_RelaxToUnfold ) warpXform->RelaxToUnfold(); elasticFunctional->SetGridEnergyWeight( effGridEnergyWeight ); elasticFunctional->SetJacobianConstraintWeight( effJacobianConstraintWeight ); elasticFunctional->SetRigidityConstraintWeight( effRigidityConstraintWeight ); } else { // handle inverse-consistent elastic functional SmartPointer symmetricFunctional = SymmetricElasticFunctional::SmartPtr::DynamicCastFrom( functional ); if ( symmetricFunctional ) { symmetricFunctional->SetWarpXform( warpXform, this->InverseWarpXform ); if ( this->m_RelaxToUnfold ) { warpXform->RelaxToUnfold(); this->InverseWarpXform->RelaxToUnfold(); } symmetricFunctional->SetGridEnergyWeight( effGridEnergyWeight ); symmetricFunctional->SetJacobianConstraintWeight( effJacobianConstraintWeight ); symmetricFunctional->SetRigidityConstraintWeight( effRigidityConstraintWeight ); symmetricFunctional->SetInverseConsistencyWeight( effInverseConsistencyWeight ); } else { // neither simple nor inverse-consistent functional: something went // badly wrong. StdErr << "Fatal coding error: Non-elastic functional in ElasticRegistration::EnterResolution.\n"; abort(); } } Superclass::EnterResolution( v, functional, idx, total ); } int ElasticRegistration::DoneResolution ( CoordinateVector::SmartPtr& v, Functional::SmartPtr& functional, const int idx, const int total ) { if ( ( this->m_RelaxWeight > 0 ) && ! RelaxationStep ) { RelaxationStep = true; this->Superclass::DoneResolution( v, functional, idx, total ); return false; // repeat with a relaxation step. } else { RelaxationStep = false; } bool repeat = ( ( idx == total ) && ( RefineGridCount < this->m_RefineGrid ) ); if ( (RefinedGridAtLevel != idx) || (idx==total) ) { if ( RefineGridCount < this->m_RefineGrid ) { if ( (!this->m_DelayRefineGrid) || RefineDelayed || ( idx == total ) ) { WarpXform::SmartPtr warpXform = WarpXform::SmartPtr::DynamicCastFrom( this->m_Xform ); if ( warpXform ) { warpXform->Refine(); if ( InverseWarpXform ) InverseWarpXform->Refine(); ++RefineGridCount; functional->GetParamVector( *v ); if ( this->m_Callback ) this->m_Callback->Comment( "Refined control point grid." ); RefinedGridAtLevel = idx; } if ( this->m_DelayRefineGrid && ( idx > 1 ) ) repeat = true; RefineDelayed = false; } else { RefineDelayed = true; } } } else { RefineDelayed = true; } return this->Superclass::DoneResolution( v, functional, idx, total ) && !repeat; } const UniformVolume::SmartPtr ElasticRegistration::GetReformattedFloatingImage( Interpolators::InterpolationEnum interpolator ) { ReformatVolume reformat; reformat.SetInterpolation( interpolator ); reformat.SetReferenceVolume( this->m_Volume_1 ); reformat.SetFloatingVolume( this->m_Volume_2 ); WarpXform::SmartPtr warpXform( this->GetTransformation() ); reformat.SetWarpXform( warpXform ); return reformat.PlainReformat(); } } // namespace cmtk cmtk-3.3.1/libs/Registration/cmtkElasticRegistration.h000066400000000000000000000200221276303427400230720ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2012 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4423 $ // // $LastChangedDate: 2012-06-11 16:13:37 -0700 (Mon, 11 Jun 2012) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkElasticRegistration_h_included_ #define __cmtkElasticRegistration_h_included_ #include #include #include namespace cmtk { /** \addtogroup Registration */ //@{ /** Generic multiresolution voxel-registration class. * By implementing member functions to retrieve parameters and report results * in derived classes, registration can be integrated into various * environments. *\version $Revision: 4423 $ $Date: 2012-06-11 16:13:37 -0700 (Mon, 11 Jun 2012) $ */ class ElasticRegistration : /// Inherit basic voxel registration functions. public VoxelRegistration { protected: /// Initial deformation. SplineWarpXform::SmartPtr InitialWarpXform; /// Optional inverse warp for inverse-consistent registration. SplineWarpXform::SmartPtr InverseWarpXform; /** If this flag is set, reference and model volume are exchanged. * By default, volume #1 is the reference and volume #2 the model image. * However, a studylist for example may contain both volumes in the wrong * order. This is caused by the fact that for CT to MRI registration, for * instance, it does not make sense to warp the CT and leave MRI fixed. */ bool ForceSwitchVolumes; /// Flag whether to adjust floating image histogram to match reference image. cmtkGetSetMacro(bool,MatchFltToRefHistogram); /// This value determines how often the control point grid is refined. cmtkGetSetMacro(int,RefineGrid); /** Flag whether to delay grid refinement. * If this flag is set, a newly entered image resolution level is run with * the previous, coarser deformation grid first before refining. */ cmtkGetSetMacro(bool,DelayRefineGrid); /// Initial spacing of the control point grid. cmtkGetSetMacro(Types::Coordinate,GridSpacing); /** Force exact grid spacing. * If this flag is set, then the CPG will be spaced at exactly the distance * given in the GridSpacing field. Otherwise, the grid spacing will be * adjusted so that there is an integral number of CPG cells that cover the * reference image domain. */ cmtkGetSetMacro(bool,ExactGridSpacing); /// This counter determines how many edge control points are fixed. unsigned int IgnoreEdge; /** Restrict deformation to one or more coordinate axes. */ const char* RestrictToAxes; /// Flag for fast mode (less accurate) of spline deformations. cmtkGetSetMacro(bool,FastMode); /// Flag for adaptive selection of active and passive parameters. cmtkGetSetMacro(bool,AdaptiveFixParameters); /** Set threshold factor for selecting passive warp parameters adaptively. * If the flag AdaptiveFixParameters is set, this value determines the * threshold by which active vs. passive parameters are selected. All * control points are set to passive for which the local region entropy is * below this factor times sum of min and max region entropy. The default * value is 0.5. */ cmtkGetSetMacro(float,AdaptiveFixThreshFactor); /// Weighting of Jacobian constraint relative to similairy measure. cmtkGetSetMacro(float,JacobianConstraintWeight); /// Weighting of rigidity constraint relative to similairy measure. cmtkGetSetMacro(float,RigidityConstraintWeight); /// Pixelwise weight map of rigidity constraint relative to similairy measure. cmtkGetSetMacro(UniformVolume::SmartPtr,RigidityConstraintMap); /// Weighting of grid bending energy constraint relative to image similarity. cmtkGetSetMacro(float,GridEnergyWeight); /// Factor by which to relax constraint weights for a relaxation step. cmtkGetSetMacro(float,RelaxWeight); /** Weight for inverse consistency weight. * If this is set to a value greater than 0, inverse consistency of the * transformation is enforced. In fact, both forward and backward * transformation are optimized simultaneously. */ cmtkGetSetMacro(float,InverseConsistencyWeight); /// Flag to turn on deformation unfolding before each level. bool m_RelaxToUnfold; /// Set flag and value for forcing values outside the floating image. virtual void SetForceOutside( const bool flag = true, const Types::DataItem value = 0 ) { this->m_ForceOutsideFlag = flag; this->m_ForceOutsideValue = value; } /** Default constructor. * Set initial values for some flags. */ ElasticRegistration(); /** Destructor. * Free local objects for this class. */ virtual ~ElasticRegistration() {}; /**\name Member functions to be overwritten. */ //@{ /** Initialize registration. * This function is called by Register before any other operations. It can * be overloaded to open status dialog windows, etc. Derived implementations * should call their base class' InitRegistration first. *\return */ virtual CallbackResult InitRegistration (); /** Enter resolution level. */ virtual void EnterResolution( CoordinateVector::SmartPtr&, Functional::SmartPtr&, const int, const int ); /** Finish resolution level. * In addition to operations necessary for general registration, we have * to make some additional modifications. In particular the sampling of * the warp transformations' control point mesh has to be refined before * entering the next resolution. */ virtual int DoneResolution( CoordinateVector::SmartPtr&, Functional::SmartPtr&, const int, const int ); //@} /// Return final transformation. SplineWarpXform::SmartPtr GetTransformation() const { return SplineWarpXform::SmartPtr::DynamicCastFrom( this->m_Xform ); } /// Get reformatted floating image. const UniformVolume::SmartPtr GetReformattedFloatingImage( Interpolators::InterpolationEnum interpolator = Interpolators::LINEAR ); private: /// Level on which the last control grid refinement was performend. int RefinedGridAtLevel; /// Number of refinements so far. int RefineGridCount; /// Convenience typedef. typedef VoxelRegistration Superclass; /// Are we currently doing a relaxation step? bool RelaxationStep; /// Have we already run the current level before refining the grid? bool RefineDelayed; /// Flag for forcing pixel values outside the floating image. bool m_ForceOutsideFlag; /// Value for forcing pixel values outside the floating image. Types::DataItem m_ForceOutsideValue; /** Create warp transformation with current settings. * This function is used to create the standard warp, as well as the * approximated inverse warp for the optional inverse-consistent * registration. *\param size Reference volume size. *\param initialAffine Initial affine transformation for the warp. */ const SplineWarpXform::SmartPtr MakeWarpXform( const Vector3D& size, const AffineXform* initialAffine ) const; /** Create functional with all settings and two given volume objects. */ Functional* MakeFunctional( UniformVolume::SmartPtr& refVolume, UniformVolume::SmartPtr& fltVolume, UniformVolume::SmartPtr& rigidityMap ) const; }; //@} } // namespace cmtk #endif // __cmtkElasticRegistration_h_included_ cmtk-3.3.1/libs/Registration/cmtkElasticRegistrationCommandLine.cxx000066400000000000000000000577461276303427400256030ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2013 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 5129 $ // // $LastChangedDate: 2014-01-09 13:48:15 -0800 (Thu, 09 Jan 2014) $ // // $LastChangedBy: torstenrohlfing $ // */ #include "cmtkElasticRegistrationCommandLine.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef CMTK_USE_SQLITE # include #endif #include #include #include #include #include #include #ifdef HAVE_SYS_TYPES_H # include #endif #ifdef HAVE_SYS_STAT_H # include #endif #ifdef HAVE_SYS_UTSNAME_H # include #endif #ifdef _MSC_VER # include #endif // #ifdef _MSC_VER namespace cmtk { /** \addtogroup Registration */ //@{ ElasticRegistrationCommandLine* ElasticRegistrationCommandLine::StaticThis = NULL; ElasticRegistrationCommandLine ::ElasticRegistrationCommandLine ( const int argc, const char *argv[] ) { this->m_Metric = 0; this->m_Algorithm = 3; this->m_GridSpacing = 15; this->m_ExactGridSpacing = 0; this->m_OutputIntermediate = 0; std::string initialTransformationFile; IntermediateResultIndex = 0; bool forceOutsideFlag = false; Types::DataItem forceOutsideValue = 0; std::string clArg1; // input studylist or reference image std::string clArg2; // empty or floating image std::string clArg3; // empty or initial transformation try { CommandLine cl( CommandLine::PROPS_XML ); cl.SetProgramInfo( CommandLine::PRG_TITLE, "B-spline nonrigid registration" ); cl.SetProgramInfo( CommandLine::PRG_DESCR, "This program performs nonrigid image registration using multi-resolution optimization of voxel-based image similarity measures " "and a multi-resolution B-spline transformation model." ); cl.SetProgramInfo( CommandLine::PRG_CATEG, "CMTK.Registration" ); typedef CommandLine::Key Key; cl.BeginGroup( "TransformationIO", "Transformation import/export" ); cl.AddOption( Key( "initial" ), &initialTransformationFile, "Initialize transformation from given path" )->SetProperties( CommandLine::PROPS_XFORM ); cl.AddOption( Key( "write-itk-xform" ), &this->m_OutputPathITK, "Output path for final transformation in ITK format" ) ->SetProperties( CommandLine::PROPS_XFORM | CommandLine::PROPS_OUTPUT ) ->SetAttribute( "type", "bspline" )->SetAttribute( "reference", "FloatingImage" ); cl.AddOption( Key( "write-reformatted" ), &this->m_ReformattedImagePath, "Write reformatted floating image." ) ->SetProperties( CommandLine::PROPS_IMAGE | CommandLine::PROPS_OUTPUT ); cl.EndGroup(); cl.BeginGroup( "Transformation", "Transformation parameters" ); cl.AddOption( Key( 'g', "grid-spacing" ), &this->m_GridSpacing, "Control point grid spacing" ); cl.AddSwitch( Key( "exact-spacing" ), &this->m_ExactGridSpacing, true, "Use exact control point spacing; do not modify spacing to fit reference image bounding box" ); cl.AddOption( Key( "refine" ), &this->m_RefineGrid, "Number of refinements (control point grid resolution levels)" ); cl.AddSwitch( Key( "delay-refine" ), &this->m_DelayRefineGrid, true, "Delay control point grid refinement; first switch to next higher image resolution" ); cl.AddOption( Key( "ignore-edge" ), &this->IgnoreEdge, "Ignore n control point layers along each image face" ); cl.AddOption( Key( "restrict" ), &this->RestrictToAxes, "Restrict deformation to coordinate dimension(s) [one or more of 'x','y','z']" ); cl.AddSwitch( Key( "no-adaptive-fix" ), &this->m_AdaptiveFixParameters, false, "Disable adaptive fixing of control points; optimize all deformation parameters" ); cl.AddOption( Key( "adaptive-fix-thresh" ), &this->m_AdaptiveFixThreshFactor, "Threshold factor for entropy criterion to fix local control points" ); cl.AddSwitch( Key( "accurate" ), &this->m_FastMode, false, "Accurate computation mode: may give slightly better results after substantially longer computation" ); cl.AddSwitch( Key( "fast" ), &this->m_FastMode, true, "Fast computation mode: may give slightly worse results than accurate mode, but saves substantial CPU time" ); cl.AddSwitch( Key( 'S', "switch" ), &this->ForceSwitchVolumes, true, "Switch reference and floating image" ); cl.EndGroup(); cl.BeginGroup( "Regularization", "Regularization parameters" ); cl.AddOption( Key( "jacobian-weight" ), &this->m_JacobianConstraintWeight, "Weight for Jacobian-based local volume preservation constraint" ); cl.AddOption( Key( "energy-weight" ), &this->m_GridEnergyWeight, "Weight for grid bending energy constraint" ); cl.AddOption( Key( "rigidity-weight" ), &this->m_RigidityConstraintWeight, "Weight for local rigidity constraint" ); cl.AddOption( Key( "ic-weight" ), &this->m_InverseConsistencyWeight, "Weight for inverse consistency constraint" ); cl.AddOption( Key( "relax" ), &this->m_RelaxWeight, "Weight relaxation factor for alternating under-constrained iterations" ); cl.AddSwitch( Key( "relax-to-unfold" ), &this->m_RelaxToUnfold, true, "Before each resolution level, regularize negative-Jacobian areas of the deformation to unfold them." ); cl.EndGroup(); cl.BeginGroup( "Optimization", "Optimization parameters" ); cl.AddOption( Key( 'e', "exploration" ), &this->m_Exploration, "Search space exploration (initial step size)" ); cl.AddOption( Key( 'a', "accuracy" ), &this->m_Accuracy, "Search accuracy (initial step size)" ); cl.AddOption( Key( 'f', "stepfactor" ), &this->OptimizerStepFactor, "Factor for search step size reduction. Must be > 0.0 and < 1.0 [default: 0.5]" ); cl.AddOption( Key( "delta-f-threshold" ), &this->m_DeltaFThreshold, "Optional threshold to terminate optimization (level) if relative change of target function drops below this value." ); cl.AddSwitch( Key( "no-maxnorm" ), &this->UseMaxNorm, false, "Use Euclid norm for gradient normalication in optimization, rather than maximum norm" ); cl.EndGroup(); cl.BeginGroup( "Resolution", "Image resolution parameters" ); cl.AddOption( Key( 's', "sampling" ), &this->m_Sampling, "Image sampling (finest resampled image resolution)" ); cl.AddOption( Key( "coarsest" ), &this->CoarsestResolution, "Upper limit for image sampling in multiresolution hierarchy" ); cl.AddSwitch( Key( "omit-original-data" ), &this->m_UseOriginalData, false, "Do not use original data in full resolution for final registration stage." ); cl.EndGroup(); cl.BeginGroup( "Images", "Image data" ); CommandLine::EnumGroup::SmartPtr metricGroup = cl.AddEnum( "registration-metric", &this->m_Metric, "Registration metric for motion estimation by image-to-image registration." ); metricGroup->AddSwitch( Key( "nmi" ), 0, "Normalized Mutual Information metric" ); metricGroup->AddSwitch( Key( "mi" ), 1, "Standard Mutual Information metric" ); metricGroup->AddSwitch( Key( "cr" ), 2, "Correlation Ratio metric" ); metricGroup->AddSwitch( Key( "msd" ), 4, "Mean Squared Difference metric" ); metricGroup->AddSwitch( Key( "ncc" ), 5, "Normalized Cross Correlation metric" ); cl.AddSwitch( Key( "match-histograms" ), &this->m_MatchFltToRefHistogram, true, "Match floating image histogram to reference image histogram." ); cl.AddOption( Key( "force-outside-value" ), &forceOutsideValue, "Force values outside field of view to this value rather than drop incomplete pixel pairs", &forceOutsideFlag ); this->m_PreprocessorRef.AttachToCommandLine( cl ); this->m_PreprocessorFlt.AttachToCommandLine( cl ); cl.BeginGroup( "Output", "Output parameters" )->SetProperties( CommandLine::PROPS_NOXML ); cl.AddOption( Key( 'o', "outlist" ), &this->Studylist, "Output path for final transformation" ); cl.AddOption( Key( 'p', "protocol" ), &this->Protocol, "Optimization protocol output file name" ); cl.AddOption( Key( 't', "time" ), &this->Time, "Computation time statistics output file name" ); cl.AddSwitch( Key( "output-intermediate" ), &this->m_OutputIntermediate, true, "Write transformation for each level [default: only write final transformation]" ); cl.EndGroup(); #ifdef CMTK_USE_SQLITE cl.BeginGroup( "Database", "Image/Transformation Database" ); cl.AddOption( Key( "db" ), &this->m_UpdateDB, "Path to image/transformation database that should be updated with the new registration and/or reformatted image." ); cl.EndGroup(); #endif cl.AddParameter( &clArg1, "ReferenceImage", "Reference (fixed) image path" )->SetProperties( CommandLine::PROPS_IMAGE ); cl.AddParameter( &clArg2, "FloatingImage", "Floating (moving) image path" )->SetProperties( CommandLine::PROPS_IMAGE | CommandLine::PROPS_OPTIONAL); cl.AddParameter( &clArg3, "InitialXform", "Initial affine transformation from reference to floating image" ) ->SetProperties( CommandLine::PROPS_NOXML | CommandLine::PROPS_XFORM | CommandLine::PROPS_OPTIONAL ); cl.Parse( argc, argv ); } catch ( const CommandLine::Exception& ex ) { StdErr << ex << "\n"; throw ExitException( 1 ); } if ( (OptimizerStepFactor <= 0) || (OptimizerStepFactor >= 1) ) { StdErr << "ERROR: step factor value " << OptimizerStepFactor << " is invalid. Must be in range (0..1)\n"; throw ExitException( 1 ); } if ( ! clArg2.empty() ) { this->SetInitialTransformation( AffineXform::SmartPtr( new AffineXform() ) ); this->Study1 = clArg1; this->Study2 = clArg2; if ( ! clArg3.empty() ) { initialTransformationFile = clArg3; } } else { InputStudylist = clArg1; DebugOutput( 1 ) << "Reading input studylist " << InputStudylist << "\n"; ClassStreamInput classStream( MountPoints::Translate(InputStudylist),"registration" ); if ( ! classStream.IsValid() ) { StdErr << "ERROR: Could not open studylist archive " << InputStudylist << ".\n"; throw ExitException( 1 ); } classStream.Seek ( "registration" ); Study1 = classStream.ReadStdString( "reference_study" ); Study2 = classStream.ReadStdString( "floating_study" ); if ( ! Study2.empty() ) { AffineXform::SmartPtr affineXform; classStream >> affineXform; this->SetInitialTransformation( affineXform ); } else { // legacy studylists have inverse transformation stored in them Study2 = classStream.ReadStdString( "model_study" ); AffineXform::SmartPtr affineXform; classStream >> affineXform; try { this->SetInitialTransformation( affineXform->GetInverse() ); } catch ( const AffineXform::MatrixType::SingularMatrixException& ) { StdErr << "ERROR: singular matrix read from initialization file in cmtk::ElasticRegistrationCommandLine constructor\n"; throw ExitException( 1 ); } } classStream.Close(); } /// Was an initial studylist given? If so, get warp if ( ! initialTransformationFile.empty() ) { try { Xform::SmartPtr initialXform( XformIO::Read( initialTransformationFile ) ); AffineXform::SmartPtr affineXform = AffineXform::SmartPtr::DynamicCastFrom( initialXform ); if ( affineXform ) { this->SetInitialTransformation( affineXform ); } else { InitialWarpXform = SplineWarpXform::SmartPtr::DynamicCastFrom( initialXform ); } } catch ( const Exception& ex ) { StdErr << "Initializing transformation from " << initialTransformationFile << " failed: " << ex.what() << "\n"; throw( ExitException( 1 ) ); } } // Did user ask for exchanged reference/floating images? if ( this->ForceSwitchVolumes ) { std::swap( Study1, Study2 ); this->SetInitialTransformation( AffineXform::SmartPtr( dynamic_cast( this->m_InitialTransformation->MakeInverse() ) ) ); } UniformVolume::SmartPtr volume( VolumeIO::ReadOriented( this->Study1 ) ); if ( !volume ) throw ConstructorFailed(); this->SetVolume_1( UniformVolume::SmartPtr( this->m_PreprocessorRef.GetProcessedImage( volume ) ) ); volume = UniformVolume::SmartPtr( VolumeIO::ReadOriented( this->Study2 ) ); if ( !volume ) throw ConstructorFailed(); this->SetVolume_2( UniformVolume::SmartPtr( this->m_PreprocessorFlt.GetProcessedImage( volume ) ) ); AffineXform::SmartPtr affineXform( AffineXform::SmartPtr::DynamicCastFrom( this->m_InitialTransformation ) ); if ( affineXform ) { if ( affineXform->MetaKeyExists( META_SPACE ) && (affineXform->GetMetaInfo( META_SPACE ) != AnatomicalOrientation::ORIENTATION_STANDARD ) ) { try { TransformChangeFromSpaceAffine toStandardSpace( *affineXform, *(this->m_Volume_1), *(this->m_Volume_2), affineXform->GetMetaInfo(META_SPACE).c_str() ); *affineXform = toStandardSpace.GetTransformation(); affineXform->SetMetaInfo( META_SPACE, AnatomicalOrientation::ORIENTATION_STANDARD ); this->SetInitialTransformation( affineXform ); } catch ( const AffineXform::MatrixType::SingularMatrixException& ) { StdErr << "ERROR: singular matrix cannot be inverted to change transformation to standard space in ElasticRegistrationCommandLine constructor.\n"; throw ExitException( 1 ); } } } if ( !this->RigidityConstraintMapFilename.empty() ) { UniformVolume::SmartPtr rigidityWeightMap( VolumeIO::ReadOriented( this->RigidityConstraintMapFilename ) ); if ( rigidityWeightMap ) { this->SetRigidityConstraintMap( rigidityWeightMap ); } else { StdErr << "ERROR: rigidity constraint mapcould not be read from " << this->RigidityConstraintMapFilename << "\n"; throw ExitException( 1 ); } } if ( forceOutsideFlag ) { this->SetForceOutside( true, forceOutsideValue ); } if ( !Protocol.empty() ) { RegistrationCallback::SmartPtr callback( new ProtocolCallback( Protocol ) ); this->SetCallback( callback ); } } ElasticRegistrationCommandLine::~ElasticRegistrationCommandLine () { #ifdef HAVE_SIGRELSE // release signal handler sigrelse( SIGUSR1 ); #endif } CallbackResult ElasticRegistrationCommandLine ::InitRegistration () { CallbackResult result = this->Superclass::InitRegistration(); if ( result != CALLBACK_OK ) return result; if ( this->m_OutputIntermediate ) this->OutputIntermediate(); // register signal handler for intermediate result output. Self::StaticThis = this; #ifndef _MSC_VER signal( SIGUSR1, cmtkElasticRegistrationCommandLineDispatchSIGUSR1 ); #endif return CALLBACK_OK; } void ElasticRegistrationCommandLine ::OutputResult ( const CoordinateVector*, const CallbackResult irq ) { if ( ! this->Studylist.empty() ) { std::string path( this->Studylist ); if ( irq != CALLBACK_OK ) path.append( "-partial" ); this->OutputWarp( path.c_str() ); } if ( !this->m_OutputPathITK.empty() ) { if ( irq != CALLBACK_OK ) SplineWarpXformITKIO::Write( this->m_OutputPathITK + "-partial", *(this->GetTransformation()), *(this->m_ReferenceVolume), *(this->m_FloatingVolume) ); else SplineWarpXformITKIO::Write( this->m_OutputPathITK, *(this->GetTransformation()), *(this->m_ReferenceVolume), *(this->m_FloatingVolume) ); } if ( !this->m_ReformattedImagePath.empty() ) { if ( irq != CALLBACK_OK ) VolumeIO::Write( *(this->GetReformattedFloatingImage()), this->m_ReformattedImagePath + "-partial" ); else VolumeIO::Write( *(this->GetReformattedFloatingImage()), this->m_ReformattedImagePath ); } #ifdef CMTK_USE_SQLITE if ( (irq == CALLBACK_OK) && !this->m_UpdateDB.empty() ) { try { ImageXformDB db( this->m_UpdateDB ); if ( !this->m_ReformattedImagePath.empty() ) { db.AddImage( this->m_ReformattedImagePath, this->m_ReferenceVolume->GetMetaInfo( META_FS_PATH ) ); } if ( ! this->Studylist.empty() ) { if ( ! this->InputStudylist.empty() ) { db.AddRefinedXform( this->Studylist, true /*invertible*/, this->InputStudylist, this->ForceSwitchVolumes ); } else { db.AddImagePairXform( this->Studylist, true /*invertible*/, this->m_ReferenceVolume->GetMetaInfo( META_FS_PATH ), this->m_FloatingVolume->GetMetaInfo( META_FS_PATH ) ); } } } catch ( const ImageXformDB::Exception& ex ) { StdErr << "DB ERROR: " << ex.what() << " on database " << this->m_UpdateDB << "\n"; } } #endif } void ElasticRegistrationCommandLine ::EnterResolution ( CoordinateVector::SmartPtr& v, Functional::SmartPtr& f, const int index, const int total ) { DebugOutput( 1 ).GetStream().printf( "\rEntering resolution level %d out of %d...\n", index, total ); this->Superclass::EnterResolution( v, f, index, total ); } int ElasticRegistrationCommandLine::DoneResolution ( CoordinateVector::SmartPtr& v, Functional::SmartPtr& f, const int index, const int total ) { if ( this->m_OutputIntermediate ) this->OutputIntermediate(); return this->Superclass::DoneResolution( v, f, index, total ); } void ElasticRegistrationCommandLine::OutputWarp ( const std::string& path ) const { ClassStreamOutput classStream( path, "studylist", ClassStreamOutput::MODE_WRITE ); if ( ! classStream.IsValid() ) return; classStream.Begin( "studylist" ); classStream.WriteInt( "num_sources", 2 ); classStream.End(); classStream.Begin( "source" ); classStream.WriteString( "studyname", CompressedStream::GetBaseName( Study1 ) ); classStream.End(); classStream.Begin( "source" ); classStream.WriteString( "studyname", CompressedStream::GetBaseName( Study2 ) ); classStream.End(); classStream.Close(); classStream.Open( path, "settings", ClassStreamOutput::MODE_WRITE ); classStream.WriteInt( "algorithm", this->m_Algorithm ); classStream.WriteBool( "use_maxnorm", UseMaxNorm ); classStream.WriteDouble( "exploration", this->m_Exploration ); classStream.WriteDouble( "accuracy", this->m_Accuracy ); classStream.WriteDouble( "min_sampling", this->m_Sampling ); classStream.WriteDouble( "coarsest_resolution", CoarsestResolution ); classStream.WriteBool( "use_original_data", this->m_UseOriginalData ); classStream.WriteBool( "fast_mode", this->m_FastMode ); classStream.WriteInt( "metric", this->m_Metric ); classStream.WriteDouble( "optimizer_step_factor", OptimizerStepFactor ); classStream.WriteDouble( "grid_spacing", this->m_GridSpacing ); classStream.WriteInt( "ignore_edge", IgnoreEdge ); classStream.WriteDouble( "jacobian_constraint_weight", this->m_JacobianConstraintWeight ); classStream.WriteDouble( "rigidity_constraint_weight", this->m_RigidityConstraintWeight ); if ( !this->RigidityConstraintMapFilename.empty() ) { classStream.WriteString( "rigidity_constraint_map_filename", RigidityConstraintMapFilename ); } classStream.WriteDouble( "energy_constraint_weight", this->m_GridEnergyWeight ); classStream.WriteDouble( "inverse_consistency_weight", this->m_InverseConsistencyWeight ); classStream.WriteDouble( "weight_relaxation", this->m_RelaxWeight ); classStream.WriteBool( "force_switch", this->ForceSwitchVolumes ); classStream.WriteInt( "refine_grid", this->m_RefineGrid ); classStream.WriteBool( "delay_refine_grid", this->m_DelayRefineGrid ); classStream.WriteBool( "adaptive_fix_parameters", this->m_AdaptiveFixParameters ); classStream.WriteDouble( "adaptive_fix_parameters_thresh", this->m_AdaptiveFixThreshFactor ); this->m_PreprocessorRef.WriteSettings( classStream ); this->m_PreprocessorFlt.WriteSettings( classStream ); classStream.Close(); classStream.Open( path, "statistics", ClassStreamOutput::MODE_WRITE ); classStream.WriteDouble( "time_level", this->GetLevelElapsedTime() ); classStream.WriteDouble( "time_total", this->GetTotalElapsedTime() ); classStream.WriteDouble( "walltime_level", this->GetLevelElapsedWalltime() ); classStream.WriteDouble( "walltime_total", this->GetTotalElapsedWalltime() ); classStream.WriteDouble( "thread_time_level", this->GetThreadLevelElapsedTime() ); classStream.WriteDouble( "thread_time_total", this->GetThreadTotalElapsedTime() ); classStream.WriteInt( "number_of_threads", Threads::NumberOfThreads ); classStream.WriteInt( "number_of_cpus", Threads::GetNumberOfProcessors() ); #ifndef _MSC_VER struct utsname name; if ( uname( &name ) >= 0 ) { classStream.WriteString( "host", name.nodename ); classStream.WriteString( "system", name.sysname ); } #endif classStream.Close(); const WarpXform::SmartPtr warp = WarpXform::SmartPtr::DynamicCastFrom( this->m_Xform ); if ( warp ) { classStream.Open( path, "registration", ClassStreamOutput::MODE_WRITE_ZLIB ); if ( classStream.IsValid() ) { classStream.Begin( "registration" ); classStream.WriteString( "reference_study", CompressedStream::GetBaseName( Study1 ) ); classStream.WriteString( "floating_study", CompressedStream::GetBaseName( Study2 ) ); if ( warp->GetInitialAffineXform() ) { classStream << (*warp->GetInitialAffineXform()); } else { classStream << *this->m_InitialTransformation; } classStream << warp; classStream.End(); } classStream.Close(); } } void ElasticRegistrationCommandLine::OutputIntermediate( const bool incrementCount ) { char path[PATH_MAX]; if ( ! this->Studylist.empty() ) { snprintf( path, sizeof( path ), "%s%clevel-%02d.list", Studylist.c_str(), (int)CMTK_PATH_SEPARATOR, IntermediateResultIndex ); } else { snprintf( path, sizeof( path ), "level-%02d.list", IntermediateResultIndex ); } this->OutputWarp( path ); if ( incrementCount ) ++IntermediateResultIndex; } CallbackResult ElasticRegistrationCommandLine::Register () { const double baselineTime = Timers::GetTimeProcess(); CallbackResult Result = this->Superclass::Register(); const int elapsed = static_cast( Timers::GetTimeProcess() - baselineTime ); if ( !this->Time.empty() ) { FILE *tfp = fopen( this->Time.c_str(), "w" ); if ( tfp ) { fprintf( tfp, "%d\n", elapsed ); fclose( tfp ); } else { std::cerr << "Could not open time file " << Time << "\n"; } } return Result; } } // namespace cmtk void cmtkElasticRegistrationCommandLineDispatchSIGUSR1( int sig ) { fprintf( stderr, "Received USR1 (%d) signal. Writing intermediate result #%d.\nNote that this result is not final.\n", sig, cmtk::ElasticRegistrationCommandLine::StaticThis->IntermediateResultIndex ); #ifndef _MSC_VER // set signal handler again. signal( sig, cmtkElasticRegistrationCommandLineDispatchSIGUSR1 ); #endif // write intermediate result. give "false" flag for index increment to // preserve to final numbering of levels. cmtk::ElasticRegistrationCommandLine::StaticThis->OutputIntermediate( true /* Increment count*/ ); } cmtk-3.3.1/libs/Registration/cmtkElasticRegistrationCommandLine.h000066400000000000000000000116221276303427400252070ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2011, 2013 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4835 $ // // $LastChangedDate: 2013-09-12 15:48:47 -0700 (Thu, 12 Sep 2013) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkElasticRegistrationCommandLine_h_included_ #define __cmtkElasticRegistrationCommandLine_h_included_ #include #include namespace cmtk { /** \addtogroup Registration */ //@{ /** Class for command line controlled voxel registration. *\author T. Rohlfing *\version $Revision: 4835 $ $Date: 2013-09-12 15:48:47 -0700 (Thu, 12 Sep 2013) $ */ class ElasticRegistrationCommandLine : /// Inherit generic elastic registration. public ElasticRegistration { public: /// This class. typedef ElasticRegistrationCommandLine Self; /// Smart pointer. typedef SmartPointer SmartPtr; /// Parent class. typedef ElasticRegistration Superclass; /** Constructor. *\param argc Number of command line arguments; this should be the argc * parameter of the main() function. *\param argv Array of command line arguments; this should be the argv * parameter of the main() function. */ ElasticRegistrationCommandLine ( const int argc, const char *argv[] ); /// Destructor. ~ElasticRegistrationCommandLine (); /** Perform registration. */ virtual CallbackResult Register (); protected: /** Initialize registration. * So far, this function has no effect other than calling the equivalent * inherited function. */ virtual CallbackResult InitRegistration(); /** Output registration result. */ virtual void OutputResult ( const CoordinateVector* v, const CallbackResult irq = CALLBACK_OK ); /** Enter resolution level. * An information is printed to stderr and to the protocol file if one is * written. */ virtual void EnterResolution( CoordinateVector::SmartPtr&, Functional::SmartPtr&, const int, const int ); /** Leave resolution level. * The transformation found so far is written to a file if desired. */ virtual int DoneResolution( CoordinateVector::SmartPtr&, Functional::SmartPtr&, const int, const int ); private: /** Name of input studylist. * This is defined by the --inlist command line parameter (not currently * supported). */ std::string InputStudylist; /** Name of output studylist. * This is defined by the -o or --outlist command line option. */ std::string Studylist; /** Name of first study to be registered. * This is given as the first non-option command line paramter. */ std::string Study1; /** Name of second study to be registered. * This is given as the second non-option command line paramter. */ std::string Study2; #ifdef CMTK_USE_SQLITE /// Database to update after registration completes. std::string m_UpdateDB; #endif /// Filename for rigidity constraint map. std::string RigidityConstraintMapFilename; /** Name of protocol output file. * This is defined by the -p or --protocol command line option. */ std::string Protocol; /** Name of elapsed time output file. * This is defined by the -t or --time command line option. */ std::string Time; /** Select whether too create intermediate warp output files (level-xx.list). */ bool m_OutputIntermediate; /// Write deformation to studylist archive. void OutputWarp ( const std::string& ) const; /// Name of output transformation file in ITK format. std::string m_OutputPathITK; /// Path for reformatted floating image. std::string m_ReformattedImagePath; public: /// Static pointer to this object. static Self* StaticThis; /// Counter for intermediate result files. int IntermediateResultIndex; /// Write intermediate deformation file. void OutputIntermediate( const bool incrementCount = true ); }; //@} } // namespace cmtk /// Signal handler that writes intermediate result during a level. extern "C" void cmtkElasticRegistrationCommandLineDispatchSIGUSR1( int sig ); #endif // #ifndef __cmtkElasticRegistrationCommandLine_h_included_ cmtk-3.3.1/libs/Registration/cmtkGroupwiseRegistrationFunctionalAffineInitializer.cxx000066400000000000000000000102411276303427400314070ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2012 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4427 $ // // $LastChangedDate: 2012-06-12 11:23:22 -0700 (Tue, 12 Jun 2012) $ // // $LastChangedBy: torstenrohlfing $ // */ #include #include namespace cmtk { /** \addtogroup Registration */ //@{ void GroupwiseRegistrationFunctionalAffineInitializer::InitializeXforms ( GroupwiseRegistrationFunctionalBase& functional, const bool alignCenters, const bool alignCenterOfMass, const bool initScales, const bool centerInTemplateFOV ) { const size_t numberOfImages = functional.m_ImageVector.size(); const Vector3D centerTemplate = functional.m_TemplateGrid->GetCenterCropRegion(); std::vector centers( numberOfImages ); std::vector firstOrderMoments; if ( initScales ) firstOrderMoments.resize( numberOfImages ); functional.m_XformVector.resize( numberOfImages ); Vector3D centerAverage; // first get all image centers (either FOV or center of mass) for ( size_t imageIdx = 0; imageIdx < numberOfImages; ++imageIdx ) { if ( alignCenters ) { if ( alignCenterOfMass ) { if ( initScales ) { centers[imageIdx] = functional.m_ImageVector[imageIdx]->GetCenterOfMass( firstOrderMoments[imageIdx] ); } else { centers[imageIdx] = functional.m_ImageVector[imageIdx]->GetCenterOfMass(); } } else { centers[imageIdx] = functional.m_ImageVector[imageIdx]->GetCenter(); } } } if ( centerInTemplateFOV ) { // if so desired, center in template image FOV centerAverage = centerTemplate; } else { // if not aligning with template FOV, compute centrioid of input image centers. std::fill( centerAverage.begin(), centerAverage.end(), 0 ); for ( size_t imageIdx = 0; imageIdx < numberOfImages; ++imageIdx ) { centerAverage += centers[imageIdx]; } // compute average of all image centers centerAverage *= (1.0 / numberOfImages); } // now make sure every image gests shifted so their center align, and the overall shift is zero for ( size_t imageIdx = 0; imageIdx < numberOfImages; ++imageIdx ) { AffineXform::SmartPtr xform( new AffineXform ); xform->SetUseLogScaleFactors( true ); xform->SetCenter( centerTemplate.begin() ); const Vector3D delta( centers[imageIdx] - centerAverage ); xform->SetXlate( delta.begin() ); functional.m_XformVector[imageIdx] = xform; } // convert first order moments to scale with average log factor 0 if ( initScales ) { UniformVolume::CoordinateVectorType avgScales( 0.0 ); UniformVolume::CoordinateVectorType fom0( firstOrderMoments[0] ); for ( size_t imageIdx = 0; imageIdx < numberOfImages; ++imageIdx ) { for ( int dim = 0; dim < 3; ++dim ) firstOrderMoments[imageIdx][dim] = log( firstOrderMoments[imageIdx][dim] / fom0[dim] ); avgScales += firstOrderMoments[imageIdx]; } avgScales *= ( 1.0 / numberOfImages ); for ( size_t imageIdx = 0; imageIdx < numberOfImages; ++imageIdx ) { firstOrderMoments[imageIdx] -= avgScales; AffineXform::SmartPtr::DynamicCastFrom( functional.m_XformVector[imageIdx] )->SetScales( firstOrderMoments[imageIdx].begin() ); } } } } // namespace cmtk cmtk-3.3.1/libs/Registration/cmtkGroupwiseRegistrationFunctionalAffineInitializer.h000066400000000000000000000051471276303427400310450ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2012 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4344 $ // // $LastChangedDate: 2012-05-11 14:51:08 -0700 (Fri, 11 May 2012) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkGroupwiseRegistrationFunctionalAffineInitializer_h_included_ #define __cmtkGroupwiseRegistrationFunctionalAffineInitializer_h_included_ #include #include #include #include #include #include #include namespace cmtk { /** \addtogroup Registration */ //@{ /** Affine initialization of groupwise registration functionals. */ class GroupwiseRegistrationFunctionalAffineInitializer { public: /// Type of this class. typedef GroupwiseRegistrationFunctionalAffineInitializer Self; /// Smart pointer. typedef SmartPointer SmartPtr; /** Initialize affine transformations. */ static void InitializeXforms( GroupwiseRegistrationFunctionalBase& functional /*!. // // $Revision: 4882 $ // // $LastChangedDate: 2013-09-27 15:16:36 -0700 (Fri, 27 Sep 2013) $ // // $LastChangedBy: torstenrohlfing $ // */ #include #include #include #include #include #include #include #include #include #include namespace cmtk { /** \addtogroup Registration */ //@{ GroupwiseRegistrationFunctionalBase ::GroupwiseRegistrationFunctionalBase() : m_FreeAndRereadImages( false ), m_ForceZeroSum( false ), m_ForceZeroSumFirstN( 0 ), m_ActiveImagesFrom( 0 ), m_ActiveImagesTo( 0 ), m_ActiveXformsFrom( 0 ), m_ActiveXformsTo( 0 ), m_TemplateNumberOfPixels( 0 ), m_TemplateNumberOfSamples( 0 ), m_UseTemplateData( false ), m_ProbabilisticSampleDensity( -1.0 ), m_ProbabilisticSampleUpdatesAfter( 1000000 ), m_ProbabilisticSampleUpdatesSince( 0 ), m_GaussianSmoothImagesSigma( -1.0 ), m_UserBackgroundValue( 0 ), m_UserBackgroundFlag( false ), m_ParametersPerXform( 0 ), m_RepeatIntensityHistogramMatching( false ) { this->m_NumberOfThreads = ThreadPool::GetGlobalThreadPool().GetNumberOfThreads(); this->m_NumberOfTasks = 4 * this->m_NumberOfThreads - 3; this->m_Data.clear(); } GroupwiseRegistrationFunctionalBase::~GroupwiseRegistrationFunctionalBase() { if ( this->m_Data.size() ) { const size_t numberOfImages = this->m_ImageVector.size(); for ( size_t i = 0; i < numberOfImages; ++i ) { if ( this->m_Data[i] ) Memory::ArrayC::Delete( this->m_Data[i] ); } } } void GroupwiseRegistrationFunctionalBase::CreateTemplateGridFromTargets ( const std::vector& targets, const int downsample ) { Types::Coordinate templateSize[3] = {0,0,0}; UniformVolume::IndexType templateDims; Types::Coordinate templateDelta = 1e10; for ( size_t i = 0; i < targets.size(); ++i ) { for ( int dim = 0; dim < 3; ++dim ) { templateSize[dim] = std::max( templateSize[dim], targets[i]->m_Size[dim] ); } templateDelta = std::min( templateDelta, targets[i]->GetMinDelta() ); } for ( int dim = 0; dim < 3; ++dim ) { templateDims[dim] = 1 + static_cast( templateSize[dim] / templateDelta ); templateSize[dim] = (templateDims[dim]-1) * templateDelta; } UniformVolume::SmartPtr templateGrid( new UniformVolume( templateDims, FixedVector<3,Types::Coordinate>::FromPointer( templateSize ) ) ); this->SetTemplateGrid( templateGrid, downsample ); } void GroupwiseRegistrationFunctionalBase::CreateTemplateGrid ( const DataGrid::IndexType& dims, const UniformVolume::CoordinateVectorType& deltas ) { UniformVolume::SmartPtr templateGrid( new UniformVolume( dims, deltas ) ); this->SetTemplateGrid( templateGrid ); } void GroupwiseRegistrationFunctionalBase::SetTemplateGrid ( UniformVolume::SmartPtr& templateGrid, const int downsample, const bool useTemplateData ) { this->m_TemplateGrid = templateGrid->Clone(); this->m_UseTemplateData = useTemplateData; if ( this->m_UseTemplateData && ! this->m_TemplateGrid->GetData() ) { UniformVolume::SmartPtr readImage( VolumeIO::ReadOriented( templateGrid->GetMetaInfo(META_FS_PATH) ) ); this->m_TemplateGrid->SetData( readImage->GetData() ); } if ( ! this->m_TemplateGrid->MetaKeyExists( META_IMAGE_ORIENTATION ) ) { this->m_TemplateGrid->SetMetaInfo( META_IMAGE_ORIENTATION, AnatomicalOrientation::ORIENTATION_STANDARD ); } if ( ! this->m_TemplateGrid->MetaKeyExists( META_IMAGE_ORIENTATION_ORIGINAL ) ) { this->m_TemplateGrid->SetMetaInfo( META_IMAGE_ORIENTATION_ORIGINAL, AnatomicalOrientation::ORIENTATION_STANDARD ); } if ( ! this->m_TemplateGrid->MetaKeyExists( META_SPACE ) ) { this->m_TemplateGrid->SetMetaInfo( META_SPACE, AnatomicalOrientation::ORIENTATION_STANDARD ); } if ( ! this->m_TemplateGrid->MetaKeyExists( META_SPACE_ORIGINAL ) ) { this->m_TemplateGrid->SetMetaInfo( META_SPACE_ORIGINAL, AnatomicalOrientation::ORIENTATION_STANDARD ); } if ( this->m_UseTemplateData ) { this->m_TemplateGrid = UniformVolume::SmartPtr( this->PrepareSingleImage( this->m_TemplateGrid ) ); } if ( downsample > 1 ) { this->m_TemplateGrid = UniformVolume::SmartPtr( this->m_TemplateGrid->GetDownsampledAndAveraged( downsample, true /*approxIsotropic*/ ) ); } this->m_TemplateNumberOfPixels = this->m_TemplateGrid->GetNumberOfPixels(); if ( this->m_UseTemplateData ) { this->CopyTemplateData(); } this->PrepareTargetImages(); } void GroupwiseRegistrationFunctionalBase ::AllocateStorage() { if ( !this->m_TemplateGrid ) { StdErr << "FATAL: must set template grid for groupwise registration before allocating storage\n"; exit( 1 ); } const size_t numberOfImages = this->m_OriginalImageVector.size(); if ( this->m_TemplateNumberOfPixels ) { if ( (this->m_ProbabilisticSampleDensity > 0) && (this->m_ProbabilisticSampleDensity < 1) ) this->m_TemplateNumberOfSamples = static_cast( this->m_ProbabilisticSampleDensity * this->m_TemplateNumberOfPixels); else this->m_TemplateNumberOfSamples = this->m_TemplateNumberOfPixels; if ( this->m_Data.size() ) { for ( size_t i = 0; i < numberOfImages; ++i ) { if ( this->m_Data[i] ) Memory::ArrayC::Delete( this->m_Data[i] ); } } this->m_Data.resize( numberOfImages ); for ( size_t i = 0; i < numberOfImages; ++i ) { this->m_Data[i] = Memory::ArrayC::Allocate( this->m_TemplateNumberOfSamples ); } this->m_TempData.resize( this->m_TemplateNumberOfSamples ); } } void GroupwiseRegistrationFunctionalBase ::SetTargetImages ( std::vector& tImages ) { this->m_OriginalImageVector = tImages; this->m_ActiveImagesFrom = 0; this->m_ActiveImagesTo = tImages.size(); this->m_ActiveXformsFrom = 0; this->m_ActiveXformsTo = tImages.size(); this->m_ProbabilisticSampleUpdatesSince = 0; } UniformVolume::SmartPtr GroupwiseRegistrationFunctionalBase ::PrepareSingleImage( UniformVolume::SmartPtr& image ) { if ( !image->GetData() ) { UniformVolume::SmartPtr readImage( VolumeIO::ReadOriented( image->GetMetaInfo( META_FS_PATH ) ) ); image->SetData( readImage->GetData() ); } TypedArray::SmartPtr data; if ( this->m_GaussianSmoothImagesSigma > 0 ) { data = UniformVolumeGaussianFilter( image ).GetFiltered3D( Units::GaussianSigma( this->m_GaussianSmoothImagesSigma * this->m_TemplateGrid->GetMinDelta() ) ); if ( this->m_FreeAndRereadImages ) { image->SetData( TypedArray::SmartPtr::Null() ); } } else { if ( this->m_FreeAndRereadImages ) { data = image->GetData(); image->SetData( TypedArray::SmartPtr::Null() ); } else { data = image->GetData()->Clone(); } } UniformVolume::SmartPtr newTargetImage = image->CloneGrid(); newTargetImage->SetData( data ); return newTargetImage; } void GroupwiseRegistrationFunctionalBase ::PrepareTargetImages() { this->m_ImageVector.resize( this->m_OriginalImageVector.size() ); for ( size_t i = 0; i < this->m_OriginalImageVector.size(); ++i ) { this->m_ImageVector[i] = this->PrepareSingleImage( this->m_OriginalImageVector[i] ); } } void GroupwiseRegistrationFunctionalBase::GetParamVector( CoordinateVector& v ) { v.SetDim( this->ParamVectorDim() ); for ( size_t idx = 0; idx < this->m_XformVector.size(); ++idx ) { this->m_XformVector[idx]->GetParamVector( v, idx * this->m_ParametersPerXform ); } } void GroupwiseRegistrationFunctionalBase::SetParamVector( CoordinateVector& v ) { size_t offset = 0; for ( size_t xIdx = 0; xIdx < this->m_XformVector.size(); ++xIdx ) { CoordinateVector vv( this->m_ParametersPerXform, v.Elements + offset, false /*free*/ ); offset += this->m_ParametersPerXform; this->m_XformVector[xIdx]->SetParamVector( vv ); } } void GroupwiseRegistrationFunctionalBase::SetParamVector ( CoordinateVector& v, const size_t xformIdx ) { const size_t offset = this->m_ParametersPerXform * xformIdx; CoordinateVector vv( this->m_ParametersPerXform, v.Elements + offset, false /*free*/ ); this->m_XformVector[xformIdx]->SetParamVector( vv ); } void GroupwiseRegistrationFunctionalBase ::SetParameter( const size_t param, const Types::Coordinate value ) { this->m_XformVector[param / this->m_ParametersPerXform]->SetParameter( param % this->m_ParametersPerXform, value ); } void GroupwiseRegistrationFunctionalBase ::SetParameter( const size_t xform, const size_t param, const Types::Coordinate value ) { this->m_XformVector[xform]->SetParameter( param, value ); } GroupwiseRegistrationFunctionalBase::ReturnType GroupwiseRegistrationFunctionalBase::EvaluateAt( CoordinateVector& v ) { if ( (this->m_ProbabilisticSampleDensity > 0) && (this->m_ProbabilisticSampleDensity < 1) ) { if ( !this->m_ProbabilisticSampleUpdatesSince ) this->UpdateProbabilisticSamples(); (++this->m_ProbabilisticSampleUpdatesSince) %= this->m_ProbabilisticSampleUpdatesAfter; } this->SetParamVector( v ); this->InterpolateAllImages(); return this->Evaluate(); } GroupwiseRegistrationFunctionalBase::ReturnType GroupwiseRegistrationFunctionalBase::EvaluateWithGradient ( CoordinateVector& v, CoordinateVector& g, const Types::Coordinate step ) { const Self::ReturnType baseValue = this->EvaluateAt( v ); for ( size_t param = 0; param < this->ParamVectorDim(); ++param ) { g[param] = 0.0; const size_t imageIndex = param / this->m_ParametersPerXform; const size_t paramIndex = param % this->m_ParametersPerXform; const Types::Coordinate pStep = this->GetParamStep( param, step ); if ( pStep > 0 ) { byte* tmp = this->m_Data[imageIndex]; this->m_Data[imageIndex] = &(this->m_TempData[0]); const Types::Coordinate p0 = v[param]; this->SetParameter( imageIndex, paramIndex, p0 + pStep ); this->InterpolateImage( imageIndex, this->m_Data[imageIndex] ); const Self::ReturnType upper = this->Evaluate(); this->SetParameter( imageIndex, paramIndex, p0 - pStep ); this->InterpolateImage( imageIndex, this->m_Data[imageIndex] ); const Self::ReturnType lower = this->Evaluate(); this->m_Data[imageIndex] = tmp; this->SetParameter( imageIndex, paramIndex, p0 ); if ( (upper > baseValue) || (lower > baseValue) ) { g[param] = (upper - lower); } } } if ( this->m_ForceZeroSum ) { this->ForceZeroSumGradient( g ); } return baseValue; } void GroupwiseRegistrationFunctionalBase::UpdateProbabilisticSamples() { this->m_ProbabilisticSamples.resize( this->m_TemplateNumberOfSamples ); const size_t firstSample = 0; const size_t lastSample = this->m_TemplateNumberOfSamples; for ( size_t i = firstSample; i < lastSample; ++i ) { const size_t sample = static_cast( this->m_TemplateNumberOfPixels * MathUtil::UniformRandom() ); this->m_ProbabilisticSamples[i] = sample; } } void GroupwiseRegistrationFunctionalBase ::InterpolateAllImages() { for ( size_t idx = this->m_ActiveImagesFrom; idx < this->m_ActiveImagesTo; ++idx ) { this->InterpolateImage( idx, this->m_Data[idx] ); } } void GroupwiseRegistrationFunctionalBase ::ForceZeroSumGradient( CoordinateVector& g ) const { const size_t numberOfXforms = this->m_XformVector.size(); const size_t zeroSumFirstN = this->m_ForceZeroSumFirstN ? this->m_ForceZeroSumFirstN : numberOfXforms; #pragma omp parallel for for ( int param = 0; param < static_cast( this->m_ParametersPerXform ); ++param ) { Types::Coordinate avg = 0; size_t pparam = param; for ( size_t idx = 0; idx < zeroSumFirstN; ++idx, pparam += this->m_ParametersPerXform ) { avg += g[pparam]; } avg *= 1.0 / zeroSumFirstN; pparam = param; for ( size_t idx = 0; idx < numberOfXforms; ++idx, pparam += this->m_ParametersPerXform ) { g[pparam] -= avg; } } // if the remaining vector is smaller than threshold, throw it out. const Types::Coordinate gMax = g.MaxNorm(); if ( gMax < 1e-3 ) { g.Clear(); } } bool GroupwiseRegistrationFunctionalBase ::Wiggle() { bool wiggle = false; if ( (this->m_ProbabilisticSampleDensity > 0) && (this->m_ProbabilisticSampleDensity < 1) ) { this->m_ProbabilisticSampleUpdatesSince = 0; wiggle = true; } if ( this->m_RepeatIntensityHistogramMatching ) { TypedArray::SmartPtr referenceData = this->m_TemplateGrid->GetData(); if ( !this->m_UseTemplateData ) referenceData = TypedArray::SmartPtr::Null(); for ( size_t i = 0; i < this->m_OriginalImageVector.size(); ++i ) { UniformVolume::SmartPtr scaledImage; if ( this->m_OriginalImageVector[i]->GetData() ) { scaledImage = UniformVolume::SmartPtr( this->m_OriginalImageVector[i]->Clone( true /*copyData*/ ) ); } else { scaledImage = UniformVolume::SmartPtr( VolumeIO::ReadOriented( this->m_OriginalImageVector[i]->GetMetaInfo( META_FS_PATH ) ) ); } UniformVolume::SmartPtr reformatImage( this->GetReformattedImage( scaledImage, i ) ); if ( referenceData ) { scaledImage->GetData()->ApplyFunctionObject( TypedArrayFunctionHistogramMatching( *(reformatImage->GetData()), *referenceData ) ); } else { referenceData = reformatImage->GetData(); } this->m_ImageVector[i] = UniformVolume::SmartPtr( this->PrepareSingleImage( scaledImage ) ); } this->InterpolateAllImages(); wiggle = true; } return wiggle; } void GroupwiseRegistrationFunctionalBase ::CopyTemplateData() { const TypedArray* dataArray = this->m_TemplateGrid->GetData(); if ( dataArray ) { const size_t size = dataArray->GetDataSize(); this->m_TemplateData.resize( size ); for ( size_t i = 0; i < size; ++i ) { Types::DataItem value; if ( dataArray->Get( value, i ) ) this->m_TemplateData[i] = static_cast( value ); else this->m_TemplateData[i] = this->m_PaddingValue; } } } void GroupwiseRegistrationFunctionalBase ::DebugWriteImages() { this->InterpolateAllImages(); UniformVolume::SmartPtr writeVolume( this->m_TemplateGrid->CloneGrid() ); writeVolume->CreateDataArray( TYPE_BYTE ); for ( size_t i = 0; i < this->m_TemplateNumberOfPixels; ++i ) { writeVolume->SetDataAt( this->m_TemplateData[i], i ); } VolumeIO::Write( *writeVolume, "template.nii" ); for ( size_t n = 0; n < this->m_ImageVector.size(); ++n ) { for ( size_t i = 0; i < this->m_TemplateNumberOfPixels; ++i ) { writeVolume->SetDataAt( this->m_Data[n][i], i ); } char path[PATH_MAX]; sprintf( path, "target%02d.nii", static_cast( n ) ); VolumeIO::Write( *writeVolume, path ); } } const UniformVolume::SmartPtr GroupwiseRegistrationFunctionalBase ::GetReformattedImage( const UniformVolume::SmartPtr& targetGrid, const size_t idx ) const { ReformatVolume reformat; reformat.SetInterpolation( Interpolators::LINEAR ); reformat.SetReferenceVolume( targetGrid ); reformat.SetFloatingVolume( this->m_OriginalImageVector[idx] ); reformat.SetWarpXform( WarpXform::SmartPtr::DynamicCastFrom( this->m_XformVector[idx] ) ); reformat.SetAffineXform( AffineXform::SmartPtr::DynamicCastFrom( this->m_XformVector[idx] ) ); if ( this->m_UserBackgroundFlag ) { reformat.SetPaddingValue( this->m_UserBackgroundValue ); } UniformVolume::SmartPtr result = reformat.PlainReformat(); if ( this->m_UserBackgroundFlag ) { result->GetData()->ClearPaddingFlag(); } return result; } } // namespace cmtk cmtk-3.3.1/libs/Registration/cmtkGroupwiseRegistrationFunctionalBase.h000066400000000000000000000412521276303427400263200ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2012 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4381 $ // // $LastChangedDate: 2012-05-30 14:18:15 -0700 (Wed, 30 May 2012) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkGroupwiseRegistrationFunctionalBase_h_included_ #define __cmtkGroupwiseRegistrationFunctionalBase_h_included_ #include #include #include #include #include #include #include namespace cmtk { /** \addtogroup Registration */ //@{ /** Base class for groupwise registration functionals. * This class provides the lowest-level components of groupwise image registration, such as * image IO, preprocessing, generic access to transformation and image vectors, and parameters * common to all algorithms such as zero-sum optimization. */ class GroupwiseRegistrationFunctionalBase : /** Inherit from abstract functional base class. */ public Functional { public: /// Type of parent class. typedef Functional Superclass; /// Type of this class. typedef GroupwiseRegistrationFunctionalBase Self; /// Smart pointer. typedef SmartPointer SmartPtr; /// Exception for "bad transformation". class BadXform {}; /// Constructor. GroupwiseRegistrationFunctionalBase(); /// Destructor. virtual ~GroupwiseRegistrationFunctionalBase(); /** Set flag for freeing and rereading images. * If registration uses smoothed images, the original data can be freed after smoothing * and reread from the file system if needed again. This saves roughly 1/2 of memory * allocation. */ virtual void SetFreeAndRereadImages( const bool flag = true ) { this->m_FreeAndRereadImages = flag; } /// Set flag for repeated histogram-based intensity matching. virtual void SetRepeatIntensityHistogramMatching( const bool flag = true ) { this->m_RepeatIntensityHistogramMatching = flag; if ( flag ) { this->SetFreeAndRereadImages( false ); } } /** Create template grid based on target images. * The template image is constructed so that is has the maximum number of * pixels of all targets in each dimension, and the minimum isotropic * pixel size. * *\param targets The vector of target images for this groupwise registration. *\param downsample Downsampling factor. The voxel size in the template * image is increased by this factor. */ virtual void CreateTemplateGridFromTargets( const std::vector& targets, const int downsample = 0 ); /** Create template grid based on geometry. */ virtual void CreateTemplateGrid( const DataGrid::IndexType& dims /*!< Template grid dimensions */, const UniformVolume::CoordinateVectorType& deltas /*!< Template grid deltas (i.e., pixel size). */ ); /** Set template grid. */ virtual void SetTemplateGrid( UniformVolume::SmartPtr& templateGrid /*!< The template grid that defines size and resolution for the implicit registration template. */, const int downsample = 1 /*!< Downsampling factor */, const bool useTemplateData = false /*!< Flag to use template data, not just grid */ ); /** Retrieve the template grid. */ virtual UniformVolume::SmartPtr& GetTemplateGrid() { return this->m_TemplateGrid; } /** Retrieve the template grid. */ virtual const UniformVolume* GetTemplateGrid() const { return this->m_TemplateGrid; } /** Set target images. *\param tImages Vector of all images to be registered. */ virtual void SetTargetImages( std::vector& tImages ); /** Get the original target images. */ virtual void GetOriginalTargetImages( std::vector& tImages ) { tImages = this->m_OriginalImageVector; } /** Get the original target images. */ virtual std::vector& GetOriginalTargetImages() { return this->m_OriginalImageVector; } /** Get number of target images. */ virtual size_t GetNumberOfTargetImages() const { return this->m_OriginalImageVector.size(); } /** Get a smart pointer to one original target image. */ virtual UniformVolume::SmartPtr GetOriginalTargetImage( const size_t imageIdx ) { return this->m_OriginalImageVector[imageIdx]; } /** Get a constant pointer to one original target image. */ virtual const UniformVolume* GetOriginalTargetImage( const size_t imageIdx ) const { return this->m_OriginalImageVector[imageIdx]; } /** Set Gaussian smoothing kernel width for target images. * Non-positive values turn off smoothing. */ virtual void SetGaussianSmoothImagesSigma( const Types::Coordinate gaussianSmoothImagesSigma ) { this->m_GaussianSmoothImagesSigma = gaussianSmoothImagesSigma; } /** Set user-defined image background value to be substituted outside the field of view. */ virtual void SetUserBackgroundValue( const byte value = 0 ) { this->m_UserBackgroundValue = value; this->m_UserBackgroundFlag = true; } /** Unset user-defined image background value. */ virtual void UnsetUserBackgroundValue() { this->m_UserBackgroundFlag = false; } /// Set flag for zero-sum updates. virtual void SetForceZeroSum( const bool forceZeroSum = true ) { this->m_ForceZeroSum = forceZeroSum; } /// Set count for restricted zero-sum updates. virtual void SetForceZeroSumFirstN( const size_t forceZeroSumFirstN ) { this->m_ForceZeroSumFirstN = forceZeroSumFirstN; this->m_ForceZeroSum = this->m_ForceZeroSum || (this->m_ForceZeroSumFirstN>0); } /** Set range of currently active images. * The "to" parameter is the index of the last active image plus one, so * it can be used directly as the upper bound in a "for" loop. */ virtual void SetActiveImagesFromTo( const size_t from, const size_t to ) { this->m_ActiveImagesFrom = from; this->m_ActiveImagesTo = to; } /** Set range of currently active transformations. * The "to" parameter is the index of the last active transformation plus one, so * it can be used directly as the upper bound in a "for" loop. */ virtual void SetActiveXformsFromTo( const size_t from, const size_t to ) { this->m_ActiveXformsFrom = from; this->m_ActiveXformsTo = to; } /** Set probabilistic sampling density. */ virtual void SetProbabilisticSampleDensity( const float density ) { this->m_ProbabilisticSampleDensity = density; } /** Set number of iterations after which probabilistic samples are updated. */ virtual void SetProbabilisticSampleUpdatesAfter( const int iterations ) { this->m_ProbabilisticSampleUpdatesAfter = iterations; this->m_ProbabilisticSampleUpdatesSince = 0; } /** Set transformations. */ void SetXforms( const std::vector& xformVector ) { this->m_XformVector.resize( xformVector.size() ); for ( size_t i = 0; i < this->m_XformVector.size(); ++i ) { this->m_XformVector[i] = xformVector[i]; } } /** Get coordinate transformation for one image in the group. *\param idx Index of the volume/transformation. *\return Transformation for the selected volume. */ virtual const Xform* GetGenericXformByIndex( const size_t idx ) const { return this->m_XformVector[idx]; } /** Get coordinate transformation for one image in the group. *\param idx Index of the volume/transformation. *\return Transformation for the selected volume. */ virtual Xform::SmartPtr GetGenericXformByIndex( const size_t idx ) { return this->m_XformVector[idx]; } /** Get coordinate transformation for one active image in the group. *\param idx Index of the volume/transformation. *\return Transformation for the selected volume. */ virtual const Xform* GetGenericActiveXformByIndex( const size_t idx ) const { return this->m_XformVector[idx + this->m_ActiveXformsFrom]; } /** Get coordinate transformation for one active image in the group. *\param idx Index of the volume/transformation. *\return Transformation for the selected volume. */ virtual Xform::SmartPtr GetGenericActiveXformByIndex( const size_t idx ) { return this->m_XformVector[idx + this->m_ActiveXformsFrom]; } /** Get parameter stepping in milimeters. *\param idx Parameter index. *\param mmStep Desired step length. This is typically used as a scalar factor for the default (1mm) step size. *\return Step of given parameter that corresponds to 1 mm effective motion. */ virtual Types::Coordinate GetParamStep( const size_t idx, const Types::Coordinate mmStep = 1 ) const { const size_t xformIdx = idx / this->m_ParametersPerXform; if ( (xformIdx >= this->m_ActiveXformsFrom) && (xformIdx < this->m_ActiveXformsTo) ) { return this->m_XformVector[xformIdx]->GetParamStep( idx % this->m_ParametersPerXform, this->m_ImageVector[xformIdx]->m_Size, mmStep ); } else { return 0.0; } } /** Return the functional's parameter vector dimension. * We assume that all transformations have the same number of parameters. * This is true for affine transformations. */ virtual size_t ParamVectorDim() const { return this->m_ParametersPerXform * this->m_XformVector.size(); } /** Return the number of variable parameters of the transformation. *\return This function returns the same value as ParamVectorDim(). * Non-varying parameters (e.g., rotation centers) are handled via * parameter step values. */ virtual size_t VariableParamVectorDim() const { return this->ParamVectorDim(); } /** Get parameter vector. */ virtual void GetParamVector( CoordinateVector& v ); /** Set parameter vector. */ virtual void SetParamVector( CoordinateVector& v ); /** Set parameter vector for a given transformation. */ virtual void SetParamVector( CoordinateVector& v, const size_t xformIdx ); /** Set single parameter value. */ virtual void SetParameter( const size_t param, const Types::Coordinate value ); /** Set single parameter value with separate xform and parameter index. */ virtual void SetParameter( const size_t xform, const size_t param, const Types::Coordinate value ); /** Evaluate functional with given parameter vector. * This function sets the current parameter vector, reformats all image data * into template space according to the current transformations, and calls * Evaluate() to compute the entropy-based functional value. *\param v Parameter vector. *\return Const function value for given parameters. */ virtual Self::ReturnType EvaluateAt ( CoordinateVector& v ); /** Compute functional value and gradient. *\param v Parameter vector. *\param g The extimated gradient of the functional is stored in this vector. *\param step Step size for finite difference gradient approximation. Default * is 1 mm. *\return Const function value for given parameters. */ virtual Self::ReturnType EvaluateWithGradient( CoordinateVector& v, CoordinateVector& g, const Types::Coordinate step = 1 ); /** Allocate storage for reformatted images etc. * This function must be called AFTER setting template grid and target images, but BEFORE * any calls to Evaluate, EvaluateAt, or EvaluateWithGradient. */ virtual void AllocateStorage(); /// Write all images for debug purposes. void DebugWriteImages(); protected: /// Number of threads in thread pool (for allocation of temporary thread memory). size_t m_NumberOfThreads; /// Number of tasks for thread pool (for allocation of task arguments and results). size_t m_NumberOfTasks; /// Flag for freeing and re-reading original images if using smoothed data. bool m_FreeAndRereadImages; /// Flag for enforcing zero-sum parameter changes. bool m_ForceZeroSum; /// Restrict zero-sum computation to first N images. size_t m_ForceZeroSumFirstN; /// Currently active images from index. size_t m_ActiveImagesFrom; /// Currently active images to index (plus 1). size_t m_ActiveImagesTo; /// Currently active transformations from index. size_t m_ActiveXformsFrom; /// Currently active transformations to index (plus 1). size_t m_ActiveXformsTo; /// Enforce gradient to be zero-sum over all images. virtual void ForceZeroSumGradient( CoordinateVector& g ) const; /// Number of pixels in template. size_t m_TemplateNumberOfPixels; /// Number of samples drawn from the pixels in template. size_t m_TemplateNumberOfSamples; /// Template grid (not pixel data). UniformVolume::SmartPtr m_TemplateGrid; /// Flag for use of template pixel data in registration. bool m_UseTemplateData; /// Prepared (smoothed, scaled etc.) data of the template image if used in registration. std::vector m_TemplateData; /// Vector of image volumes with pre-scaled pixel values. std::vector m_ImageVector; /// Vector of original image volumes. std::vector m_OriginalImageVector; /// Vector of transformations std::vector m_XformVector; /// Probabilistic sample count. float m_ProbabilisticSampleDensity; /// Pixel indices of probabilistic samples. std::vector m_ProbabilisticSamples; /** Number of iterations (calls to Evaluate()) after which probabilistic * samples are updated. */ int m_ProbabilisticSampleUpdatesAfter; /** Current number of iterations since last update of probabilistic samples. */ int m_ProbabilisticSampleUpdatesSince; /** Update probablistic samples. * This function generates a new list of random pixel indices for sampling * the images. It is called every m_ProbabilisticSampleUpdatesAfter calls to * Evaluate(). */ virtual void UpdateProbabilisticSamples(); /** Interpolate all moving images. * By default, this only calls InterpolateImage() for each image. */ virtual void InterpolateAllImages(); /** Interpolate given moving image to template. *\param idx Index of of to reformat to template. This also determines which * transformation is used. *\param destination The reformatted pixel data is stored in this array. * Sufficient memory (for as many pixels as there are in the template grid) * must be allocated there. */ virtual void InterpolateImage( const size_t idx, byte* const destination ) { UNUSED(idx); UNUSED(destination); } // cannot make this pure virtual because we need to instantiate for affine initialization /// Vector of reformatted and rescaled image data. std::vector m_Data; /// Temporary data allocated at correct size of template grid. std::vector m_TempData; /// Kernel width in [mm] for Gaussian smoothing of target images. Types::Coordinate m_GaussianSmoothImagesSigma; /// Value used to mark regions outside the FOV. static const byte m_PaddingValue = 255; /// User-defined value to fill regions outside FOV. byte m_UserBackgroundValue; /// Flag for user-defined background value. bool m_UserBackgroundFlag; /// Number of parameters per transformation.. size_t m_ParametersPerXform; /// Repeat histogram-based intensity matching after each stage. bool m_RepeatIntensityHistogramMatching; /// Update probabilistic sample table.. virtual bool Wiggle(); /// Prepare data for one image. virtual UniformVolume::SmartPtr PrepareSingleImage( UniformVolume::SmartPtr& image ); /// Smooth and pre-scale target images. virtual void PrepareTargetImages(); /// Reformat one image to a given target grid. virtual const UniformVolume::SmartPtr GetReformattedImage( const UniformVolume::SmartPtr& targetGrid, const size_t idx ) const; private: /// Copy template data from TypedArray to byte vector. void CopyTemplateData(); /// Initializer class shall be our friend. friend class GroupwiseRegistrationFunctionalAffineInitializer; }; //@} } // namespace cmtk #endif // #ifndef __cmtkGroupwiseRegistrationFunctionalBase_h_included_ cmtk-3.3.1/libs/Registration/cmtkGroupwiseRegistrationFunctionalXformTemplate.cxx000066400000000000000000000140441276303427400306070ustar00rootroot00000000000000/* // // Copyright 2016 Google, Inc. // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2011 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 5411 $ // // $LastChangedDate: 2016-01-16 15:07:00 -0800 (Sat, 16 Jan 2016) $ // // $LastChangedBy: torstenrohlfing $ // */ //#define DEBUG_COMM #include #include #include namespace cmtk { /** \addtogroup Registration */ //@{ template GroupwiseRegistrationFunctionalXformTemplate::GroupwiseRegistrationFunctionalXformTemplate() { // allocate storage for four times me parallel tasks than threads in the global thread pool. this->m_InterpolateTaskInfo.resize( 4 * ThreadPool::GetGlobalThreadPool().GetNumberOfThreads() ); } template void GroupwiseRegistrationFunctionalXformTemplate ::InterpolateImage ( const size_t idx, byte* const destination ) { for ( size_t task = 0; task < this->m_InterpolateTaskInfo.size(); ++task ) { this->m_InterpolateTaskInfo[task].thisObject = this; this->m_InterpolateTaskInfo[task].m_Idx = idx; this->m_InterpolateTaskInfo[task].m_Destination = destination; } ThreadPool& threadPool = ThreadPool::GetGlobalThreadPool(); if ( this->m_ProbabilisticSamples.size() ) threadPool.Run( InterpolateImageProbabilisticThread, this->m_InterpolateTaskInfo ); else threadPool.Run( InterpolateImageThread, this->m_InterpolateTaskInfo ); // Sum number of pixels outside FOV from all tasks. size_t numberOfOutsidePixels = 0; for ( size_t task = 0; task < this->m_InterpolateTaskInfo.size(); ++task ) { numberOfOutsidePixels += this->m_InterpolateTaskInfo[task].m_NumberOfOutsidePixels; } // Check whether more than defined proportion threshold was outside if ( numberOfOutsidePixels > static_cast( this->m_MaxRelativeNumberOutsidePixels * this->m_TemplateNumberOfSamples ) ) { throw typename Self::BadXform(); } } template void GroupwiseRegistrationFunctionalXformTemplate::InterpolateImageThread ( void *const args, const size_t taskIdx, const size_t taskCnt, const size_t, const size_t ) { InterpolateImageThreadParameters* threadParameters = static_cast( args ); const Self* This = threadParameters->thisObject; const size_t idx = threadParameters->m_Idx; byte* destination = threadParameters->m_Destination; const TXform* xform = This->GetXformByIndex(idx); const UniformVolume* target = This->m_ImageVector[idx]; const byte paddingValue = This->m_PaddingValue; const byte backgroundValue = This->m_UserBackgroundFlag ? This->m_PrivateUserBackgroundValue : paddingValue; threadParameters->m_NumberOfOutsidePixels = 0; Vector3D v; byte value; const byte* dataPtr = static_cast( target->GetData()->GetDataPtr() ); const Types::GridIndexType dimsX = This->m_TemplateGrid->GetDims()[AXIS_X]; const Types::GridIndexType dimsY = This->m_TemplateGrid->GetDims()[AXIS_Y]; const Types::GridIndexType dimsZ = This->m_TemplateGrid->GetDims()[AXIS_Z]; for ( Types::GridIndexType z = taskIdx; (z < dimsZ); z += taskCnt ) { byte *wptr = destination + z * dimsX * dimsY; for ( Types::GridIndexType y = 0; (y < dimsY); ++y ) { for ( Types::GridIndexType x = 0; x < dimsX; ++x ) { This->m_TemplateGrid->GetGridLocation( v, x, y, z ); xform->ApplyInPlace( v ); if ( target->ProbeData( value, dataPtr, v ) ) { *wptr = value; } else { *wptr = backgroundValue; ++threadParameters->m_NumberOfOutsidePixels; } ++wptr; } } } } template void GroupwiseRegistrationFunctionalXformTemplate::InterpolateImageProbabilisticThread ( void *const args, const size_t taskIdx, const size_t taskCnt, const size_t, const size_t ) { InterpolateImageThreadParameters* threadParameters = static_cast( args ); const Self* This = threadParameters->thisObject; const size_t idx = threadParameters->m_Idx; byte* destination = threadParameters->m_Destination; threadParameters->m_NumberOfOutsidePixels = 0; const TXform* xform = This->GetXformByIndex(idx); const UniformVolume* target = This->m_ImageVector[idx]; const byte paddingValue = This->m_PaddingValue; const byte backgroundValue = This->m_UserBackgroundFlag ? This->m_PrivateUserBackgroundValue : paddingValue; Vector3D v; byte value; const byte* dataPtr = static_cast( target->GetData()->GetDataPtr() ); const size_t startIdx = taskIdx * (This->m_ProbabilisticSamples.size() / taskCnt); const size_t endIdx = ( taskIdx == taskCnt ) ? This->m_ProbabilisticSamples.size() : (taskIdx+1) * (This->m_ProbabilisticSamples.size() / taskCnt); byte *wptr = destination + startIdx; for ( size_t i = startIdx; i < endIdx; ++i, ++wptr ) { const size_t offset = This->m_ProbabilisticSamples[i]; This->m_TemplateGrid->GetGridLocation( v, offset ); xform->ApplyInPlace( v ); if ( target->ProbeData( value, dataPtr, v ) ) { *wptr = value; } else { *wptr = backgroundValue; ++threadParameters->m_NumberOfOutsidePixels; } } } //@} } // namespace cmtk cmtk-3.3.1/libs/Registration/cmtkGroupwiseRegistrationFunctionalXformTemplate.h000066400000000000000000000102651276303427400302350ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2011 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 3608 $ // // $LastChangedDate: 2011-12-02 10:47:18 -0800 (Fri, 02 Dec 2011) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkGroupwiseRegistrationFunctionalXformTemplate_h_included_ #define __cmtkGroupwiseRegistrationFunctionalXformTemplate_h_included_ #include #include #include #include #include #include #include #include namespace cmtk { /** \addtogroup Registration */ //@{ /** Trannsformation-dependent class template for groupwise registration functionals. * This template provides the common generic interface for all transformation-model dependent * specialized templates. */ template class GroupwiseRegistrationFunctionalXformTemplate : /** Inherit from generic groupwise functional. */ public GroupwiseRegistrationFunctionalXformTemplateBase { public: /// Type of this class. typedef GroupwiseRegistrationFunctionalXformTemplateBase Superclass; /// Type of this class. typedef GroupwiseRegistrationFunctionalXformTemplate Self; /// Smart pointer. typedef SmartPointer SmartPtr; /// Constructor. GroupwiseRegistrationFunctionalXformTemplate(); /// Destructor. virtual ~GroupwiseRegistrationFunctionalXformTemplate() {} protected: /** Interpolate given moving image to template. *\param idx Index of of to reformat to template. This also determines which * transformation is used. *\param destination The reformatted pixel data is stored in this array. * Sufficient memory (for as many pixels as there are in the template grid) * must be allocated there. */ virtual void InterpolateImage( const size_t idx, byte* const destination ); private: /// Thread parameters with no further data. typedef ThreadParameters ThreadParametersType; /// Thread function parameters for image interpolation. class InterpolateImageThreadParameters : /// Inherit from generic thread parameters. public ThreadParametersType { public: /// Index of the image to be interpolated. size_t m_Idx; /// Pointer to storage that will hold the reformatted pixel data. byte* m_Destination; /// Return parameter: number of reformatted pixels outside floating image field of view. This is to detect pathological transformation parameters. size_t m_NumberOfOutsidePixels; }; /// Task info blocks. std::vector m_InterpolateTaskInfo; /// Image interpolation thread function. static void InterpolateImageThread( void *const args, const size_t taskIdx, const size_t taskCnt, const size_t, const size_t ); /// Image interpolation thread function with probabilistic sampling. static void InterpolateImageProbabilisticThread( void *const args, const size_t taskIdx, const size_t taskCnt, const size_t, const size_t ); }; //@} } // namespace cmtk #include #include #endif // #ifndef __cmtkGroupwiseRegistrationFunctionalXformTemplate_h_included_ cmtk-3.3.1/libs/Registration/cmtkGroupwiseRegistrationFunctionalXformTemplateBase.cxx000066400000000000000000000075411276303427400314060ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2011, 2013 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4882 $ // // $LastChangedDate: 2013-09-27 15:16:36 -0700 (Fri, 27 Sep 2013) $ // // $LastChangedBy: torstenrohlfing $ // */ //#define DEBUG_COMM #include #include #include namespace cmtk { /** \addtogroup Registration */ //@{ template GroupwiseRegistrationFunctionalXformTemplateBase::GroupwiseRegistrationFunctionalXformTemplateBase() : m_HistogramBins( 64 ), m_HistogramKernelRadiusMax( 0 ), m_MaxRelativeNumberOutsidePixels( 0.99f ), // if there is an image with more then 99% pixels outside FOV, registration probably failed m_PrivateUserBackgroundValue( 0.0 ), m_CropImageHistograms( false ) {} template GroupwiseRegistrationFunctionalXformTemplateBase::~GroupwiseRegistrationFunctionalXformTemplateBase() { } template void GroupwiseRegistrationFunctionalXformTemplateBase ::SetNumberOfHistogramBins( const size_t numberOfHistogramBins ) { this->m_HistogramBins = numberOfHistogramBins; if ( this->m_OriginalImageVector.size() ) { std::cerr << "WARNING: you called GroupwiseRegistrationFunctionalBase::SetNumberOfHistogramBins(),\n" << " but target images were already set. To be safe, I am re-generating\n" << " pre-scaled images.\n\n"; this->SetTargetImages( this->m_OriginalImageVector ); } } template UniformVolume::SmartPtr GroupwiseRegistrationFunctionalXformTemplateBase ::PrepareSingleImage( UniformVolume::SmartPtr& image ) { UniformVolume::SmartPtr newTargetImage = this->Superclass::PrepareSingleImage( image ); TypedArray::SmartPtr data = newTargetImage->GetData(); if ( this->m_CropImageHistograms ) { data->PruneHistogram( true, false, this->m_HistogramBins ); } data->RescaleToRange( Types::DataItemRange( this->m_HistogramKernelRadiusMax, this->m_HistogramKernelRadiusMax + this->m_HistogramBins-1 ) ); newTargetImage->SetData( TypedArray::SmartPtr( data->Convert( TYPE_BYTE ) ) ); return newTargetImage; } template void GroupwiseRegistrationFunctionalXformTemplateBase ::PrepareTargetImages() { this->m_ImageVector.resize( this->m_OriginalImageVector.size() ); const size_t imageFrom = 0; const size_t imageSkip = 1; for ( size_t i = imageFrom; i < this->m_ImageVector.size(); i += imageSkip ) { this->m_ImageVector[i] = UniformVolume::SmartPtr( this->PrepareSingleImage( this->m_OriginalImageVector[i] ) ); } this->m_PrivateUserBackgroundValue = this->m_UserBackgroundValue + this->m_HistogramKernelRadiusMax; } //@} } // namespace cmtk #include #include template class cmtk::GroupwiseRegistrationFunctionalXformTemplateBase; template class cmtk::GroupwiseRegistrationFunctionalXformTemplateBase; cmtk-3.3.1/libs/Registration/cmtkGroupwiseRegistrationFunctionalXformTemplateBase.h000066400000000000000000000130161276303427400310250ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2011 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 3608 $ // // $LastChangedDate: 2011-12-02 10:47:18 -0800 (Fri, 02 Dec 2011) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkGroupwiseRegistrationFunctionalXformTemplateBase_h_included_ #define __cmtkGroupwiseRegistrationFunctionalXformTemplateBase_h_included_ #include #include #include #include #include #include #include #include namespace cmtk { /** \addtogroup Registration */ //@{ /** Base class template for groupwise registration functionals. * This class template adds to its base class all basic functionality that depends * on the coordinate transformation model (affine vs. nonrigid) but does not require * implementation by explicit specialization. In other words, this class provides the * interface that is common to all transformation models. * * The next level of derived classes exist in several specialized variants * that implement the transformation-dependent interfaces, i.e., member functions * that exist only for certain transformation models. */ template class GroupwiseRegistrationFunctionalXformTemplateBase : /** Inherit from generic groupwise functional. */ public GroupwiseRegistrationFunctionalBase { public: /// Type of this class. typedef GroupwiseRegistrationFunctionalBase Superclass; /// Type of this class. typedef GroupwiseRegistrationFunctionalXformTemplateBase Self; /// Smart pointer. typedef SmartPointer SmartPtr; /// Transformation type. typedef TXform XformType; /// Smart pointer to transformation type. typedef typename XformType::SmartPtr XformPointer; /// Constructor. GroupwiseRegistrationFunctionalXformTemplateBase(); /// Destructor. virtual ~GroupwiseRegistrationFunctionalXformTemplateBase(); /// Set number of histogram bins. virtual void SetNumberOfHistogramBins( const size_t numberOfHistogramBins ); /// Set number of histogram bins. virtual void SetCropImageHistograms( const bool crop = true ) { this->m_CropImageHistograms = crop; } /** Get coordinate transformation for one image in the group. *\param idx Index of the volume/transformation. *\return Transformation for the selected volume. */ virtual const XformType* GetXformByIndex( const size_t idx ) const { return XformType::SmartPtr::DynamicCastFrom( this->m_XformVector[idx] ); } /** Get coordinate transformation for one image in the group. *\param idx Index of the volume/transformation. *\return Transformation for the selected volume. */ virtual typename XformType::SmartPtr GetXformByIndex( const size_t idx ) { return XformType::SmartPtr::DynamicCastFrom( this->m_XformVector[idx] ); } /** Get coordinate transformation for one active image in the group. *\param idx Index of the volume/transformation. *\return Transformation for the selected volume. */ virtual const XformType* GetActiveXformByIndex( const size_t idx ) const { return XformType::SmartPtr::DynamicCastFrom( this->m_XformVector[idx + this->m_ActiveXformsFrom] ); } /** Get coordinate transformation for one active image in the group. *\param idx Index of the volume/transformation. *\return Transformation for the selected volume. */ virtual typename XformType::SmartPtr GetActiveXformByIndex( const size_t idx ) { return XformType::SmartPtr::DynamicCastFrom( this->m_XformVector[idx + this->m_ActiveXformsFrom] ); } protected: /** Number of (usable) histogram bins. */ size_t m_HistogramBins; /** Maximal radius of histogram kernels. */ size_t m_HistogramKernelRadiusMax; /** Threshold for maximum fraction of reformatted pixels from any given image that may be outside FOV. * If the number of outside pixels for any one image exceeds this threshold (as a fraction of * total number of reformatted pixels) then an exception is thrown. */ float m_MaxRelativeNumberOutsidePixels; /** User-defined background value from parent class, transformed to histogram bin index. */ byte m_PrivateUserBackgroundValue; private: /// Crop image histograms to get rid of high-intensity low-probability samples. bool m_CropImageHistograms; /// Prepare data for one image. virtual UniformVolume::SmartPtr PrepareSingleImage( UniformVolume::SmartPtr& image ); /// Smooth and pre-scale target images. virtual void PrepareTargetImages(); }; //@} } // namespace cmtk #endif // #ifndef __cmtkGroupwiseRegistrationFunctionalXformTemplateBase_h_included_ cmtk-3.3.1/libs/Registration/cmtkGroupwiseRegistrationFunctionalXformTemplate_Affine.h000066400000000000000000000225541276303427400315110ustar00rootroot00000000000000/* // // Copyright 2016, Google, Inc. // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2012 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 5411 $ // // $LastChangedDate: 2016-01-16 15:07:00 -0800 (Sat, 16 Jan 2016) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkGroupwiseRegistrationFunctionalXformTemplate_Affine_h_included_ #define __cmtkGroupwiseRegistrationFunctionalXformTemplate_Affine_h_included_ #include #include #include namespace cmtk { /** Template specialization for groupwise affine registration functionals. * This class is the specialization of the generic transformation-dependent * functional class template, specialized for affine transformations. */ template<> class GroupwiseRegistrationFunctionalXformTemplate : /** Inherit from generic groupwise functional. */ public GroupwiseRegistrationFunctionalXformTemplateBase { public: /// Type of this class. typedef GroupwiseRegistrationFunctionalXformTemplateBase Superclass; /// Type of this class. typedef GroupwiseRegistrationFunctionalXformTemplate Self; /// Smart pointer. typedef SmartPointer SmartPtr; /// Constructor. GroupwiseRegistrationFunctionalXformTemplate() : m_XformNumberDOFs( 9 ) { this->m_ParametersPerXform = AffineXform::TotalNumberOfParameters; } /// Destructor. virtual ~GroupwiseRegistrationFunctionalXformTemplate() {} /// Set number of degrees of freedom per transformation. void SetXformNumberDOFs( const int numberDOFs ) { this->m_XformNumberDOFs = numberDOFs; std::vector::iterator it = this->m_XformVector.begin(); while ( it != this->m_XformVector.end() ) { AffineXform::SmartPtr::DynamicCastFrom(*it)->SetNumberDOFs( this->m_XformNumberDOFs ); ++it; } } /** Set affine transformations. */ void SetXforms( const std::vector& xformVector ) { this->m_XformVector.resize( xformVector.size() ); for ( size_t i = 0; i < this->m_XformVector.size(); ++i ) { AffineXform::SmartPtr xform( new AffineXform( *(xformVector[i]) ) ); xform->SetNumberDOFs( this->m_XformNumberDOFs ); xform->SetUseLogScaleFactors( true ); const Vector3D center = this->m_ImageVector[i]->GetCenterCropRegion(); xform->ChangeCenter( center ); this->m_XformVector[i] = xform; } } protected: /// Number of DOFs per transformation. int m_XformNumberDOFs; /** Interpolate given moving image to template. *\param idx Index of of to reformat to template. This also determines which * transformation is used. *\param destination The reformatted pixel data is stored in this array. * Sufficient memory (for as many pixels as there are in the template grid) * must be allocated there. */ virtual void InterpolateImage( const size_t idx, byte* const destination ) { const TransformedVolumeAxes gridHash( *this->m_TemplateGrid, this->GetXformByIndex( idx ) ); ThreadPool& threadPool = ThreadPool::GetGlobalThreadPool(); std::vector params( 4 * threadPool.GetNumberOfThreads() ); for ( size_t thread = 0; thread < params.size(); ++thread ) { params[thread].thisObject = this; params[thread].m_Idx = idx; params[thread].m_Destination = destination; params[thread].m_HashX = gridHash[0]; params[thread].m_HashY = gridHash[1]; params[thread].m_HashZ = gridHash[2]; } if ( (this->m_ProbabilisticSampleDensity > 0) && (this->m_ProbabilisticSampleDensity < 1) ) threadPool.Run( InterpolateImageProbabilisticThread, params ); else threadPool.Run( InterpolateImageThread, params ); } /// Image interpolation thread function. static void InterpolateImageThread( void *const args, const size_t taskIdx, const size_t taskCnt, const size_t, const size_t ) { InterpolateImageThreadParameters* threadParameters = static_cast( args ); const Self* This = threadParameters->thisObject; const size_t idx = threadParameters->m_Idx; byte* destination = threadParameters->m_Destination; const UniformVolume* target = This->m_ImageVector[idx]; const byte paddingValue = This->m_PaddingValue; const byte backgroundValue = This->m_UserBackgroundFlag ? This->m_PrivateUserBackgroundValue : paddingValue; Vector3D v; byte value; const byte* dataPtr = static_cast( target->GetData()->GetDataPtr() ); const Types::GridIndexType dimsX = This->m_TemplateGrid->GetDims()[AXIS_X]; const Types::GridIndexType dimsY = This->m_TemplateGrid->GetDims()[AXIS_Y]; const Types::GridIndexType dimsZ = This->m_TemplateGrid->GetDims()[AXIS_Z]; const Types::GridIndexType rowCount = ( dimsY * dimsZ ); const Types::GridIndexType rowFrom = ( rowCount / taskCnt ) * taskIdx; const Types::GridIndexType rowTo = ( taskIdx == (taskCnt-1) ) ? rowCount : ( rowCount / taskCnt ) * ( taskIdx + 1 ); Types::GridIndexType rowsToDo = rowTo - rowFrom; Types::GridIndexType yFrom = rowFrom % dimsY; Types::GridIndexType zFrom = rowFrom / dimsY; UniformVolume::CoordinateVectorType planeStart, rowStart; byte *wptr = destination + rowFrom * dimsX; for ( Types::GridIndexType z = zFrom; (z < dimsZ) && rowsToDo; ++z ) { planeStart = threadParameters->m_HashZ[z]; for ( Types::GridIndexType y = yFrom; (y < dimsY) && rowsToDo; yFrom = 0, ++y, --rowsToDo ) { (rowStart = planeStart) += threadParameters->m_HashY[y]; for ( Types::GridIndexType x = 0; x < dimsX; ++x ) { (v = rowStart) += threadParameters->m_HashX[x]; if ( target->ProbeData( value, dataPtr, v ) ) { *wptr = value; } else { *wptr = backgroundValue; } ++wptr; } } } } /// Image interpolation with probabilistic sampling thread function. static void InterpolateImageProbabilisticThread( void *const args, const size_t taskIdx, const size_t taskCnt, const size_t, const size_t ) { InterpolateImageThreadParameters* threadParameters = static_cast( args ); const Self* This = threadParameters->thisObject; const size_t idx = threadParameters->m_Idx; byte* destination = threadParameters->m_Destination; const AffineXform* xform = This->GetXformByIndex( idx ); const UniformVolume* target = This->m_ImageVector[idx]; const byte paddingValue = This->m_PaddingValue; const byte backgroundValue = This->m_UserBackgroundFlag ? This->m_PrivateUserBackgroundValue : paddingValue; byte value; const byte* dataPtr = static_cast( target->GetData()->GetDataPtr() ); const size_t startIdx = taskIdx * (This->m_ProbabilisticSamples.size() / taskCnt); const size_t endIdx = ( taskIdx == (taskCnt-1) ) ? This->m_ProbabilisticSamples.size() : (taskIdx+1) * (This->m_ProbabilisticSamples.size() / taskCnt); byte *wptr = destination + startIdx; for ( size_t i = startIdx; i < endIdx; ++i, ++wptr ) { const size_t offset = This->m_ProbabilisticSamples[i]; const UniformVolume::CoordinateVectorType v = xform->Apply( This->m_TemplateGrid->GetGridLocation( offset ) ); if ( target->ProbeData( value, dataPtr, v ) ) { *wptr = value; } else { *wptr = backgroundValue; } } } private: /// Thread function parameters for image interpolation. class InterpolateImageThreadParameters : /// Inherit from generic thread parameters. public ThreadParameters { public: /// Index of the image to be interpolated. size_t m_Idx; /// Pointer to storage that will hold the reformatted pixel data. byte* m_Destination; const Vector3D* m_HashX; const Vector3D* m_HashY; const Vector3D* m_HashZ; }; friend ClassStreamOutput& operator<<( ClassStreamOutput& stream, const GroupwiseRegistrationFunctionalXformTemplate& func ); friend ClassStreamInput& operator>>( ClassStreamInput& stream, GroupwiseRegistrationFunctionalXformTemplate& func ); }; ClassStreamOutput& operator<<( ClassStreamOutput& stream, const GroupwiseRegistrationFunctionalXformTemplate& func ); ClassStreamInput& operator>>( ClassStreamInput& stream, GroupwiseRegistrationFunctionalXformTemplate& func ); } // namespace cmtk #endif // #ifndef __cmtkGroupwiseRegistrationFunctionalXformTemplate_Affine_h_included_ cmtk-3.3.1/libs/Registration/cmtkGroupwiseRegistrationFunctionalXformTemplate_Affine_IO.cxx000066400000000000000000000070731276303427400324520ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2012 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4427 $ // // $LastChangedDate: 2012-06-12 11:23:22 -0700 (Tue, 12 Jun 2012) $ // // $LastChangedBy: torstenrohlfing $ // */ #include #include #include namespace cmtk { /** \addtogroup Registration */ //@{ ClassStreamOutput& operator<< ( ClassStreamOutput& stream, const GroupwiseRegistrationFunctionalXformTemplate& func ) { stream.Begin( "template" ); stream.WriteIntArray( "dims", func.m_TemplateGrid->GetDims().begin(), 3 ); stream.WriteCoordinateArray( "delta", func.m_TemplateGrid->Deltas().begin(), 3 ); stream.WriteCoordinateArray( "size", func.m_TemplateGrid->m_Size.begin(), 3 ); stream.WriteCoordinateArray( "origin", func.m_TemplateGrid->m_Offset.begin(), 3 ); stream.End(); for ( size_t idx = 0; idx < func.m_XformVector.size(); ++idx ) { stream.WriteString( "target", func.m_OriginalImageVector[idx]->GetMetaInfo( META_FS_PATH ).c_str() ); stream << (*func.GetXformByIndex( idx )); } return stream; } ClassStreamInput& operator>> ( ClassStreamInput& stream, GroupwiseRegistrationFunctionalXformTemplate& func ) { if ( ! stream.Seek( "template" ) ) { StdErr << "ERROR: no 'template' section in input archive\n"; return stream; } int dims[3]; stream.ReadIntArray( "dims", dims, 3 ); Types::Coordinate size[3]; stream.ReadCoordinateArray( "size", size, 3 ); Types::Coordinate origin[3]; stream.ReadCoordinateArray( "origin", origin, 3 ); stream.End(); UniformVolume::SmartPtr templateGrid( new UniformVolume( UniformVolume::IndexType::FromPointer( dims ), UniformVolume::CoordinateVectorType::FromPointer( size ) ) ); templateGrid->SetOffset( FixedVector<3,Types::Coordinate>::FromPointer( origin ) ); std::vector imageVector; std::vector xformVector; char* targetPath = stream.ReadString( "target", NULL /*default*/, false /*forward*/ ); while ( targetPath ) { UniformVolume::SmartPtr image( VolumeIO::ReadOriented( targetPath ) ); if ( ! image || ! image->GetData() ) { StdErr << "ERROR: Could not open image " << targetPath << "\n"; exit( 1 ); } imageVector.push_back( image ); AffineXform::SmartPtr xform; stream >> xform; xformVector.push_back( xform ); free( targetPath ); targetPath = stream.ReadString( "target", NULL /*default*/, true /*forward*/ ); } func.SetTargetImages( imageVector ); func.SetTemplateGrid( templateGrid ); func.SetXforms( xformVector ); return stream; } } // namespace cmtk cmtk-3.3.1/libs/Registration/cmtkGroupwiseRegistrationFunctionalXformTemplate_SplineWarp.cxx000066400000000000000000000276351276303427400327650ustar00rootroot00000000000000/* // // Copyright 2016 Google, Inc. // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2013 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 1768 $ // // $LastChangedDate: 2010-05-27 15:16:01 -0700 (Thu, 27 May 2010) $ // // $LastChangedBy: torstenrohlfing $ // */ #include #include #include namespace cmtk { GroupwiseRegistrationFunctionalXformTemplate ::GroupwiseRegistrationFunctionalXformTemplate() : m_MaximumNumberOfPixelsVOI( 0 ), m_MaximumNumberOfPixelsPerLineVOI( 0 ), m_ForceZeroSumNoAffine( false ) { this->m_ParametersPerXform = 0; this->m_WarpFastMode = true; this->m_PartialGradientMode = false; this->m_PartialGradientThreshold = static_cast( 0.01 ); this->m_DeactivateUninformativeMode = false; this->m_NumberOfActiveControlPoints = 0; this->m_JacobianConstraintWeight = 0.0; this->m_BendingEnergyWeight = 0.0; this->SetFreeAndRereadImages( true ); } void GroupwiseRegistrationFunctionalXformTemplate ::SetTemplateGrid ( UniformVolume::SmartPtr& templateGrid, const int downsample, const bool useTemplateData ) { this->Superclass::SetTemplateGrid( templateGrid, downsample, useTemplateData ); if ( this->m_XformVector.size() ) { for ( size_t i = 0; i < this->m_XformVector.size(); ++i ) { dynamic_cast( *(this->m_XformVector[i]) ).RegisterVolume( *(this->m_TemplateGrid) ); } this->UpdateVolumesOfInfluence(); } } void GroupwiseRegistrationFunctionalXformTemplate ::InitializeXformsFromAffine ( const Types::Coordinate gridSpacing, std::vector initialAffineXformsVector, const bool exactSpacing ) { this->m_InitialAffineXformsVector = initialAffineXformsVector; this->m_XformVector.resize( this->m_ImageVector.size() ); this->m_InitialRotationsVector.resize( this->m_ImageVector.size() ); for ( size_t i = 0; i < this->m_ImageVector.size(); ++i ) { SplineWarpXform::SmartPtr xform( new SplineWarpXform( this->m_TemplateGrid->m_Size, gridSpacing, initialAffineXformsVector[i], exactSpacing ) ); xform->RegisterVolume( *(this->m_TemplateGrid) ); this->m_XformVector[i] = xform; this->m_InitialRotationsVector[i] = AffineXform::SmartPtr( initialAffineXformsVector[i] ); // create all-zero parameter vector CoordinateVector v( initialAffineXformsVector[i]->ParamVectorDim(), 0.0 ); // copy rotation angles for ( size_t p = 3; p < 6; ++p ) v[p] = initialAffineXformsVector[i]->GetParameter( p ); // create rotation-only transformation this->m_InitialRotationsVector[i]->SetParamVector( v ); } this->m_ParametersPerXform = this->m_XformVector[0]->VariableParamVectorDim(); this->UpdateVolumesOfInfluence(); } void GroupwiseRegistrationFunctionalXformTemplate ::RefineTransformationGrids() { for ( size_t i = 0; i < this->m_XformVector.size(); ++i ) { this->GetXformByIndex(i)->Refine(); dynamic_cast( *(this->m_XformVector[i]) ).RegisterVolume( *(this->m_TemplateGrid) ); } this->m_ParametersPerXform = this->m_XformVector[0]->VariableParamVectorDim(); this->UpdateVolumesOfInfluence(); } void GroupwiseRegistrationFunctionalXformTemplate ::UpdateVolumesOfInfluence() { const UniformVolume::CoordinateRegionType templateDomain( this->m_TemplateGrid->m_Offset, this->m_TemplateGrid->m_Offset + this->m_TemplateGrid->m_Size ); this->m_VolumeOfInfluenceArray.resize( this->m_ParametersPerXform / 3 ); this->m_MaximumNumberOfPixelsPerLineVOI = 0; this->m_MaximumNumberOfPixelsVOI = 0; const SplineWarpXform& xform0 = *(this->GetXformByIndex(0)); for ( size_t param = 0; param < this->m_ParametersPerXform; param += 3 ) { DataGrid::RegionType& voi = this->m_VolumeOfInfluenceArray[param/3]; voi = this->m_TemplateGrid->GetGridRange( xform0.GetVolumeOfInfluence( param, templateDomain ) ); this->m_MaximumNumberOfPixelsVOI = std::max( voi.Size(), this->m_MaximumNumberOfPixelsVOI ); this->m_MaximumNumberOfPixelsPerLineVOI = std::max( voi.To()[0]-voi.From()[0], this->m_MaximumNumberOfPixelsPerLineVOI ); } } bool GroupwiseRegistrationFunctionalXformTemplate ::UpdateParamStepArray() { bool changed = false; this->m_ParamStepArray.resize( this->ParamVectorDim() ); if ( ( this->m_DeactivateUninformativeMode || this->m_DisableControlPointsMask ) && ( this->m_ActiveControlPointFlags.size() == this->m_ParametersPerXform / 3 ) ) { for ( size_t param = 0; param < this->ParamVectorDim(); ++param ) { const Types::Coordinate pOld = this->m_ParamStepArray[param]; this->m_ParamStepArray[param] = this->GetParamStep( param ); if ( ! this->m_ActiveControlPointFlags[(param%this->m_ParametersPerXform)/3] ) { this->m_ParamStepArray[param] = 0; } if ( pOld != this->m_ParamStepArray[param] ) changed = true; } } else { for ( size_t param = 0; param < this->ParamVectorDim(); ++param ) { const Types::Coordinate pOld = this->m_ParamStepArray[param]; this->m_ParamStepArray[param] = this->GetParamStep( param ); if ( pOld != this->m_ParamStepArray[param] ) changed = true; } } return changed; } void GroupwiseRegistrationFunctionalXformTemplate ::ForceZeroSumGradient( CoordinateVector& g ) const { const size_t numberOfXforms = this->m_XformVector.size(); if ( this->m_ForceZeroSumNoAffine ) { for ( size_t xform = 0; xform < numberOfXforms; ++xform ) { Types::Coordinate* gX = &g[xform*this->m_ParametersPerXform]; const AffineXform* affineXform = this->m_InitialRotationsVector[xform]->GetInverse(); if ( affineXform ) { #pragma omp parallel for for ( int param = 0; param < static_cast( this->m_ParametersPerXform ); param += 3 ) { const FixedVector<3,Types::Coordinate> projected( affineXform->RotateScaleShear( FixedVector<3,Types::Coordinate>::FromPointer( gX+param ) ) ); for ( size_t i = 0; i<3; ++i ) gX[param+i] = projected[i]; } } } } this->Superclass::ForceZeroSumGradient( g ); if ( this->m_ForceZeroSumNoAffine ) { for ( size_t xform = 0; xform < numberOfXforms; ++xform ) { Types::Coordinate* gX = &g[xform*this->m_ParametersPerXform]; const AffineXform* affineXform = this->m_InitialRotationsVector[xform]; if ( affineXform ) { #pragma omp parallel for for ( int param = 0; param < static_cast( this->m_ParametersPerXform ); param += 3 ) { const FixedVector<3,Types::Coordinate> projected( affineXform->RotateScaleShear( FixedVector<3,Types::Coordinate>::FromPointer( gX+param ) ) ); for ( size_t i = 0; i<3; ++i ) gX[param+i] = projected[i]; } } } } } bool GroupwiseRegistrationFunctionalXformTemplate ::Wiggle() { bool wiggle = this->Superclass::Wiggle(); if ( this->m_PartialGradientMode ) { wiggle = wiggle || this->UpdateParamStepArray(); } return wiggle; } void GroupwiseRegistrationFunctionalXformTemplate ::InterpolateImage ( const size_t idx, byte* const destination ) { ThreadPool& threadPool = ThreadPool::GetGlobalThreadPool(); const size_t numberOfThreads = threadPool.GetNumberOfThreads(); std::vector params( numberOfThreads ); for ( size_t thread = 0; thread < numberOfThreads; ++thread ) { params[thread].thisObject = this; params[thread].m_Idx = idx; params[thread].m_Destination = destination; } threadPool.Run( InterpolateImageThread, params ); } void GroupwiseRegistrationFunctionalXformTemplate ::InterpolateImageThread ( void* args, const size_t taskIdx, const size_t taskCnt, const size_t, const size_t ) { InterpolateImageThreadParameters* threadParameters = static_cast( args ); const Self* This = threadParameters->thisObject; const size_t idx = threadParameters->m_Idx; byte* destination = threadParameters->m_Destination; const SplineWarpXform* xform = This->GetXformByIndex(idx); const UniformVolume* target = This->m_ImageVector[idx]; const byte* dataPtr = static_cast( target->GetData()->GetDataPtr() ); const byte paddingValue = This->m_PaddingValue; const byte backgroundValue = This->m_UserBackgroundFlag ? This->m_PrivateUserBackgroundValue : paddingValue; const Types::GridIndexType dimsX = This->m_TemplateGrid->GetDims()[AXIS_X]; const Types::GridIndexType dimsY = This->m_TemplateGrid->GetDims()[AXIS_Y]; const Types::GridIndexType dimsZ = This->m_TemplateGrid->GetDims()[AXIS_Z]; std::vector v( dimsX ); byte value; const Types::GridIndexType rowCount = ( dimsY * dimsZ ); const Types::GridIndexType rowFrom = ( rowCount / taskCnt ) * taskIdx; const Types::GridIndexType rowTo = ( taskIdx == (taskCnt-1) ) ? rowCount : ( rowCount / taskCnt ) * ( taskIdx + 1 ); Types::GridIndexType rowsToDo = rowTo - rowFrom; Types::GridIndexType yFrom = rowFrom % dimsY; Types::GridIndexType zFrom = rowFrom / dimsY; byte *wptr = destination + rowFrom * dimsX; for ( Types::GridIndexType z = zFrom; (z < dimsZ) && rowsToDo; ++z ) { for ( Types::GridIndexType y = yFrom; (y < dimsY) && rowsToDo; yFrom = 0, ++y, --rowsToDo ) { xform->GetTransformedGridRow( dimsX, &v[0], 0, y, z ); for ( Types::GridIndexType x = 0; x < dimsX; ++x ) { if ( target->ProbeData( value, dataPtr, v[x] ) ) { *wptr = value; } else { *wptr = backgroundValue; } ++wptr; } } } } void GroupwiseRegistrationFunctionalXformTemplate::UpdateActiveControlPoints() { const size_t numberOfControlPoints = this->m_VolumeOfInfluenceArray.size(); if ( numberOfControlPoints ) { this->m_ActiveControlPointFlags.resize( numberOfControlPoints ); std::fill( this->m_ActiveControlPointFlags.begin(), this->m_ActiveControlPointFlags.end(), true ); this->m_NumberOfActiveControlPoints = numberOfControlPoints; } if ( this->m_DisableControlPointsMask ) { size_t cntDisabled = 0; const UniformVolume::CoordinateRegionType templateDomain( this->m_TemplateGrid->m_Offset, this->m_TemplateGrid->m_Offset + this->m_TemplateGrid->m_Size ); const SplineWarpXform& xform0 = *(this->GetXformByIndex(0)); for ( size_t cp = 0; cp < numberOfControlPoints; ++cp ) { const DataGrid::RegionType maskRegion = this->m_DisableControlPointsMask->GetGridRange( xform0.GetVolumeOfInfluence( 3*cp, templateDomain, false /*force slow, accurate mode*/ ) ); for ( RegionIndexIterator it( maskRegion ); it != it.end(); ++it ) { if ( this->m_DisableControlPointsMask->GetDataAt( this->m_DisableControlPointsMask->GetOffsetFromIndex( it.Index() ) ) > 0 ) { this->m_ActiveControlPointFlags[cp] = false; ++cntDisabled; break; } } } DebugOutput( 2 ) << "Disabled " << cntDisabled << " control points due to provided mask.\n"; } } } // namespace cmtk cmtk-3.3.1/libs/Registration/cmtkGroupwiseRegistrationFunctionalXformTemplate_SplineWarp.h000066400000000000000000000225211276303427400323770ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2012 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 1768 $ // // $LastChangedDate: 2010-05-27 15:16:01 -0700 (Thu, 27 May 2010) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkGroupwiseRegistrationFunctionalXformTemplate_SplineWarp_h_included_ #define __cmtkGroupwiseRegistrationFunctionalXformTemplate_SplineWarp_h_included_ #include namespace cmtk { /** Template specialization for groupwise nonrigid registration functionals. * This class is the specialization of the generic transformation-dependent * functional class template, specialized for nonrigid (B-spline FFD) transformations. * * As such, this class provides functionality such as: initialization of * FFDs from affine transformations, grid refinement, and deformation constraints. */ template<> class GroupwiseRegistrationFunctionalXformTemplate : /** Inherit from generic groupwise functional. */ public GroupwiseRegistrationFunctionalXformTemplateBase { public: /// Type of this class. typedef GroupwiseRegistrationFunctionalXformTemplateBase Superclass; /// Type of this class. typedef GroupwiseRegistrationFunctionalXformTemplate Self; /// Smart pointer. typedef SmartPointer SmartPtr; /// Constructor. GroupwiseRegistrationFunctionalXformTemplate(); /// Destructor. virtual ~GroupwiseRegistrationFunctionalXformTemplate() {}; /// Initialize nonrigid from affine transformations. virtual void InitializeXformsFromAffine( const Types::Coordinate gridSpacing /*!< Control point grid spacing in real-world units*/, std::vector initialAffineXformsVector /*!< Vector of initial affine coordinate transformations*/, const bool exactSpacing = true /*!< If set, the control point spacing will be exactly as given in the first parameter*/ ); /** Initialize spline warp transformations. */ virtual void InitializeXforms( const Types::Coordinate gridSpacing /*!< Control point grid spacing in real-world units*/, const bool exactSpacing = true /*!< If set, the control point spacing will be exactly as given in the first parameter*/ ) { this->InitializeXformsFromAffine( gridSpacing, this->m_InitialAffineXformsVector, exactSpacing ); } /// Refine transformation control point grids. virtual void RefineTransformationGrids(); /// Set flag for exclusion of affine components in unbiased groupwise deformation. void SetForceZeroSumNoAffine( const bool noaffine = true ) { this->m_ForceZeroSumNoAffine = noaffine; } /// Set partial gradient mode. void SetPartialGradientMode( const bool partialGradientMode = false, const float partialGradientThreshold = 0.0 ) { this->m_PartialGradientMode = partialGradientMode; this->m_PartialGradientThreshold = partialGradientThreshold; } /// Set deactivate uninformative control points mode. void SetDeactivateUninformativeMode( const bool dum = true ) { this->m_DeactivateUninformativeMode = dum; } /** Set range of currently active transformations. * Call inherited function, then update local step size array. */ virtual void SetActiveXformsFromTo( const size_t from, const size_t to ) { this->Superclass::SetActiveXformsFromTo( from, to ); this->UpdateParamStepArray(); } /// Call inherited function and allocate local storage. virtual void SetTemplateGrid( UniformVolume::SmartPtr& templateGrid, const int downsample = 1, const bool useTemplateData = false ); /// Set mask for explicitly disabling control points. virtual void SetDisableControlPointsMask( UniformVolume::SmartConstPtr mask ) { this->m_DisableControlPointsMask = mask; } protected: /// Maximum number of pixels in any VOI. size_t m_MaximumNumberOfPixelsVOI; /// Maximum number of pixels per line in any VOI. size_t m_MaximumNumberOfPixelsPerLineVOI; /// Update volumes of influence for warp parameters. virtual void UpdateVolumesOfInfluence(); /// Update deactivated control points. virtual void UpdateActiveControlPoints(); /** Interpolate given moving image to template. * This function overrides the interpolation function provided by the base * class. It makes use of the fact that affine transformations preserve * parallel lines for more efficient computation. *\param idx Index of of to reformat to template. This also determines which * transformation is used. *\param destination The reformatted pixel data is stored in this array. * Sufficient memory (for as many pixels as there are in the template grid) * must be allocated there. */ virtual void InterpolateImage( const size_t idx, byte* const destination ); /** Enforce gradient to be zero-sum over all images. * This function essentially calls the inherited function of the same name. However, * if this->m_ForceZeroSumNoAffine is true, then the initial affine transformations * of each warp are eliminated from the gradient prior to calling the inherited * function, and they are re-applied afterwards. This way, the unbiased property of * the transformation set is made invariant under the affine transformation components. */ virtual void ForceZeroSumGradient( CoordinateVector& g ) const; /** "Wiggle" functional a little, i.e., by updating probabilistic sampling. *\return True if the functional actually changed slightly, false if no "wiggle" * has actually taken place. This signals the optimizer whether it's worth trying * the current stage again (true) or not (false). */ bool Wiggle(); protected: /// Flag for correction of affine components in unbiased warp. bool m_ForceZeroSumNoAffine; /// Flag for fast warp mode, i.e., reduced control point influence volume. bool m_WarpFastMode; /// Weight for jacobian constraint term. float m_JacobianConstraintWeight; /// Weight for grid bending energy term. float m_BendingEnergyWeight; /** Flag for partial gradient computation. * If this is set, gradient components under a given threshold are deactivated * and not used for gradient approximation. */ bool m_PartialGradientMode; /// Mask volume for explicitly disabling control points affecting mask foreground. UniformVolume::SmartConstPtr m_DisableControlPointsMask; /// Initial affine transformations. std::vector m_InitialAffineXformsVector; /// Rotation components of initial affine transformations. std::vector m_InitialRotationsVector; /// Current parameter steppings for the warp parameters. std::vector m_ParamStepArray; /// Update parameter steppings for the warp parameters. virtual bool UpdateParamStepArray(); /// Volumes of influence for the warp parameters. std::vector m_VolumeOfInfluenceArray; /// Threshold for partial gradient computation. Types::Coordinate m_PartialGradientThreshold; /// Deactivate uninformative control points mode. bool m_DeactivateUninformativeMode; /// List of flags for deactivated control points. std::vector m_ActiveControlPointFlags; /// Number of deactivated control points. size_t m_NumberOfActiveControlPoints; /// Update local information by control point - to be implemented by derived classes if needed. virtual void UpdateInformationByControlPoint() {} private: /// Thread function parameters for image interpolation. class InterpolateImageThreadParameters : /// Inherit from generic thread parameters. public ThreadParameters { public: /// Index of the image to be interpolated. size_t m_Idx; /// Pointer to storage that will hold the reformatted pixel data. byte* m_Destination; }; /// Image interpolation thread function. static void InterpolateImageThread( void* args, const size_t taskIdx, const size_t taskCnt, const size_t, const size_t ); friend ClassStreamOutput& operator<<( ClassStreamOutput& stream, const GroupwiseRegistrationFunctionalXformTemplate& func ); friend ClassStreamInput& operator>>( ClassStreamInput& stream, GroupwiseRegistrationFunctionalXformTemplate& func ); }; ClassStreamOutput& operator<<( ClassStreamOutput& stream, const GroupwiseRegistrationFunctionalXformTemplate& func ); ClassStreamInput& operator>>( ClassStreamInput& stream, GroupwiseRegistrationFunctionalXformTemplate& func ); } // namespace cmtk #endif // #ifndef __cmtkGroupwiseRegistrationFunctionalXformTemplate_SplineWarp_h_included_ cmtk-3.3.1/libs/Registration/cmtkGroupwiseRegistrationFunctionalXformTemplate_SplineWarp_IO.cxx000066400000000000000000000072721276303427400333470ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2012 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4427 $ // // $LastChangedDate: 2012-06-12 11:23:22 -0700 (Tue, 12 Jun 2012) $ // // $LastChangedBy: torstenrohlfing $ // */ #include #include #include #include #include #include namespace cmtk { /** \addtogroup Registration */ //@{ ClassStreamOutput& operator<< ( ClassStreamOutput& stream, const GroupwiseRegistrationFunctionalXformTemplate& func ) { stream.Begin( "template" ); stream.WriteIntArray( "dims", func.m_TemplateGrid->GetDims().begin(), 3 ); stream.WriteCoordinateArray( "delta", func.m_TemplateGrid->Deltas().begin(), 3 ); stream.WriteCoordinateArray( "size", func.m_TemplateGrid->m_Size.begin(), 3 ); stream.WriteCoordinateArray( "origin", func.m_TemplateGrid->m_Offset.begin(), 3 ); stream.End(); for ( size_t idx = 0; idx < func.m_XformVector.size(); ++idx ) { stream.WriteString( "target", func.m_OriginalImageVector[idx]->GetMetaInfo( META_FS_PATH ).c_str() ); stream << func.GetXformByIndex(idx); } return stream; } ClassStreamInput& operator>> ( ClassStreamInput& stream, GroupwiseRegistrationFunctionalXformTemplate& func ) { if ( ! stream.Seek( "template" ) ) { StdErr << "ERROR: no 'template' section in input archive\n"; return stream; } int dims[3]; stream.ReadIntArray( "dims", dims, 3 ); Types::Coordinate size[3]; stream.ReadCoordinateArray( "size", size, 3 ); Types::Coordinate origin[3]; stream.ReadCoordinateArray( "origin", origin, 3 ); stream.End(); UniformVolume::SmartPtr templateGrid( new UniformVolume( UniformVolume::IndexType::FromPointer( dims ), UniformVolume::CoordinateVectorType::FromPointer( size ) ) ); templateGrid->SetOffset( FixedVector<3,Types::Coordinate>::FromPointer( origin ) ); std::vector imageVector; std::vector xformVector; char* targetPath = stream.ReadString( "target", NULL /*default*/, false /*forward*/ ); while ( targetPath ) { UniformVolume::SmartPtr image( VolumeIO::ReadOriented( targetPath ) ); if ( ! image || ! image->GetData() ) { StdErr << "Could not open image " << targetPath << "\n"; exit( 1 ); } imageVector.push_back( image ); AffineXform::SmartPtr xform; stream >> xform; xformVector.push_back( xform ); free( targetPath ); targetPath = stream.ReadString( "target", NULL /*default*/, true /*forward*/ ); } func.m_InitialAffineXformsVector = xformVector; func.SetTargetImages( imageVector ); func.SetTemplateGrid( templateGrid ); return stream; } } // namespace cmtk cmtk-3.3.1/libs/Registration/cmtkGroupwiseRegistrationOutput.cxx000066400000000000000000000155011276303427400252740ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2011, 2013 SRI International // // Copyright 2015 Google, Inc. // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 5393 $ // // $LastChangedDate: 2015-11-06 20:39:27 -0800 (Fri, 06 Nov 2015) $ // // $LastChangedBy: torstenrohlfing $ // */ #include "cmtkGroupwiseRegistrationOutput.h" #include #include #include #include #include #include #include #include #include #include namespace cmtk { /** \addtogroup Registration */ //@{ bool GroupwiseRegistrationOutput::WriteGroupwiseArchive( const char* path ) const { // create class stream archive. if ( path ) { ClassStreamOutput stream; if ( this->m_OutputRootDirectory ) { char completePath[PATH_MAX]; snprintf( completePath, sizeof( completePath ), "%s%c%s", this->m_OutputRootDirectory, (int)CMTK_PATH_SEPARATOR, path ); stream.Open( completePath, ClassStreamOutput::MODE_WRITE_ZLIB ); } else stream.Open( path, ClassStreamOutput::MODE_WRITE_ZLIB ); if ( ! stream.IsValid() ) return false; stream << *this->m_Functional; stream.Close(); } return true; } bool GroupwiseRegistrationOutput::WriteXformsSeparateArchives ( const std::string& path, const std::string& templatePath ) { if ( !path.empty() ) { for ( size_t img = 0; img < this->m_Functional->GetNumberOfTargetImages(); ++img ) { StudyList slist; Study::SmartPtr refstudy; if ( this->m_OutputRootDirectory && ! this->m_ExistingTemplatePath ) { refstudy = slist.AddStudy( std::string( this->m_OutputRootDirectory ) + CMTK_PATH_SEPARATOR + templatePath ); } else { refstudy = slist.AddStudy( templatePath ); } const UniformVolume* image = this->m_Functional->GetOriginalTargetImage( img ); Study::SmartPtr imgstudy = slist.AddStudy( image->GetMetaInfo( META_FS_PATH ).c_str() ); WarpXform::SmartPtr warpXform = WarpXform::SmartPtr::DynamicCastFrom( this->m_Functional->GetGenericXformByIndex( img ) ); if ( warpXform ) { AffineXform::SmartPtr affineXform( warpXform->GetInitialAffineXform() ); slist.AddXform( refstudy, imgstudy, affineXform, warpXform ); } else { AffineXform::SmartPtr affineXform = AffineXform::SmartPtr::DynamicCastFrom( this->m_Functional->GetGenericXformByIndex( img ) ); slist.AddXform( refstudy, imgstudy, affineXform ); } std::ostringstream fullPath; if ( this->m_OutputRootDirectory ) { fullPath << this->m_OutputRootDirectory << CMTK_PATH_SEPARATOR; } fullPath << path << CMTK_PATH_SEPARATOR << "target-"; fullPath.fill( '0' ); fullPath.width( 3 ); fullPath << img << ".list"; ClassStreamStudyList::Write( fullPath.str(), &slist ); } } return true; } bool GroupwiseRegistrationOutput::WriteAverageImage( const char* path, const cmtk::Interpolators::InterpolationEnum interp, const cmtk::ScalarDataType pixelType, const bool useTemplateData ) { // reformat output and generate average images if ( path ) { UniformVolume::SmartPtr templateGrid = this->m_Functional->GetTemplateGrid(); const size_t numberOfPixels = templateGrid->GetNumberOfPixels(); TypedArray::SmartPtr average( TypedArray::Create( pixelType, numberOfPixels ) ); float* averagePtr = static_cast( average->GetDataPtr() ); std::vector count; if ( useTemplateData ) { if ( ! templateGrid->GetData() ) { UniformVolume::SmartPtr readImage( VolumeIO::ReadOriented( templateGrid->GetMetaInfo( META_FS_PATH ) ) ); templateGrid->SetData( readImage->GetData() ); } for ( size_t px = 0; px < numberOfPixels; ++px ) { averagePtr[px] = static_cast( templateGrid->GetDataAt( px ) ); } count.resize(numberOfPixels, 1); } else { average->Fill( 0 ); count.resize(numberOfPixels, 0); } DebugOutput( 1 ) << "Reformating output images\n"; const size_t idxFrom = 0; const size_t idxSkip = 1; for ( size_t idx = idxFrom; idx < this->m_Functional->GetNumberOfTargetImages(); idx += idxSkip ) { UniformVolume::SmartPtr floatingVolume = this->m_Functional->GetOriginalTargetImage( idx ); if ( !floatingVolume->GetData() ) floatingVolume = UniformVolume::SmartPtr( VolumeIO::ReadOriented( floatingVolume->GetMetaInfo( META_FS_PATH ) ) ); cmtk::ReformatVolume reformat; reformat.SetReferenceVolume( templateGrid ); reformat.SetFloatingVolume( floatingVolume ); reformat.SetInterpolation( interp ); AffineXform::SmartPtr affineXform = AffineXform::SmartPtr::DynamicCastFrom( this->m_Functional->GetGenericXformByIndex( idx ) ); if ( affineXform ) { reformat.SetAffineXform( affineXform ); } WarpXform::SmartPtr warpXform = WarpXform::SmartPtr::DynamicCastFrom( this->m_Functional->GetGenericXformByIndex( idx ) ); if ( warpXform ) reformat.SetWarpXform( warpXform ); UniformVolume::SmartPtr ref( reformat.PlainReformat() ); const TypedArray* data = ref->GetData(); #pragma omp parallel for for ( int i = 0; i < static_cast( numberOfPixels ); ++i ) { Types::DataItem v; if ( data->Get( v, i ) ) { averagePtr[i] += static_cast( v ); ++count[i]; } } } #pragma omp parallel for for ( int i = 0; i < static_cast( numberOfPixels ); ++i ) { if ( count[i] ) averagePtr[i] /= count[i]; else average->SetPaddingAt( i ); } templateGrid->SetData( average ); if ( this->m_OutputRootDirectory ) { char fullPath[PATH_MAX]; snprintf( fullPath, sizeof( fullPath ), "%s%c%s", this->m_OutputRootDirectory, CMTK_PATH_SEPARATOR, path ); VolumeIO::Write( *templateGrid, fullPath ); } else { VolumeIO::Write( *templateGrid, path ); } } return 0; } } // namespace cmtk cmtk-3.3.1/libs/Registration/cmtkGroupwiseRegistrationOutput.h000066400000000000000000000071571276303427400247310ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2011, 2013 SRI International // // Copyright 2015 Google, Inc. // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 5393 $ // // $LastChangedDate: 2015-11-06 20:39:27 -0800 (Fri, 06 Nov 2015) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkGroupwiseRegistrationOutput_h_included_ #define __cmtkGroupwiseRegistrationOutput_h_included_ #include #include #include #include #include #include namespace cmtk { /** \addtogroup Registration */ //@{ /// Class for output of groupwise registration results. class GroupwiseRegistrationOutput { public: /// Functional base class. typedef GroupwiseRegistrationFunctionalBase FunctionalType; /// Pointer to functional base class. typedef FunctionalType::SmartPtr FunctionalPointer; /// Constructors: link to functional. GroupwiseRegistrationOutput( FunctionalPointer& functional = FunctionalPointer::Null() ) : m_ExistingTemplatePath( false ), m_OutputRootDirectory( NULL ) { this->m_Functional = functional; } /// Set flag for existing template path. void SetExistingTemplatePath( const bool flag ) { this->m_ExistingTemplatePath = flag; } /// Set functional with implicit dynamic cast. template void SetFunctional( SmartPointer& functional ) { this->m_Functional = FunctionalType::SmartPtr::DynamicCastFrom( functional ); } /// Set root directory for all output files. void SetOutputRootDirectory( const char* rootDir ) { this->m_OutputRootDirectory = rootDir; } /// Write template specifications and transformations to a single file. bool WriteGroupwiseArchive( const char* path ) const; /// Write each transformations to a different typedstream archive. bool WriteXformsSeparateArchives( const std::string& path, const std::string& templatePath ); /// Reformat and write average image. bool WriteAverageImage( const char* path /*. // // $Revision: 4812 $ // // $LastChangedDate: 2013-09-09 14:26:32 -0700 (Mon, 09 Sep 2013) $ // // $LastChangedBy: torstenrohlfing $ // */ #include #include #include #include #include namespace cmtk { /** \addtogroup Registration */ //@{ template GroupwiseRegistrationRMIFunctional ::GroupwiseRegistrationRMIFunctional() : m_TotalNumberOfSamples( 0 ) { this->SetNumberOfHistogramBins( 255 ); } template void GroupwiseRegistrationRMIFunctional ::SetTemplateGrid ( UniformVolume::SmartPtr& templateGrid, const int downsample, const bool useTemplateData ) { this->Superclass::SetTemplateGrid( templateGrid, downsample, useTemplateData ); } template typename GroupwiseRegistrationRMIFunctional::ReturnType GroupwiseRegistrationRMIFunctional ::Evaluate() { ThreadPool& threadPool = ThreadPool::GetGlobalThreadPool(); const size_t numberOfImages = this->m_ImageVector.size(); this->m_CovarianceMatrix.Resize( numberOfImages ); // needs no reset this->m_TotalNumberOfSamples = 0; this->m_SumOfProductsMatrix.resize( numberOfImages * (1+numberOfImages) / 2 ); std::fill( this->m_SumOfProductsMatrix.begin(), this->m_SumOfProductsMatrix.end(), 0 ); this->m_SumsVector.resize( numberOfImages ); std::fill( this->m_SumsVector.begin(), this->m_SumsVector.end(), 0 ); this->m_ThreadSumOfProductsMatrix.resize( this->m_NumberOfThreads ); this->m_ThreadSumsVector.resize( this->m_NumberOfThreads ); std::vector params( this->m_NumberOfTasks ); for ( size_t taskIdx = 0; taskIdx < this->m_NumberOfTasks; ++taskIdx ) { params[taskIdx].thisObject = this; } if ( this->m_ProbabilisticSamples.size() ) threadPool.Run( EvaluateProbabilisticThread, params ); else threadPool.Run( EvaluateThread, params ); return this->GetMetric( this->m_SumOfProductsMatrix, this->m_SumsVector, this->m_TotalNumberOfSamples, this->m_CovarianceMatrix ); } template typename GroupwiseRegistrationRMIFunctional::ReturnType GroupwiseRegistrationRMIFunctional ::GetMetric ( const SumsAndProductsVectorType& sumOfProductsMatrix, const SumsAndProductsVectorType& sumsVector, const unsigned int totalNumberOfSamples, typename GroupwiseRegistrationRMIFunctional::CovarianceMatrixType& covarianceMatrix ) const { const size_t imagesFrom = this->m_ActiveImagesFrom; const size_t imagesTo = this->m_ActiveImagesTo; const size_t numberOfImages = imagesTo - imagesFrom; size_t midx = 0; for ( size_t j = 0; j < numberOfImages; ++j ) { for ( size_t i = 0; i <= j; ++i, ++midx ) { covarianceMatrix(i,j) = (sumOfProductsMatrix[midx] - ((1.0 * sumsVector[i] * sumsVector[j]) / totalNumberOfSamples)) / totalNumberOfSamples; } } const std::vector eigenvalues = EigenValuesSymmetricMatrix( covarianceMatrix ).GetEigenvalues(); const typename Self::ReturnType EIGENVALUE_THRESHOLD = 1e-6; typename Self::ReturnType determinant = 1.0; for ( size_t i = 0; i < numberOfImages; ++i ) { if ( eigenvalues[i] > EIGENVALUE_THRESHOLD ) determinant *= eigenvalues[i]; } if ( determinant > 0 ) { const static double alpha = 1.41893853320467; typename Self::ReturnType metric = numberOfImages*alpha + .5*log( determinant ); return -metric; } else { return -FLT_MAX; } } template void GroupwiseRegistrationRMIFunctional ::EvaluateThread ( void *const args, const size_t taskIdx, const size_t taskCnt, const size_t threadIdx, const size_t ) { EvaluateThreadParameters* threadParameters = static_cast( args ); Self* This = threadParameters->thisObject; const Self* ThisConst = threadParameters->thisObject; const size_t imagesFrom = ThisConst->m_ActiveImagesFrom; const size_t imagesTo = ThisConst->m_ActiveImagesTo; const size_t numberOfImages = imagesTo - imagesFrom; const byte paddingValue = ThisConst->m_PaddingValue; SumsAndProductsVectorType& sumOfProductsMatrix = This->m_ThreadSumOfProductsMatrix[threadIdx]; sumOfProductsMatrix.resize( numberOfImages * (1+numberOfImages) / 2 ); std::fill( sumOfProductsMatrix.begin(), sumOfProductsMatrix.end(), 0 ); SumsAndProductsVectorType& sumsVector = This->m_ThreadSumsVector[threadIdx]; sumsVector.resize( numberOfImages ); std::fill( sumsVector.begin(), sumsVector.end(), 0 ); size_t totalNumberOfSamples = 0; const size_t numberOfPixels = ThisConst->m_TemplateNumberOfPixels; const size_t pixelsPerTask = 1+(numberOfPixels / taskCnt); const size_t pixelFrom = taskIdx * pixelsPerTask; const size_t pixelTo = std::min( numberOfPixels, pixelFrom + pixelsPerTask ); for ( size_t ofs = pixelFrom; ofs < pixelTo; ++ofs ) { bool allValid = This->m_Data[imagesFrom][ofs] != paddingValue; for ( size_t j = imagesFrom+1; allValid && (j < imagesTo); ++j ) { allValid = This->m_Data[j][ofs] != paddingValue; } if ( allValid ) { size_t midx = 0; for ( size_t j = imagesFrom; j < imagesTo; ++j ) { const int dataJ = This->m_Data[j][ofs]; sumsVector[j-imagesFrom] += dataJ; for ( size_t i = imagesFrom; i <= j; ++i, ++midx ) { const int dataI = This->m_Data[i][ofs]; sumOfProductsMatrix[midx] += dataI * dataJ; ++totalNumberOfSamples; } } } } // add our contribution to total results This->m_MutexLock.Lock(); size_t midx = 0; for ( size_t j = imagesFrom; j < imagesTo; ++j ) { This->m_SumsVector[j-imagesFrom] += sumsVector[j-imagesFrom]; for ( size_t i = imagesFrom; i <= j; ++i, ++midx ) { This->m_SumOfProductsMatrix[midx] += sumOfProductsMatrix[midx]; } } This->m_TotalNumberOfSamples += totalNumberOfSamples; This->m_MutexLock.Unlock(); } template void GroupwiseRegistrationRMIFunctional ::EvaluateProbabilisticThread ( void *const args, const size_t taskIdx, const size_t taskCnt, const size_t threadIdx, const size_t ) { EvaluateThreadParameters* threadParameters = static_cast( args ); Self* This = threadParameters->thisObject; const Self* ThisConst = threadParameters->thisObject; const size_t imagesFrom = ThisConst->m_ActiveImagesFrom; const size_t imagesTo = ThisConst->m_ActiveImagesTo; const size_t numberOfImages = imagesTo - imagesFrom; const byte paddingValue = ThisConst->m_PaddingValue; SumsAndProductsVectorType& sumOfProductsMatrix = This->m_ThreadSumOfProductsMatrix[threadIdx]; sumOfProductsMatrix.resize( numberOfImages * (1+numberOfImages) / 2 ); std::fill( sumOfProductsMatrix.begin(), sumOfProductsMatrix.end(), 0 ); SumsAndProductsVectorType& sumsVector = This->m_ThreadSumsVector[threadIdx]; sumsVector.resize( numberOfImages ); std::fill( sumsVector.begin(), sumsVector.end(), 0 ); const size_t numberOfSamples = ThisConst->m_ProbabilisticSamples.size(); const size_t samplesPerTask = 1+(numberOfSamples / taskCnt); const size_t sampleFrom = taskIdx * samplesPerTask; const size_t sampleTo = std::min( numberOfSamples, sampleFrom + samplesPerTask ); size_t totalNumberOfSamples = 0; for ( size_t sample = sampleFrom; sample < sampleTo; ++sample ) { bool allValid = This->m_Data[imagesFrom][sample] != paddingValue; for ( size_t j = imagesFrom+1; allValid && (j < imagesTo); ++j ) { allValid = (This->m_Data[j][sample] != paddingValue); } if ( allValid ) { size_t midx = 0; for ( size_t j = imagesFrom; j < imagesTo; ++j ) { const int dataJ = This->m_Data[j][sample]; sumsVector[j-imagesFrom] += dataJ; for ( size_t i = imagesFrom; i <= j; ++i, ++midx ) { const int dataI = This->m_Data[i][sample]; sumOfProductsMatrix[midx] += dataI * dataJ; ++totalNumberOfSamples; } } } } // add our contribution to total results This->m_MutexLock.Lock(); size_t midx = 0; for ( size_t j = imagesFrom; j < imagesTo; ++j ) { This->m_SumsVector[j-imagesFrom] += sumsVector[j-imagesFrom]; for ( size_t i = imagesFrom; i <= j; ++i, ++midx ) { This->m_SumOfProductsMatrix[midx] += sumOfProductsMatrix[midx]; } } This->m_TotalNumberOfSamples += totalNumberOfSamples; This->m_MutexLock.Unlock(); } template typename GroupwiseRegistrationRMIFunctional::ReturnType GroupwiseRegistrationRMIFunctional ::EvaluateWithGradient ( CoordinateVector& v, CoordinateVector& g, const Types::Coordinate step ) { const typename Self::ReturnType baseValue = this->EvaluateAt( v ); for ( size_t param = 0; param < this->ParamVectorDim(); ++param ) { g[param] = 0.0; const size_t imageIndex = param / this->m_ParametersPerXform; const size_t paramIndex = param % this->m_ParametersPerXform; const Types::Coordinate pStep = this->GetParamStep( param, step ); if ( pStep > 0 ) { byte* tmp = this->m_Data[imageIndex]; this->m_Data[imageIndex] = &(this->m_TempData[0]); const Types::Coordinate p0 = v[param]; this->SetParameter( imageIndex, paramIndex, p0 + pStep ); this->InterpolateImage( imageIndex, this->m_Data[imageIndex] ); const typename Self::ReturnType upper = this->Evaluate(); this->SetParameter( imageIndex, paramIndex, p0 - pStep ); this->InterpolateImage( imageIndex, this->m_Data[imageIndex] ); const typename Self::ReturnType lower = this->Evaluate(); this->m_Data[imageIndex] = tmp; this->SetParameter( imageIndex, paramIndex, p0 ); if ( (upper > baseValue) || (lower > baseValue) ) { g[param] = (upper - lower); } } } if ( this->m_ForceZeroSum ) { this->ForceZeroSumGradient( g ); } return baseValue; } template bool GroupwiseRegistrationRMIFunctional ::Wiggle() { bool wiggle = this->Superclass::Wiggle(); if ( wiggle ) { } return wiggle; } //@} } // namespace cmtk #include #include template class cmtk::GroupwiseRegistrationRMIFunctional; template class cmtk::GroupwiseRegistrationRMIFunctional; cmtk-3.3.1/libs/Registration/cmtkGroupwiseRegistrationRMIFunctional.h000066400000000000000000000123121276303427400260700ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2011, 2013 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4812 $ // // $LastChangedDate: 2013-09-09 14:26:32 -0700 (Mon, 09 Sep 2013) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkGroupwiseRegistrationRMIFunctional_h_included_ #define __cmtkGroupwiseRegistrationRMIFunctional_h_included_ #include #include #include #include #include #include #include #include #include #include namespace cmtk { /** \addtogroup Registration */ //@{ /** Functional for groupwise registration. */ template class GroupwiseRegistrationRMIFunctional : public GroupwiseRegistrationFunctionalXformTemplate { public: /// Type of this class. typedef GroupwiseRegistrationFunctionalXformTemplate Superclass; /// Type of this class. typedef GroupwiseRegistrationRMIFunctional Self; /// Smart pointer. typedef SmartPointer SmartPtr; /// Transformation type. typedef TXform XformType; /// Smart pointer to transformation type. typedef typename XformType::SmartPtr XformPointer; /// Constructor. GroupwiseRegistrationRMIFunctional(); /// Destructor. virtual ~GroupwiseRegistrationRMIFunctional() {} /** Set template grid. */ virtual void SetTemplateGrid( UniformVolume::SmartPtr& templateGrid /*!< The template grid that defines size and resolution for the implicit registration template.*/, const int downsample = 1 /*!< Downsampling factor */, const bool useTemplateData = false /*!< Flag to use template pixel data, not just grid, in registration */ ); /** Compute functional value and gradient. *\param v Parameter vector. *\param g The extimated gradient of the functional is stored in this vector. *\param step Step size for finite difference gradient approximation. Default * is 1 mm. *\return Const function value for given parameters. */ virtual typename Self::ReturnType EvaluateWithGradient( CoordinateVector& v, CoordinateVector& g, const Types::Coordinate step = 1 ); /// Evaluate functional with currently set parameters. virtual typename Self::ReturnType Evaluate(); protected: /// Covariance matrix type. typedef SymmetricMatrix CovarianceMatrixType; /// Actual covariance matrix. typename Self::CovarianceMatrixType m_CovarianceMatrix; /// Type for vectors of sums and products. typedef std::vector SumsAndProductsVectorType; /// Sum of products matrix. SumsAndProductsVectorType m_SumOfProductsMatrix; /// Sums vector. SumsAndProductsVectorType m_SumsVector; /// Compute metric from partial matrices using temporary matrix storage. typename Self::ReturnType GetMetric ( const SumsAndProductsVectorType& sumOfProductsMatrix, const SumsAndProductsVectorType& sumsVector, const unsigned int totalNumberOfSamples, typename Self::CovarianceMatrixType& covarianceMatrix ) const; /// Sum of products matrix. std::vector m_ThreadSumOfProductsMatrix; /// Sums vector. std::vector m_ThreadSumsVector; /** Total number of samples that went into CC computation. */ unsigned int m_TotalNumberOfSamples; /// Mutex for schared data structures. MutexLock m_MutexLock; /// Update probabilistic sample table.. virtual bool Wiggle(); private: /// Thread parameters with no further data. typedef ThreadParameters ThreadParametersType; /// Thread parameter for entropy evaluation. class EvaluateThreadParameters : /// Inherit from generic thread parameter class. public ThreadParametersType { }; /// Evaluate functional with currently set parameters. static void EvaluateThread( void *const threadParameters, const size_t taskIdx, const size_t taskCnt, const size_t threadIdx, const size_t ); /// Evaluate functional with currently set parameters with probabilistic sampling. static void EvaluateProbabilisticThread( void *const threadParameters, const size_t taskIdx, const size_t taskCnt, const size_t threadIdx, const size_t ); }; //@} } // namespace cmtk #endif // #ifndef __cmtkGroupwiseRegistrationRMIFunctional_h_included_ cmtk-3.3.1/libs/Registration/cmtkHausdorffDistance.cxx000066400000000000000000000046461276303427400231000ustar00rootroot00000000000000/* // // Copyright 2011 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 3358 $ // // $LastChangedDate: 2011-08-11 13:12:32 -0700 (Thu, 11 Aug 2011) $ // // $LastChangedBy: torstenrohlfing $ // */ #include "cmtkHausdorffDistance.h" #include #include #include #include cmtk::HausdorffDistance::HausdorffDistance( UniformVolume::SmartConstPtr& image0, UniformVolume::SmartConstPtr& image1 ) : m_Image0( image0 ), m_Image1( image1 ) { if ( !this->m_Image0->GridMatches( *(this->m_Image1) ) ) { cmtk::StdErr << "ERROR: the two image grids don't match.\n"; throw cmtk::ExitException( 1 ); } } cmtk::Types::Coordinate cmtk::HausdorffDistance::GetBinary() const { typedef UniformDistanceMap DistanceMapType; UniformVolume::SmartConstPtr distance0 = DistanceMapType( *(this->m_Image0), DistanceMapType::DEFAULT ).Get(); UniformVolume::SmartConstPtr distance1 = DistanceMapType( *(this->m_Image1), DistanceMapType::DEFAULT ).Get(); return std::max( Self::HalfDistanceBinary( *(this->m_Image0), *distance1 ), Self::HalfDistanceBinary( *(this->m_Image1), *distance0 ) ); } cmtk::Types::Coordinate cmtk::HausdorffDistance::HalfDistanceBinary( const UniformVolume& image, const UniformVolume& dmap ) { Types::Coordinate maxDistance = 0; const size_t nPixels = image.GetNumberOfPixels(); for ( size_t n = 0; n < nPixels; ++n ) { if ( image.GetDataAt( n ) ) { maxDistance = std::max( maxDistance, dmap.GetDataAt( n ) ); } } return maxDistance; } cmtk-3.3.1/libs/Registration/cmtkHausdorffDistance.h000066400000000000000000000042031276303427400225120ustar00rootroot00000000000000/* // // Copyright 2011 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 3358 $ // // $LastChangedDate: 2011-08-11 13:12:32 -0700 (Thu, 11 Aug 2011) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkHausdorffDistance_h_included_ #define __cmtkHausdorffDistance_h_included_ #include #include namespace cmtk { /** \addtogroup Registration */ //@{ /** Class for computing Hausdorff distance between two label images. * Distance computation is implemented via the Euclidean distance maps of the two images. */ class HausdorffDistance { public: /// This class. typedef HausdorffDistance Self; /// Constructor. HausdorffDistance( UniformVolume::SmartConstPtr& image0, UniformVolume::SmartConstPtr& image1 ); /// Get distance of two binary label maps. Types::Coordinate GetBinary() const; private: /// First image. UniformVolume::SmartConstPtr m_Image0; /// Second image. UniformVolume::SmartConstPtr m_Image1; /// Utility function: compute "half" (i.e., one direction) of the distance term from an image (treated as binary map) and the distance map of the other image. static Types::Coordinate HalfDistanceBinary( const UniformVolume& image, const UniformVolume& dmap ); }; //@} } // namespace cmtk #endif // #ifndef __cmtkHausdorffDistance_h_included_ cmtk-3.3.1/libs/Registration/cmtkImagePairAffineRegistration.cxx000066400000000000000000000175061276303427400250450ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2011, 2013 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 5309 $ // // $LastChangedDate: 2014-04-10 18:01:34 -0700 (Thu, 10 Apr 2014) $ // // $LastChangedBy: torstenrohlfing $ // */ #include "cmtkImagePairAffineRegistration.h" #include #include #include #include #include #include #include #include #include #include #include #include #include namespace cmtk { /** \addtogroup Registration */ //@{ ImagePairAffineRegistration::ImagePairAffineRegistration () : m_Initializer( MakeInitialAffineTransformation::FOV ), m_SymmetricFwdBwd( false ), m_MatchFltToRefHistogram( false ), m_RestrictToInPlane( -1 ) { } ImagePairAffineRegistration::~ImagePairAffineRegistration () { } CallbackResult ImagePairAffineRegistration::InitRegistration () { CallbackResult result = this->Superclass::InitRegistration(); if ( result != CALLBACK_OK ) return result; this->m_ReferenceVolume = this->m_Volume_1; this->m_FloatingVolume = this->m_Volume_2; if ( this->m_MatchFltToRefHistogram ) { this->GetVolume_2()->GetData()->ApplyFunctionObject( TypedArrayFunctionHistogramMatching( *(this->GetVolume_2()->GetData()), *(this->GetVolume_1()->GetData()) ) ); } AffineXform::SmartPtr affineXform; if ( this->m_InitialTransformation ) { if ( this->m_InitialXformIsInverse ) { affineXform = AffineXform::SmartPtr( this->m_InitialTransformation->MakeInverse() ); } else { affineXform = AffineXform::SmartPtr( this->m_InitialTransformation ); } } else { affineXform = AffineXform::SmartPtr( MakeInitialAffineTransformation::Create( *this->m_ReferenceVolume, *this->m_FloatingVolume, this->m_Initializer ) ); } this->m_Xform = affineXform; Vector3D center = this->m_ReferenceVolume->GetCenterCropRegion(); affineXform->ChangeCenter( center ); if ( this->m_UseOriginalData ) { this->m_ParameterStack.push( Self::LevelParameters::SmartPtr( new Self::LevelParameters( -1 ) ) ); } Types::Coordinate currSampling = std::max( this->m_Sampling, 2 * std::min( this->m_ReferenceVolume->GetMinDelta(), this->m_FloatingVolume->GetMinDelta())); double coarsest = this->m_CoarsestResolution; if ( coarsest <= 0 ) coarsest = this->m_MaxStepSize; for ( ; (currSampling<=coarsest); currSampling *= 2 ) { this->m_ParameterStack.push( Self::LevelParameters::SmartPtr( new Self::LevelParameters( currSampling ) ) ); } this->m_Optimizer = Optimizer::SmartPtr( new BestNeighbourOptimizer( this->m_OptimizerStepFactor ) ); this->m_Optimizer->SetCallback( this->m_Callback ); // default to rigid transformation if ( NumberDOFs.empty() ) NumberDOFs.push_back( 6 ); // push guard elements NumberDOFs.push_back( -1 ); NumberDOFsFinal.push_back( -1 ); // intialize iterator. NumberDOFsIterator = NumberDOFs.begin(); return CALLBACK_OK; } Functional* ImagePairAffineRegistration ::MakeFunctional( const int /*level*/, const Superclass::LevelParameters* parameters ) { const Self::LevelParameters* levelParameters = dynamic_cast( parameters ); if ( ! levelParameters ) { StdErr << "CODING ERROR: wrong RTTI for 'parameters'\n"; exit( 1 ); } AffineXform::SmartPtr affineXform = AffineXform::SmartPtr::DynamicCastFrom( this->m_Xform ); if ( ! affineXform ) { StdErr << "CODING ERROR: wrong RTTI for 'this->m_Xform'\n"; exit( 1 ); } UniformVolume::SmartPtr nextRef, nextFlt; if ( levelParameters->m_Resolution > 0 ) { nextRef = UniformVolume::SmartPtr( this->m_ReferenceVolume->GetResampled( levelParameters->m_Resolution ) ); nextFlt = UniformVolume::SmartPtr( this->m_FloatingVolume->GetResampled( levelParameters->m_Resolution ) ); } else { // for final, original resolution just take input volumes. nextRef = this->m_ReferenceVolume; nextFlt = this->m_FloatingVolume; } if ( this->m_SymmetricFwdBwd ) { ImagePairSymmetricAffineRegistrationFunctional *functional = ImagePairSymmetricAffineRegistrationFunctional::Create( this->m_Metric, nextRef, nextFlt, this->m_FloatingImageInterpolation, affineXform ); functional->SetForceOutside( this->m_ForceOutsideFlag, this->m_ForceOutsideValue ); if ( this->m_RestrictToInPlane >=0 ) { functional->SetRestrictToInPlane( this->m_RestrictToInPlane ); } return functional; } else { ImagePairAffineRegistrationFunctional *functional = ImagePairAffineRegistrationFunctional::Create( this->m_Metric, nextRef, nextFlt, this->m_FloatingImageInterpolation, affineXform ); functional->SetForceOutside( this->m_ForceOutsideFlag, this->m_ForceOutsideValue ); if ( this->m_RestrictToInPlane >=0 ) { functional->SetRestrictToInPlane( this->m_RestrictToInPlane ); } return functional; } } void ImagePairAffineRegistration::EnterResolution ( CoordinateVector::SmartPtr& v, Functional::SmartPtr& f, const int level, const int total ) { if ( *NumberDOFsIterator < 0 ) { if ( (level == total) && (NumberDOFsFinal.size()>1) ) NumberDOFsIterator = NumberDOFsFinal.begin(); else NumberDOFsIterator = NumberDOFs.begin(); } AffineXform::SmartPtr affineXform = AffineXform::SmartPtr::DynamicCastFrom( this->m_Xform ); if ( affineXform ) { affineXform->SetNumberDOFs( *NumberDOFsIterator ); if ( this->m_Callback ) { char buffer[64]; snprintf( buffer, sizeof( buffer ), "Setting Number DOFs to %d.", *NumberDOFsIterator ); this->m_Callback->Comment( buffer ); } } this->Superclass::EnterResolution( v, f, level, total ); } int ImagePairAffineRegistration::DoneResolution ( CoordinateVector::SmartPtr& v, Functional::SmartPtr& f, const int level, const int total ) { this->Superclass::DoneResolution( v, f, level, total ); ++NumberDOFsIterator; return (*NumberDOFsIterator < 0); } AffineXform::SmartPtr ImagePairAffineRegistration::GetTransformation() const { AffineXform::SmartPtr affineXform = AffineXform::SmartPtr::DynamicCastFrom( this->m_Xform ); return affineXform; } const UniformVolume::SmartPtr ImagePairAffineRegistration::GetReformattedFloatingImage( Interpolators::InterpolationEnum interpolator ) const { ReformatVolume reformat; reformat.SetInterpolation( interpolator ); reformat.SetReferenceVolume( this->m_Volume_1 ); reformat.SetFloatingVolume( this->m_Volume_2 ); AffineXform::SmartPtr affineXform( this->GetTransformation() ); reformat.SetAffineXform( affineXform ); return reformat.PlainReformat(); } } // namespace cmtk cmtk-3.3.1/libs/Registration/cmtkImagePairAffineRegistration.h000066400000000000000000000135631276303427400244710ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2012 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 3722 $ // // $LastChangedDate: 2012-01-11 13:38:56 -0800 (Wed, 11 Jan 2012) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkImagePairAffineRegistration_h_included_ #define __cmtkImagePairAffineRegistration_h_included_ #include #include #include #include #include #include namespace cmtk { /** \addtogroup Registration */ //@{ /** Class for affine multi-resolution voxel registration. */ class ImagePairAffineRegistration : /// Inherit general voxel registration interface and functionality. public ImagePairRegistration { protected: /** Flag for initial alignment of volume centers. */ cmtkGetSetMacro(MakeInitialAffineTransformation::Mode,Initializer); /// Flag: use symmetric registration functional for simultaneous forward/inverse transformation computation. bool m_SymmetricFwdBwd; /// Flag whether to adjust floating image histogram to match reference image. cmtkGetSetMacro(bool,MatchFltToRefHistogram); /** Numbers of degrees of freedom. * This list contains the numbers of degrees of freedom for every resolution * level. Registration is repeated with the same data as many times as there * are entries in this list. If the derived classes do not set any entries, * InitRegistration() will push a "6" into the list, resulting in an affine * registration. */ std::vector NumberDOFs; /** Numbers of degrees of freedom for the final resolution level. * Just as "NumberDOFs", this list defines the sequence of numbers of degrees * of freedom for the finest resolution level. */ std::vector NumberDOFsFinal; /** Restrict to in-plane transformations. * If this is set to a number between 0 and 2, it indexes a coordinate axis, * and computed transformations are restricted to be in-plane with respect to * planes perpendicular to that axis. */ int m_RestrictToInPlane; /** Initialize registration. * This function is called by Register before any other operations. It can * be overloaded to open status dialog windows, etc. Derived implementations * should call their base class' InitRegistration first. *\return Overriding functions should return a value other than * CALLBACK_OK if the registration is to be interrupted. */ virtual CallbackResult InitRegistration (); /** Enter a resolution level. * This function mainly determines the next effective number of degrees of * freedom of the optimization. It sets the transformation object accordingly * and writes a comment to the "Callback" object. Afterwards, the inherited * EnterResolution() function is called. */ virtual void EnterResolution( CoordinateVector::SmartPtr& v, Functional::SmartPtr& f, const int level, const int total ); /** Finish resolution level. * This function determines whether there are any remaining numbers in the * effective NumberDOFs list. If so, the list iterator is advanced and 0 is * returned, indicating that the current resolution level is to be repeated. * With no number left, 1 is returned indicating that the current level has * been completed. */ virtual int DoneResolution( CoordinateVector::SmartPtr& v, Functional::SmartPtr& f, const int level, const int total ); public: /// This class. typedef ImagePairAffineRegistration Self; /// Parent class. typedef ImagePairRegistration Superclass; /** Default constructor. */ ImagePairAffineRegistration (); /** Destructor. */ virtual ~ImagePairAffineRegistration (); /// Return final transformation. AffineXform::SmartPtr GetTransformation() const; /// Get reformatted floating image. const UniformVolume::SmartPtr GetReformattedFloatingImage( Interpolators::InterpolationEnum interpolator = Interpolators::LINEAR ) const; /// Add a number to the general list of numbers of DOFs. void AddNumberDOFs( const int numberDOFs ) { NumberDOFs.push_back( numberDOFs ); } /// Add a number to the list of numbers of DOFs for the last level. void AddNumberDOFsFinal( const int numberDOFs ) { NumberDOFsFinal.push_back( numberDOFs ); } private: /// Iterator for NumberDOFs and NumberDOFsFinal std::vector::iterator NumberDOFsIterator; /// Base class for registration level parameters. class LevelParameters /// Inherit from superclass parameters. : public Superclass::LevelParameters { public: /// Constructor: take image resolution. LevelParameters( const Types::Coordinate resolution ) : m_Resolution( resolution ) {} /// Image resolution for this level. Types::Coordinate m_Resolution; }; /** Create functional with all settings for next level. */ virtual Functional* MakeFunctional( const int level, const Superclass::LevelParameters* ); }; //@} } // namespace cmtk #endif // __cmtkImagePairAffineRegistration_h_included_ cmtk-3.3.1/libs/Registration/cmtkImagePairAffineRegistrationCommandLine.cxx000066400000000000000000000604611276303427400271520ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2013 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 5316 $ // // $LastChangedDate: 2014-04-11 13:43:18 -0700 (Fri, 11 Apr 2014) $ // // $LastChangedBy: torstenrohlfing $ // */ #include "cmtkImagePairAffineRegistrationCommandLine.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef CMTK_USE_SQLITE # include #endif #ifdef HAVE_SYS_TYPES_H # include #endif #ifdef HAVE_SYS_STAT_H # include #endif #ifdef HAVE_SYS_UTSNAME_H # include #endif #ifdef _MSC_VER # include #endif #include #include #include namespace cmtk { /** \addtogroup Registration */ //@{ ImagePairAffineRegistrationCommandLine ::ImagePairAffineRegistrationCommandLine ( const int argc, const char* argv[] ) { this->m_Metric = 0; this->m_AutoMultiLevels = 0; this->m_CoarsestResolution = -1; this->m_MaxStepSize = 8; this->m_MinStepSize = 0.1; this->m_Sampling = 1.0; bool forceOutsideFlag = false; Types::DataItem forceOutsideValue = 0; std::string inStudylist; std::string InitialStudylist; std::string clArg1; // input studylist or reference image std::string clArg2; // empty or floating image try { CommandLine cl( CommandLine::PROPS_XML ); cl.SetProgramInfo( CommandLine::PRG_TITLE, "Rigid and affine registration" ); cl.SetProgramInfo( CommandLine::PRG_DESCR, "This program performs rigid and affine image registration using multi-resolution optimization of voxel-based image similarity measures." ); cl.SetProgramInfo( CommandLine::PRG_CATEG, "CMTK.Registration.Experimental" ); typedef CommandLine::Key Key; cl.BeginGroup( "Automation", "Automation Options" ); cl.AddOption( Key( "auto-multi-levels" ), &this->m_AutoMultiLevels, "Automatic optimization and resolution parameter generation for levels" ); cl.BeginGroup( "Optimization", "Optimization settings" ); cl.AddOption( Key( "max-stepsize" ), &this->m_MaxStepSize, "Maximum optimizer step size, which determines search space exploration." ); cl.AddOption( Key( "min-stepsize" ), &this->m_MinStepSize, "Minimum optimizer step size, which determines precision." ); cl.AddOption( Key( "stepfactor" ), &this->m_OptimizerStepFactor, "Factor for search step size reduction. Must be > 0.0 and < 1.0" ); cl.AddOption( Key( "delta-f-threshold" ), &this->m_DeltaFThreshold, "Optional threshold to terminate optimization (level) if relative change of target function drops below this value." ); cl.EndGroup(); cl.BeginGroup( "Resolution", "Image resolution parameters" ); cl.AddOption( Key( 's', "sampling" ), &this->m_Sampling, "Image sampling (finest resampled image resolution)" ); cl.AddOption( Key( "coarsest" ), &this->m_CoarsestResolution, "Upper limit for image sampling in multiresolution hierarchy" ); cl.AddSwitch( Key( "omit-original-data" ), &this->m_UseOriginalData, false, "Do not use original data in full resolution, omit final stage in multiresolution hierarchy, thus reducing computation time." ); cl.EndGroup(); cl.BeginGroup( "Transformation", "Transformation parameters" ); cl.AddVector( Key( "dofs" ), this->NumberDOFs, "Add number of degrees of freedom. This can be 3 (translation), 6 (rigid: translation and rotation), " "7 (rigid plus global scale), 9 (rigid plus anisotropic scales), 12 (rigid plus scales plus shears), 3003 (translation plus shear), 3033 (translation, shear, and scale) or 3303 (rigid plus shears, but no scale). " "This option can be repeated, in which case DOFs are used for successive optimization runs in the order that they appear." ); cl.AddVector( Key( "dofs-final" ), this->NumberDOFsFinal, "Add number of degrees of freedom for final level only [can be repeated]" ); cl.AddSwitch( Key( "symmetric" ), &this->m_SymmetricFwdBwd, true, "Use symmetric registration functional to simultaneously estimate forward and inverse transformation. " "This increases ragistration time substantially but produces a result that is invariant under exchange of fixed and moving image. " "It may also be more robust and/or more accurate than forward-only registration." ); CommandLine::EnumGroup::SmartPtr inPlaneGroup = cl.AddEnum( "restrict-in-plane", &this->m_RestrictToInPlane, "Restrict the affine transformation to be in-plane for planes perpendicular to a given coordinate axis." ); inPlaneGroup->AddSwitch( Key( "xy" ), 2, "Transformation restricted to in-plane for 'xy' plane (perpendicular to z coordinate axis)." ); inPlaneGroup->AddSwitch( Key( "xz" ), 1, "Transformation restricted to in-plane for 'xz' plane (perpendicular to y coordinate axis)." ); inPlaneGroup->AddSwitch( Key( "yz" ), 0, "Transformation restricted to in-plane for 'yz' plane (perpendicular to x coordinate axis)." ); inPlaneGroup->AddSwitch( Key( "none" ), -1, "Full 3D affine transformation is computed." ); CommandLine::EnumGroup::SmartPtr initGroup = cl.AddEnum( "init", &this->m_Initializer, "Select initializer for the affine trasnformation." ); initGroup->AddSwitch( Key( "none" ), MakeInitialAffineTransformation::NONE, "Use input transformation, or identity transformation if none was provided." ); initGroup->AddSwitch( Key( "fov" ), MakeInitialAffineTransformation::FOV, "Align centers of field of view (or crop regions) using a translation." ); initGroup->AddSwitch( Key( "com" ), MakeInitialAffineTransformation::COM, "Align centers of mass using a translation." ); initGroup->AddSwitch( Key( "pax" ), MakeInitialAffineTransformation::PAX, "Align images by rotation using principal axes and translation using centers of mass." ); initGroup->AddSwitch( Key( "physical" ), MakeInitialAffineTransformation::PHYS, "Align images by rotation using direction vectors stored in input images and translation using image origins." ); cl.AddOption( Key( "initial" ), &InitialStudylist, "Initialize transformation from given path" )->SetProperties( CommandLine::PROPS_XFORM ); cl.AddSwitch( Key( "initial-is-inverse" ), &this->m_InitialXformIsInverse, true, "Invert initial transformation before initializing registration" ); cl.EndGroup(); cl.BeginGroup( "Image data", "Image data" ); CommandLine::EnumGroup::SmartPtr metricGroup = cl.AddEnum( "registration-metric", &this->m_Metric, "Registration metric for motion estimation by image-to-image registration." ); metricGroup->AddSwitch( Key( "nmi" ), 0, "Normalized Mutual Information metric" ); metricGroup->AddSwitch( Key( "mi" ), 1, "Standard Mutual Information metric" ); metricGroup->AddSwitch( Key( "cr" ), 2, "Correlation Ratio metric" ); metricGroup->AddSwitch( Key( "rms" ), 3, "Root of Mean Squaresa metric (this is the square root of MSD)" ); metricGroup->AddSwitch( Key( "msd" ), 4, "Mean Squared Difference metric" ); metricGroup->AddSwitch( Key( "ncc" ), 5, "Normalized Cross Correlation metric" ); cl.BeginGroup( "Interpolation", "Floating Image Interpolation Options" ); cmtk::CommandLine::EnumGroup::SmartPtr kernelGroup = cl.AddEnum( "interpolation", &this->m_FloatingImageInterpolation, "Interpolation method for floating image sampling:" ); kernelGroup->AddSwitch( Key( "nearest-neighbor" ), Interpolators::NEAREST_NEIGHBOR, "Nearest neighbor interpolation (for intensity and label data)" ); kernelGroup->AddSwitch( Key( "linear" ), Interpolators::LINEAR, "Trilinear interpolation" ); kernelGroup->AddSwitch( Key( "cubic" ), Interpolators::CUBIC, "Tricubic interpolation" ); kernelGroup->AddSwitch( Key( "cosine-sinc" ), Interpolators::COSINE_SINC, "Cosine-windowed sinc interpolation (most accurate but slowest)" ); kernelGroup->AddSwitch( Key( "partial-volume" ), Interpolators::PARTIALVOLUME, "Partial volume interpolation (for label data)" ); kernelGroup->AddSwitch( Key( "automatic" ), Interpolators::DEFAULT, "Select interpolation automatically based on data type: linear for grey-level data, nearest neighbor for label data." ); cl.AddSwitch( Key( "match-histograms" ), &this->m_MatchFltToRefHistogram, true, "Match floating image histogram to reference image histogram." ); cl.AddOption( Key( "force-outside-value" ), &forceOutsideValue, "Force values outside field of view to this value rather than drop incomplete pixel pairs", &forceOutsideFlag ); this->m_PreprocessorRef.AttachToCommandLine( cl ); this->m_PreprocessorFlt.AttachToCommandLine( cl ); cl.BeginGroup( "Output", "Output parameters" )->SetProperties( CommandLine::PROPS_NOXML ); cl.AddOption( Key( 'o', "output" ), &this->Studylist, "Output path for final transformation" ); cl.AddOption( Key( "write-matrix" ), &this->OutMatrixName, "Output path for final transformation in matrix format" ); cl.AddOption( Key( "write-parameters" ), &this->OutParametersName, "Output path for final transformation in plain parameter list format" ); cl.AddOption( Key( "write-protocol" ), &this->m_ProtocolFileName, "Optimization protocol output file name" ); cl.AddOption( Key( "write-time" ), &this->Time, "Computation time statistics output file name" ); cl.EndGroup(); cl.BeginGroup( "SlicerImport", "Import Results into Slicer" ); cl.AddOption( Key( "write-itk" ), &this->m_OutputPathITK, "Output path for final transformation in ITK format" ) ->SetProperties( CommandLine::PROPS_XFORM | CommandLine::PROPS_OUTPUT ) ->SetAttribute( "reference", "FloatingImage" ); cl.AddOption( Key( "write-reformatted" ), &this->m_ReformattedImagePath, "Write reformatted floating image." )->SetProperties( CommandLine::PROPS_IMAGE | CommandLine::PROPS_OUTPUT ); cl.EndGroup(); #ifdef CMTK_USE_SQLITE cl.BeginGroup( "Database", "Image/Transformation Database" ); cl.AddOption( Key( "db" ), &this->m_UpdateDB, "Path to image/transformation database that should be updated with the new registration and/or reformatted image." ); cl.EndGroup(); #endif cl.AddParameter( &clArg1, "ReferenceImage", "Reference (fixed) image path" )->SetProperties( CommandLine::PROPS_IMAGE ); cl.AddParameter( &clArg2, "FloatingImage", "Floating (moving) image path" )->SetProperties( CommandLine::PROPS_IMAGE | CommandLine::PROPS_OPTIONAL ); cl.Parse( argc, argv ); } catch ( const CommandLine::Exception& ex ) { StdErr << ex << "\n"; throw cmtk::ExitException( 1 ); } if ( (this->m_OptimizerStepFactor <= 0) || (this->m_OptimizerStepFactor >= 1) ) { StdErr << "ERROR: step factor value " << this->m_OptimizerStepFactor << " is invalid. Must be in range (0..1)\n"; throw cmtk::ExitException( 1 ); } // check for supported numbers of degrees of freedom const std::set supportedDOFs = AffineXform::GetSupportedDOFs(); for ( std::vector::iterator it = this->NumberDOFs.begin(); it != this->NumberDOFs.end(); ++it ) { if ( *it == 603 ) // fix legacy value *it = 3303; if ( supportedDOFs.find( *it ) == supportedDOFs.end() ) { StdErr << "ERROR: DOF number " << *it << " is not supported.\n"; throw cmtk::ExitException( 1 ); } } // check for supported numbers of degrees of freedom for ( std::vector::iterator it = this->NumberDOFsFinal.begin(); it != this->NumberDOFsFinal.end(); ++it ) { if ( *it == 603 ) // fix legacy value *it = 3303; if ( supportedDOFs.find( *it ) == supportedDOFs.end() ) { StdErr << "ERROR: DOF number " << *it << " is not supported.\n"; throw cmtk::ExitException( 1 ); } } if ( ! clArg2.empty() ) { this->Study1 = clArg1; this->Study2 = clArg2; } else { inStudylist = clArg1; if ( ! InitialStudylist.empty() ) { StdErr << "WARNING: transformation of input studylist will be overriden by transformation provided with '--initial'.\n"; } DebugOutput( 1 ) << "Reading input studylist " << inStudylist << ".\n"; ClassStreamInput typedStream( MountPoints::Translate(inStudylist), "registration" ); if ( ! typedStream.IsValid() ) { StdErr << "ERROR: could not open studylist archive " << inStudylist << ".\n"; throw cmtk::ExitException( 1 ); } typedStream.Seek ( "registration" ); this->Study1 = typedStream.ReadStdString( "reference_study" ); this->Study2 = typedStream.ReadStdString( "floating_study" ); if ( !this->Study2.empty() ) { AffineXform::SmartPtr affineXform; typedStream >> affineXform; this->SetInitialTransformation( affineXform ); } else { // legacy studylists have inverse transformation in them this->Study2 = typedStream.ReadStdString( "model_study" ); AffineXform::SmartPtr affineXform; typedStream >> affineXform; try { this->SetInitialTransformation( affineXform->GetInverse() ); } catch ( const AffineXform::MatrixType::SingularMatrixException& ) { StdErr << "ERROR: singular matrix from legacy transformation file cannot be inverted in ImagePairAffineRegistrationCommandLine constructor.\n"; throw ExitException( 1 ); } } typedStream.Close(); } if ( this->Study1.empty() ) { StdErr << "ERROR: reference image path is empty.\n"; throw cmtk::ExitException( 1 ); } if ( this->Study2.empty() ) { StdErr << "ERROR: floating image path is empty.\n"; throw cmtk::ExitException( 1 ); } UniformVolume::SmartPtr volume( VolumeIO::ReadOriented( this->Study1 ) ); if ( !volume ) { StdErr << "ERROR: volume " << this->Study1 << " could not be read\n"; throw cmtk::ExitException( 1 ); } this->SetVolume_1( UniformVolume::SmartPtr( this->m_PreprocessorRef.GetProcessedImage( volume ) ) ); volume = UniformVolume::SmartPtr( VolumeIO::ReadOriented( this->Study2 ) ); if ( !volume ) { StdErr << "ERROR: volume " << this->Study2 << " could not be read\n"; throw cmtk::ExitException( 1 ); } this->SetVolume_2( UniformVolume::SmartPtr( this->m_PreprocessorFlt.GetProcessedImage( volume ) ) ); if ( ! InitialStudylist.empty() ) { Xform::SmartPtr xform( XformIO::Read( InitialStudylist ) ); if ( ! xform ) { StdErr << "ERROR: could not read transformation from " << InitialStudylist << "\n"; throw cmtk::ExitException( 1 ); } AffineXform::SmartPtr affine( AffineXform::SmartPtr::DynamicCastFrom( xform ) ); if ( ! affine ) { StdErr << "ERROR: transformation " << InitialStudylist << " is not affine.\n"; throw cmtk::ExitException( 1 ); } if ( affine->GetMetaInfo( META_SPACE ) != AnatomicalOrientation::ORIENTATION_STANDARD ) { try { TransformChangeFromSpaceAffine toStandardSpace( *affine, *(this->m_Volume_1), *(this->m_Volume_2) ); *affine = toStandardSpace.GetTransformation(); affine->SetMetaInfo(META_SPACE, AnatomicalOrientation::ORIENTATION_STANDARD ); } catch ( const AffineXform::MatrixType::SingularMatrixException& ) { StdErr << "ERROR: singular matrix cannot be inverted to change transformation to standard space in ImagePairAffineRegistrationCommandLine constructor.\n"; throw ExitException( 1 ); } } this->SetInitialTransformation( affine ); } if ( this->m_Initializer != MakeInitialAffineTransformation::NONE ) { if ( ! (inStudylist.empty() && InitialStudylist.empty()) ) { StdErr << "INFO: initial transformation was provided. Selected transformation initializer will be ignored.\n"; } } if ( !this->m_ProtocolFileName.empty() ) { RegistrationCallback::SmartPtr callback( new ProtocolCallback( this->m_ProtocolFileName ) ); this->SetCallback( callback ); } if ( forceOutsideFlag ) { this->SetForceOutside( true, forceOutsideValue ); } } CallbackResult ImagePairAffineRegistrationCommandLine::InitRegistration () { CallbackResult Result = Superclass::InitRegistration(); return Result; } void ImagePairAffineRegistrationCommandLine::OutputResultMatrix( const std::string& matrixName ) const { const AffineXform::MatrixType& matrix = this->GetTransformation()->Matrix; FILE* mfile = fopen( matrixName.c_str(), "w" ); if ( mfile ) { for ( int i = 0; i < 4; ++i ) { fprintf( mfile, "%e\t%e\t%e\t%e\n", static_cast( matrix[0][i] ), static_cast( matrix[1][i] ), static_cast( matrix[2][i] ), static_cast( matrix[3][i] ) ); } fclose( mfile ); } } void ImagePairAffineRegistrationCommandLine::OutputResultParameters ( const std::string& paramsName, const CoordinateVector& v ) const { FILE* pfile = fopen( paramsName.c_str(), "w" ); if ( pfile ) { for ( unsigned int idx=0; idx < v.Dim; ++idx ) fprintf( pfile, "#%u: %f\n", idx, v.Elements[idx] ); fclose( pfile ); } } void ImagePairAffineRegistrationCommandLine::OutputResultList( const std::string& studyList ) const { ClassStreamOutput classStream( studyList, "studylist", ClassStreamOutput::MODE_WRITE ); if ( !classStream.IsValid() ) return; classStream.Begin( "studylist" ); classStream.WriteInt( "num_sources", 2 ); classStream.End(); classStream.Begin( "source" ); classStream.WriteString( "studyname", CompressedStream::GetBaseName( Study1 ) ); classStream.End(); classStream.Begin( "source" ); classStream.WriteString( "studyname", CompressedStream::GetBaseName( Study2 ) ); classStream.End(); classStream.Close(); classStream.Open( studyList, "registration", ClassStreamOutput::MODE_WRITE ); classStream.Begin( "registration" ); classStream.WriteString( "reference_study", CompressedStream::GetBaseName( Study1 ) ); classStream.WriteString( "floating_study", CompressedStream::GetBaseName( Study2 ) ); classStream << *(this->GetTransformation()); classStream.End(); classStream.Close(); classStream.Open( studyList, "settings", ClassStreamOutput::MODE_WRITE ); classStream.WriteDouble( "exploration", this->m_MaxStepSize ); classStream.WriteDouble( "accuracy", this->m_MinStepSize ); classStream.WriteDouble( "min_sampling", this->m_Sampling ); classStream.WriteDouble( "coarsest_resolution", this->m_CoarsestResolution ); classStream.WriteInt( "metric", this->m_Metric ); classStream.WriteDouble( "optimizer_step_factor", this->m_OptimizerStepFactor ); classStream.WriteString( "initializer", MakeInitialAffineTransformation::GetModeName( this->m_Initializer ) ); this->m_PreprocessorRef.WriteSettings( classStream ); this->m_PreprocessorFlt.WriteSettings( classStream ); classStream.Close(); classStream.Open( studyList, "statistics", ClassStreamOutput::MODE_WRITE ); classStream.WriteDouble( "time", this->GetTotalElapsedTime() ); classStream.WriteDouble( "walltime", this->GetTotalElapsedWalltime() ); #ifdef CMTK_USE_PTHREADS classStream.WriteDouble( "thread_time", this->GetThreadTotalElapsedTime() ); #endif #ifndef _MSC_VER struct utsname name; if ( uname( &name ) >= 0 ) { classStream.WriteString( "host", name.nodename ); classStream.WriteString( "system", name.sysname ); } #endif classStream.Close(); } void ImagePairAffineRegistrationCommandLine::OutputResult ( const CoordinateVector* v, const CallbackResult irq ) { DebugOutput( 1 ) << "Resulting transformation parameters: \n"; for ( unsigned int idx=0; idxDim; ++idx ) DebugOutput( 1 ).GetStream().printf( "#%u: %f\n", idx, v->Elements[idx] ); if ( !this->OutMatrixName.empty() ) { if ( irq != CALLBACK_OK ) this->OutputResultMatrix( this->OutMatrixName + "-partial" ); else this->OutputResultMatrix( this->OutMatrixName ); } if ( !this->OutParametersName.empty() ) { if ( irq != CALLBACK_OK ) this->OutputResultParameters( this->OutParametersName + "-partial", *v ); else this->OutputResultParameters( this->OutParametersName, *v ); } if ( !this->Studylist.empty() ) { if ( irq != CALLBACK_OK ) this->OutputResultList( this->Studylist + "-partial" ); else this->OutputResultList( this->Studylist ); } if ( !this->m_OutputPathITK.empty() ) { try { TransformChangeToSpaceAffine toNative( *(this->GetTransformation()), *(this->m_Volume_1), *(this->m_Volume_2), AnatomicalOrientationBase::SPACE_ITK ); if ( irq != CALLBACK_OK ) AffineXformITKIO::Write( this->m_OutputPathITK + "-partial", toNative.GetTransformation() ); else AffineXformITKIO::Write( this->m_OutputPathITK, toNative.GetTransformation() ); } catch ( const AffineXform::MatrixType::SingularMatrixException& ) { StdErr << "ERROR: singular matrix cannot be inverted to change transformation to standard space in ImagePairAffineRegistrationCommandLine::OutputResult\n"; throw ExitException( 1 ); } } if ( !this->m_ReformattedImagePath.empty() ) { if ( irq != CALLBACK_OK ) VolumeIO::Write( *(this->GetReformattedFloatingImage()), this->m_ReformattedImagePath + "-partial" ); else VolumeIO::Write( *(this->GetReformattedFloatingImage()), this->m_ReformattedImagePath ); } #ifdef CMTK_USE_SQLITE if ( !this->m_UpdateDB.empty() ) { try { ImageXformDB db( this->m_UpdateDB ); if ( !this->m_ReformattedImagePath.empty() ) { db.AddImage( this->m_ReformattedImagePath, this->m_ReferenceVolume->GetMetaInfo( META_FS_PATH ) ); } if ( ! this->Studylist.empty() ) { if ( ! this->m_InitialXformPath.empty() ) { db.AddRefinedXform( this->Studylist, true /*invertible*/, this->m_InitialXformPath, this->m_InitialXformIsInverse ); } else { db.AddImagePairXform( this->Studylist, true /*invertible*/, this->m_ReferenceVolume->GetMetaInfo( META_FS_PATH ), this->m_FloatingVolume->GetMetaInfo( META_FS_PATH ) ); } } } catch ( const ImageXformDB::Exception& ex ) { StdErr << "DB ERROR: " << ex.what() << " on database " << this->m_UpdateDB << "\n"; } } #endif } void ImagePairAffineRegistrationCommandLine::EnterResolution ( CoordinateVector::SmartPtr& v, Functional::SmartPtr& f, const int index, const int total ) { DebugOutput( 1 ).GetStream().printf( "\rEntering resolution level %d out of %d...\n", index, total ); this->Superclass::EnterResolution( v, f, index, total ); } CallbackResult ImagePairAffineRegistrationCommandLine::Register () { const double baselineTime = Timers::GetTimeProcess(); CallbackResult Result = Superclass::Register(); const int elapsed = static_cast( Timers::GetTimeProcess() - baselineTime ); if ( !this->Time.empty() ) { FILE *tfp = fopen( this->Time.c_str(), "w" ); if ( tfp ) { fprintf( tfp, "%d\n", elapsed ); fclose( tfp ); } else { std::cerr << "Could not open time file " << Time << "\n"; } } return Result; } } // namespace cmtk cmtk-3.3.1/libs/Registration/cmtkImagePairAffineRegistrationCommandLine.h000066400000000000000000000126711276303427400265770ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2011, 2013 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4835 $ // // $LastChangedDate: 2013-09-12 15:48:47 -0700 (Thu, 12 Sep 2013) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkImagePairAffineRegistrationCommandLine_h_included_ #define __cmtkImagePairAffineRegistrationCommandLine_h_included_ #include #include namespace cmtk { /** \addtogroup Registration */ //@{ /** Class for command line-controlled affine registration. *\author T. Rohlfing */ class ImagePairAffineRegistrationCommandLine : /// Inherit generic affine registration. public ImagePairAffineRegistration { public: /// This class. typedef ImagePairAffineRegistrationCommandLine Self; /// Smart pointer. typedef SmartPointer SmartPtr; /// Convenience typedef. typedef ImagePairAffineRegistration Superclass; /** Constructor. *\param argc Number of command line arguments; this should be the argc * parameter of the main() function. *\param argv Array of command line arguments; this should be the argv * parameter of the main() function. *\exception ConstructorFailed This exception is thrown if there where * invalid or unknown options or missing required parameters. In all these * cases, an information text describing the known options will have been * written to the standard error stream before throwing the exception. */ ImagePairAffineRegistrationCommandLine ( const int argc, const char *argv [] ); /** Perform registration. */ virtual CallbackResult Register (); protected: /** Initialize registration. * So far, this function has no effect other than calling the equivalent * inherited function. */ virtual CallbackResult InitRegistration(); /** Output registration result. * This function write the transformation that was found to a studylist * archive with the name provided by command line arguments. The result is * also printed to stderr in parameter list form. *\param v The vector of resulting transformation parameters. *\param irq The interrupt status - this allows the output function to determine whether computation finished or was interrupted. */ virtual void OutputResult ( const CoordinateVector* v, const CallbackResult irq = CALLBACK_OK ); /** Enter resolution level. * An information is printed to stderr and to the protocol file if one is * written. */ virtual void EnterResolution( CoordinateVector::SmartPtr&, Functional::SmartPtr&, const int, const int ); private: /** Path of the actual input transformation, if any. * If two input transformations are specified, i.e., one as the input studylist and one via * the "--initial" command line switch, then this variable holds the path of the transformation * that was actually used (the one specified with "--initial"). This is used when the optional * image/transformation database is updated. */ std::string m_InitialXformPath; /// Path for reformatted floating image. std::string m_ReformattedImagePath; /** Name of output studylist. * This is defined by the -o or --outlist command line option. */ std::string Studylist; /** Name of the output matrix file. * This is defined by the "--out-matrix" command line argument. */ std::string OutMatrixName; /** Name of the output parameter file. * This is defined by the "--out-params" command line argument. */ std::string OutParametersName; /// Name of output transformation file in ITK format. std::string m_OutputPathITK; /** Name of first study to be registered. * This is given as the first non-option command line paramter. */ std::string Study1; /** Name of second study to be registered. * This is given as the second non-option command line paramter. */ std::string Study2; #ifdef CMTK_USE_SQLITE /// Database to update after registration completes. std::string m_UpdateDB; #endif /** Name of elapsed time output file. * This is defined by the -t or --time command line option. */ std::string Time; /// Protocol file name. std::string m_ProtocolFileName; /// Output result as matrix (text) file. void OutputResultMatrix( const std::string& matrixName ) const; /// Output result (and statistics) as studylist archive. void OutputResultParameters( const std::string& paramsName, const CoordinateVector& v ) const; /// Output result (and statistics) as studylist archive. void OutputResultList( const std::string& studyList ) const; }; //@} } // namespace cmtk #endif // #ifndef _COMMANDLINEVOXELREGISTRATION_H_ cmtk-3.3.1/libs/Registration/cmtkImagePairAffineRegistrationFunctional.cxx000066400000000000000000000074761276303427400270750ustar00rootroot00000000000000/* // // Copyright 2016 Google, Inc. // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2012 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 5398 $ // // $LastChangedDate: 2016-01-14 18:29:18 -0800 (Thu, 14 Jan 2016) $ // // $LastChangedBy: torstenrohlfing $ // */ #include #include #include #include #include #include #include #include namespace cmtk { /** \addtogroup Registration */ //@{ Types::Coordinate ImagePairAffineRegistrationFunctional::GetParamStep( const size_t idx, const Types::Coordinate mmStep ) const { switch ( this->m_RestrictToInPlane ) { case 0: switch ( idx ) { case 0: //xlate x case 4: //rot y case 5: //rot z case 6: //scale x case 9: //shear xy case 10: //shear xz return 0.0; default: break; } break; case 1: switch ( idx ) { case 1: //xlate y case 3: //rot x case 5: //rot z case 7: //scale y case 9: //shear xy case 11: //shear yz return 0.0; default: break; } break; case 2: switch ( idx ) { case 2: //xlate z case 3: //rot x case 4: //rot y case 8: //scale z case 10: //shear xz case 11: //shear yz return 0.0; default: break; } break; default: break; } return this->m_AffineXform->GetParamStep( idx, this->m_FloatingSize, mmStep ); } ImagePairAffineRegistrationFunctional* ImagePairAffineRegistrationFunctional ::Create ( const int metric, UniformVolume::SmartPtr& refVolume, UniformVolume::SmartPtr& fltVolume, const Interpolators::InterpolationEnum interpolation, AffineXform::SmartPtr& affineXform ) { switch ( metric ) { case 0: return new ImagePairAffineRegistrationFunctionalTemplate( refVolume, fltVolume, interpolation, affineXform ); case 1: return new ImagePairAffineRegistrationFunctionalTemplate( refVolume, fltVolume, interpolation, affineXform ); case 2: return new ImagePairAffineRegistrationFunctionalTemplate( refVolume, fltVolume, interpolation, affineXform ); case 3: return new ImagePairAffineRegistrationFunctionalTemplate( refVolume, fltVolume, interpolation, affineXform ); case 4: return new ImagePairAffineRegistrationFunctionalTemplate( refVolume, fltVolume, interpolation, affineXform ); case 5: return new ImagePairAffineRegistrationFunctionalTemplate( refVolume, fltVolume, interpolation, affineXform ); default: break; } return NULL; } } // namespace cmtk cmtk-3.3.1/libs/Registration/cmtkImagePairAffineRegistrationFunctional.h000066400000000000000000000272751276303427400265210ustar00rootroot00000000000000/* // // Copyright 2016 Google, Inc. // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2012 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 5398 $ // // $LastChangedDate: 2016-01-14 18:29:18 -0800 (Thu, 14 Jan 2016) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkImagePairAffineRegistrationFunctional_h_included_ #define __cmtkImagePairAffineRegistrationFunctional_h_included_ #include #include #include #include #include #include #include #include #include #include #include #include namespace cmtk { /** \addtogroup Registration */ //@{ /** Base-class for affine registration functionals. */ class ImagePairAffineRegistrationFunctional : /// Inherit from voxel matching functional. public ImagePairRegistrationFunctional { public: /// This class type. typedef ImagePairAffineRegistrationFunctional Self; /// Superclass. typedef ImagePairRegistrationFunctional Superclass; /// Smart pointer to this class. typedef SmartPointer SmartPtr; /// Return parameter vector. virtual void GetParamVector ( CoordinateVector& v ) { this->m_AffineXform->GetParamVector( v ); } /// Return parameter stepping. virtual Types::Coordinate GetParamStep( const size_t idx, const Types::Coordinate mmStep = 1 ) const; /// Return the transformation's parameter vector dimension. virtual size_t ParamVectorDim() const { return this->m_AffineXform->ParamVectorDim(); } /// Return the number of variable parameters of the transformation. virtual size_t VariableParamVectorDim() const { return this->m_AffineXform->VariableParamVectorDim(); } /// Set optional restriction to axis-orthogonal in-plane transformations. void SetRestrictToInPlane( const int axis ) { this->m_RestrictToInPlane = axis; } protected: /// Current coordinate transformation. AffineXform::SmartPtr m_AffineXform; /** Restrict to in-plance transformations. * If this is -1 (default), then the full 3D transformation is optimized. If set to 0, 1, or 2, the * value is the index of a coordinate axis (x, y, or z), and the transformation is restricted to * components in-plane perpendicular to that axis, e.g., rotation around the axis and translations * perpendicular to it. */ int m_RestrictToInPlane; /// Utility object for volume clipping. VolumeClipping Clipper; /** Perform clipping/cropping in z-direction. * This function computes the intersection of reference and floating data in * z-direction. It determines the range of indices of those planes in the * reference that intersect the floating. This is the range over which to * for-loop during metric computation. *\param clipper A volume clipping object with clipping boundaries and grid * orientation set. *\param origin Starting point of the reference volume. *\param start Upon return, this reference is set to the index of first plane * in the reference that intersects the floating. *\param end Upon return, this reference is set to one plus the index of the * last plane in the reference that intersects the floating. *\return 1 if there is an intersection of reference and floating, 0 if there * isn't. The range of indices returned in "start" and "end" is only * guaranteed to be valid if 1 is the return value. */ Types::GridIndexType ClipZ ( const VolumeClipping& clipper, const Vector3D& origin, DataGrid::IndexType::ValueType& start, DataGrid::IndexType::ValueType &end ) const { // perform clipping Types::Coordinate fromFactor, toFactor; if (! clipper.ClipZ( fromFactor, toFactor, origin ) ) return 0; // there is an intersection: Look up the corresponding grid indices start = static_cast( (this->m_ReferenceDims[2]-1)*fromFactor ); end = 1+std::min( (Types::GridIndexType)(this->m_ReferenceDims[2]-1), (Types::GridIndexType)(1 + ((this->m_ReferenceDims[2]-1)*toFactor) ) ); // finally, apply cropping boundaries of the reference volume start = std::max( start, this->m_ReferenceCropRegion.From()[2] ); end = std::min( end, this->m_ReferenceCropRegion.To()[2] ); // return 1 iff index range is non-empty. return (start < end ); } /** Perform clipping/cropping in x-direction. * This function computes the intersection of reference and floating data in * x-direction. It determines the range of indices of those voxels in the * current reference row that intersect the floating image. This is the range * over which to for-loop during metric computation. * * Compared to ClipZ and ClipY, this step has to operate very exact as there * is no further level that would reduce remaining invalid voxels. Therefore, * clipper.ClipX() is called with an extended initial range of indices and an * explicitly open upper bound. * * This is necessary to discriminate inside-boundary from on-boundary voxels. * For the right, upper and back boundary, on-boundary voxels are already * outside the allowed range as the upper boundaries of the volume are open * in terms of interpolation. *\param clipper A volume clipping object with clipping boundaries and grid * orientation set. *\param origin Starting point of the current row in the reference volume. *\param start Upon return, this reference is set to the index of first voxel * in the reference that intersects the floating image. *\param end Upon return, this reference is set to one plus the index of the * last voxel in the reference that intersects the floating image. *\return 1 if there is an intersection of the current reference row and * the floating, 0 if there isn't. The range of indices returned in "start" * and "end" is only guaranteed to be valid if 1 is the return value. */ Types::GridIndexType ClipX ( const VolumeClipping& clipper, const Vector3D& origin, DataGrid::IndexType::ValueType& start, DataGrid::IndexType::ValueType &end ) const { // perform clipping Types::Coordinate fromFactor, toFactor; if ( ! clipper.ClipX( fromFactor, toFactor, origin, 0, 2, false, true ) ) return 0; fromFactor = std::min( 1.0, fromFactor ); // there is an intersection: Look up the corresponding grid indices start = std::max( 0, (Types::GridIndexType)((this->m_ReferenceDims[0]-1)*fromFactor)-1 ); while ( ( start*this->m_ReferenceGrid->m_Delta[0] < fromFactor*this->m_ReferenceSize[0]) && ( start < this->m_ReferenceDims[0] ) ) ++start; if ( (toFactor > 1.0) || (start == this->m_ReferenceDims[0]) ) { end = this->m_ReferenceDims[0]; } else { end = std::min( this->m_ReferenceDims[0]-2, (Types::GridIndexType)(1 + (this->m_ReferenceDims[0]-1)*toFactor)); while ( end*this->m_ReferenceGrid->m_Delta[0] > toFactor*this->m_ReferenceSize[0] ) // 'if' not sufficient! --end; ++end; // otherwise end=1+min(...) and ...[0][end-1] above!! } // finally, apply cropping boundaries of the reference volume start = std::max( start, this->m_ReferenceCropRegion.From()[0] ); end = std::min( end, this->m_ReferenceCropRegion.To()[0] ); // return 1 iff index range is non-empty. return (start < end ); } /** Perform clipping/cropping in y-direction. * This function computes the intersection of reference and floating data in * y-direction. It determines the range of indices of those rows in the * current reference plane that intersect the floating image. This is the * range over which to for-loop during metric computation. *\param clipper A volume clipping object with clipping boundaries and grid * orientation set. *\param origin Starting point of the current plane in the reference volume. *\param start Upon return, this reference is set to the index of first row * in the reference that intersects the floating image. *\param end Upon return, this reference is set to one plus the index of the * last row in the reference that intersects the floating image. *\return 1 if there is an intersection of the current reference plane and * the floating, 0 if there isn't. The range of indices returned in "start" * and "end" is only guaranteed to be valid if 1 is the return value. */ Types::GridIndexType ClipY ( const VolumeClipping& clipper, const Vector3D& origin, DataGrid::IndexType::ValueType& start, DataGrid::IndexType::ValueType &end ) const { // perform clipping Types::Coordinate fromFactor, toFactor; if ( !clipper.ClipY( fromFactor, toFactor, origin ) ) return 0; // there is an intersection: Look up the corresponding grid indices start = static_cast( (this->m_ReferenceDims[1]-1)*fromFactor ); if ( toFactor > 1.0 ) { end = this->m_ReferenceDims[1]; } else { end = 1+std::min( this->m_ReferenceDims[1]-1, (Types::GridIndexType)(1+(this->m_ReferenceDims[1]-1)*toFactor ) ); } // finally, apply cropping boundaries of the reference volume start = std::max( start, this->m_ReferenceCropRegion.From()[1] ); end = std::min( end, this->m_ReferenceCropRegion.To()[1] ); // return 1 iff index range is non-empty. return (start < end ); } public: /// Constructor. ImagePairAffineRegistrationFunctional( UniformVolume::SmartPtr refVolume, UniformVolume::SmartPtr modVolume, AffineXform::SmartPtr& affineXform ) : ImagePairRegistrationFunctional( refVolume, modVolume ), m_AffineXform( affineXform ), m_RestrictToInPlane( -1 ) {} /// Destructor. virtual ~ImagePairAffineRegistrationFunctional() {} /** Constructor function for affine voxel registration functionals. * This function takes the index of a metric in the list of available voxel * similarity measures plus all required objects. It the creates an appropriate * instance of ImagePairAffineRegistrationFunctional with the correct metric class as template * parameter. */ static ImagePairAffineRegistrationFunctional* Create( const int metric /*!< Index of image similarity measure.*/, UniformVolume::SmartPtr& refVolume /*!< Reference volume.*/, UniformVolume::SmartPtr& fltVolume /*!< Floating volume*/, const Interpolators::InterpolationEnum interpolation /*!< Floating volume intnerpolation.*/, AffineXform::SmartPtr& affineXform /*!< Use this affine transformation.*/ ); }; //@} } // namespace cmtk #endif // #ifndef __cmtkImagePairAffineRegistrationFunctional_h_included_ cmtk-3.3.1/libs/Registration/cmtkImagePairAffineRegistrationFunctionalTemplate.h000066400000000000000000000156061276303427400302100ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2011 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 2752 $ // // $LastChangedDate: 2011-01-17 11:33:31 -0800 (Mon, 17 Jan 2011) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkImagePairAffineRegistrationFunctionalTemplate_h_included_ #define __cmtkImagePairAffineRegistrationFunctionalTemplate_h_included_ #include #include #include #include #ifdef CMTK_BUILD_DEMO # include #endif // #ifdef CMTK_BUILD_DEMO namespace cmtk { /** \addtogroup Registration */ //@{ /** Functional that evaluates a voxel-based similarity measure. * This class defines the type of functional that is optimized during * voxel-based registration. It holds references to reference and floating data * and computes similarity as well as its gradient w.r.t. a given * transformation. * * The metric to be optimized is given by a template parameter, therefore * allowing inlined code to be generated for efficient evaluation. */ template class ImagePairAffineRegistrationFunctionalTemplate : /// Inherit from affine voxel matching functional public ImagePairAffineRegistrationFunctional { public: /// This class type. typedef ImagePairAffineRegistrationFunctionalTemplate Self; /// Smart pointer to this class. typedef SmartPointer SmartPtr; /// Superclass. typedef ImagePairAffineRegistrationFunctional Superclass; /// Return type. typedef Functional::ReturnType ReturnType; /** Constructor. * Init pointers to volume and transformation objects and initialize * internal data structures. *\param reference The reference (i.e. static) volume. *\param floating The floating (i.e. transformed) volume. *\param interpolation ID of the interpolator to use for the floating image. *\param affineXform A transformation template. This object determines the type * of transformation to be optimized. Its initial value is not relevant. */ ImagePairAffineRegistrationFunctionalTemplate( UniformVolume::SmartPtr& reference, UniformVolume::SmartPtr& floating, const Interpolators::InterpolationEnum interpolation, AffineXform::SmartPtr& affineXform ) : ImagePairAffineRegistrationFunctional( reference, floating, affineXform ), m_NumberOfThreads( ThreadPool::GetGlobalThreadPool().GetNumberOfThreads() ) { this->m_Metric = ImagePairSimilarityMeasure::SmartPtr( new VM( reference, floating, interpolation ) ); this->m_ThreadMetric.resize( m_NumberOfThreads, dynamic_cast( *(this->m_Metric) ) ); } /// Destructor. virtual ~ImagePairAffineRegistrationFunctionalTemplate() {} /// Evaluate with new parameter vector. virtual typename Self::ReturnType EvaluateAt ( CoordinateVector& v ) { this->m_AffineXform->SetParamVector( v ); return this->Evaluate(); } #ifdef CMTK_BUILD_DEMO /// Create a snapshot (to disk) of current functional result. virtual void SnapshotAt( ParameterVectorType& v ) { this->m_AffineXform->SetParamVector( v ); static int it = 0; char path[PATH_MAX]; snprintf( path, PATH_MAX, "registration-%03d.xform", it++ ); XformIO::Write( this->m_AffineXform, path ); } #endif /** Compute functional value with volume clipping. * This function iterates over all voxels of the reference image that - after * applying the current coordinate transformation - are located inside the * mode image. This set of voxels is determined on-the-fly by an extension of * Liang and Barsky's "Parameterized Line-Clipping" technique. * * From the resulting sequence of reference/floating voxel pairs, the * selected voxel-based similarity measure (metric) is computed. *\return The computed similarity measure as returned by the "Metric" * subobject. *\see VolumeClipping */ virtual typename Self::ReturnType Evaluate(); /** Number of threads that this object was created for. * This is the actual maximum number of threads running at any time, but not * necessarily the number of parallel tasks to be completed. * All duplicated data structures are generated with the multiplicity given * by this value. It is determined from Threads when the object is first * instanced. It cannot be changed afterwards. */ size_t m_NumberOfThreads; /// Metric objects for the separate threads. std::vector m_ThreadMetric; /// Mutex lock for access to global Metric field. MutexLock m_MetricMutex; /** Thread parameter block for incremental gradient computation. * This structure holds all thread-specific information. A pointer to an * instance of this structure is given to EvaluateGradientThread() for * each thread created. */ typedef struct { /// Pointer to the functional object that created the thread. Self *thisObject; /// Axes hash. const TransformedVolumeAxes* AxesHash; /// First plane of clipped reference volume. DataGrid::IndexType::ValueType StartZ; /// Last plane of clipped reference volume. DataGrid::IndexType::ValueType EndZ; } EvaluateTaskInfo; /// Info blocks for parallel threads evaluating functional gradient. std::vector m_EvaluateTaskInfo; /** Compute functional gradient as a thread. * This function (i.e., each thread) iterates over all parameters of the * current warp transformation. Among all active (i.e., not disabled) * parameters, it selects the ones that have an index with modulus * equal to the threads index when divided by the total number of threads. * For these parameters, the thread computes the partial derivative of the * functional by finite-difference approximation. */ static void EvaluateThread( void *const args, const size_t taskIdx, const size_t taskCnt, const size_t threadIdx, const size_t ); }; //@} } // namespace cmtk #include "cmtkImagePairAffineRegistrationFunctionalTemplate.txx" #endif // #ifndef __cmtkImagePairAffineRegistrationFunctionalTemplate_h_included_ cmtk-3.3.1/libs/Registration/cmtkImagePairAffineRegistrationFunctionalTemplate.txx000066400000000000000000000133721276303427400306020ustar00rootroot00000000000000/* // // Copyright 2016 Google, Inc. // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2010 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 5398 $ // // $LastChangedDate: 2016-01-14 18:29:18 -0800 (Thu, 14 Jan 2016) $ // // $LastChangedBy: torstenrohlfing $ // */ #include namespace cmtk { template typename ImagePairAffineRegistrationFunctionalTemplate::ReturnType ImagePairAffineRegistrationFunctionalTemplate ::Evaluate() { const TransformedVolumeAxes axesHash( *this->m_ReferenceGrid, this->m_AffineXform, this->m_FloatingGrid->Deltas().begin(), this->m_FloatingGrid->m_Offset.begin() ); const Vector3D *axesHashX = axesHash[0], *axesHashY = axesHash[1], *axesHashZ = axesHash[2]; this->m_Metric->Reset(); const DataGrid::IndexType& Dims = this->m_ReferenceGrid->GetDims(); const Types::GridIndexType DimsX = Dims[0], DimsY = Dims[1], DimsZ = Dims[2]; this->Clipper.SetDeltaX( axesHashX[DimsX-1] - axesHashX[0] ); this->Clipper.SetDeltaY( axesHashY[DimsY-1] - axesHashY[0] ); this->Clipper.SetDeltaZ( axesHashZ[DimsZ-1] - axesHashZ[0] ); this->Clipper.SetClippingBoundaries( this->m_FloatingCropRegionFractIndex ); DataGrid::IndexType::ValueType startZ, endZ; if ( this->ClipZ( this->Clipper, axesHashZ[0], startZ, endZ ) ) { startZ = std::max( startZ, this->m_ReferenceCropRegion.From()[2] ); endZ = std::min( endZ, this->m_ReferenceCropRegion.To()[2] + 1 ); const int numberOfTasks = std::min( 4 * this->m_NumberOfThreads - 3, endZ - startZ + 1 ); this->m_EvaluateTaskInfo.resize( numberOfTasks ); for ( int taskIdx = 0; taskIdx < numberOfTasks; ++taskIdx ) { this->m_EvaluateTaskInfo[taskIdx].thisObject = this; this->m_EvaluateTaskInfo[taskIdx].AxesHash = &axesHash; this->m_EvaluateTaskInfo[taskIdx].StartZ = startZ; this->m_EvaluateTaskInfo[taskIdx].EndZ = endZ; } ThreadPool::GetGlobalThreadPool().Run( EvaluateThread, this->m_EvaluateTaskInfo ); } return this->m_Metric->Get(); } template void ImagePairAffineRegistrationFunctionalTemplate ::EvaluateThread( void *const args, const size_t taskIdx, const size_t taskCnt, const size_t threadIdx, const size_t ) { typename Self::EvaluateTaskInfo *info = static_cast( args ); Self *me = info->thisObject; VM& metric = dynamic_cast( *(me->m_Metric) ); VM& threadMetric = me->m_ThreadMetric[threadIdx]; threadMetric.Reset(); const Vector3D *hashX = (*info->AxesHash)[0], *hashY = (*info->AxesHash)[1], *hashZ = (*info->AxesHash)[2]; Vector3D pFloating; const DataGrid::IndexType& Dims = me->m_ReferenceGrid->GetDims(); const Types::GridIndexType DimsX = Dims[0], DimsY = Dims[1]; Types::GridIndexType fltIdx[3]; Types::Coordinate fltFrac[3]; Vector3D rowStart; Vector3D planeStart; DataGrid::IndexType::ValueType pX, pY, pZ; // Loop over all remaining planes for ( pZ = info->StartZ + taskIdx; pZ < info->EndZ; pZ += taskCnt ) { // Offset of current reference voxel Types::GridIndexType r = pZ * DimsX * DimsY; planeStart = hashZ[pZ]; DataGrid::IndexType::ValueType startY, endY; if ( me->ClipY( me->Clipper, planeStart, startY, endY ) ) { startY = std::max( startY, me->m_ReferenceCropRegion.From()[1] ); endY = std::min( endY, me->m_ReferenceCropRegion.To()[1] + 1 ); r += startY * DimsX; // Loop over all remaining rows for ( pY = startY; pYClipX( me->Clipper, rowStart, startX, endX ) ) { startX = std::max( startX, me->m_ReferenceCropRegion.From()[0] ); endX = std::min( endX, me->m_ReferenceCropRegion.To()[0] + 1 ); r += startX; // Loop over all remaining voxels in current row for ( pX = startX; pXm_FloatingGrid->FindVoxelByIndex( pFloating, fltIdx, fltFrac ) ) { threadMetric.Increment( sampleX, metric.GetSampleY( fltIdx, fltFrac ) ); } else { if ( me->m_ForceOutsideFlag ) { threadMetric.Increment( sampleX, me->m_ForceOutsideValueRescaled ); } } } } r += (DimsX-endX); } else { r += DimsX; } } r += (DimsY-endY) * DimsX; } else { r += DimsY * DimsX; } } me->m_MetricMutex.Lock(); metric.Add( threadMetric ); me->m_MetricMutex.Unlock(); } } // namnespace cmtk cmtk-3.3.1/libs/Registration/cmtkImagePairNonrigidRegistration.cxx000066400000000000000000000323061276303427400254210ustar00rootroot00000000000000/* // // Copyright 2004-2012 SRI International // // Copyright 1997-2009 Torsten Rohlfing // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4426 $ // // $LastChangedDate: 2012-06-12 10:30:56 -0700 (Tue, 12 Jun 2012) $ // // $LastChangedBy: torstenrohlfing $ // */ #include "cmtkImagePairNonrigidRegistration.h" #include #include #include #include #include #include #include #include #include #include namespace cmtk { /** \addtogroup Registration */ //@{ ImagePairNonrigidRegistration::ImagePairNonrigidRegistration () : InitialWarpXform( NULL ), InverseWarpXform( NULL ), m_MatchFltToRefHistogram( false ), m_RepeatMatchFltToRefHistogram( false ), m_InverseConsistencyWeight( 0.0 ), m_RelaxToUnfold( false ) { this->m_Metric = 0; this->m_Algorithm = 3; this->m_GridSpacing = 15; this->m_ExactGridSpacing = 0; this->m_GridSpacing = 10; RestrictToAxes = NULL; this->m_RefineGrid = 0; RefinedGridAtLevel = -1; RefineGridCount = 0; this->m_DelayRefineGrid = 0; RefineDelayed = false; IgnoreEdge = 0; this->m_FastMode = false; this->m_AdaptiveFixParameters = 1; this->m_AdaptiveFixThreshFactor = 0.5; this->m_JacobianConstraintWeight = 0; this->m_GridEnergyWeight = 0; this->m_RelaxWeight = -1; this->m_InverseConsistencyWeight = 0.0; RelaxationStep = false; } CallbackResult ImagePairNonrigidRegistration::InitRegistration () { this->m_ReferenceVolume = this->m_Volume_1; this->m_FloatingVolume = this->m_Volume_2; Vector3D center = this->m_FloatingVolume->GetCenterCropRegion(); this->m_InitialTransformation->ChangeCenter( center ); Types::Coordinate currSampling = std::max( this->m_Sampling, 2 * std::min( this->m_ReferenceVolume->GetMinDelta(), this->m_FloatingVolume->GetMinDelta())); // If no initial transformation exists, create one from the defined // parameters. if ( InitialWarpXform ) { // If we have an initial transformation from somewhere, use that. // This will override all registration parameters otherwise defined, // for example grid spacing and deformation type. InitialWarpXform->SetIgnoreEdge( IgnoreEdge ); InitialWarpXform->SetFastMode( this->m_FastMode ); // MIPSpro needs explicit. this->m_Xform = Xform::SmartPtr::DynamicCastFrom( InitialWarpXform ); } else { SplineWarpXform::SmartPtr warpXform( this->MakeWarpXform( this->m_ReferenceVolume->m_Size, this->m_InitialTransformation ) ); if ( this->m_InverseConsistencyWeight > 0 ) InverseWarpXform = SplineWarpXform::SmartPtr( this->MakeWarpXform( this->m_FloatingVolume->m_Size, this->m_InitialTransformation->GetInverse() ) ); // MIPSpro needs explicit: this->m_Xform = Xform::SmartPtr::DynamicCastFrom( warpXform ); } if ( this->m_MaxStepSize <= 0 ) { const SplineWarpXform* warp = SplineWarpXform::SmartPtr::DynamicCastFrom( this->m_Xform ); this->m_MaxStepSize = 0.25 * std::max( warp->m_Spacing[0], std::max( warp->m_Spacing[1], warp->m_Spacing[2] ) ); } if ( this->m_CoarsestResolution <= 0 ) this->m_CoarsestResolution = this->m_MaxStepSize; if ( this->m_UseOriginalData ) { this->m_ParameterStack.push( Self::LevelParameters::SmartPtr( new Self::LevelParameters( -1 ) ) ); } for ( ;(currSampling<=this->m_CoarsestResolution); currSampling *= 2 ) { this->m_ParameterStack.push( Self::LevelParameters::SmartPtr( new Self::LevelParameters( currSampling ) ) ); } switch ( this->m_Algorithm ) { case 0: this->m_Optimizer = Optimizer::SmartPtr( new BestNeighbourOptimizer( this->m_OptimizerStepFactor ) ); break; case 1: case 2: this->m_Optimizer = Optimizer::SmartPtr( NULL ); break; case 3: { BestDirectionOptimizer *optimizer = new BestDirectionOptimizer( this->m_OptimizerStepFactor ); optimizer->SetUseMaxNorm( this->m_UseMaxNorm ); this->m_Optimizer = Optimizer::SmartPtr( optimizer ); break; } } this->m_Optimizer->SetCallback( this->m_Callback ); return this->Superclass::InitRegistration(); } SplineWarpXform::SmartPtr ImagePairNonrigidRegistration::MakeWarpXform ( const UniformVolume::CoordinateVectorType& size, const AffineXform* initialAffine ) const { SplineWarpXform::SmartPtr warpXform( new SplineWarpXform( size, this->m_GridSpacing, initialAffine, this->m_ExactGridSpacing ) ); warpXform->SetIgnoreEdge( this->IgnoreEdge ); warpXform->SetFastMode( this->m_FastMode ); return warpXform; } Functional* ImagePairNonrigidRegistration::MakeFunctional ( const int level, const Superclass::LevelParameters* parameters ) { const Self::LevelParameters* levelParameters = dynamic_cast( parameters ); if ( ! levelParameters ) { StdErr << "CODING ERROR: wrong RTTI for 'parameters'\n"; exit( 1 ); } WarpXform::SmartPtr warpXform = WarpXform::SmartPtr::DynamicCastFrom( this->m_Xform ); if ( ! warpXform ) { StdErr << "CODING ERROR: wrong RTTI for 'this->m_Xform'\n"; exit( 1 ); } UniformVolume::SmartPtr referenceVolume( this->m_ReferenceVolume ); UniformVolume::SmartPtr floatingVolume( this->m_FloatingVolume ); if ( !level && this->m_MatchFltToRefHistogram ) { floatingVolume = UniformVolume::SmartPtr( floatingVolume->Clone( true /*copyData*/ ) ); floatingVolume->GetData()->ApplyFunctionObject( TypedArrayFunctionHistogramMatching( *(floatingVolume->GetData()), *(referenceVolume->GetData()) ) ); } else { if ( this->m_RepeatMatchFltToRefHistogram ) { floatingVolume = UniformVolume::SmartPtr( floatingVolume->Clone( true /*copyData*/ ) ); UniformVolume::SmartPtr reformat( this->GetReformattedFloatingImage( Interpolators::NEAREST_NEIGHBOR ) ); floatingVolume->GetData()->ApplyFunctionObject( TypedArrayFunctionHistogramMatching( *(reformat->GetData()), *(referenceVolume->GetData()) ) ); } } if ( levelParameters->m_Resolution > 0 ) { // for resample if not final, original resolution referenceVolume = UniformVolume::SmartPtr( referenceVolume->GetResampled( levelParameters->m_Resolution ) ); floatingVolume = UniformVolume::SmartPtr( floatingVolume->GetResampled( levelParameters->m_Resolution ) ); } if ( this->m_InverseConsistencyWeight > 0 ) { ImagePairSymmetricNonrigidRegistrationFunctional *newFunctional = ImagePairSymmetricNonrigidRegistrationFunctional::Create( this->m_Metric, referenceVolume, floatingVolume, this->m_FloatingImageInterpolation ); newFunctional->SetInverseConsistencyWeight( this->m_InverseConsistencyWeight ); newFunctional->SetAdaptiveFixParameters( this->m_AdaptiveFixParameters ); newFunctional->SetAdaptiveFixThreshFactor( this->m_AdaptiveFixThreshFactor ); newFunctional->SetJacobianConstraintWeight( this->m_JacobianConstraintWeight ); newFunctional->SetGridEnergyWeight( this->m_GridEnergyWeight ); return newFunctional; } else { ImagePairNonrigidRegistrationFunctional *newFunctional = ImagePairNonrigidRegistrationFunctional::Create( this->m_Metric, referenceVolume, floatingVolume, this->m_FloatingImageInterpolation ); newFunctional->SetActiveCoordinates( this->RestrictToAxes ); newFunctional->SetAdaptiveFixParameters( this->m_AdaptiveFixParameters ); newFunctional->SetAdaptiveFixThreshFactor( this->m_AdaptiveFixThreshFactor ); newFunctional->SetJacobianConstraintWeight( this->m_JacobianConstraintWeight ); newFunctional->SetForceOutside( this->m_ForceOutsideFlag, this->m_ForceOutsideValue ); newFunctional->SetGridEnergyWeight( this->m_GridEnergyWeight ); return newFunctional; } } void ImagePairNonrigidRegistration::EnterResolution ( CoordinateVector::SmartPtr& v, Functional::SmartPtr& functional, const int idx, const int total ) { float effGridEnergyWeight = this->m_GridEnergyWeight; float effJacobianConstraintWeight = this->m_JacobianConstraintWeight; float effInverseConsistencyWeight = this->m_InverseConsistencyWeight; if ( (this->m_RelaxWeight > 0) && !this->RelaxationStep ) { effGridEnergyWeight *= this->m_RelaxWeight; effJacobianConstraintWeight *= this->m_RelaxWeight; effInverseConsistencyWeight *= this->m_RelaxWeight; } SplineWarpXform::SmartPtr warpXform = SplineWarpXform::SmartPtr::DynamicCastFrom( this->m_Xform ); // handle simple nonrigid functional SmartPointer nonrigidFunctional = ImagePairNonrigidRegistrationFunctional::SmartPtr::DynamicCastFrom( functional ); if ( nonrigidFunctional ) { nonrigidFunctional->SetWarpXform( warpXform ); if ( this->m_RelaxToUnfold ) // has to come after functional->SetWarpXform so reference volume is registered with warp grid warpXform->RelaxToUnfold(); nonrigidFunctional->SetGridEnergyWeight( effGridEnergyWeight ); nonrigidFunctional->SetJacobianConstraintWeight( effJacobianConstraintWeight ); } else { // handle inverse-consistent nonrigid functional SmartPointer symmetricFunctional = ImagePairSymmetricNonrigidRegistrationFunctional::SmartPtr::DynamicCastFrom( functional ); if ( symmetricFunctional ) { symmetricFunctional->SetWarpXform( warpXform, this->InverseWarpXform ); if ( this->m_RelaxToUnfold ) // has to come after functional->SetWarpXform so reference volume is registered with warp grid { warpXform->RelaxToUnfold(); this->InverseWarpXform->RelaxToUnfold(); } symmetricFunctional->SetGridEnergyWeight( effGridEnergyWeight ); symmetricFunctional->SetJacobianConstraintWeight( effJacobianConstraintWeight ); symmetricFunctional->SetInverseConsistencyWeight( effInverseConsistencyWeight ); } else { // neither simple nor inverse-consistent functional: something went // badly wrong. StdErr << "Fatal coding error: Non-nonrigid functional in ImagePairNonrigidRegistration::EnterResolution.\n"; abort(); } } Superclass::EnterResolution( v, functional, idx, total ); } int ImagePairNonrigidRegistration::DoneResolution ( CoordinateVector::SmartPtr& v, Functional::SmartPtr& functional, const int idx, const int total ) { if ( ( this->m_RelaxWeight > 0 ) && ! RelaxationStep ) { RelaxationStep = true; this->Superclass::DoneResolution( v, functional, idx, total ); return false; // repeat with a relaxation step. } else { RelaxationStep = false; } bool repeat = ( ( idx == total ) && ( RefineGridCount < this->m_RefineGrid ) ); if ( (RefinedGridAtLevel != idx) || (idx==total) ) { if ( RefineGridCount < this->m_RefineGrid ) { if ( (!this->m_DelayRefineGrid) || RefineDelayed || ( idx == total ) ) { WarpXform::SmartPtr warpXform = WarpXform::SmartPtr::DynamicCastFrom( this->m_Xform ); if ( warpXform ) { warpXform->Refine(); if ( InverseWarpXform ) InverseWarpXform->Refine(); ++RefineGridCount; functional->GetParamVector( *v ); if ( this->m_Callback ) this->m_Callback->Comment( "Refined control point grid." ); RefinedGridAtLevel = idx; } if ( this->m_DelayRefineGrid && ( idx > 1 ) ) repeat = true; RefineDelayed = false; } else { RefineDelayed = true; } } } else { RefineDelayed = true; } return this->Superclass::DoneResolution( v, functional, idx, total ) && !repeat; } const UniformVolume::SmartPtr ImagePairNonrigidRegistration::GetReformattedFloatingImage( Interpolators::InterpolationEnum interpolator ) const { ReformatVolume reformat; reformat.SetInterpolation( interpolator ); reformat.SetReferenceVolume( this->m_Volume_1 ); reformat.SetFloatingVolume( this->m_Volume_2 ); WarpXform::SmartPtr warpXform( this->GetTransformation() ); reformat.SetWarpXform( warpXform ); if ( this->m_ForceOutsideFlag ) { reformat.SetPaddingValue( this->m_ForceOutsideValue ); } UniformVolume::SmartPtr result = reformat.PlainReformat(); if ( this->m_ForceOutsideFlag ) { result->GetData()->ClearPaddingFlag(); } return result; } } // namespace cmtk cmtk-3.3.1/libs/Registration/cmtkImagePairNonrigidRegistration.h000066400000000000000000000175121276303427400250500ustar00rootroot00000000000000/* // // Copyright 2004-2012 SRI International // // Copyright 1997-2009 Torsten Rohlfing // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4423 $ // // $LastChangedDate: 2012-06-11 16:13:37 -0700 (Mon, 11 Jun 2012) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkImagePairNonrigidRegistration_h_included_ #define __cmtkImagePairNonrigidRegistration_h_included_ #include #include #include #include namespace cmtk { /** \addtogroup Registration */ //@{ /** Generic multiresolution voxel-registration class. * By implementing member functions to retrieve parameters and report results * in derived classes, registration can be integrated into various * environments. *\version $Revision: 4423 $ $Date: 2012-06-11 16:13:37 -0700 (Mon, 11 Jun 2012) $ */ class ImagePairNonrigidRegistration : /// Inherit basic voxel registration functions. public ImagePairRegistration { public: /// This class. typedef ImagePairNonrigidRegistration Self; /// Parent class. typedef ImagePairRegistration Superclass; protected: /// Initial deformation. SplineWarpXform::SmartPtr InitialWarpXform; /// Optional inverse warp for inverse-consistent registration. SplineWarpXform::SmartPtr InverseWarpXform; /// Flag whether to adjust floating image histogram to match reference image. cmtkGetSetMacro(bool,MatchFltToRefHistogram); /** Flag for repeated application of histogram-based intensity matching. * If this flag is set, histogram-based intensity matching is repeatedly applied * throughout the registration process to match the floating image intensities * with consideration for changing volume proportions as the deformation progresses. */ cmtkGetSetMacroDefault(bool,RepeatMatchFltToRefHistogram,true); /// This value determines how often the control point grid is refined. cmtkGetSetMacro(int,RefineGrid); /** Flag whether to delay grid refinement. * If this flag is set, a newly entered image resolution level is run with * the previous, coarser deformation grid first before refining. */ cmtkGetSetMacro(bool,DelayRefineGrid); /// Initial spacing of the control point grid. cmtkGetSetMacro(Types::Coordinate,GridSpacing); /** Force exact grid spacing. * If this flag is set, then the CPG will be spaced at exactly the distance * given in the GridSpacing field. Otherwise, the grid spacing will be * adjusted so that there is an integral number of CPG cells that cover the * reference image domain. */ cmtkGetSetMacro(bool,ExactGridSpacing); /// This counter determines how many edge control points are fixed. unsigned int IgnoreEdge; /** Restrict deformation to one or more coordinate axes. */ const char* RestrictToAxes; /// Flag for fast mode (less accurate) of spline deformations. cmtkGetSetMacro(bool,FastMode); /// Flag for adaptive selection of active and passive parameters. cmtkGetSetMacro(bool,AdaptiveFixParameters); /** Set threshold factor for selecting passive warp parameters adaptively. * If the flag AdaptiveFixParameters is set, this value determines the * threshold by which active vs. passive parameters are selected. All * control points are set to passive for which the local region entropy is * below this factor times sum of min and max region entropy. The default * value is 0.5. */ cmtkGetSetMacro(float,AdaptiveFixThreshFactor); /// Weighting of Jacobian constraint relative to similairy measure. cmtkGetSetMacro(float,JacobianConstraintWeight); /// Weighting of grid bending energy constraint relative to image similarity. cmtkGetSetMacro(float,GridEnergyWeight); /// Factor by which to relax constraint weights for a relaxation step. cmtkGetSetMacro(float,RelaxWeight); /** Weight for inverse consistency weight. * If this is set to a value greater than 0, inverse consistency of the * transformation is enforced. In fact, both forward and backward * transformation are optimized simultaneously. */ cmtkGetSetMacro(float,InverseConsistencyWeight); /// Flag to turn on deformation unfolding before each level. bool m_RelaxToUnfold; /** Default constructor. * Set initial values for some flags. */ ImagePairNonrigidRegistration(); /** Destructor. * Free local objects for this class. */ virtual ~ImagePairNonrigidRegistration() {}; /**\name Member functions to be overwritten. */ //@{ /** Initialize registration. * This function is called by Register before any other operations. It can * be overloaded to open status dialog windows, etc. Derived implementations * should call their base class' InitRegistration first. *\return */ virtual CallbackResult InitRegistration (); /** Enter resolution level. */ virtual void EnterResolution( CoordinateVector::SmartPtr&, Functional::SmartPtr&, const int, const int ); /** Finish resolution level. * In addition to operations necessary for general registration, we have * to make some additional modifications. In particular the sampling of * the warp transformations' control point mesh has to be refined before * entering the next resolution. */ virtual int DoneResolution( CoordinateVector::SmartPtr&, Functional::SmartPtr&, const int, const int ); //@} /// Return final transformation. SplineWarpXform::SmartPtr GetTransformation() const { return SplineWarpXform::SmartPtr::DynamicCastFrom( this->m_Xform ); } /// Get reformatted floating image. const UniformVolume::SmartPtr GetReformattedFloatingImage( Interpolators::InterpolationEnum interpolator = Interpolators::LINEAR ) const; private: /// Level on which the last control grid refinement was performend. int RefinedGridAtLevel; /// Number of refinements so far. int RefineGridCount; /// Are we currently doing a relaxation step? bool RelaxationStep; /// Have we already run the current level before refining the grid? bool RefineDelayed; /** Create warp transformation with current settings. * This function is used to create the standard warp, as well as the * approximated inverse warp for the optional inverse-consistent * registration. *\param size Reference volume size. *\param initialAffine Initial affine transformation for the warp. */ SplineWarpXform::SmartPtr MakeWarpXform( const UniformVolume::CoordinateVectorType& size, const AffineXform* initialAffine ) const; /// Base class for registration level parameters. class LevelParameters /// Inherit from superclass parameters. : public Superclass::LevelParameters { public: /// Constructor: take image resolution. LevelParameters( const Types::Coordinate resolution ) : m_Resolution( resolution ) {} /// Image resolution for this level. Types::Coordinate m_Resolution; }; /** Create functional with current level settings. */ virtual Functional* MakeFunctional( const int level, const Superclass::LevelParameters* levelParameters ); }; //@} } // namespace cmtk #endif // __cmtkImagePairNonrigidRegistration_h_included_ cmtk-3.3.1/libs/Registration/cmtkImagePairNonrigidRegistrationCommandLine.cxx000066400000000000000000000613511276303427400275320ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2013 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 5129 $ // // $LastChangedDate: 2014-01-09 13:48:15 -0800 (Thu, 09 Jan 2014) $ // // $LastChangedBy: torstenrohlfing $ // */ #include "cmtkImagePairNonrigidRegistrationCommandLine.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef CMTK_USE_SQLITE # include #endif #ifdef HAVE_SYS_TYPES_H # include #endif #ifdef HAVE_SYS_STAT_H # include #endif #ifdef HAVE_SYS_UTSNAME_H # include #endif #ifdef _MSC_VER # include #endif // #ifdef _MSC_VER #include #include #include #include #include namespace cmtk { /** \addtogroup Registration */ //@{ ImagePairNonrigidRegistrationCommandLine* ImagePairNonrigidRegistrationCommandLine::StaticThis = NULL; ImagePairNonrigidRegistrationCommandLine ::ImagePairNonrigidRegistrationCommandLine ( const int argc, const char *argv[] ) { this->m_OutputIntermediate = 0; IntermediateResultIndex = 0; bool forceOutsideFlag = false; Types::DataItem forceOutsideValue = 0; std::string clArg1; // input studylist or reference image std::string clArg2; // empty or floating image std::string clArg3; // empty or initial transformation try { CommandLine cl( CommandLine::PROPS_XML ); cl.SetProgramInfo( CommandLine::PRG_TITLE, "B-spline nonrigid registration" ); cl.SetProgramInfo( CommandLine::PRG_DESCR, "This program performs nonrigid image registration using multi-resolution optimization of voxel-based image similarity measures " "and a multi-resolution B-spline transformation model." ); cl.SetProgramInfo( CommandLine::PRG_CATEG, "CMTK.Registration" ); typedef CommandLine::Key Key; cl.BeginGroup( "TransformationIO", "Transformation import/export" ); cl.AddOption( Key( "initial" ), &this->m_InitialTransformationFile, "Initialize transformation from given path" )->SetProperties( CommandLine::PROPS_XFORM ); cl.AddSwitch( Key( "invert-initial" ), &this->m_InitialTransformationInverse, true, "Invert given (affine) initial transformation." ); cl.AddOption( Key( "write-itk-xform" ), &this->m_OutputPathITK, "Output path for final transformation in ITK format" ) ->SetProperties( CommandLine::PROPS_XFORM | CommandLine::PROPS_OUTPUT ) ->SetAttribute( "type", "bspline" )->SetAttribute( "reference", "FloatingImage" ); cl.AddOption( Key( "write-reformatted" ), &this->m_ReformattedImagePath, "Write reformatted floating image." ) ->SetProperties( CommandLine::PROPS_IMAGE | CommandLine::PROPS_OUTPUT ); cl.EndGroup(); cl.BeginGroup( "Transformation", "Transformation parameters" ); cl.AddOption( Key( "grid-spacing" ), &this->m_GridSpacing, "Control point grid spacing" ); cl.AddOption( Key( "grid-refine" ), &this->m_RefineGrid, "Number of refinements (control point grid resolution levels)" ); cl.AddSwitch( Key( "delay-refine" ), &this->m_DelayRefineGrid, true, "Delay control point grid refinement; first switch to next higher image resolution" ); cl.AddSwitch( Key( "exact-spacing" ), &this->m_ExactGridSpacing, true, "Use exact control point spacing; do not modify spacing to fit reference image bounding box" ); cl.AddOption( Key( "ignore-edge" ), &this->IgnoreEdge, "Ignore n control point layers along each image face" ); cl.AddOption( Key( "restrict" ), &this->RestrictToAxes, "Restrict deformation to coordinate dimension(s) [one or more of 'x','y','z']" ); cl.AddSwitch( Key( "no-adaptive-fix" ), &this->m_AdaptiveFixParameters, false, "Disable adaptive fixing of control points; optimize all deformation parameters" ); cl.AddOption( Key( "adaptive-fix-thresh" ), &this->m_AdaptiveFixThreshFactor, "Threshold factor for entropy criterion to fix local control points" ); cl.AddSwitch( Key( "accurate" ), &this->m_FastMode, false, "Accurate computation mode: may give slightly better results after substantially longer computation" ); cl.AddSwitch( Key( "fast" ), &this->m_FastMode, true, "Fast computation mode: may give slightly worse results than accurate mode, but saves substantial CPU time" ); cl.EndGroup(); cl.BeginGroup( "Regularization", "Regularization parameters" ); cl.AddOption( Key( "jacobian-constraint-weight" ), &this->m_JacobianConstraintWeight, "Weight for Jacobian-based local volume preservation constraint" ); cl.AddOption( Key( "smoothness-constraint-weight" ), &this->m_GridEnergyWeight, "Weight for smoothness constraint based on second-order grid bending energy." ); cl.AddOption( Key( "inverse-consistency-weight" ), &this->m_InverseConsistencyWeight, "Weight for inverse consistency constraint" ); cl.AddOption( Key( "constraint-relaxation-factor" ), &this->m_RelaxWeight, "Weight relaxation factor for alternating under-constrained iterations" ); cl.AddSwitch( Key( "relax-to-unfold" ), &this->m_RelaxToUnfold, true, "Before each resolution level, regularize negative-Jacobian areas of the deformation to unfold them." ); cl.EndGroup(); cl.BeginGroup( "Optimization", "Optimization parameters" ); cl.AddOption( Key( "max-stepsize" ), &this->m_MaxStepSize, "Maximum optimizer step size, which determines search space exploration." ); cl.AddOption( Key( "min-stepsize" ), &this->m_MinStepSize, "Minimum optimizer step size, which determines precision." ); cl.AddOption( Key( "stepfactor" ), &this->m_OptimizerStepFactor, "Factor for search step size reduction. Must be > 0.0 and < 1.0 [default: 0.5]" ); cl.AddOption( Key( "delta-f-threshold" ), &this->m_DeltaFThreshold, "Optional threshold to terminate optimization (level) if relative change of target function drops below this value." ); cl.AddSwitch( Key( "no-maxnorm" ), &this->m_UseMaxNorm, false, "Use Euclid norm for gradient normalication in optimization, rather than maximum norm" ); cl.EndGroup(); cl.BeginGroup( "Resolution", "Image resolution parameters" ); cl.AddOption( Key( 's', "sampling" ), &this->m_Sampling, "Image sampling (finest resampled image resolution)" ); cl.AddOption( Key( "coarsest" ), &this->m_CoarsestResolution, "Upper limit for image sampling in multiresolution hierarchy" ); cl.AddSwitch( Key( "omit-original-data" ), &this->m_UseOriginalData, false, "Do not use original data in full resolution for final registration stage." ); cl.EndGroup(); cl.BeginGroup( "Images", "Image data" ); CommandLine::EnumGroup::SmartPtr metricGroup = cl.AddEnum( "registration-metric", &this->m_Metric, "Registration metric for motion estimation by image-to-image registration." ); metricGroup->AddSwitch( Key( "nmi" ), 0, "Normalized Mutual Information metric" ); metricGroup->AddSwitch( Key( "mi" ), 1, "Standard Mutual Information metric" ); metricGroup->AddSwitch( Key( "cr" ), 2, "Correlation Ratio metric" ); metricGroup->AddSwitch( Key( "rms" ), 3, "Root of Mean Squaresa metric (this is the square root of MSD)" ); metricGroup->AddSwitch( Key( "msd" ), 4, "Mean Squared Difference metric" ); metricGroup->AddSwitch( Key( "ncc" ), 5, "Normalized Cross Correlation metric" ); cl.BeginGroup( "Interpolation", "Floating Image Interpolation Options" ); cmtk::CommandLine::EnumGroup::SmartPtr kernelGroup = cl.AddEnum( "interpolation", &this->m_FloatingImageInterpolation, "Interpolation method for floating image sampling:" ); kernelGroup->AddSwitch( Key( "nearest-neighbor" ), Interpolators::NEAREST_NEIGHBOR, "Nearest neighbor interpolation (for intensity and label data)" ); kernelGroup->AddSwitch( Key( "linear" ), Interpolators::LINEAR, "Trilinear interpolation" ); kernelGroup->AddSwitch( Key( "cubic" ), Interpolators::CUBIC, "Tricubic interpolation" ); kernelGroup->AddSwitch( Key( "cosine-sinc" ), Interpolators::COSINE_SINC, "Cosine-windowed sinc interpolation (most accurate but slowest)" ); kernelGroup->AddSwitch( Key( "partial-volume" ), Interpolators::PARTIALVOLUME, "Partial volume interpolation (for label data)" ); kernelGroup->AddSwitch( Key( "automatic" ), Interpolators::DEFAULT, "Select interpolation automatically based on data type: linear for grey-level data, nearest neighbor for label data." ); cl.AddSwitch( Key( "match-histograms" ), &this->m_MatchFltToRefHistogram, true, "Match floating image histogram to reference image histogram." ); cl.AddSwitch( Key( "repeat-match-histograms" ), &this->m_RepeatMatchFltToRefHistogram, true, "Repeat histogram matching after every level of the registration to account for volume changes. When registering masked data, it is advisable to also use the --force-outside-value option to prevent poorly matched histograms." ); cl.AddOption( Key( "force-outside-value" ), &forceOutsideValue, "Force values outside field of view to this value rather than drop incomplete pixel pairs", &forceOutsideFlag ); this->m_PreprocessorRef.AttachToCommandLine( cl ); this->m_PreprocessorFlt.AttachToCommandLine( cl ); cl.BeginGroup( "Output", "Output parameters" )->SetProperties( CommandLine::PROPS_NOXML ); cl.AddOption( Key( 'o', "outlist" ), &this->Studylist, "Output path for final transformation" ); cl.AddOption( Key( 't', "time" ), &this->Time, "Computation time statistics output file name" ); cl.AddSwitch( Key( "output-intermediate" ), &this->m_OutputIntermediate, true, "Write transformation for each level [default: only write final transformation]" ); cl.EndGroup(); #ifdef CMTK_USE_SQLITE cl.BeginGroup( "Database", "Image/Transformation Database" ); cl.AddOption( Key( "db" ), &this->m_UpdateDB, "Path to image/transformation database that should be updated with the new registration and/or reformatted image." ); cl.EndGroup(); #endif cl.AddParameter( &clArg1, "ReferenceImage", "Reference (fixed) image path" )->SetProperties( CommandLine::PROPS_IMAGE ); cl.AddParameter( &clArg2, "FloatingImage", "Floating (moving) image path" )->SetProperties( CommandLine::PROPS_IMAGE | CommandLine::PROPS_OPTIONAL); cl.AddParameter( &clArg3, "InitialXform", "Initial affine transformation from reference to floating image" ) ->SetProperties( CommandLine::PROPS_NOXML | CommandLine::PROPS_XFORM | CommandLine::PROPS_OPTIONAL ); cl.Parse( argc, argv ); } catch ( const CommandLine::Exception& ex ) { StdErr << ex << "\n"; exit( 1 ); } if ( (this->m_OptimizerStepFactor <= 0) || (this->m_OptimizerStepFactor >= 1) ) { StdErr << "ERROR: step factor value " << this->m_OptimizerStepFactor << " is invalid. Must be in range (0..1)\n"; exit( 1 ); } if ( ! clArg2.empty() ) { this->SetInitialTransformation( AffineXform::SmartPtr( new AffineXform() ) ); this->Study1 = clArg1; this->Study2 = clArg2; if ( ! clArg3.empty() ) { this->m_InitialTransformationFile = clArg3; } } else { InputStudylist = clArg1; DebugOutput( 1 ) << "Reading input studylist" << InputStudylist << "\n"; ClassStreamInput classStream( MountPoints::Translate(InputStudylist),"registration" ); if ( ! classStream.IsValid() ) { StdErr << "ERROR: Could not open studylist archive " << InputStudylist << ".\n"; exit( 1 ); } classStream.Seek ( "registration" ); Study1 = classStream.ReadStdString( "reference_study" ); Study2 = classStream.ReadStdString( "floating_study" ); if ( ! Study2.empty() ) { AffineXform::SmartPtr affineXform; classStream >> affineXform; this->SetInitialTransformation( affineXform ); } else { // legacy studylists have inverse transformation stored in them Study2 = classStream.ReadStdString( "model_study" ); AffineXform::SmartPtr affineXform; classStream >> affineXform; try { this->SetInitialTransformation( affineXform->GetInverse() ); } catch ( const AffineXform::MatrixType::SingularMatrixException& ) { StdErr << "ERROR: singular matrix from initialization file in cmtk::ImagePairNonrigidRegistrationCommandLine constructor\n"; throw ExitException( 1 ); } } classStream.Close(); } // Was an initial studylist given? If so, get warp if ( ! this->m_InitialTransformationFile.empty() ) { Xform::SmartPtr initialXform( XformIO::Read( this->m_InitialTransformationFile ) ); if ( initialXform ) { AffineXform::SmartPtr affineXform = AffineXform::SmartPtr::DynamicCastFrom( initialXform ); if ( affineXform ) { if ( this->m_InitialTransformationInverse ) { try { this->SetInitialTransformation( affineXform->GetInverse() ); } catch ( const AffineXform::MatrixType::SingularMatrixException& ) { StdErr << "ERROR: singular matrix from initialization file in cmtk::ImagePairNonrigidRegistrationCommandLine constructor\n"; throw ExitException( 1 ); } } else { this->SetInitialTransformation( affineXform ); } } else { InitialWarpXform = SplineWarpXform::SmartPtr::DynamicCastFrom( initialXform ); } } else { StdErr << "ERROR: could not read initial transformation from " << this->m_InitialTransformationFile << "\n"; throw ExitException( 1 ); } } UniformVolume::SmartPtr volume( VolumeIO::ReadOriented( this->Study1 ) ); if ( !volume ) throw ConstructorFailed(); this->SetVolume_1( UniformVolume::SmartPtr( this->m_PreprocessorRef.GetProcessedImage( volume ) ) ); volume = UniformVolume::SmartPtr( VolumeIO::ReadOriented( this->Study2 ) ); if ( !volume ) throw ConstructorFailed(); this->SetVolume_2( UniformVolume::SmartPtr( this->m_PreprocessorFlt.GetProcessedImage( volume ) ) ); AffineXform::SmartPtr affineXform( AffineXform::SmartPtr::DynamicCastFrom( this->m_InitialTransformation ) ); if ( affineXform ) { if ( affineXform->MetaKeyExists( META_SPACE ) && (affineXform->GetMetaInfo( META_SPACE ) != AnatomicalOrientation::ORIENTATION_STANDARD ) ) { try { TransformChangeFromSpaceAffine toStandardSpace( *affineXform, *(this->m_Volume_1), *(this->m_Volume_2), affineXform->GetMetaInfo( META_SPACE ).c_str() ); *affineXform = toStandardSpace.GetTransformation(); affineXform->SetMetaInfo( META_SPACE, AnatomicalOrientation::ORIENTATION_STANDARD ); this->SetInitialTransformation( affineXform ); } catch ( const AffineXform::MatrixType::SingularMatrixException& ) { StdErr << "ERROR: singular matrix cannot be inverted to change transformation to standard space in ElasticRegistrationCommandLine constructor.\n"; throw ExitException( 1 ); } } } if ( forceOutsideFlag ) { this->SetForceOutside( true, forceOutsideValue ); } } ImagePairNonrigidRegistrationCommandLine::~ImagePairNonrigidRegistrationCommandLine () { #ifdef HAVE_SIGRELSE // release signal handler sigrelse( SIGUSR1 ); #endif } CallbackResult ImagePairNonrigidRegistrationCommandLine ::InitRegistration () { CallbackResult result = this->Superclass::InitRegistration(); if ( result != CALLBACK_OK ) return result; if ( this->m_OutputIntermediate ) this->OutputIntermediate(); // register signal handler for intermediate result output. Self::StaticThis = this; #ifndef _MSC_VER signal( SIGUSR1, cmtkImagePairNonrigidRegistrationCommandLineDispatchSIGUSR1 ); #endif return CALLBACK_OK; } void ImagePairNonrigidRegistrationCommandLine ::OutputResult ( const CoordinateVector*, const CallbackResult irq ) { if ( !this->Studylist.empty() ) { if ( irq != CALLBACK_OK ) this->OutputWarp( this->Studylist + "-partial" ); else this->OutputWarp( this->Studylist ); } if ( !this->m_OutputPathITK.empty() ) { if ( irq != CALLBACK_OK ) SplineWarpXformITKIO::Write( this->m_OutputPathITK + "-partial", *(this->GetTransformation()), *(this->m_ReferenceVolume), *(this->m_FloatingVolume) ); else SplineWarpXformITKIO::Write( this->m_OutputPathITK, *(this->GetTransformation()), *(this->m_ReferenceVolume), *(this->m_FloatingVolume) ); } if ( !this->m_ReformattedImagePath.empty() ) { if ( irq != CALLBACK_OK ) VolumeIO::Write( *(this->GetReformattedFloatingImage() ), this->m_ReformattedImagePath + "-partial" ); else VolumeIO::Write( *(this->GetReformattedFloatingImage() ), this->m_ReformattedImagePath ); } #ifdef CMTK_USE_SQLITE if ( (irq == CALLBACK_OK) && !this->m_UpdateDB.empty() ) { try { ImageXformDB db( this->m_UpdateDB ); if ( !this->m_ReformattedImagePath.empty() ) { db.AddImage( this->m_ReformattedImagePath, this->m_ReferenceVolume->GetMetaInfo( META_FS_PATH ) ); } if ( ! this->Studylist.empty() ) { if ( ! this->InputStudylist.empty() ) { db.AddRefinedXform( this->Studylist, true /*invertible*/, this->InputStudylist ); } else { if ( ! this->m_InitialTransformationFile.empty() ) { db.AddRefinedXform( this->Studylist, true /*invertible*/, this->m_InitialTransformationFile, m_InitialTransformationInverse ); } else { db.AddImagePairXform( this->Studylist, true /*invertible*/, this->m_ReferenceVolume->GetMetaInfo( META_FS_PATH ), this->m_FloatingVolume->GetMetaInfo( META_FS_PATH ) ); } } } } catch ( const ImageXformDB::Exception& ex ) { StdErr << "DB ERROR: " << ex.what() << " on database " << this->m_UpdateDB << "\n"; } } #endif } void ImagePairNonrigidRegistrationCommandLine ::EnterResolution ( CoordinateVector::SmartPtr& v, Functional::SmartPtr& f, const int index, const int total ) { DebugOutput( 1 ).GetStream().printf( "\rEntering resolution level %d out of %d...\n", index, total ); this->Superclass::EnterResolution( v, f, index, total ); } int ImagePairNonrigidRegistrationCommandLine::DoneResolution ( CoordinateVector::SmartPtr& v, Functional::SmartPtr& f, const int index, const int total ) { if ( this->m_OutputIntermediate ) this->OutputIntermediate(); return this->Superclass::DoneResolution( v, f, index, total ); } void ImagePairNonrigidRegistrationCommandLine::OutputWarp ( const std::string& path ) const { ClassStreamOutput classStream( path, "studylist", ClassStreamOutput::MODE_WRITE ); if ( ! classStream.IsValid() ) return; classStream.Begin( "studylist" ); classStream.WriteInt( "num_sources", 2 ); classStream.End(); classStream.Begin( "source" ); classStream.WriteString( "studyname", CompressedStream::GetBaseName( Study1 ) ); classStream.End(); classStream.Begin( "source" ); classStream.WriteString( "studyname", CompressedStream::GetBaseName( Study2 ) ); classStream.End(); classStream.Close(); classStream.Open( path, "settings", ClassStreamOutput::MODE_WRITE ); classStream.WriteInt( "algorithm", this->m_Algorithm ); classStream.WriteBool( "use_maxnorm", this->m_UseMaxNorm ); classStream.WriteDouble( "exploration", this->m_MaxStepSize ); classStream.WriteDouble( "accuracy", this->m_MinStepSize ); classStream.WriteDouble( "min_sampling", this->m_Sampling ); classStream.WriteDouble( "coarsest_resolution", this->m_CoarsestResolution ); classStream.WriteBool( "use_original_data", this->m_UseOriginalData ); classStream.WriteBool( "fast_mode", this->m_FastMode ); classStream.WriteInt( "metric", this->m_Metric ); classStream.WriteDouble( "optimizer_step_factor", this->m_OptimizerStepFactor ); classStream.WriteDouble( "grid_spacing", this->m_GridSpacing ); classStream.WriteInt( "ignore_edge", IgnoreEdge ); classStream.WriteDouble( "jacobian_constraint_weight", this->m_JacobianConstraintWeight ); classStream.WriteDouble( "energy_constraint_weight", this->m_GridEnergyWeight ); classStream.WriteDouble( "inverse_consistency_weight", this->m_InverseConsistencyWeight ); classStream.WriteDouble( "weight_relaxation", this->m_RelaxWeight ); classStream.WriteInt( "refine_grid", this->m_RefineGrid ); classStream.WriteBool( "delay_refine_grid", this->m_DelayRefineGrid ); classStream.WriteBool( "adaptive_fix_parameters", this->m_AdaptiveFixParameters ); classStream.WriteDouble( "adaptive_fix_parameters_thresh", this->m_AdaptiveFixThreshFactor ); this->m_PreprocessorRef.WriteSettings( classStream ); this->m_PreprocessorFlt.WriteSettings( classStream ); classStream.Close(); classStream.Open( path, "statistics", ClassStreamOutput::MODE_WRITE ); classStream.WriteDouble( "time_level", this->GetLevelElapsedTime() ); classStream.WriteDouble( "time_total", this->GetTotalElapsedTime() ); classStream.WriteDouble( "walltime_level", this->GetLevelElapsedWalltime() ); classStream.WriteDouble( "walltime_total", this->GetTotalElapsedWalltime() ); classStream.WriteDouble( "thread_time_level", this->GetThreadLevelElapsedTime() ); classStream.WriteDouble( "thread_time_total", this->GetThreadTotalElapsedTime() ); classStream.WriteInt( "number_of_threads", Threads::NumberOfThreads ); classStream.WriteInt( "number_of_cpus", Threads::GetNumberOfProcessors() ); #ifndef _MSC_VER struct utsname name; if ( uname( &name ) >= 0 ) { classStream.WriteString( "host", name.nodename ); classStream.WriteString( "system", name.sysname ); } #endif classStream.Close(); const WarpXform::SmartPtr warp = WarpXform::SmartPtr::DynamicCastFrom( this->m_Xform ); if ( warp ) { classStream.Open( path, "registration", ClassStreamOutput::MODE_WRITE_ZLIB ); if ( classStream.IsValid() ) { classStream.Begin( "registration" ); classStream.WriteString( "reference_study", CompressedStream::GetBaseName( this->Study1 ) ); classStream.WriteString( "floating_study", CompressedStream::GetBaseName( this->Study2 ) ); if ( warp->GetInitialAffineXform() ) { classStream << (*warp->GetInitialAffineXform()); } else { classStream << *this->m_InitialTransformation; } classStream << warp; classStream.End(); } classStream.Close(); } } void ImagePairNonrigidRegistrationCommandLine::OutputIntermediate( const bool incrementCount ) { char path[PATH_MAX]; if ( ! Studylist.empty() ) { snprintf( path, sizeof( path ), "%s%clevel-%02d.list", Studylist.c_str(), (int)CMTK_PATH_SEPARATOR, IntermediateResultIndex ); } else { snprintf( path, sizeof( path ), "level-%02d.list", IntermediateResultIndex ); } this->OutputWarp( path ); if ( incrementCount ) ++IntermediateResultIndex; } CallbackResult ImagePairNonrigidRegistrationCommandLine::Register () { const double baselineTime = Timers::GetTimeProcess(); CallbackResult Result = this->Superclass::Register(); const int elapsed = static_cast( Timers::GetTimeProcess() - baselineTime ); if ( Time ) { FILE *tfp = fopen( Time, "w" ); if ( tfp ) { fprintf( tfp, "%d\n", elapsed ); fclose( tfp ); } else { std::cerr << "Could not open time file " << Time << "\n"; } } return Result; } } // namespace cmtk void cmtkImagePairNonrigidRegistrationCommandLineDispatchSIGUSR1( int sig ) { fprintf( stderr, "Received USR1 (%d) signal. Writing intermediate result #%d.\nNote that this result is not final.\n", sig, cmtk::ImagePairNonrigidRegistrationCommandLine::StaticThis->IntermediateResultIndex ); #ifndef _MSC_VER // set signal handler again. signal( sig, cmtkImagePairNonrigidRegistrationCommandLineDispatchSIGUSR1 ); #endif // write intermediate result. give "false" flag for index increment to // preserve to final numbering of levels. cmtk::ImagePairNonrigidRegistrationCommandLine::StaticThis->OutputIntermediate( true /* Increment count*/ ); } cmtk-3.3.1/libs/Registration/cmtkImagePairNonrigidRegistrationCommandLine.h000066400000000000000000000124131276303427400271520ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2011, 2013 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4835 $ // // $LastChangedDate: 2013-09-12 15:48:47 -0700 (Thu, 12 Sep 2013) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkImagePairNonrigidRegistrationCommandLine_h_included_ #define __cmtkImagePairNonrigidRegistrationCommandLine_h_included_ #include #include namespace cmtk { /** \addtogroup Registration */ //@{ /** Class for command line controlled voxel registration. *\author T. Rohlfing *\version $Revision: 4835 $ $Date: 2013-09-12 15:48:47 -0700 (Thu, 12 Sep 2013) $ */ class ImagePairNonrigidRegistrationCommandLine : /// Inherit generic elastic registration. public ImagePairNonrigidRegistration { public: /// This class. typedef ImagePairNonrigidRegistrationCommandLine Self; /// Smart pointer. typedef SmartPointer SmartPtr; /// Parent class. typedef ImagePairNonrigidRegistration Superclass; /** Constructor. *\param argc Number of command line arguments; this should be the argc * parameter of the main() function. *\param argv Array of command line arguments; this should be the argv * parameter of the main() function. */ ImagePairNonrigidRegistrationCommandLine ( const int argc, const char *argv[] ); /// Destructor. ~ImagePairNonrigidRegistrationCommandLine (); /** Perform registration. */ virtual CallbackResult Register (); protected: /** Initialize registration. * So far, this function has no effect other than calling the equivalent * inherited function. */ virtual CallbackResult InitRegistration(); /** Output registration result. * This function write the transformation that was found to a studylist * archive with the name provided by command line arguments. The result is * also printed to stderr in parameter list form. *\param v The vector of resulting transformation parameters. *\param irq The interrupt status - this allows the output function to determine whether computation finished or was interrupted. */ virtual void OutputResult ( const CoordinateVector* v, const CallbackResult irq = CALLBACK_OK ); /** Enter resolution level. * An information is printed to stderr and to the protocol file if one is * written. */ virtual void EnterResolution( CoordinateVector::SmartPtr&, Functional::SmartPtr&, const int, const int ); /** Leave resolution level. * The transformation found so far is written to a file if desired. */ virtual int DoneResolution( CoordinateVector::SmartPtr&, Functional::SmartPtr&, const int, const int ); private: /** Name of input studylist. */ std::string InputStudylist; /// Initial transformation file. std::string m_InitialTransformationFile; /// Flag whether initial transformation is inverted. bool m_InitialTransformationInverse; /** Name of output studylist. * This is defined by the -o or --outlist command line option. */ std::string Studylist; /** Name of first study to be registered. * This is given as the first non-option command line paramter. */ std::string Study1; /** Name of second study to be registered. * This is given as the second non-option command line paramter. */ std::string Study2; /** Name of elapsed time output file. * This is defined by the -t or --time command line option. */ const char *Time; /** Select whether too create intermediate warp output files (level-xx.list). */ bool m_OutputIntermediate; /// Write deformation to studylist archive. void OutputWarp ( const std::string& ) const; #ifdef CMTK_USE_SQLITE /// Database to update after registration completes. std::string m_UpdateDB; #endif /// Name of output transformation file in ITK format. std::string m_OutputPathITK; /// Path for reformatted floating image. std::string m_ReformattedImagePath; public: /// Static pointer to this object. static Self* StaticThis; /// Counter for intermediate result files. int IntermediateResultIndex; /// Write intermediate deformation file. void OutputIntermediate( const bool incrementCount = true ); }; //@} } // namespace cmtk /// Signal handler that writes intermediate result during a level. extern "C" void cmtkImagePairNonrigidRegistrationCommandLineDispatchSIGUSR1( int sig ); #endif // #ifndef __cmtkImagePairNonrigidRegistrationCommandLine_h_included_ cmtk-3.3.1/libs/Registration/cmtkImagePairNonrigidRegistrationFunctional.cxx000066400000000000000000000165401276303427400274460ustar00rootroot00000000000000/* // // Copyright 2004-2013 SRI International // // Copyright 1997-2010 Torsten Rohlfing // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4877 $ // // $LastChangedDate: 2013-09-24 13:25:18 -0700 (Tue, 24 Sep 2013) $ // // $LastChangedBy: torstenrohlfing $ // */ #include "cmtkImagePairNonrigidRegistrationFunctional.h" #include #include #include #include #include #include #include #include #include namespace cmtk { /** \addtogroup Registration */ //@{ ImagePairNonrigidRegistrationFunctional::ImagePairNonrigidRegistrationFunctional ( UniformVolume::SmartPtr& reference, UniformVolume::SmartPtr& floating ) : ImagePairRegistrationFunctional( reference, floating ), m_ActiveCoordinates( NULL ), m_JacobianConstraintWeight( 0.0 ), m_GridEnergyWeight( 0.0 ), m_InverseConsistencyWeight( 0.0 ), m_Regularize( false ) { this->m_NumberOfThreads = ThreadPool::GetGlobalThreadPool().GetNumberOfThreads(); this->m_NumberOfTasks = 4 * this->m_NumberOfThreads - 3; Dim = 0; this->m_ReferenceDomain = UniformVolume::CoordinateRegionType( UniformVolume::CoordinateVectorType( 0.0 ), reference->m_Size ); this->m_AdaptiveFixParameters = false; this->m_AdaptiveFixThreshFactor = 0.5; this->VolumeOfInfluence = NULL; this->m_ThreadWarp.resize( this->m_NumberOfThreads ); this->m_ThreadVectorCache = Memory::ArrayC::Allocate( this->m_NumberOfThreads ); for ( size_t thread = 0; thread < this->m_NumberOfThreads; ++thread ) this->m_ThreadVectorCache[thread] = Memory::ArrayC::Allocate( this->m_ReferenceDims[0] ); this->m_WarpedVolume = NULL; this->m_DimsX = this->m_ReferenceGrid->GetDims()[0]; this->m_DimsY = this->m_ReferenceGrid->GetDims()[1]; this->m_DimsZ = this->m_ReferenceGrid->GetDims()[2]; this->m_FltDimsX = this->m_FloatingGrid->GetDims()[0]; this->m_FltDimsY = this->m_FloatingGrid->GetDims()[1]; } ImagePairNonrigidRegistrationFunctional::~ImagePairNonrigidRegistrationFunctional() { for ( size_t thread = 0; thread < this->m_NumberOfThreads; ++thread ) if ( this->m_ThreadVectorCache[thread] ) Memory::ArrayC::Delete( this->m_ThreadVectorCache[thread] ); Memory::ArrayC::Delete( this->m_ThreadVectorCache ); } void ImagePairNonrigidRegistrationFunctional::WeightedDerivative ( double& lower, double& upper, SplineWarpXform& warp, const int param, const Types::Coordinate step ) const { if ( this->m_JacobianConstraintWeight > 0 ) { double lowerConstraint = 0, upperConstraint = 0; warp.GetJacobianConstraintDerivative( lowerConstraint, upperConstraint, param, VolumeOfInfluence[param], step ); lower -= this->m_JacobianConstraintWeight * lowerConstraint; upper -= this->m_JacobianConstraintWeight * upperConstraint; } if ( this->m_GridEnergyWeight > 0 ) { double lowerEnergy = 0, upperEnergy = 0; warp.GetGridEnergyDerivative( lowerEnergy, upperEnergy, param, step ); lower -= this->m_GridEnergyWeight * lowerEnergy; upper -= this->m_GridEnergyWeight * upperEnergy; } // Catch infinite values that result from a folding grid. Effectively // prevent this by setting the gradient term to 0. if ( !finite(upper) || !finite(lower) ) { lower = upper = 0; } else { if ( this->m_LandmarkPairs ) { double lowerMSD, upperMSD; warp.GetDerivativeLandmarksMSD( lowerMSD, upperMSD, *(this->m_LandmarkPairs), param, step ); lower -= this->m_LandmarkErrorWeight * lowerMSD; upper -= this->m_LandmarkErrorWeight * upperMSD; } if ( this->m_InverseTransformation ) { double lowerIC, upperIC; warp.GetDerivativeInverseConsistencyError( lowerIC, upperIC, this->m_InverseTransformation, this->m_ReferenceGrid, &(this->VolumeOfInfluence[param]), param, step ); lower -= this->m_InverseConsistencyWeight * lowerIC; upper -= this->m_InverseConsistencyWeight * upperIC; } } } void ImagePairNonrigidRegistrationFunctional::SetWarpXform ( SplineWarpXform::SmartPtr& warp ) { this->m_Warp = warp; if ( this->m_Warp ) { this->m_Warp->RegisterVolume( *(this->m_ReferenceGrid) ); if ( Dim != this->m_Warp->VariableParamVectorDim() ) { Dim = this->m_Warp->VariableParamVectorDim(); this->m_StepScaleVector.resize( Dim ); VolumeOfInfluence = Memory::ArrayC::Allocate( Dim ); } DataGrid::RegionType *VOIptr = VolumeOfInfluence; for ( size_t dim=0; dimm_StepScaleVector[dim] = this->GetParamStep( dim ); *VOIptr = this->GetReferenceGridRange( this->m_Warp->GetVolumeOfInfluence( dim, this->m_ReferenceDomain ) ); } for ( size_t thread = 0; thread < this->m_NumberOfThreads; ++thread ) { if ( thread ) { this->m_ThreadWarp[thread] = this->m_Warp->Clone(); this->m_ThreadWarp[thread]->RegisterVolume( *(this->m_ReferenceGrid) ); } else { this->m_ThreadWarp[thread] = this->m_Warp; } } } } ImagePairNonrigidRegistrationFunctional* ImagePairNonrigidRegistrationFunctional::Create ( const int metric, UniformVolume::SmartPtr& refVolume, UniformVolume::SmartPtr& fltVolume, const Interpolators::InterpolationEnum interpolation ) { switch ( metric ) { case 0: return new ImagePairNonrigidRegistrationFunctionalTemplate( refVolume, fltVolume, interpolation ); case 1: return new ImagePairNonrigidRegistrationFunctionalTemplate( refVolume, fltVolume, interpolation ); case 2: return new ImagePairNonrigidRegistrationFunctionalTemplate( refVolume, fltVolume, interpolation ); case 3: return new ImagePairNonrigidRegistrationFunctionalTemplate( refVolume, fltVolume, interpolation ); case 4: return new ImagePairNonrigidRegistrationFunctionalTemplate( refVolume, fltVolume, interpolation ); case 5: return new ImagePairNonrigidRegistrationFunctionalTemplate( refVolume, fltVolume, interpolation ); default: return NULL; } return NULL; } } // namespace cmtk cmtk-3.3.1/libs/Registration/cmtkImagePairNonrigidRegistrationFunctional.h000066400000000000000000000213711276303427400270710ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2012 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4093 $ // // $LastChangedDate: 2012-03-27 13:05:25 -0700 (Tue, 27 Mar 2012) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkImagePairNonrigidRegistrationFunctional_h_included_ #define __cmtkImagePairNonrigidRegistrationFunctional_h_included_ #include #include #include #include #include #include #include #ifdef HAVE_IEEEFP_H # include #endif #include namespace cmtk { /** \addtogroup Registration */ //@{ /** Common base class for all elastic registration functionals. * This class holds all members that are not related to the effective metric * and therefore need not be present in the derived template class. */ class ImagePairNonrigidRegistrationFunctional : /// Inherit basic image pair registration functional class. public ImagePairRegistrationFunctional { public: /// This class. typedef ImagePairNonrigidRegistrationFunctional Self; /// Smart pointer to this class. typedef SmartPointer SmartPtr; /// Superclass. typedef ImagePairRegistrationFunctional Superclass; /// Virtual destructor. virtual ~ImagePairNonrigidRegistrationFunctional (); /** Set active and passive warp parameters adaptively. * If this flag is set, the functional will adaptively determine active and * passive parameters of the warp transformation prior to gradient * computation. */ cmtkGetSetMacroDefault(bool,AdaptiveFixParameters,true); /** Set threshold factor for selecting passive warp parameters adaptively. * If the flag AdaptiveFixParameters is set, this value determines the * threshold by which active vs. passive parameters are selected. All * control points are set to passive for which the local region entropy is * below this factor times sum of min and max region entropy. The default * value is 0.5. */ cmtkGetSetMacro(double,AdaptiveFixThreshFactor); /** Active coordinate directions. */ cmtkGetSetMacroString(ActiveCoordinates); /** Weight of the Jacobian constraint relative to voxel similarity measure. * If this is zero, only the voxel-based similarity will be computed. */ cmtkGetSetMacroDefault(double,JacobianConstraintWeight,0); /** Weight of the grid energy relative to voxel similarity measure. * If this is zero, only the voxel-based similarity will be computed. If * equal to one, only the grid energy will be computed. */ cmtkGetSetMacroDefault(double,GridEnergyWeight,0); /** Set Warp transformation. * This virtual function will be overridden by the derived classes that add * the actual warp transformation as a template parameters. It serves as a * common access point to update the warp transformation after construction * of the functional. */ virtual void SetWarpXform( SplineWarpXform::SmartPtr& warp ) = 0; /// Set inverse transformation. void SetInverseTransformation( SplineWarpXform::SmartPtr& inverseTransformation ) { this->m_InverseTransformation = inverseTransformation; } /// Set inverse consistency weight void SetInverseConsistencyWeight( const double inverseConsistencyWeight ) { this->m_InverseConsistencyWeight = inverseConsistencyWeight; } /// Get parameter stepping in milimeters. virtual Types::Coordinate GetParamStep( const size_t idx, const Types::Coordinate mmStep = 1 ) const { return this->m_Warp->GetParamStep( idx, this->m_FloatingSize, mmStep ); } /// Return the transformation's parameter vector dimension. virtual size_t ParamVectorDim() const { return this->m_Warp->ParamVectorDim(); } /// Return the number of variable parameters of the transformation. virtual size_t VariableParamVectorDim() const { return this->m_Warp->VariableParamVectorDim(); } /// Return parameter vector. virtual void GetParamVector ( CoordinateVector& v ) { this->m_Warp->GetParamVector( v ); } /// Constructor function. static ImagePairNonrigidRegistrationFunctional* Create ( const int metric, UniformVolume::SmartPtr& refVolume, UniformVolume::SmartPtr& fltVolume, const Interpolators::InterpolationEnum interpolation ); protected: /// Constructor. ImagePairNonrigidRegistrationFunctional( UniformVolume::SmartPtr& reference, UniformVolume::SmartPtr& floating ); /// Array of warp transformation objects for the parallel threads. std::vector m_ThreadWarp; /// Array of storage for simultaneously retrieving multiple deformed vectors. Vector3D **m_ThreadVectorCache; /** Number of actual parallel threads used for computations. * All duplicated data structures are generated with the multiplicity given * by this value. It is determined from Threads when the object is first * instanced. It cannot be changed afterwards. */ size_t m_NumberOfThreads; /// Number of parallel tasks. size_t m_NumberOfTasks; /// Baseline transformed volume. Types::DataItem *m_WarpedVolume; /// Shortcut variables for x, y, z dimension of the reference image. DataGrid::IndexType::ValueType m_DimsX, m_DimsY, m_DimsZ; /// Shorcut variables for x and y dimensions of the floating image. DataGrid::IndexType::ValueType m_FltDimsX, m_FltDimsY; /// Pointer to the local warp transformation. SplineWarpXform::SmartPtr m_Warp; /// Optional inverse transformation for inverse-consistent deformation. SplineWarpXform::SmartPtr m_InverseTransformation; /// Weight for inverse consistency constraint. double m_InverseConsistencyWeight; /// Return weighted combination of voxel similarity and grid energy. Self::ReturnType WeightedTotal( const Self::ReturnType metric, const SplineWarpXform& warp ) const { double result = metric; if ( this->m_JacobianConstraintWeight > 0 ) { result -= this->m_JacobianConstraintWeight * warp.GetJacobianConstraint(); } if ( this->m_GridEnergyWeight > 0 ) { result -= this->m_GridEnergyWeight * warp.GetGridEnergy(); } if ( !finite( result ) ) return -FLT_MAX; if ( this->m_LandmarkPairs ) { result -= this->m_LandmarkErrorWeight * warp.GetLandmarksMSD( *(this->m_LandmarkPairs) ); } if ( this->m_InverseTransformation ) { result -= this->m_InverseConsistencyWeight * warp.GetInverseConsistencyError( this->m_InverseTransformation, this->m_ReferenceGrid ); } return static_cast( result ); } /// Return weighted combination of similarity and grid energy derivatives. void WeightedDerivative( double& lower, double& upper, SplineWarpXform& warp, const int param, const Types::Coordinate step ) const; /** Regularize the deformation. */ cmtkGetSetMacroDefault(bool,Regularize,false); /// Dimension of warp parameter vector size_t Dim; /** Parameter scaling vector. * This array holds the scaling factors for all warp parameters as returned * by the transformation class. These factors can be used to equalized all * parameter modifications during gradient computation etc. */ std::vector m_StepScaleVector; /** Volume of influence table. * This array holds the precomputed volumes of influence for all * transformation parameters. Six successive numbers per parameter define the * voxel range with respect to the reference colume grid that is affected by * the respective parameter. */ DataGrid::RegionType *VolumeOfInfluence; /// Coordinate domain of the reference image. UniformVolume::CoordinateRegionType m_ReferenceDomain; /// Make smart pointer class friend so we can keep destructor protected. friend class SmartPointer; }; //@} } // namespace cmtk #endif // __cmtkImagePairNonrigidRegistrationFunctional_h_included_ cmtk-3.3.1/libs/Registration/cmtkImagePairNonrigidRegistrationFunctionalTemplate.h000066400000000000000000000406541276303427400305720ustar00rootroot00000000000000/* // // Copyright 2016 Google, Inc. // // Copyright 2004-2011, 2013 SRI International // // Copyright 1997-2009 Torsten Rohlfing // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 5400 $ // // $LastChangedDate: 2016-01-14 19:42:40 -0800 (Thu, 14 Jan 2016) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkImagePairNonrigidRegistrationFunctionalTemplate_h_included_ #define __cmtkImagePairNonrigidRegistrationFunctionalTemplate_h_included_ #include #include #include #include #ifdef CMTK_BUILD_DEMO # include #endif // #ifdef CMTK_BUILD_DEMO namespace cmtk { /** \addtogroup Registration */ //@{ /** Parallel elastic registration functional. * This class provides multi-threaded implementations for the most * time-consuming tasks performed by ImagePairNonrigidRegistrationFunctional and its * derived classes. */ template class ImagePairNonrigidRegistrationFunctionalTemplate /// Inherit from general image pair registration functional. : public ImagePairNonrigidRegistrationFunctional { protected: /** Metric object for incremental computation. * Before computing the incremental metric after change of one parameter, * the global metric is copied to this object. It is then used for in-place * application of all necessary changes, leaving the original metric intact. *\see #EvaluateIncremental */ SmartPointer m_IncrementalMetric; public: /// This class. typedef ImagePairNonrigidRegistrationFunctionalTemplate Self; /// Smart pointer to this class. typedef SmartPointer SmartPtr; /// Superclass. typedef ImagePairNonrigidRegistrationFunctional Superclass; /// Constructor. ImagePairNonrigidRegistrationFunctionalTemplate( UniformVolume::SmartPtr& reference, UniformVolume::SmartPtr& floating, const Interpolators::InterpolationEnum interpolation ) : ImagePairNonrigidRegistrationFunctional( reference, floating ), WarpNeedsFixUpdate( false ) { this->m_InfoTaskGradient.resize( this->m_NumberOfTasks ); this->m_InfoTaskComplete.resize( this->m_NumberOfTasks ); this->m_Metric = ImagePairSimilarityMeasure::SmartPtr( new VM( reference, floating, interpolation ) ); this->m_TaskMetric.resize( this->m_NumberOfThreads, dynamic_cast( *this->m_Metric ) ); } /** Destructor. * Free all per-thread data structures. */ virtual ~ImagePairNonrigidRegistrationFunctionalTemplate() {} /// Set flag and value for forcing values outside the floating image. virtual void SetForceOutside ( const bool flag = true, const Types::DataItem value = 0 ) { this->m_ForceOutsideFlag = flag; this->m_ForceOutsideValueRescaled = this->m_Metric->GetFloatingValueScaled( value ); } /** Set warp transformation. */ virtual void SetWarpXform ( SplineWarpXform::SmartPtr& warp ) { Superclass::SetWarpXform( warp ); this->WarpNeedsFixUpdate = true; } /// Match intensities of reference and floating images. virtual void MatchRefFltIntensities(); /** Evaluate functional for the complete image data. * This function builds the pre-computed deformed floating image that is * later used for rapid gradient computation. */ typename Self::ReturnType Evaluate() { this->m_Metric->Reset(); if ( ! this->m_WarpedVolume ) { this->m_WarpedVolume = Memory::ArrayC::Allocate( this->m_DimsX * this->m_DimsY * this->m_DimsZ ); } const size_t numberOfTasks = std::min( this->m_NumberOfTasks, this->m_DimsY * this->m_DimsZ ); for ( size_t taskIdx = 0; taskIdx < numberOfTasks; ++taskIdx ) { this->m_InfoTaskComplete[taskIdx].thisObject = this; } for ( size_t taskIdx = 0; taskIdx < this->m_NumberOfThreads; ++taskIdx ) { this->m_TaskMetric[taskIdx].Reset(); } ThreadPool::GetGlobalThreadPool().Run( EvaluateCompleteThread, this->m_InfoTaskComplete, numberOfTasks ); for ( size_t taskIdx = 0; taskIdx < this->m_NumberOfThreads; ++taskIdx ) { dynamic_cast( *(this->m_Metric) ).Add( this->m_TaskMetric[taskIdx] ); } return this->WeightedTotal( this->m_Metric->Get(), *(this->m_ThreadWarp[0]) ); } /** Evaluate functional after change of a single parameter. *\param warp The current deformation. *\param localMetric The local metric oobject for partial recomputation. *\param voi Volume-of-Influence for the parameter under consideration. *\param vectorCache Pre-allocated cache storage for locally used transformed vectors. *\return The metric after recomputation over the given volume-of-influence. */ typename Self::ReturnType EvaluateIncremental( const SplineWarpXform& warp, VM& localMetric, const DataGrid::RegionType& voi, Vector3D *const vectorCache ) { Vector3D *pVec; Types::GridIndexType pX, pY, pZ, r; Types::GridIndexType fltIdx[3]; Types::Coordinate fltFrac[3]; Types::GridIndexType endLineIncrement = ( voi.From()[0] + ( this->m_DimsX - voi.To()[0]) ); Types::GridIndexType endPlaneIncrement = this->m_DimsX * ( voi.From()[1] + (this->m_DimsY - voi.To()[1]) ); const Types::DataItem unsetY = DataTypeTraits::ChoosePaddingValue(); localMetric = dynamic_cast( *this->m_Metric ); r = voi.From()[0] + this->m_DimsX * ( voi.From()[1] + this->m_DimsY * voi.From()[2] ); for ( pZ = voi.From()[2]; pZm_Metric->GetSampleX( sampleX, r ) ) { if ( this->m_WarpedVolume[r] != unsetY ) localMetric.Decrement( sampleX, this->m_WarpedVolume[r] ); // Tell us whether the current location is still within the floating volume and get the respective voxel. *pVec *= this->m_FloatingInverseDelta; if ( this->m_FloatingGrid->FindVoxelByIndex( *pVec, fltIdx, fltFrac ) ) { // Continue metric computation. localMetric.Increment( sampleX, this->m_Metric->GetSampleY( fltIdx, fltFrac ) ); } else { if ( this->m_ForceOutsideFlag ) { localMetric.Increment( sampleX, this->m_ForceOutsideValueRescaled ); } } } } r += endLineIncrement; } r += endPlaneIncrement; } return localMetric.Get(); } /// Compute functional value and gradient. virtual typename Self::ReturnType EvaluateWithGradient( CoordinateVector& v, CoordinateVector& g, const typename Self::ParameterType step = 1 ) { const typename Self::ReturnType current = this->EvaluateAt( v ); if ( this->m_AdaptiveFixParameters && this->WarpNeedsFixUpdate ) { this->UpdateWarpFixedParameters(); } // Make sure we don't create more threads than we have parameters. // Actually, we shouldn't create more than the number of ACTIVE parameters. // May add this at some point. Anyway, unless we have A LOT of processors, // we shouldn't really ever have more threads than active parameters :)) const size_t numberOfTasks = std::min( this->m_NumberOfTasks, this->Dim ); for ( size_t taskIdx = 0; taskIdx < numberOfTasks; ++taskIdx ) { this->m_InfoTaskGradient[taskIdx].thisObject = this; this->m_InfoTaskGradient[taskIdx].Step = step; this->m_InfoTaskGradient[taskIdx].Gradient = g.Elements; this->m_InfoTaskGradient[taskIdx].BaseValue = current; this->m_InfoTaskGradient[taskIdx].Parameters = &v; } ThreadPool::GetGlobalThreadPool().Run( EvaluateGradientThread, this->m_InfoTaskGradient, numberOfTasks ); return current; } /// Evaluate functional. virtual typename Self::ReturnType EvaluateAt ( CoordinateVector& v ) { this->m_ThreadWarp[0]->SetParamVector( v ); return this->Evaluate(); } #ifdef CMTK_BUILD_DEMO /// Create a snapshot (to disk) of current functional result. virtual void SnapshotAt( ParameterVectorType& v ) { this->m_ThreadWarp[0]->SetParamVector( v ); static int it = 0; char path[PATH_MAX]; snprintf( path, PATH_MAX, "warp-%03d.xform", it++ ); XformIO::Write( this->m_ThreadWarp[0], path ); } #endif private: /** Metric object for threadwise computation. * The objects in this array are the per-thread equivalent of the * ImagePairNonrigidRegistrationFunctional::IncrementalMetric object. */ std::vector m_TaskMetric; /** Thread parameter block for incremental gradient computation. * This structure holds all thread-specific information. A pointer to an * instance of this structure is given to EvaluateGradientThread() for * each thread created. */ class EvaluateGradientTaskInfo { public: /** Pointer to the functional object that created the thread. */ Self *thisObject; /// Current parameter vector. CoordinateVector *Parameters; /// Current global coordinate stepping. typename Self::ParameterType Step; /// Pointer to gradient vector that is the target for computation results. Types::Coordinate *Gradient; /// Base functional value used for comparing new values to. double BaseValue; }; /// Info blocks for parallel threads evaluating functional gradient. std::vector m_InfoTaskGradient; /** Compute functional gradient as a thread. * This function (i.e., each thread) iterates over all parameters of the * current warp transformation. Among all active (i.e., not disabled) * parameters, it selects the ones that have an index with modulus * equal to the threads index when divided by the total number of threads. * For these parameters, the thread computes the partial derivative of the * functional by finite-difference approximation. */ static void EvaluateGradientThread( void* arg, const size_t taskIdx, const size_t taskCnt, const size_t threadIdx, const size_t ) { typename Self::EvaluateGradientTaskInfo *info = static_cast( arg ); Self *me = info->thisObject; SplineWarpXform& myWarp = *(me->m_ThreadWarp[threadIdx]); myWarp.SetParamVector( *info->Parameters ); VM& threadMetric = me->m_TaskMetric[threadIdx]; Vector3D *vectorCache = me->m_ThreadVectorCache[threadIdx]; Types::Coordinate *p = myWarp.m_Parameters; Types::Coordinate pOld; double upper, lower; const DataGrid::RegionType *voi = me->VolumeOfInfluence + taskIdx; for ( size_t dim = taskIdx; dim < me->Dim; dim+=taskCnt, voi+=taskCnt ) { if ( me->m_StepScaleVector[dim] <= 0 ) { info->Gradient[dim] = 0; } else { const typename Self::ParameterType thisStep = info->Step * me->m_StepScaleVector[dim]; pOld = p[dim]; p[dim] += thisStep; upper = me->EvaluateIncremental( myWarp, threadMetric, *voi, vectorCache ); p[dim] = pOld - thisStep; lower = me->EvaluateIncremental( myWarp, threadMetric, *voi, vectorCache ); p[dim] = pOld; me->WeightedDerivative( lower, upper, myWarp, dim, thisStep ); if ( (upper > info->BaseValue ) || (lower > info->BaseValue) ) { // strictly mathematically speaking, we should divide here by step*StepScaleVector[dim], but StepScaleVector[idx] is either zero or a constant independent of idx info->Gradient[dim] = upper - lower; } else { info->Gradient[dim] = 0; } } } } /** Thread parameter block for complete functional evaluation. * This structure holds all thread-specific information. A pointer to an * instance of this structure is given to EvaluateGradientThread() for * each thread created. */ class EvaluateCompleteTaskInfo { public: /** Pointer to the functional object that created the thread. */ Self *thisObject; }; /** Info blocks for parallel threads evaluating complete functional. */ std::vector m_InfoTaskComplete; /// Multi-threaded implementation of complete metric evaluation. static void EvaluateCompleteThread ( void *arg, const size_t taskIdx, const size_t taskCnt, const size_t threadIdx, const size_t ) { typename Self::EvaluateCompleteTaskInfo *info = static_cast( arg ); Self *me = info->thisObject; const SplineWarpXform& warp = *(me->m_ThreadWarp[0]); VM& threadMetric = me->m_TaskMetric[threadIdx]; Vector3D *vectorCache = me->m_ThreadVectorCache[threadIdx]; Types::DataItem* warpedVolume = me->m_WarpedVolume; const Types::DataItem unsetY = ( me->m_ForceOutsideFlag ) ? me->m_ForceOutsideValueRescaled : DataTypeTraits::ChoosePaddingValue(); Vector3D *pVec; Types::GridIndexType pX, pY, pZ; Types::GridIndexType fltIdx[3]; Types::Coordinate fltFrac[3]; Types::GridIndexType rowCount = ( me->m_DimsY * me->m_DimsZ ); Types::GridIndexType rowFrom = ( rowCount / taskCnt ) * taskIdx; Types::GridIndexType rowTo = ( taskIdx == (taskCnt-1) ) ? rowCount : ( rowCount / taskCnt ) * ( taskIdx + 1 ); Types::GridIndexType rowsToDo = rowTo - rowFrom; Types::GridIndexType pYfrom = rowFrom % me->m_DimsY; Types::GridIndexType pZfrom = rowFrom / me->m_DimsY; Types::GridIndexType r = rowFrom * me->m_DimsX; for ( pZ = pZfrom; (pZ < me->m_DimsZ) && rowsToDo; ++pZ ) { for ( pY = pYfrom; (pY < me->m_DimsY) && rowsToDo; pYfrom = 0, ++pY, --rowsToDo ) { warp.GetTransformedGridRow( me->m_DimsX, vectorCache, 0, pY, pZ ); pVec = vectorCache; for ( pX = 0; pXm_DimsX; ++pX, ++r, ++pVec ) { // Tell us whether the current location is still within the // floating volume and get the respective voxel. *pVec *= me->m_FloatingInverseDelta; if ( me->m_FloatingGrid->FindVoxelByIndex( *pVec, fltIdx, fltFrac ) ) { // Continue metric computation. warpedVolume[r] = me->m_Metric->GetSampleY( fltIdx, fltFrac ); Types::DataItem value; if ( me->m_Metric->GetSampleX( value, r ) ) { threadMetric.Increment( value, warpedVolume[r] ); } } else { warpedVolume[r] = unsetY; } } } } } private: /** Warp's fixed parameters need to be updated. * This flag is set when the warp transformation is set or modified. It * signals that the active and passive parameters of the transformation * will have to be updated before the next gradient computation. */ bool WarpNeedsFixUpdate; #ifdef _OPENMP /// Histogram used for consistency computation. std::vector::SmartPtr> m_ThreadConsistencyHistograms; #else /// Histogram used for consistency computation. JointHistogram::SmartPtr m_ConsistencyHistogram; #endif // #ifdef _OPENMP /** Update set of active and passive parameters. * This function computes local entropies in the neighborhood of all control * points of the Warp transformation. Those control points for which both * reference and floating image have less than half the maximum entropy in * this neighborhood as compared to the rest of the image are set passive. * The passive parameters are not considered for gradient computation and * therefore save significant computation time. */ void UpdateWarpFixedParameters(); }; //@} } // namespace cmtk #include "cmtkImagePairNonrigidRegistrationFunctionalTemplate.txx" #endif // __cmtkImagePairNonrigidRegistrationFunctionalTemplate_h_included_ cmtk-3.3.1/libs/Registration/cmtkImagePairNonrigidRegistrationFunctionalTemplate.txx000066400000000000000000000214031276303427400311550ustar00rootroot00000000000000/* // // Copyright 2016 Google, Inc. // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2012 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 5400 $ // // $LastChangedDate: 2016-01-14 19:42:40 -0800 (Thu, 14 Jan 2016) $ // // $LastChangedBy: torstenrohlfing $ // */ #include #include #include #ifdef _OPENMP # include #endif template void cmtk::ImagePairNonrigidRegistrationFunctionalTemplate::MatchRefFltIntensities() { const Types::DataItem paddingValue = DataTypeTraits::ChoosePaddingValue(); TypedArray::SmartPtr warpedArray( TypedArray::Create( TYPE_ITEM, this->m_WarpedVolume, this->m_FloatingGrid->GetNumberOfPixels(), true /*padding*/, &paddingValue ) ); UniformVolume::SmartPtr floatingCopy( this->m_FloatingGrid->Clone() ); floatingCopy->GetData()->ApplyFunctionObject( TypedArrayFunctionHistogramMatching( *warpedArray, *(this->m_ReferenceGrid->GetData()) ) ); this->m_Metric->SetFloatingVolume( floatingCopy ); } template void cmtk::ImagePairNonrigidRegistrationFunctionalTemplate::UpdateWarpFixedParameters() { int numCtrlPoints = this->Dim / 3; std::vector mapRef( numCtrlPoints ); std::vector mapMod( numCtrlPoints ); int inactive = 0; const Types::DataItem unsetY = DataTypeTraits::ChoosePaddingValue(); if ( this->m_ReferenceDataClass == DATACLASS_LABEL ) { if ( this->m_ActiveCoordinates ) this->m_Warp->SetParametersActive( this->m_ActiveCoordinates ); else this->m_Warp->SetParametersActive(); #pragma omp parallel for reduction(+:inactive) for ( int ctrl = 0; ctrl < numCtrlPoints; ++ctrl ) { /// We cannot use the precomputed table of VOIs here because in "fast" mode, these VOIs are smaller than we want them here. const DataGrid::RegionType voi = this->GetReferenceGridRange( this->m_Warp->GetVolumeOfInfluence( 3 * ctrl, this->m_ReferenceDomain, false /*disable fast mode*/ ) ); int r = voi.From()[0] + this->m_DimsX * ( voi.From()[1] + this->m_DimsY * voi.From()[2] ); bool active = false; for ( int pZ = voi.From()[2]; (pZ < voi.To()[2]) && !active; ++pZ ) { for ( int pY = voi.From()[1]; (pY < voi.To()[1]) && !active; ++pY ) { for ( int pX = voi.From()[0]; (pX < voi.To()[0]); ++pX, ++r ) { if ( ( this->m_Metric->GetSampleX( r ) != 0 ) || ( ( this->m_WarpedVolume[r] != unsetY ) && ( this->m_WarpedVolume[r] != 0 ) ) ) { active = true; break; } } r += ( voi.From()[0] + ( this->m_DimsX-voi.To()[0] ) ); } r += this->m_DimsX * ( voi.From()[1] + ( this->m_DimsY-voi.To()[1] ) ); } if ( !active ) { inactive += 3; int dim = 3 * ctrl; for ( int idx=0; idx<3; ++idx, ++dim ) { this->m_Warp->SetParameterInactive( dim ); } } } } else { #ifdef _OPENMP if ( this->m_ThreadConsistencyHistograms.size() < static_cast( omp_get_max_threads() ) ) { this->m_ThreadConsistencyHistograms.resize( omp_get_max_threads() ); const Types::GridIndexType numSamplesX = this->m_Metric->GetNumberOfSamplesX(); const Types::DataItemRange rangeX = this->m_Metric->GetDataRangeX(); const unsigned int numBinsX = JointHistogramBase::CalcNumBins( numSamplesX, rangeX ); const Types::GridIndexType numSamplesY = this->m_Metric->GetNumberOfSamplesY(); const Types::DataItemRange rangeY = this->m_Metric->GetDataRangeY(); const unsigned int numBinsY = JointHistogramBase::CalcNumBins( numSamplesY, rangeY ); for ( size_t thread = 0; thread < static_cast( omp_get_max_threads() ); ++thread ) { if ( ! this->m_ThreadConsistencyHistograms[thread] ) { this->m_ThreadConsistencyHistograms[thread] = JointHistogram::SmartPtr( new JointHistogram() ); this->m_ThreadConsistencyHistograms[thread]->Resize( numBinsX, numBinsY ); this->m_ThreadConsistencyHistograms[thread]->SetRangeX( rangeX ); this->m_ThreadConsistencyHistograms[thread]->SetRangeY( rangeY ); } } } #else if ( !this->m_ConsistencyHistogram ) { this->m_ConsistencyHistogram = JointHistogram::SmartPtr( new JointHistogram() ); const Types::GridIndexType numSamplesX = this->m_Metric->GetNumberOfSamplesX(); const Types::DataItemRange rangeX = this->m_Metric->GetDataRangeX(); const unsigned int numBinsX = JointHistogramBase::CalcNumBins( numSamplesX, rangeX ); const Types::GridIndexType numSamplesY = this->m_Metric->GetNumberOfSamplesY(); const Types::DataItemRange rangeY = this->m_Metric->GetDataRangeY(); const unsigned int numBinsY = JointHistogramBase::CalcNumBins( numSamplesY, rangeY ); this->m_ConsistencyHistogram->Resize( numBinsX, numBinsY ); this->m_ConsistencyHistogram->SetRangeX( rangeX ); this->m_ConsistencyHistogram->SetRangeY( rangeY ); } #endif #pragma omp parallel for for ( int ctrl = 0; ctrl < numCtrlPoints; ++ctrl ) { #ifdef _OPENMP JointHistogram& histogram = *(this->m_ThreadConsistencyHistograms[ omp_get_thread_num() ]); #else JointHistogram& histogram = *(this->m_ConsistencyHistogram); #endif histogram.Reset(); // We cannot use the precomputed table of VOIs here because in "fast" mode, these VOIs are smaller than we want them here. const DataGrid::RegionType voi = this->GetReferenceGridRange( this->m_Warp->GetVolumeOfInfluence( 3 * ctrl, this->m_ReferenceDomain, false /*disable fast mode*/ ) ); int r = voi.From()[0] + this->m_DimsX * ( voi.From()[1] + this->m_DimsY * voi.From()[2] ); const int endOfLine = ( voi.From()[0] + ( this->m_DimsX-voi.To()[0]) ); const int endOfPlane = this->m_DimsX * ( voi.From()[1] + (this->m_DimsY-voi.To()[1]) ); for ( int pZ = voi.From()[2]; pZm_WarpedVolume[r] != unsetY ) { histogram.Increment( histogram.ValueToBinX( this->m_Metric->GetSampleX( r ) ), histogram.ValueToBinY( this->m_WarpedVolume[r] ) ); } } r += endOfLine; } r += endOfPlane; } histogram.GetMarginalEntropies( mapRef[ctrl], mapMod[ctrl] ); } double refMin = HUGE_VAL, refMax = -HUGE_VAL; double modMin = HUGE_VAL, modMax = -HUGE_VAL; for ( int ctrl=0; ctrl refMax ) refMax = mapRef[ctrl]; if ( mapMod[ctrl] < modMin ) modMin = mapMod[ctrl]; if ( mapMod[ctrl] > modMax ) modMax = mapMod[ctrl]; } const double refThresh = refMin + this->m_AdaptiveFixThreshFactor * (refMax - refMin); const double modThresh = modMin + this->m_AdaptiveFixThreshFactor * (modMax - modMin); if ( this->m_ActiveCoordinates ) this->m_Warp->SetParametersActive( this->m_ActiveCoordinates ); else this->m_Warp->SetParametersActive(); for ( int ctrl=0; ctrlm_Warp->SetParameterInactive( dim ); } inactive += 3; } } } for ( size_t idx = 0; idx < this->Dim; ++idx ) { if ( this->m_Warp->GetParameterActive( idx ) ) { this->m_StepScaleVector[idx] = this->GetParamStep( idx ); } else { this->m_StepScaleVector[idx] = 0; } } DebugOutput( 1 ).GetStream().printf( "Deactivated %d out of %d parameters.\n", inactive, (int)this->Dim ); this->WarpNeedsFixUpdate = false; } cmtk-3.3.1/libs/Registration/cmtkImagePairRegistration.cxx000066400000000000000000000140351276303427400237260ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2011, 2013 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4824 $ // // $LastChangedDate: 2013-09-11 12:13:30 -0700 (Wed, 11 Sep 2013) $ // // $LastChangedBy: torsten_at_home $ // */ #include "cmtkImagePairRegistration.h" #include #include #include #include #include #include #ifdef HAVE_SYS_UTSNAME_H # include #endif namespace cmtk { /** \addtogroup Registration */ //@{ ImagePairRegistration::ImagePairRegistration () : m_Metric( 0 ), m_FloatingImageInterpolation( Interpolators::DEFAULT ), m_AutoMultiLevels( 0 ), m_MaxStepSize( -1 ), m_MinStepSize( -1 ), m_DeltaFThreshold( 0.0 ), m_Sampling( -1 ), m_ForceOutsideFlag( false ), m_ForceOutsideValue( 0.0 ), m_PreprocessorRef( "Reference", "ref" ), m_PreprocessorFlt( "Floating", "flt" ), m_InitialTransformation( NULL ), m_InitialXformIsInverse( false ), m_Xform( NULL ), m_Optimizer( NULL ), m_TimeStartRegistration ( 0.0 ), m_TimeStartLevel( 0.0 ), m_WalltimeStartRegistration( 0.0 ), m_WalltimeStartLevel( 0.0 ), m_ThreadTimeStartRegistration( 0.0 ), m_ThreadTimeStartLevel( 0.0 ) { this->m_Callback = RegistrationCallback::SmartPtr( new RegistrationCallback() ); this->m_Sampling = -1; this->m_CoarsestResolution = -1; this->m_UseOriginalData = true; this->m_Algorithm = 0; this->m_UseMaxNorm = true; this->m_OptimizerStepFactor = 0.5; } CallbackResult ImagePairRegistration::InitRegistration () { if ( this->m_AutoMultiLevels > 0 ) { const Types::Coordinate minDelta = std::min( this->m_Volume_1->GetMinDelta(), this->m_Volume_2->GetMinDelta() ); const Types::Coordinate maxDelta = std::max( this->m_Volume_1->GetMaxDelta(), this->m_Volume_2->GetMaxDelta() ); this->m_MinStepSize = 0.1 * minDelta; this->m_Sampling = maxDelta; this->m_MaxStepSize = maxDelta * (1<<(this->m_AutoMultiLevels-1)); } if ( this->m_Sampling <= 0 ) this->m_Sampling = std::max( this->m_Volume_1->GetMaxDelta(), this->m_Volume_2->GetMaxDelta() ); if ( this->m_MaxStepSize <= 0 ) this->m_MaxStepSize = 8.0 * this->m_Sampling; if ( this->m_MinStepSize <= 0 ) this->m_MinStepSize = this->m_Sampling / 128; this->m_TimeStartLevel = this->m_TimeStartRegistration = cmtk::Timers::GetTimeProcess(); this->m_WalltimeStartLevel = this->m_WalltimeStartRegistration = cmtk::Timers::GetWalltime(); this->m_ThreadTimeStartLevel = this->m_ThreadTimeStartRegistration = cmtk::Timers::GetTimeThread(); return CALLBACK_OK; } CallbackResult ImagePairRegistration::Register () { CallbackResult irq = this->InitRegistration(); if ( irq != CALLBACK_OK ) { this->DoneRegistration(); return irq; } this->m_Optimizer->SetDeltaFThreshold( this->m_DeltaFThreshold ); Types::Coordinate currentExploration = this->m_MaxStepSize; CoordinateVector::SmartPtr v( new CoordinateVector() ); const size_t NumResolutionLevels = this->m_ParameterStack.size(); Progress::Begin( 0, NumResolutionLevels, 1, "Multi-level Registration" ); unsigned int index = 1; while ( ! this->m_ParameterStack.empty() && ( irq == CALLBACK_OK ) ) { Functional::SmartPtr nextFunctional( this->MakeFunctional( index-1, this->m_ParameterStack.top() ) ); this->m_ParameterStack.pop(); // Reference functional as we still need if after the optimization when // calling DoneResolution(). // nextFunctional->Reference(); this->m_Optimizer->SetFunctional( nextFunctional ); int doneResolution = 0; while ( ! doneResolution && ( irq == CALLBACK_OK ) ) { this->EnterResolution( v, nextFunctional, index, NumResolutionLevels ); if ( irq == CALLBACK_OK ) { Types::Coordinate effectiveAccuracy = (index == NumResolutionLevels) ? std::max( this->m_MinStepSize, currentExploration/1024 ) : this->m_MinStepSize; irq = this->m_Optimizer->Optimize( *v, currentExploration, effectiveAccuracy ); this->m_Xform->SetParamVector( *v ); } doneResolution = this->DoneResolution( v, nextFunctional, index, NumResolutionLevels ); } this->m_Optimizer->SetFunctional( Functional::SmartPtr::Null() ); currentExploration *= 0.5; Progress::SetProgress( index ); ++index; } Progress::Done(); this->OutputResult( v, irq ); this->DoneRegistration( v ); return irq; } void ImagePairRegistration::DoneRegistration( const CoordinateVector* v ) { if ( v ) this->m_Xform->SetParamVector( *v ); } void ImagePairRegistration::EnterResolution ( CoordinateVector::SmartPtr& v, Functional::SmartPtr& f, const int idx, const int total ) { if ( this->m_Callback ) { char comment[128]; snprintf( comment, sizeof( comment ), "Entering resolution level %d out of %d.", idx, total ); this->m_Callback->Comment( comment ); } this->m_TimeStartLevel = cmtk::Timers::GetTimeProcess(); this->m_WalltimeStartLevel = cmtk::Timers::GetWalltime(); this->m_ThreadTimeStartLevel = cmtk::Timers::GetTimeThread(); f->GetParamVector( *v ); } } // namespace cmtk cmtk-3.3.1/libs/Registration/cmtkImagePairRegistration.h000066400000000000000000000324741276303427400233620ustar00rootroot00000000000000/* // // Copyright 2004-2013 SRI International // // Copyright 1997-2009 Torsten Rohlfing // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4833 $ // // $LastChangedDate: 2013-09-12 13:34:33 -0700 (Thu, 12 Sep 2013) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkImagePairRegistration_h_included_ #define __cmtkImagePairRegistration_h_included_ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace cmtk { /** \addtogroup Registration */ //@{ /** Generic multiresolution voxel-registration class. * By implementing member functions to retrieve parameters and report results * in derived classes, registration can be integrated into various * environments. */ class ImagePairRegistration { public: /// This class. typedef ImagePairRegistration Self; /// Smart pointer. typedef SmartPointer SmartPtr; protected: /// Image pair similarity measure to use as the registration metric. int m_Metric; /** Override default interpolation method. * For intensity images, the default interpolator is LINEAR, for label images * it is NEARESTNEIGHBOR. These are used if this field is left at its initial * value, DEFAULT. */ cmtkGetSetMacro(Interpolators::InterpolationEnum,FloatingImageInterpolation); /// Optimization algorithm to use. cmtkGetSetMacro(int,Algorithm); /// Number of levels for automatic parameter generation. unsigned int m_AutoMultiLevels; /// Maximum optimization step size (this determines search space exploration). double m_MaxStepSize; /// Minimum optimization step size (this determines search precision). double m_MinStepSize; /** Coarsest resolution to resample image data to. * If this value is unset, ie. less than or equal to zero, then the coarsest * resolution is automatically computed from the initial step size * (Exploration). */ double m_CoarsestResolution; /// Flag whether the last resolution level uses the original images. bool m_UseOriginalData; /// Factor between optimization step sizes. double m_OptimizerStepFactor; /// Use maximum norm instead of Euclid where applicable. bool m_UseMaxNorm; /// Threshold for terminating optimization based on changes of the target function. Optimizer::ReturnType m_DeltaFThreshold; /** Image sampling. * This is the finest resampled image resolution in the multi-resolution image pyramid. * The only finer resolution images are the original ones. */ Types::Coordinate m_Sampling; /// Flag for forcing pixel values outside the floating image. bool m_ForceOutsideFlag; /// Value for forcing pixel values outside the floating image. Types::DataItem m_ForceOutsideValue; /// First data volume. cmtkGetSetMacro(UniformVolume::SmartPtr,Volume_1); /// Second data volume. cmtkGetSetMacro(UniformVolume::SmartPtr,Volume_2); /** Reference data volume. * This is a pointer to the actual reference volume, which is either Volume_1 or Volume_2 above, * depending on whether registration was instructed to switch the two or not. */ cmtkGetSetMacro(UniformVolume::SmartPtr,ReferenceVolume); /** Floating data volume. * This is a pointer to the actual floating volume, which is either Volume_2 or Volume_1 above, * depending on whether registration was instructed to switch the two or not. */ cmtkGetSetMacro(UniformVolume::SmartPtr,FloatingVolume); /// Set flag and value for forcing values outside the floating image. virtual void SetForceOutside( const bool flag = true, const Types::DataItem value = 0 ) { this->m_ForceOutsideFlag = flag; this->m_ForceOutsideValue = value; } /// Local class for preprocessing image data, e.g., by histogram operations, thresholding, and cropping. class ImagePreprocessor { public: /// Data class string ("grey", "labels", or "binary") const char* m_DataClassString; /// Data class ID. DataClass m_DataClass; /// Flag for pixel padding. bool m_PaddingFlag; /// Padding value. Types::DataItem m_PaddingValue; /// Lower threshold flag. bool m_LowerThresholdActive; /// Lower threshold value. Types::DataItem m_LowerThresholdValue; /// Upper threshold flag. bool m_UpperThresholdActive; /// Upper threshold value. Types::DataItem m_UpperThresholdValue; /// Flag for image histogram pruning. bool m_UsePruneHistogramBins; /// Prune histogram for image: number of target bins. unsigned int m_PruneHistogramBins; /// Flag for histogram equalization. bool m_HistogramEqualization; /// Radius for median filter (0 - no filtering). int m_MedianFilterRadius; /// Flag for application of Sobel edge detection filter. bool m_SobelFilter; /// Crop region in index coordinates. const char* m_CropIndex; /// Crop region in world coordinates. const char* m_CropWorld; /// Flag for auto cropping. bool m_AutoCropFlag; /// Auto cropping level. Types::DataItem m_AutoCropLevel; /// Constructor. ImagePreprocessor( const std::string& name /*!< There are two preprocessors, for reference and floating image: this parameter names a parameter group for this instance.*/, const std::string& key /*!< This parameter gives a string key that is appended to each command line option so that reference and floating preprocessors do not collide.*/ ); /// Attach this preprocessor to a command line parse. void AttachToCommandLine( CommandLine& cl /*!< The command line object to add our options to.*/ ); /// Get pre-processed image from original image. UniformVolume::SmartPtr GetProcessedImage( const UniformVolume* original ); /// Write settings of this object to class stream for archiving. void WriteSettings( ClassStreamOutput& stream ) const; private: /// Store the name that identifies this instance ("Reference" or "Floating") std::string m_Name; /// Store the key that identifies this instance ("ref" or "flt") std::string m_Key; }; /// Image preprocessor for reference image. ImagePreprocessor m_PreprocessorRef; /// Image preprocessor for floating image. ImagePreprocessor m_PreprocessorFlt; /// Pointer to callback object. cmtkGetSetMacro(RegistrationCallback::SmartPtr,Callback); /// Initial transformation. cmtkGetSetMacro(AffineXform::SmartPtr,InitialTransformation); /// FLag whether initial transformation is an inverse. cmtkGetSetMacro(bool,InitialXformIsInverse); /// Current / final transformation. Xform::SmartPtr m_Xform; /// Base class for registration level parameters. class LevelParameters { public: /// This class. typedef LevelParameters Self; /// Smart pointer. typedef SmartPointer SmartPtr; /// Virtual destructor. virtual ~LevelParameters() {} }; /// Stack of functional objects for the resolution steps. std::stack m_ParameterStack; /// Make functional for a set of registration level parameters. virtual Functional* MakeFunctional( const int level, const Self::LevelParameters* levelParameters ) = 0; /// Pointer to optimizer object. Optimizer::SmartPtr m_Optimizer; /**\name Member functions to be overwritten. */ //@{ /** Initialize registration. * This function is called by Register before any other operations. It can * be overloaded to open status dialog windows, etc. Derived implementations * should call their base class' InitRegistration first. *\return */ virtual CallbackResult InitRegistration (); /** Output registration result. * This function is called after finishing registration. It can overloaded * to report the resulting transformation, export it to an encapsulating * application, etc... */ virtual void OutputResult ( const CoordinateVector* /*!< The vector of resulting transformation parameters. */, const CallbackResult = CALLBACK_OK /*!< The interrupt status - this allows the output function to determine whether computation finished or was interrupted.*/ ) {} /** Finalize registration. * This function is called after registration has been terminated. It can * be used to destroy progress dialog windows, free memory etc. Its last * operation should be a call to the respective parent class' implementation. */ virtual void DoneRegistration ( const CoordinateVector* v = NULL); /** Enter resolution level. * This function is called before entering each resolution level. It can * be used to update status displays etc. *\param v Current parameter vector. *\param f Functional for next level. *\param idx Index of the current resolution level. 0 is first (coarsest), * subsequent (finer) resolutions have increasing numbers. *\param total Total number of resolution levels. */ virtual void EnterResolution( CoordinateVector::SmartPtr& v, Functional::SmartPtr& f, const int idx, const int total ); /** Finish resolution level. * This function is called after every resolution level. It should do any * necessary cleanups resulting from the previous call to EnterRegistration. *\return If the current level is finished, 1 is returned. Otherwise, ie. * if the derived class requests another run of the same level, 0 may be * returned. This is used for example by the affine registration in order * to make repeated runs of the same level with different numbers of degrees * of freedom. Be careful not to create any inifinite loops. */ virtual int DoneResolution( CoordinateVector::SmartPtr&, Functional::SmartPtr&, const int, const int ) { return 1; } //@} public: /// Exception class. class ConstructorFailed {}; /** Default constructor. */ ImagePairRegistration (); /** Destructor. */ virtual ~ImagePairRegistration () {} /** Do registration. * This function must be called to start the multiresolution optimization * using the specified parameters. *\return 1 if registration was terminated by a user interrupt, 0 if * registration was finished. */ virtual CallbackResult Register (); /** Return total elapsed process time. */ double GetTotalElapsedTime() const { return cmtk::Timers::GetTimeProcess() - this->m_TimeStartRegistration; } /** Return elapsed process time during current level. */ double GetLevelElapsedTime() const { return cmtk::Timers::GetTimeProcess() - this->m_TimeStartLevel; } /** Return total elapsed walltime. */ double GetTotalElapsedWalltime() const { return cmtk::Timers::GetWalltime() - this->m_WalltimeStartRegistration; } /** Return elapsed walltime during current level. */ double GetLevelElapsedWalltime() const { return cmtk::Timers::GetWalltime() - this->m_WalltimeStartLevel; } /** Return total elapsed thread time. */ double GetThreadTotalElapsedTime() const { return cmtk::Timers::GetTimeThread() - this->m_ThreadTimeStartRegistration; } /** Return elapsed thread time during current level. */ double GetThreadLevelElapsedTime() const { return cmtk::Timers::GetTimeThread() - this->m_ThreadTimeStartLevel; } private: /** Time of registration start. * This is used as the reference for absolute computation time calculation. */ double m_TimeStartRegistration; /** Time of entering the current resolution level. * This is used as the reference for per-level computation time calculation. */ double m_TimeStartLevel; /** Reference walltime of registration start. * This is used as the reference for absolute computation time calculation. */ double m_WalltimeStartRegistration; /** Reference walltime of entering the current resolution level. * This is used as the reference for per-level computation time calculation. */ double m_WalltimeStartLevel; /** Time of registration start. * This is used as the reference for absolute computation time calculation. */ double m_ThreadTimeStartRegistration; /** Time of entering the current resolution level. * This is used as the reference for per-level computation time calculation. */ double m_ThreadTimeStartLevel; }; //@} } // namespace cmtk #endif // #ifndef __cmtkImagePairRegistration_h_included_ cmtk-3.3.1/libs/Registration/cmtkImagePairRegistrationFunctional.cxx000066400000000000000000000065171276303427400257570ustar00rootroot00000000000000/* // // Copyright 2016 Google, Inc. // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2012 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 5398 $ // // $LastChangedDate: 2016-01-14 18:29:18 -0800 (Thu, 14 Jan 2016) $ // // $LastChangedBy: torstenrohlfing $ // */ #include #include #include namespace cmtk { /** \addtogroup Registration */ //@{ void ImagePairRegistrationFunctional::InitFloating( UniformVolume::SmartConstPtr& floating ) { this->m_FloatingGrid = floating; this->m_FloatingDims = this->m_FloatingGrid->GetDims(); this->m_FloatingSize = this->m_FloatingGrid->m_Size; this->m_FloatingCropRegionCoordinates = this->m_FloatingGrid->GetHighResCropRegion(); for ( int dim = 0; dim < 3; ++dim ) { this->m_FloatingInverseDelta[dim] = 1.0 / this->m_FloatingGrid->m_Delta[dim]; this->m_FloatingCropRegionFractIndex.From()[dim] = this->m_FloatingCropRegionCoordinates.From()[dim] * this->m_FloatingInverseDelta[dim]; this->m_FloatingCropRegionFractIndex.To()[dim] = this->m_FloatingCropRegionCoordinates.To()[dim] * this->m_FloatingInverseDelta[dim]; } this->m_FloatingDataClass = floating->GetData()->GetDataClass(); } void ImagePairRegistrationFunctional::InitReference( UniformVolume::SmartConstPtr& reference ) { this->m_ReferenceGrid = reference; this->m_ReferenceDims = this->m_ReferenceGrid->GetDims(); this->m_ReferenceSize = this->m_ReferenceGrid->m_Size; this->m_ReferenceCropRegion = this->m_ReferenceGrid->CropRegion(); for ( int dim = 0; dim < 3; ++dim ) this->m_ReferenceInverseDelta[dim] = 1.0 / this->m_ReferenceGrid->m_Delta[dim]; this->m_ReferenceDataClass = reference->GetData()->GetDataClass(); } const DataGrid::RegionType ImagePairRegistrationFunctional::GetReferenceGridRange ( const UniformVolume::CoordinateRegionType& region ) const { const DataGrid::IndexType& cropRegionFrom = this->m_ReferenceCropRegion.From(); const DataGrid::IndexType& cropRegionTo = this->m_ReferenceCropRegion.To(); DataGrid::IndexType from, to; for ( int i = 0; i < 3; ++i ) { from[i] = std::min( cropRegionTo[i]-1, std::max( cropRegionFrom[i], static_cast( region.From()[i] * this->m_ReferenceInverseDelta[i] ) ) ); to[i] = 1+std::max( cropRegionFrom[i], std::min( cropRegionTo[i]-1, 1+static_cast( region.To()[i] * this->m_ReferenceInverseDelta[i] ) ) ); } return DataGrid::RegionType( from, to ); } } // namespace cmtk cmtk-3.3.1/libs/Registration/cmtkImagePairRegistrationFunctional.h000066400000000000000000000131411276303427400253730ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2013 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4812 $ // // $LastChangedDate: 2013-09-09 14:26:32 -0700 (Mon, 09 Sep 2013) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkImagePairRegistrationFunctional_h_included_ #define __cmtkImagePairRegistrationFunctional_h_included_ #include #include #include #include #include #include #include #include #include #include #include #include namespace cmtk { /** \addtogroup Registration */ //@{ /** Base class for voxel matching functionals. * This class is used as the common base class for more specific functional * classes. It contains all data structure and functions that do not depend on * any template parameters introduced later in the inheritance hierarchy. It * should therefore help avoiding unnecessary code duplication. */ class ImagePairRegistrationFunctional : public Functional, private CannotBeCopied { public: /// This class. typedef ImagePairRegistrationFunctional Self; /// Superclass. typedef Functional Superclass; protected: /// Pointer to the reference grid. UniformVolume::SmartConstPtr m_ReferenceGrid; /// Pointer to the floating grid. UniformVolume::SmartConstPtr m_FloatingGrid; /// Data class of reference image. DataClass m_ReferenceDataClass; /// Data class of floating image. DataClass m_FloatingDataClass; /// Rectangular crop region in the reference volume. DataGrid::RegionType m_ReferenceCropRegion; /// Optional list of matched landmarks. cmtkGetSetMacro(LandmarkPairList::SmartConstPtr,LandmarkPairs); /// Weight for the landmark registration error relative to image similarity. cmtkGetSetMacro(Self::ReturnType,LandmarkErrorWeight); public: /** Constructor. * Init pointers to volume and transformation objects and initialize * internal data structures. *\param reference The reference (i.e. static) volume. *\param floating The floating (i.e. transformed) volume. */ ImagePairRegistrationFunctional( UniformVolume::SmartConstPtr& reference, UniformVolume::SmartConstPtr& floating ) : m_ForceOutsideFlag( false ), m_ForceOutsideValueRescaled( 0.0 ) { this->InitFloating( floating ); this->InitReference( reference ); this->m_LandmarkErrorWeight = 0; } /** Destructor. */ virtual ~ImagePairRegistrationFunctional() {} /// Set flag and value for forcing values outside the floating image. virtual void SetForceOutside ( const bool flag = true, const Types::DataItem value = 0 ) { this->m_ForceOutsideFlag = flag; this->m_ForceOutsideValueRescaled = this->m_Metric->GetFloatingValueScaled( value ); } protected: /// The metric (similarity measure) object. ImagePairSimilarityMeasure::SmartPtr m_Metric; /// Grid dimensions of the floating volume. DataGrid::IndexType m_FloatingDims; /// Extents of the floating volume in real-world coordinates. UniformVolume::CoordinateVectorType m_FloatingSize; /// Inverse pixel sizes of the floating volume. UniformVolume::CoordinateVectorType m_FloatingInverseDelta; /// Coordinates of the floating image's cropping region. UniformVolume::CoordinateRegionType m_FloatingCropRegionCoordinates; /// Fractional index starting coordinate of the floating's cropping region. UniformVolume::CoordinateRegionType m_FloatingCropRegionFractIndex; /// Grid dimensions of the reference volume. DataGrid::IndexType m_ReferenceDims; /// Extents of the reference volume in real-world coordinates. UniformVolume::CoordinateVectorType m_ReferenceSize; /// Inverse pixel deltas of the reference volume. UniformVolume::CoordinateVectorType m_ReferenceInverseDelta; /// Flag for forcing pixel values outside the floating image. bool m_ForceOutsideFlag; /// Rescaled byte value for forcing pixel values outside the floating image. Types::DataItem m_ForceOutsideValueRescaled; /** Find rectilinear area in original reference grid. *\return The smallest region of reference grid voxels that contains the given coordinate range. */ const DataGrid::RegionType GetReferenceGridRange ( const UniformVolume::CoordinateRegionType& region /*!< Coordinate region.*/ ) const; private: /// Initialize internal data structures for floating image. void InitFloating( UniformVolume::SmartConstPtr& floating ); /// Initialize internal data structures for reference image. void InitReference( UniformVolume::SmartConstPtr& reference ); }; //@} } // namespace cmtk #endif // __cmtkImagePairRegistrationFunctional_h_included_ cmtk-3.3.1/libs/Registration/cmtkImagePairRegistrationImagePreprocessor.cxx000066400000000000000000000202541276303427400273000ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2013 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4873 $ // // $LastChangedDate: 2013-09-24 12:53:12 -0700 (Tue, 24 Sep 2013) $ // // $LastChangedBy: torstenrohlfing $ // */ #include "cmtkImagePairRegistration.h" #include #include #include namespace cmtk { ImagePairRegistration::ImagePreprocessor::ImagePreprocessor( const std::string& name, const std::string& key ) : m_DataClassString( NULL ), m_DataClass( DATACLASS_GREY ), m_PaddingFlag( false ), m_PaddingValue( 0 ), m_LowerThresholdActive( false ), m_LowerThresholdValue( -CMTK_ITEM_MAX ), m_UpperThresholdActive( false ), m_UpperThresholdValue( CMTK_ITEM_MAX ), m_UsePruneHistogramBins( false ), m_PruneHistogramBins( 0 ), m_HistogramEqualization( false ), m_MedianFilterRadius( 0 ), m_SobelFilter( false ), m_CropIndex( NULL ), m_CropWorld( NULL ), m_AutoCropFlag( false ), m_AutoCropLevel( 0 ), m_Name( name ), m_Key( key ) { } void ImagePairRegistration::ImagePreprocessor::AttachToCommandLine ( CommandLine& cl ) { cl.BeginGroup( this->m_Name, this->m_Name + " Image Preprocessing" )->SetProperties( CommandLine::PROPS_NOXML ); cl.AddOption( CommandLine::Key( std::string( "class-" ) + this->m_Key ), &this->m_DataClassString, "Data class: grey (default) or label" ); cl.AddOption( CommandLine::Key( std::string( "pad-" ) + this->m_Key ), &this->m_PaddingValue, "Padding value", &this->m_PaddingFlag ); cl.AddOption( CommandLine::Key( std::string( "thresh-min-" ) + this->m_Key ), &this->m_LowerThresholdValue, "Minimum value truncation threshold", &this->m_LowerThresholdActive ); cl.AddOption( CommandLine::Key( std::string( "thresh-max-" ) + this->m_Key ), &this->m_UpperThresholdValue, "Maximum value truncation threshold", &this->m_UpperThresholdActive ); cl.AddOption( CommandLine::Key( std::string( "prune-histogram-" ) + this->m_Key ), &this->m_PruneHistogramBins, "Number of bins for histogram-based pruning", &this->m_UsePruneHistogramBins ); cl.AddSwitch( CommandLine::Key( std::string( "histogram-equalization-" ) + this->m_Key ), &this->m_HistogramEqualization, true, "Apply histogram equalization" ); cl.AddOption( CommandLine::Key( std::string( "median-filter-radius-" ) + this->m_Key ), &this->m_MedianFilterRadius, "Apply median filter with given radius" ); cl.AddSwitch( CommandLine::Key( std::string( "sobel-filter-" ) + this->m_Key ), &this->m_SobelFilter, true, "Apply Sobel edge detection filter" ); cl.AddOption( CommandLine::Key( std::string( "crop-index-" ) + this->m_Key ), &this->m_CropIndex, "Cropping region in pixel index coordinates [parsed as %d,%d,%d,%d,%d,%d for i0,j0,k0,i1,j1,k1]" ); cl.AddOption( CommandLine::Key( std::string( "crop-world-" ) + this->m_Key ), &this->m_CropWorld, "Cropping region in world coordinates [parsed as %f,%f,%f,%f,%f,%f for x0,y0,z0,x1,y1,z1]" ); cl.AddOption( CommandLine::Key( std::string( "crop-thresh-" ) + this->m_Key ), &this->m_AutoCropLevel, "Automatic cropping based on threshold", &this->m_AutoCropFlag ); cl.EndGroup(); } UniformVolume::SmartPtr ImagePairRegistration::ImagePreprocessor::GetProcessedImage( const UniformVolume* original ) { UniformVolume::SmartPtr volume( original->Clone() ); TypedArray::SmartPtr data = volume->GetData(); if ( this->m_DataClassString ) { this->m_DataClass = StringToDataClass( this->m_DataClassString ); data->SetDataClass( this->m_DataClass ); } if ( this->m_PaddingFlag ) { data->SetPaddingValue( this->m_PaddingValue ); } if ( this->m_LowerThresholdActive || this->m_UpperThresholdActive ) { data->Threshold( Types::DataItemRange( this->m_LowerThresholdValue, this->m_UpperThresholdValue ) ); } if ( this->m_PruneHistogramBins ) { data->PruneHistogram( true /*pruneHi*/, false /*pruneLo*/, this->m_PruneHistogramBins ); } if ( this->m_HistogramEqualization ) { data->ApplyFunctionObject( TypedArrayFunctionHistogramEqualization( *data ) ); } if ( this->m_MedianFilterRadius ) { volume->SetData( DataGridFilter( volume ).GetDataMedianFiltered( this->m_MedianFilterRadius ) ); } if ( this->m_SobelFilter ) { volume->SetData( DataGridFilter( volume ).GetDataSobelFiltered() ); } if ( this->m_CropIndex ) { int cropFrom[3], cropTo[3]; if ( 6 != sscanf( this->m_CropIndex, "%6d,%6d,%6d,%6d,%6d,%6d", cropFrom, cropFrom+1, cropFrom+2, cropTo, cropTo+1, cropTo+2 ) ) { StdErr << "Option index coordinate cropping expects six integer parameters but got '" << this->m_CropIndex << "'\n"; exit( 1 ); } for ( int dim=0; dim<3; ++dim ) { if ( cropTo[dim] < 0 ) { cropTo[dim] = volume->GetDims()[dim] + cropTo[dim] + 1; } } volume->CropRegion() = DataGrid::RegionType( DataGrid::IndexType::FromPointer( cropFrom ), DataGrid::IndexType::FromPointer( cropTo ) ); } if ( this->m_CropWorld ) { float crop[6]; if ( 6 != sscanf( this->m_CropWorld, "%15f,%15f,%15f,%15f,%15f,%15f", crop, crop+1, crop+2, crop+3, crop+4, crop+5 ) ) { StdErr << "Option world coordinate cropping expects six floating-point parameters but got '" << this->m_CropWorld << "'\n"; exit( 1 ); } Types::Coordinate realCropFrom[3], realCropTo[3]; for ( int dim=0; dim<3; ++dim ) { realCropFrom[dim] = crop[dim]; if ( crop[3+dim] < 0 ) { realCropTo[dim] = volume->m_Size[dim] + crop[3+dim]; } else { realCropTo[dim] = crop[3+dim]; } } volume->SetHighResCropRegion( UniformVolume::CoordinateRegionType( UniformVolume::CoordinateRegionType::IndexType::FromPointer( realCropFrom ), UniformVolume::CoordinateRegionType::IndexType::FromPointer( realCropTo ) ) ); } if ( this->m_AutoCropFlag ) { volume->AutoCrop( this->m_AutoCropLevel, true /*recrop*/ ); } return volume; } void ImagePairRegistration::ImagePreprocessor::WriteSettings ( ClassStreamOutput& stream ) const { stream.Begin( std::string( "preprocessing_" ) + this->m_Key ); switch ( this->m_DataClass ) { case DATACLASS_GREY: stream.WriteString( "dataclass", "GreyLevel" ); break; case DATACLASS_LABEL: stream.WriteString( "dataclass", "LabelField" ); break; default: stream.WriteString( "dataclass", "Unknown" ); break; } if ( this->m_PaddingFlag ) stream.WriteDouble( "padding_value", this->m_PaddingValue ); if ( this->m_LowerThresholdActive ) stream.WriteDouble( "thresh_lower", this->m_LowerThresholdValue ); if ( this->m_UpperThresholdActive ) stream.WriteDouble( "thresh_upper", this->m_UpperThresholdValue ); if ( this->m_PruneHistogramBins ) stream.WriteInt( "prune_histogram_bins", this->m_PruneHistogramBins ); if ( this->m_HistogramEqualization ) stream.WriteBool( "histogram_equalization", true ); if ( this->m_SobelFilter ) stream.WriteBool( "sobel_filter", true ); if ( this->m_CropIndex ) stream.WriteString( "crop_index", this->m_CropIndex ); if ( this->m_CropWorld ) stream.WriteString( "crop_world", this->m_CropWorld ); if ( this->m_AutoCropFlag ) stream.WriteDouble( "auto_crop_level", this->m_AutoCropLevel ); stream.End(); } } // namespace cmtk cmtk-3.3.1/libs/Registration/cmtkImagePairSimilarityJointHistogram.cxx000066400000000000000000000115751276303427400262720ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2012 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 3964 $ // // $LastChangedDate: 2012-03-06 09:46:25 -0800 (Tue, 06 Mar 2012) $ // // $LastChangedBy: torstenrohlfing $ // */ #include namespace cmtk { /** \addtogroup Registration */ //@{ ImagePairSimilarityJointHistogram::ImagePairSimilarityJointHistogram ( UniformVolume::SmartConstPtr& refVolume, UniformVolume::SmartConstPtr& fltVolume, const Interpolators::InterpolationEnum interpolation ) : ImagePairSimilarityMeasure( interpolation ) { Superclass::SetReferenceVolume( Self::PrescaleData( refVolume, &this->m_NumberOfBinsX, &this->m_ScaleFactorReference, &this->m_ScaleOffsetReference ) ); Superclass::SetFloatingVolume( Self::PrescaleData( fltVolume, &this->m_NumberOfBinsY, &this->m_ScaleFactorFloating, &this->m_ScaleOffsetFloating ) ); this->m_JointHistogram.Resize( this->m_NumberOfBinsX, this->m_NumberOfBinsY ); } void ImagePairSimilarityJointHistogram::SetReferenceVolume( const UniformVolume::SmartConstPtr& refVolume ) { Superclass::SetReferenceVolume( Self::PrescaleData( refVolume, &this->m_NumberOfBinsX, &this->m_ScaleFactorReference, &this->m_ScaleOffsetReference ) ); this->m_JointHistogram.Resize( this->m_NumberOfBinsX, this->m_NumberOfBinsY ); } void ImagePairSimilarityJointHistogram::SetFloatingVolume( const UniformVolume::SmartConstPtr& fltVolume ) { Superclass::SetFloatingVolume( Self::PrescaleData( fltVolume, &this->m_NumberOfBinsY, &this->m_ScaleFactorFloating, &this->m_ScaleOffsetFloating ) ); this->m_JointHistogram.Resize( this->m_NumberOfBinsX, this->m_NumberOfBinsY ); } UniformVolume::SmartPtr ImagePairSimilarityJointHistogram::PrescaleData ( const UniformVolume::SmartConstPtr& volume, size_t* numberOfBins, Types::DataItem* scaleFactor, Types::DataItem* scaleOffset ) { UniformVolume::SmartPtr newVolume( volume->CloneGrid() ); newVolume->CreateDataArray( TYPE_ITEM ); const size_t numberOfPixels = volume->GetNumberOfPixels(); Types::DataItem value = 0; Types::DataItem minValue = FLT_MAX; Types::DataItem maxValue = -FLT_MAX; const DataGrid::IndexType& cropFrom = volume->CropRegion().From(); const DataGrid::IndexType& cropTo = volume->CropRegion().To(); const DataGrid::IndexType increments = volume->GetCropRegionIncrements(); int offset = increments[0]; for ( int z = cropFrom[2]; z < cropTo[2]; ++z, offset += increments[2] ) { for ( int y = cropFrom[1]; y < cropTo[1]; ++y, offset += increments[1] ) { for ( int x = cropFrom[0]; x < cropTo[0]; ++x, ++offset ) { if ( volume->GetDataAt( value, offset ) ) { if ( value > maxValue ) maxValue = value; if ( value < minValue ) minValue = value; } } } } switch ( volume->GetData()->GetDataClass() ) { case DATACLASS_LABEL: { *numberOfBins = 1 + static_cast(maxValue-minValue); if ( *numberOfBins > 254 ) { StdErr << "Fatal error: Cannot handle more than 254 different labels.\n"; exit( 1 ); } *scaleOffset = -minValue; *scaleFactor = 1.0; for ( size_t idx = 0; idx < numberOfPixels; ++idx ) { if ( volume->GetDataAt( value, idx ) ) newVolume->SetDataAt( static_cast( value + *scaleOffset ), idx ); else newVolume->GetData()->SetPaddingAt( idx ); } } break; default: // Handle everything else as grey-level data. case DATACLASS_GREY: { *numberOfBins = JointHistogramBase::CalcNumBins( volume ); *scaleFactor = (*numberOfBins-1) / ( maxValue - minValue ); *scaleOffset = -minValue * *scaleFactor; for ( size_t idx = 0; idx < numberOfPixels; ++idx ) { if ( volume->GetDataAt( value, idx ) ) { value = std::max( std::min( value, maxValue ), minValue ); newVolume->SetDataAt( static_cast( floor(*scaleFactor*value+*scaleOffset) ), idx ); } else { newVolume->GetData()->SetPaddingAt( idx ); } } } break; } return newVolume; } } // namespace cmtk cmtk-3.3.1/libs/Registration/cmtkImagePairSimilarityJointHistogram.h000066400000000000000000000136101276303427400257070ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2011 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 2994 $ // // $LastChangedDate: 2011-03-14 11:27:30 -0700 (Mon, 14 Mar 2011) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkImagePairSimilarityJointHistogram_h_included_ #define __cmtkImagePairSimilarityJointHistogram_h_included_ #include #include #include #include #include #include namespace cmtk { /** \addtogroup Registration */ //@{ #ifdef _MSC_VER #pragma warning (disable:4521) #endif /** Base class for voxel metrics with pre-converted image data. */ class ImagePairSimilarityJointHistogram : /// Inherit generic image pair similarity class. public ImagePairSimilarityMeasure { public: /// This type. typedef ImagePairSimilarityJointHistogram Self; /// Smart pointer. typedef SmartPointer SmartPtr; /// Parent class. typedef ImagePairSimilarityMeasure Superclass; /// Return type: same as cmtk::Functional. typedef Functional::ReturnType ReturnType; /** Constructor. * For reference and model volume, InitDataset is called. *\param refVolume The reference (fixed) volume. *\param fltVolume The floating (moving, transformed) volume. *\param interpolation ID of the interpolator to use for the floating image. */ ImagePairSimilarityJointHistogram( UniformVolume::SmartConstPtr& refVolume, UniformVolume::SmartConstPtr& fltVolume, const Interpolators::InterpolationEnum interpolation = Interpolators::DEFAULT ); /** Default constructor. */ ImagePairSimilarityJointHistogram() {}; /** Virtual destructor. */ virtual ~ImagePairSimilarityJointHistogram() {}; /** Set reference volume. * In addition to setting the reference volume via the base class, this function * also performs pre-scaling and parameter selection using Self::PrescaleData(). * Afterwards the joint histogram size is re-allocated. */ virtual void SetReferenceVolume( const UniformVolume::SmartConstPtr& refVolume ); /** Set floating volume. * In addition to setting the floating volume via the base class, this function * also performs pre-scaling and parameter selection using Self::PrescaleData(). * Afterwards the joint histogram size is re-allocated. */ virtual void SetFloatingVolume( const UniformVolume::SmartConstPtr& fltVolume ); /// Reset computation: clear joint histogram. virtual void Reset () { this->m_JointHistogram.Reset(); } /** Add a pair of values to the metric. */ template void Increment( const T a, const T b ) { this->m_JointHistogram.Increment( static_cast( a ), std::max( 0, std::min( this->m_NumberOfBinsY-1, static_cast( b ) ) ) ); } /** Remove a pair of values from the metric. */ template void Decrement( const T a, const T b ) { this->m_JointHistogram.Decrement( static_cast( a ), std::max( 0, std::min( this->m_NumberOfBinsY-1, static_cast( b ) ) ) ); } /// Add another metric object to this one. void Add ( const Self& other ) { this->m_JointHistogram.AddJointHistogram( other.m_JointHistogram ); } /// Add another metric object to this one. void Remove ( const Self& other ) { this->m_JointHistogram.RemoveJointHistogram( other.m_JointHistogram ); } /// Get scaled floating value if this metric rescales (implemented in derived classes), or input value if it does not (done here as the default). virtual Types::DataItem GetFloatingValueScaled( const Types::DataItem value ) const { return static_cast( floor(this->m_ScaleFactorFloating*value+this->m_ScaleOffsetFloating) ); } protected: /// Number of X bins (reference image) size_t m_NumberOfBinsX; /// Number of Y bins (floating image) size_t m_NumberOfBinsY; /// The joint histogram. JointHistogram m_JointHistogram; private: /** Duplicate and pre-scale image data so that we have the histogram bin numbers readily available. *\return A new volume with the same geometry as the input volume, but for DATACLASS_GREY, all pixel * values will have been rescaled to represent histogram bin indexes directly. */ UniformVolume::SmartPtr PrescaleData( const UniformVolume::SmartConstPtr& volume /*!< Input volume.*/, size_t* numberOfBins /*!< Output: number of bins that the histogram should allocate for the output volume.*/, Types::DataItem* scaleFactor /*!< Data scaling factor.*/, Types::DataItem* scaleOffset /*!< Data scaling offset.*/ ); /// Store reference data rescaling offset. Types::DataItem m_ScaleOffsetReference; /// Store reference data rescaling factor. Types::DataItem m_ScaleFactorReference; /// Store floating data rescaling offset. Types::DataItem m_ScaleOffsetFloating; /// Store floating data rescaling factor. Types::DataItem m_ScaleFactorFloating; }; //@} } // namespace cmtk #endif // #ifndef __cmtkImagePairSimilarityJointHistogram_h_included_ cmtk-3.3.1/libs/Registration/cmtkImagePairSimilarityMeasure.cxx000066400000000000000000000057321276303427400247300ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2010 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 2398 $ // // $LastChangedDate: 2010-10-05 14:54:37 -0700 (Tue, 05 Oct 2010) $ // // $LastChangedBy: torstenrohlfing $ // */ #include #include #include #include #include namespace cmtk { /** \addtogroup Registration */ //@{ ImagePairSimilarityMeasure::ImagePairSimilarityMeasure ( const UniformVolume::SmartConstPtr& refVolume, const UniformVolume::SmartConstPtr& fltVolume, const Interpolators::InterpolationEnum interpolation ) : m_InterpolationMethod( interpolation ) { this->SetReferenceVolume( refVolume ); this->SetFloatingVolume( fltVolume ); } void ImagePairSimilarityMeasure::SetReferenceVolume( const UniformVolume::SmartConstPtr& refVolume ) { this->m_ReferenceVolume = refVolume; this->m_ReferenceData = this->m_ReferenceVolume->GetData(); } void ImagePairSimilarityMeasure::SetFloatingVolume( const UniformVolume::SmartConstPtr& fltVolume ) { this->m_FloatingVolume = fltVolume; this->m_FloatingData = fltVolume->GetData(); if ( this->m_InterpolationMethod == Interpolators::DEFAULT ) { // decide based on floating image data class. switch ( this->m_FloatingData->GetDataClass() ) { case DATACLASS_UNKNOWN : case DATACLASS_GREY : this->m_InterpolationMethod = Interpolators::LINEAR; this->m_FloatingImageInterpolator = cmtk::UniformVolumeInterpolatorBase::SmartPtr( new cmtk::UniformVolumeInterpolator( *fltVolume ) ); break; case DATACLASS_LABEL : this->m_InterpolationMethod = Interpolators::NEAREST_NEIGHBOR; this->m_FloatingImageInterpolator = cmtk::UniformVolumeInterpolatorBase::SmartPtr( new cmtk::UniformVolumeInterpolator( *fltVolume ) ); break; } } else { this->m_FloatingImageInterpolator = ReformatVolume::CreateInterpolator( this->m_InterpolationMethod, fltVolume ); } } } // namespace cmtk cmtk-3.3.1/libs/Registration/cmtkImagePairSimilarityMeasure.h000066400000000000000000000120771276303427400243550ustar00rootroot00000000000000/* // // Copyright 2016 Google, Inc. // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2012 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 5398 $ // // $LastChangedDate: 2016-01-14 18:29:18 -0800 (Thu, 14 Jan 2016) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkImagePairSimilarityMeasure_h_included_ #define __cmtkImagePairSimilarityMeasure_h_included_ #include #include #include #include #include #include namespace cmtk { /** \addtogroup Registration */ //@{ /** Base class for voxel metrics with pre-converted image data. */ class ImagePairSimilarityMeasure { public: /// This type. typedef ImagePairSimilarityMeasure Self; /// Smart pointer. typedef SmartPointer SmartPtr; /// Return type: same as cmtk::Functional. typedef Functional::ReturnType ReturnType; /** Constructor. */ ImagePairSimilarityMeasure( const UniformVolume::SmartConstPtr& refVolume /*!< The reference image.*/, const UniformVolume::SmartConstPtr& fltVolume /*!< The floating image.*/, const Interpolators::InterpolationEnum interpolation = Interpolators::DEFAULT /*!< User-selected interpolation kernel*/ ); /** Default constructor. */ ImagePairSimilarityMeasure( const Interpolators::InterpolationEnum interpolation = Interpolators::DEFAULT /*!< User-selected interpolation kernel*/ ) : m_InterpolationMethod( interpolation ) {}; /// Virtual destructor. virtual ~ImagePairSimilarityMeasure() {}; /// Set reference volume. virtual void SetReferenceVolume( const UniformVolume::SmartConstPtr& refVolume ); /** Set floating volume. * When the floating volume is set, a new interpolator object is also created. */ virtual void SetFloatingVolume( const UniformVolume::SmartConstPtr& fltVolume ); /// Reset metric computation. virtual void Reset() {} /// Get a value from the X distribution (reference image). Types::DataItem GetSampleX ( const Types::GridIndexType index ) const { Types::DataItem data; this->m_ReferenceData->Get( data, index ); return data; } /// Get a value from the X distribution (reference image). bool GetSampleX ( Types::DataItem& sample, const size_t index ) const { return this->m_ReferenceData->Get( sample, index ); } /// Get number of samples in the X data (reference image pixels). size_t GetNumberOfSamplesX() const { return this->m_ReferenceData->GetDataSize(); } /// Get value range of X data (reference data). const Types::DataItemRange GetDataRangeX() const { return this->m_ReferenceData->GetRange(); } /// Interpolate a value from the Y distribution (floating image). Types::DataItem GetSampleY( const Types::GridIndexType* index, const Types::Coordinate* frac ) const { return this->m_FloatingImageInterpolator->GetDataDirect( index, frac ); } /// Get number of samples in the Y data (floating image pixels). size_t GetNumberOfSamplesY() const { return this->m_FloatingData->GetDataSize(); } /// Get value range of Y data (floating data). const Types::DataItemRange GetDataRangeY() const { return this->m_FloatingData->GetRange(); } /// Get scaled floating value if this metric rescales (implemented in derived classes), or input value if it does not (done here as the default). virtual Types::DataItem GetFloatingValueScaled( const Types::DataItem value ) const { return value; } /// Get the value of the metric. virtual Self::ReturnType Get() const = 0; private: /// Smart pointer to reference volume. UniformVolume::SmartConstPtr m_ReferenceVolume; /// Smart pointer to reference image data. TypedArray::SmartConstPtr m_ReferenceData; /// Smart pointer to floating volume. UniformVolume::SmartConstPtr m_FloatingVolume; /// Smart pointer to floating image data. TypedArray::SmartConstPtr m_FloatingData; /// Interpolation method ID. Interpolators::InterpolationEnum m_InterpolationMethod; /// Floating image interpolator. cmtk::UniformVolumeInterpolatorBase::SmartConstPtr m_FloatingImageInterpolator; }; //@} } // namespace cmtk #endif // #ifndef __cmtkImagePairSimilarityMeasure_h_included_ cmtk-3.3.1/libs/Registration/cmtkImagePairSimilarityMeasureCR.cxx000066400000000000000000000072161276303427400251540ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2010 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 2022 $ // // $LastChangedDate: 2010-07-21 15:26:03 -0700 (Wed, 21 Jul 2010) $ // // $LastChangedBy: torstenrohlfing $ // */ #include "cmtkImagePairSimilarityMeasureCR.h" namespace cmtk { /** \addtogroup Registration */ //@{ ImagePairSimilarityMeasureCR::ImagePairSimilarityMeasureCR ( const UniformVolume::SmartPtr& refVolume, const UniformVolume::SmartPtr& fltVolume, const Interpolators::InterpolationEnum interpolation ) : ImagePairSimilarityMeasure( refVolume, fltVolume, interpolation ) { NumBinsX = std::max( std::min( refVolume->GetNumberOfPixels(), 128 ), 8 ); HistogramI.Resize( NumBinsX ); NumBinsY = std::max( std::min( fltVolume->GetNumberOfPixels(), 128 ), 8 ); HistogramJ.Resize( NumBinsY ); HistogramI.SetRange( refVolume->GetData()->GetRange() ); SumJ.resize( NumBinsX ); SumJ2.resize( NumBinsX ); fltVolume->GetData()->GetStatistics( MuJ, SigmaSqJ ); HistogramJ.SetRange( fltVolume->GetData()->GetRange() ); SumI.resize( NumBinsY ); SumI2.resize( NumBinsY ); refVolume->GetData()->GetStatistics( MuI, SigmaSqI ); } ImagePairSimilarityMeasureCR::ReturnType ImagePairSimilarityMeasureCR::Get () const { const double invSampleCount = 1.0 / HistogramI.SampleCount(); // initialize variable for the weighted sum of the sigma^2 values over all // reference intensity classes. double sumSigmaSquare = 0; // run over all bins, i.e., reference classes for ( unsigned int j = 0; j < NumBinsX; ++j ) { // are there any values in the current class? if ( HistogramI[j] ) { // compute mean floating value for this reference class const double mu = SumJ[j] / HistogramI[j]; // compute variance of floating values for this reference class const double sigmaSq = ( mu*mu*HistogramI[j] - 2.0*mu*SumJ[j] + SumJ2[j] ) / HistogramI[j]; // update sum over all classes with weighted sigma^2 for this class. sumSigmaSquare += (invSampleCount * HistogramI[j]) * sigmaSq; } } // compute (supposedly) correlation ratio Self::ReturnType cr = static_cast( 1.0 - (1.0 / SigmaSqJ ) * sumSigmaSquare ); sumSigmaSquare = 0; for ( unsigned int i = 0; i < NumBinsY; ++i ) { if ( HistogramJ[i] ) { const double mu = SumI[i] / HistogramJ[i]; const double sigmaSq = ( mu*mu*HistogramJ[i] - 2.0*mu*SumI[i] + SumI2[i] ) / HistogramJ[i]; // update sum over all classes with weighted sigma^2 for this class. sumSigmaSquare += (invSampleCount * HistogramJ[i]) * sigmaSq; } } // add reverse correlation ratio cr += static_cast(1.0 - (1.0 / SigmaSqI ) * sumSigmaSquare); return cr; } } // namespace cmtk cmtk-3.3.1/libs/Registration/cmtkImagePairSimilarityMeasureCR.h000066400000000000000000000134541276303427400246020ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2010 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 2398 $ // // $LastChangedDate: 2010-10-05 14:54:37 -0700 (Tue, 05 Oct 2010) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkImagePairSimilarityMeasureCR_h_included_ #define __cmtkImagePairSimilarityMeasureCR_h_included_ #include #include #include #include namespace cmtk { /** \addtogroup Registration */ //@{ /** Pairwise image similarity measure "correlation ratio". */ class ImagePairSimilarityMeasureCR : /// Inherit generic image pair similarity measure public ImagePairSimilarityMeasure { public: /// This type. typedef ImagePairSimilarityMeasureCR Self; /// Smart pointer. typedef SmartPointer SmartPtr; /** Constructor. * The inherited constructor is called to initialize the given datasets. * Afterwards, the original (untransformed) probability distribution * functions of model and reference are calculated. */ ImagePairSimilarityMeasureCR ( const UniformVolume::SmartPtr& refVolume, const UniformVolume::SmartPtr& fltVolume, const Interpolators::InterpolationEnum interpolation = Interpolators::DEFAULT ); /// Destructor: free internal data structures. virtual ~ImagePairSimilarityMeasureCR() {} /** Reset computation. * Initialize arrays that hold the sums of all floating values and their * squares, separated by histogram classes of the reference image. */ virtual void Reset() { HistogramI.Reset(); HistogramJ.Reset(); std::fill( SumI.begin(), SumI.end(), 0 ); std::fill( SumJ.begin(), SumJ.end(), 0 ); std::fill( SumI2.begin(), SumI2.end(), 0 ); std::fill( SumJ2.begin(), SumJ2.end(), 0 ); } /** Continue incremental calculation. */ /** Add a pair of values to the metric. */ template void Increment( const T a, const T b ) { // what's the reference histogram bin? size_t bin = HistogramI.ValueToBin( a ); // count this sample HistogramI.Increment( bin ); // add floating value to sum of values for this class SumJ[bin] += b; // add squared floating value to sum of squared values for this class SumJ2[bin] += b * b; // same in reverse bin = HistogramJ.ValueToBin( b ); HistogramJ.Increment( bin ); SumI[bin] += a; SumI2[bin] += a * a; } /** Remove a pair of values from the metric. */ template void Decrement( const T a, const T b ) { // what's the reference histogram bin? size_t bin = HistogramI.ValueToBin( a ); // count this sample HistogramI.Decrement( bin ); // add floating value to sum of values for this class SumJ[bin] -= b; // add squared floating value to sum of squared values for this class SumJ2[bin] -= b * b; // same in reverse bin = HistogramJ.ValueToBin( b ); HistogramJ.Decrement( bin ); SumI[bin] -= a; SumI2[bin] -= a * a; } /** */ void Add ( const Self& other ) { HistogramI.AddHistogram( other.HistogramI ); for ( size_t bin = 0; bin < NumBinsX; ++bin ) { SumJ[bin] += other.SumJ[bin]; SumJ2[bin] += other.SumJ2[bin]; } HistogramJ.AddHistogram( other.HistogramJ ); for ( size_t bin = 0; bin < NumBinsY; ++bin ) { SumI[bin] += other.SumI[bin]; SumI2[bin] += other.SumI2[bin]; } } /** */ void Remove ( const Self& other ) { HistogramI.RemoveHistogram( other.HistogramI ); for ( size_t bin = 0; bin < NumBinsX; ++bin ) { SumJ[bin] -= other.SumJ[bin]; SumJ2[bin] -= other.SumJ2[bin]; } HistogramJ.RemoveHistogram( other.HistogramJ ); for ( size_t bin = 0; bin < NumBinsY; ++bin ) { SumI[bin] -= other.SumI[bin]; SumI2[bin] -= other.SumI2[bin]; } } /// Return correlation ratio. virtual Self::ReturnType Get () const; private: /// Number of bins for the X-distribution. size_t NumBinsX; /// Array with sums of all Y-values by X-bins. std::vector SumJ; /// Array with sums of squares of all Y-values by X-bins. std::vector SumJ2; /// Histogram with counts of all X-values. Histogram HistogramI; // Variance of complete floating image for normalization. Types::DataItem SigmaSqJ; // Mean of complete floating image for normalization. Types::DataItem MuJ; /// Number of bins for the Y-distribution. size_t NumBinsY; /// Array with sums of all X-values by Y-bins. std::vector SumI; /// Array with sums of squares of all X-values by Y-bins. std::vector SumI2; /// Histogram with counts of all X-values. Histogram HistogramJ; // Variance of complete floating image for normalization. Types::DataItem SigmaSqI; // Mean of complete floating image for normalization. Types::DataItem MuI; }; //@} } // namespace cmtk #endif // #ifndef __cmtkImagePairSimilarityMeasureCR_h_included_ cmtk-3.3.1/libs/Registration/cmtkImagePairSimilarityMeasureMI.h000066400000000000000000000056111276303427400245770ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2011 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 2752 $ // // $LastChangedDate: 2011-01-17 11:33:31 -0800 (Mon, 17 Jan 2011) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkImagePairSimilarityMeasureMI_h_included_ #define __cmtkImagePairSimilarityMeasureMI_h_included_ #include #include #include namespace cmtk { /** \addtogroup Registration */ //@{ #ifdef _MSC_VER #pragma warning (disable:4521) #endif /** Base class for voxel metrics with pre-converted image data. */ class ImagePairSimilarityMeasureMI : /// Inherit generic image pair similarity class. public ImagePairSimilarityJointHistogram { public: /// This type. typedef ImagePairSimilarityMeasureMI Self; /// Smart pointer. typedef SmartPointer SmartPtr; /// Return type: same as cmtk::Functional. typedef Functional::ReturnType ReturnType; /** Constructor. * For reference and floating volume, InitDataset is called. *\param refVolume The reference (fixed) volume. *\param fltVolume The model (transformed) volume. *\param interpolation ID of the interpolator to use for the floating image. */ ImagePairSimilarityMeasureMI( UniformVolume::SmartConstPtr& refVolume, UniformVolume::SmartConstPtr& fltVolume, const Interpolators::InterpolationEnum interpolation = Interpolators::DEFAULT ) : ImagePairSimilarityJointHistogram( refVolume, fltVolume, interpolation ) {} /** Default constructor. */ ImagePairSimilarityMeasureMI() {}; /** Virtual destructor. */ virtual ~ImagePairSimilarityMeasureMI() {}; /// Get the value of the metric. virtual Self::ReturnType Get() const { double HX, HY; this->m_JointHistogram.GetMarginalEntropies(HX,HY); const double HXY = this->m_JointHistogram.GetJointEntropy(); return static_cast( HX + HY - HXY ); } }; //@} } // namespace cmtk #endif // #ifndef __cmtkImagePairSimilarityMeasureMI_h_included_ cmtk-3.3.1/libs/Registration/cmtkImagePairSimilarityMeasureMSD.cxx000066400000000000000000000030301276303427400252610ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2010, 2013 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4873 $ // // $LastChangedDate: 2013-09-24 12:53:12 -0700 (Tue, 24 Sep 2013) $ // // $LastChangedBy: torstenrohlfing $ // */ #include "cmtkImagePairSimilarityMeasureMSD.h" namespace cmtk { /** \addtogroup Registration */ //@{ ImagePairSimilarityMeasureMSD::ImagePairSimilarityMeasureMSD ( const UniformVolume::SmartConstPtr& refVolume, const UniformVolume::SmartConstPtr& fltVolume, const Interpolators::InterpolationEnum interpolation ) : ImagePairSimilarityMeasure( refVolume, fltVolume, interpolation ), m_SumOfDifferences( 0.0 ), m_NumberOfSamples( 0 ) {} } // namespace cmtk cmtk-3.3.1/libs/Registration/cmtkImagePairSimilarityMeasureMSD.h000066400000000000000000000073011276303427400247130ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2011 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 2752 $ // // $LastChangedDate: 2011-01-17 11:33:31 -0800 (Mon, 17 Jan 2011) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkImagePairSimilarityMeasureMSD_h_included_ #define __cmtkImagePairSimilarityMeasureMSD_h_included_ #include #include #include #include #include #include namespace cmtk { /** \addtogroup Registration */ //@{ #ifdef _MSC_VER #pragma warning (disable:4521) #endif /** Mean squared difference metric. */ class ImagePairSimilarityMeasureMSD : /// Inherit generic pairwise similarity measure class public ImagePairSimilarityMeasure { public: /// This type. typedef ImagePairSimilarityMeasureMSD Self; /// Smart pointer. typedef SmartPointer SmartPtr; /// Parent class. typedef ImagePairSimilarityMeasure Superclass; /** Constructor. * For reference and model volume, InitDataset is called. *\param refVolume The reference (fixed) volume. *\param fltVolume The floating (moving) volume. *\param interpolation ID of the interpolator to use for the floating image. */ ImagePairSimilarityMeasureMSD( const UniformVolume::SmartConstPtr& refVolume, const UniformVolume::SmartConstPtr& fltVolume, const Interpolators::InterpolationEnum interpolation = Interpolators::DEFAULT ); /** Virtual destructor. */ virtual ~ImagePairSimilarityMeasureMSD() {}; /** Add a pair of values to the metric. */ template void Increment( const T a, const T b ) { ++this->m_NumberOfSamples; this->m_SumOfDifferences -= MathUtil::Square( a - b ); } /** Remove a pair of values from the metric. */ template void Decrement( const T a, const T b ) { --this->m_NumberOfSamples; this->m_SumOfDifferences += MathUtil::Square( a - b ); } /// Reset internal variables for next computation. virtual void Reset () { this->m_SumOfDifferences = 0; this->m_NumberOfSamples = 0; } /// Get the value of the metric. virtual Self::ReturnType Get() const { return static_cast( this->m_SumOfDifferences / this->m_NumberOfSamples ); } void Add ( const Self& other ) { this->m_SumOfDifferences += other.m_SumOfDifferences; this->m_NumberOfSamples += other.m_NumberOfSamples; } void Remove ( const Self& other ) { this->m_SumOfDifferences -= other.m_SumOfDifferences; this->m_NumberOfSamples -= other.m_NumberOfSamples; } private: /// this->m_SumOfDifferences of all sample pair differences double m_SumOfDifferences; /// Number of sample pairs. int m_NumberOfSamples; }; //@} } // namespace cmtk #endif // #ifndef __cmtkImagePairSimilarityMeasureMSD_h_included_ cmtk-3.3.1/libs/Registration/cmtkImagePairSimilarityMeasureNCC.cxx000066400000000000000000000043651276303427400252550ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2010, 2013 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4873 $ // // $LastChangedDate: 2013-09-24 12:53:12 -0700 (Tue, 24 Sep 2013) $ // // $LastChangedBy: torstenrohlfing $ // */ #include "cmtkImagePairSimilarityMeasureNCC.h" namespace cmtk { /** \addtogroup Registration */ //@{ ImagePairSimilarityMeasureNCC::ImagePairSimilarityMeasureNCC ( const UniformVolume::SmartPtr& refVolume, const UniformVolume::SmartPtr& fltVolume, const Interpolators::InterpolationEnum interpolation ) : ImagePairSimilarityMeasure( refVolume, fltVolume, interpolation ), SumX( 0.0 ), SumY( 0.0 ), SumXY( 0.0 ), SumSqX( 0.0 ), SumSqY( 0.0 ), Samples( 0 ) {} ImagePairSimilarityMeasureNCC ::ImagePairSimilarityMeasureNCC( const Self& other ) : ImagePairSimilarityMeasure( other ) { SumX = other.SumX; SumY = other.SumY; SumXY = other.SumXY; SumSqX = other.SumSqX; SumSqY = other.SumSqY; Samples = other.Samples; } ImagePairSimilarityMeasureNCC::ReturnType ImagePairSimilarityMeasureNCC ::Get() const { const double muX = SumX / Samples; const double muY = SumY / Samples; const double p = SumXY - muY * SumX - muX * SumY + Samples * muX * muY; const double qX = SumSqX - 2.0 * muX * SumX + Samples * muX * muX; const double qY = SumSqY - 2.0 * muY * SumY + Samples * muY * muY; return static_cast( p / sqrt( qX * qY ) ); } } // namespace cmtk cmtk-3.3.1/libs/Registration/cmtkImagePairSimilarityMeasureNCC.h000066400000000000000000000103611276303427400246730ustar00rootroot00000000000000/* // // Copyright 2016 Google, Inc. // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2011 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 5398 $ // // $LastChangedDate: 2016-01-14 18:29:18 -0800 (Thu, 14 Jan 2016) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkImagePairSimilarityMeasureNCC_h_included_ #define __cmtkImagePairSimilarityMeasureNCC_h_included_ #include #include #include #include #include #include namespace cmtk { /** \addtogroup Registration */ //@{ #ifdef _MSC_VER #pragma warning (disable:4521) #endif /** Normalized Cross Correlation Metric. */ class ImagePairSimilarityMeasureNCC : /// Inherit generic pairwise similarity measure class public ImagePairSimilarityMeasure { public: /// This type. typedef ImagePairSimilarityMeasureNCC Self; /// Smart pointer. typedef SmartPointer SmartPtr; /** Default constructor. */ ImagePairSimilarityMeasureNCC() {}; /** Virtual destructor. */ virtual ~ImagePairSimilarityMeasureNCC() {}; /** Constructor. * For reference and floating volume, InitDataset is called. *\param refVolume The reference (fixed) volume. *\param fltVolume The floating (moving) volume. *\param interpolation ID of the interpolation algorithm to use for the floating image. */ ImagePairSimilarityMeasureNCC ( const UniformVolume::SmartPtr& refVolume, const UniformVolume::SmartPtr& fltVolume, const Interpolators::InterpolationEnum interpolation = Interpolators::DEFAULT ); /// Constant copy constructor. ImagePairSimilarityMeasureNCC ( const Self& other ); /** Add a pair of values to the metric. */ template void Increment( const T a, const T b ) { ++Samples; SumX += a; SumY += b; SumSqX += a * a; SumSqY += b * b; SumXY += a * b; } /** Remove a pair of values from the metric. */ template void Decrement( const T a, const T b ) { --Samples; SumX -= a; SumY -= b; SumSqX -= a * a; SumSqY -= b * b; SumXY -= a * b; } /// Start with a new computation. virtual void Reset () { SumX = SumY = SumSqX = SumSqY = SumXY = 0; Samples = 0; } /// Compute cross correlation. virtual Self::ReturnType Get() const; /// Add contribution from another (partial) metric object. void Add ( const Self& other ) { SumX += other.SumX; SumY += other.SumY; SumXY += other.SumXY; SumSqX += other.SumSqX; SumSqY += other.SumSqY; Samples += other.Samples; } /// Remove contribution from another (partial) metric object. void Remove ( const Self& other ) { assert( Samples >= other.Samples ); SumX -= other.SumX; SumY -= other.SumY; SumXY -= other.SumXY; SumSqX -= other.SumSqX; SumSqY -= other.SumSqY; Samples -= other.Samples; } private: /// Sum over all samples in X distribution. double SumX; /// Sum over all samples in Y distribution. double SumY; /// Sum over products of corresponding samples in X and Y distribution. double SumXY; /// Sum over all squared samples in X distribution. double SumSqX; /// Sum over all squared samples in Y distribution. double SumSqY; /// Number of samples. size_t Samples; }; //@} } // namespace cmtk #endif // #ifndef __cmtkImagePairSimilarityMeasureNCC_h_included_ cmtk-3.3.1/libs/Registration/cmtkImagePairSimilarityMeasureNMI.h000066400000000000000000000056061276303427400247210ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2011 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 2752 $ // // $LastChangedDate: 2011-01-17 11:33:31 -0800 (Mon, 17 Jan 2011) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkImagePairSimilarityMeasureNMI_h_included_ #define __cmtkImagePairSimilarityMeasureNMI_h_included_ #include #include #include namespace cmtk { /** \addtogroup Registration */ //@{ #ifdef _MSC_VER #pragma warning (disable:4521) #endif /** Base class for voxel metrics with pre-converted image data. */ class ImagePairSimilarityMeasureNMI : /// Inherit generic image pair similarity class. public ImagePairSimilarityJointHistogram { public: /// This type. typedef ImagePairSimilarityMeasureNMI Self; /// Smart pointer. typedef SmartPointer SmartPtr; /// Return type: same as cmtk::Functional. typedef Functional::ReturnType ReturnType; /** Constructor. * For reference and floating volume, InitDataset is called. *\param refVolume The reference (fixed) volume. *\param fltVolume The model (transformed) volume. *\param interpolation ID of the selected floating image interpolation. */ ImagePairSimilarityMeasureNMI( UniformVolume::SmartConstPtr& refVolume, UniformVolume::SmartConstPtr& fltVolume, const Interpolators::InterpolationEnum interpolation = Interpolators::DEFAULT ) : ImagePairSimilarityJointHistogram( refVolume, fltVolume, interpolation ) {} /** Default constructor. */ ImagePairSimilarityMeasureNMI() {}; /** Virtual destructor. */ virtual ~ImagePairSimilarityMeasureNMI() {}; /// Get the value of the metric. virtual Self::ReturnType Get() const { double HX, HY; this->m_JointHistogram.GetMarginalEntropies(HX,HY); const double HXY = this->m_JointHistogram.GetJointEntropy(); return static_cast( (HX + HY) / HXY ); } }; //@} } // namespace cmtk #endif // #ifndef __cmtkImagePairSimilarityMeasureNMI_h_included_ cmtk-3.3.1/libs/Registration/cmtkImagePairSimilarityMeasureRMS.cxx000066400000000000000000000027321276303427400253070ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2010 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 2156 $ // // $LastChangedDate: 2010-08-04 11:28:34 -0700 (Wed, 04 Aug 2010) $ // // $LastChangedBy: torstenrohlfing $ // */ #include "cmtkImagePairSimilarityMeasureRMS.h" namespace cmtk { /** \addtogroup Registration */ //@{ ImagePairSimilarityMeasureRMS::ImagePairSimilarityMeasureRMS ( const UniformVolume::SmartConstPtr& refVolume, const UniformVolume::SmartConstPtr& fltVolume, const Interpolators::InterpolationEnum interpolation ) : ImagePairSimilarityMeasureMSD( refVolume, fltVolume, interpolation ) {} } // namespace cmtk cmtk-3.3.1/libs/Registration/cmtkImagePairSimilarityMeasureRMS.h000066400000000000000000000051331276303427400247320ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2011 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 2752 $ // // $LastChangedDate: 2011-01-17 11:33:31 -0800 (Mon, 17 Jan 2011) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkImagePairSimilarityMeasureRMS_h_included_ #define __cmtkImagePairSimilarityMeasureRMS_h_included_ #include #include #include #include #include namespace cmtk { /** \addtogroup Registration */ //@{ #ifdef _MSC_VER #pragma warning (disable:4521) #endif /** Mean squared difference metric. */ class ImagePairSimilarityMeasureRMS : /// Inherit MSD similarity measure. public ImagePairSimilarityMeasureMSD { public: /// This type. typedef ImagePairSimilarityMeasureRMS Self; /// Smart pointer. typedef SmartPointer SmartPtr; /// Parent class. typedef ImagePairSimilarityMeasureMSD Superclass; /** Constructor. * For reference and model volume, InitDataset is called. *\param refVolume The reference (fixed) volume. *\param fltVolume The floating (moving) volume. *\param interpolation ID of the interpolation algorithm to use for the floating image. */ ImagePairSimilarityMeasureRMS( const UniformVolume::SmartConstPtr& refVolume, const UniformVolume::SmartConstPtr& fltVolume, const Interpolators::InterpolationEnum interpolation = Interpolators::DEFAULT ); /** Virtual destructor. */ virtual ~ImagePairSimilarityMeasureRMS() {}; /// Get the value of the metric. virtual Self::ReturnType Get() const { return -sqrt( -this->Superclass::Get() ); } }; //@} } // namespace cmtk #endif // #ifndef __cmtkImagePairSimilarityMeasureRMS_h_included_ cmtk-3.3.1/libs/Registration/cmtkImagePairSymmetricAffineRegistrationFunctional.cxx000066400000000000000000000056221276303427400307610ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2011 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 3227 $ // // $LastChangedDate: 2011-05-16 10:33:40 -0700 (Mon, 16 May 2011) $ // // $LastChangedBy: torstenrohlfing $ // */ #include "cmtkImagePairSymmetricAffineRegistrationFunctional.h" #include #include #include #include #include #include #include #include namespace cmtk { /** \addtogroup Registration */ //@{ ImagePairSymmetricAffineRegistrationFunctional* ImagePairSymmetricAffineRegistrationFunctional ::Create( const int metric, UniformVolume::SmartPtr& refVolume, UniformVolume::SmartPtr& fltVolume, const Interpolators::InterpolationEnum interpolation, AffineXform::SmartPtr& affineXform ) { switch ( metric ) { case 0: return new ImagePairSymmetricAffineRegistrationFunctionalTemplate( refVolume, fltVolume, interpolation, affineXform ); case 1: return new ImagePairSymmetricAffineRegistrationFunctionalTemplate( refVolume, fltVolume, interpolation, affineXform ); case 2: return new ImagePairSymmetricAffineRegistrationFunctionalTemplate( refVolume, fltVolume, interpolation, affineXform ); case 3: return NULL; // masked NMI retired case 4: return new ImagePairSymmetricAffineRegistrationFunctionalTemplate( refVolume, fltVolume, interpolation, affineXform ); case 5: return new ImagePairSymmetricAffineRegistrationFunctionalTemplate( refVolume, fltVolume, interpolation, affineXform ); default: return NULL; } return NULL; } } // namespace cmtk cmtk-3.3.1/libs/Registration/cmtkImagePairSymmetricAffineRegistrationFunctional.h000066400000000000000000000056221276303427400304060ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2012, 2014 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 5309 $ // // $LastChangedDate: 2014-04-10 18:01:34 -0700 (Thu, 10 Apr 2014) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkImagePairSymmetricAffineRegistrationFunctional_h_included_ #define __cmtkImagePairSymmetricAffineRegistrationFunctional_h_included_ #include #include #include #include #include namespace cmtk { /** \addtogroup Registration */ //@{ /// Symmtric affine registration functional for simultaneous forward/inverse transformation estimation. class ImagePairSymmetricAffineRegistrationFunctional : /** Inherit from generic functional. */ public Functional { public: /// This class. typedef ImagePairSymmetricAffineRegistrationFunctional Self; /// Smart pointer to this class. typedef SmartPointer SmartPtr; /// Superclass. typedef Functional Superclass; /// Constructor. ImagePairSymmetricAffineRegistrationFunctional( AffineXform::SmartPtr& affineXform ) : m_FwdXform( affineXform ) {}; /// Set warp for forward and backward functional. virtual void SetXform( AffineXform::SmartPtr& forward ) { this->m_FwdXform = forward; } /// Set flag and value for forcing values outside the floating image. virtual void SetForceOutside( const bool flag = true, const Types::DataItem value = 0 ) = 0; /// Set optional restriction to axis-orthogonal in-plane transformations. virtual void SetRestrictToInPlane( const int axis ) = 0; /// Constructor function. static ImagePairSymmetricAffineRegistrationFunctional* Create ( const int metric, UniformVolume::SmartPtr& refVolume, UniformVolume::SmartPtr& fltVolume, const Interpolators::InterpolationEnum interpolation, AffineXform::SmartPtr& affineXform ); protected: /// Forward transformation. AffineXform::SmartPtr m_FwdXform; }; //@} } // namespace cmtk #endif // #ifndef __cmtkImagePairSymmetricAffineRegistrationFunctional_h_included_ cmtk-3.3.1/libs/Registration/cmtkImagePairSymmetricAffineRegistrationFunctionalTemplate.h000066400000000000000000000107651276303427400321060ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2011, 2014 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 5309 $ // // $LastChangedDate: 2014-04-10 18:01:34 -0700 (Thu, 10 Apr 2014) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkImagePairSymmetricAffineRegistrationFunctionalTemplate_h_included_ #define __cmtkImagePairSymmetricAffineRegistrationFunctionalTemplate_h_included_ #include #include "cmtkImagePairSymmetricAffineRegistrationFunctionalTemplate.h" #include #include #include namespace cmtk { /** \addtogroup Registration */ //@{ /// Template for symmtric affine registration functional for simultaneous forward/inverse transformation estimation. template class ImagePairSymmetricAffineRegistrationFunctionalTemplate : /** Inherit from non-template base functional class. */ public ImagePairSymmetricAffineRegistrationFunctional { public: /// This class. typedef ImagePairSymmetricAffineRegistrationFunctionalTemplate Self; /// Smart pointer to this class. typedef SmartPointer SmartPtr; /// Superclass. typedef ImagePairSymmetricAffineRegistrationFunctional Superclass; /// The forward functional. ImagePairAffineRegistrationFunctionalTemplate FwdFunctional; /// The backward functional. ImagePairAffineRegistrationFunctionalTemplate BwdFunctional; /// Constructor. ImagePairSymmetricAffineRegistrationFunctionalTemplate( UniformVolume::SmartPtr& reference, UniformVolume::SmartPtr& floating, const Interpolators::InterpolationEnum interpolation, AffineXform::SmartPtr& affineXform ) : Superclass( affineXform ), FwdFunctional( reference, floating, interpolation, affineXform ), BwdFunctional( floating, reference, interpolation, affineXform->GetInverse() ) {} /// Set flag and value for forcing values outside the floating image. virtual void SetForceOutside ( const bool flag = true, const Types::DataItem value = 0 ) { this->FwdFunctional.SetForceOutside( flag, value ); this->BwdFunctional.SetForceOutside( flag, value ); } /// Set optional restriction to axis-orthogonal in-plane transformations. virtual void SetRestrictToInPlane( const int axis ) { this->FwdFunctional.SetRestrictToInPlane( axis ); } /// Return parameter vector. virtual void GetParamVector ( CoordinateVector& v ) { this->FwdFunctional.GetParamVector( v ); } /// Evaluate functional value. virtual typename Self::ReturnType EvaluateAt ( CoordinateVector& v ) { this->m_FwdXform->SetParamVector( v ); CoordinateVector vInv; this->m_FwdXform->GetInverse()->GetParamVector( vInv ); return this->FwdFunctional.EvaluateAt( v ) + this->BwdFunctional.EvaluateAt( vInv ); } /// Evaluate functional with current parameter vector. virtual typename Self::ReturnType Evaluate () { return this->FwdFunctional.Evaluate() + this->BwdFunctional.Evaluate(); } /// Get parameter stepping in milimeters. virtual Types::Coordinate GetParamStep( const size_t idx, const Types::Coordinate mmStep = 1 ) const { return this->FwdFunctional.GetParamStep( idx, mmStep ); } /// Return the transformation's parameter vector dimension. virtual size_t ParamVectorDim() const { return this->FwdFunctional.ParamVectorDim(); } /// Return the number of variable parameters of the transformation. virtual size_t VariableParamVectorDim() const { return this->FwdFunctional.VariableParamVectorDim(); } }; //@} } // namespace cmtk #endif // #ifndef __cmtkImagePairSymmetricAffineRegistrationFunctionalTemplate_h_included_ cmtk-3.3.1/libs/Registration/cmtkImagePairSymmetricNonrigidRegistrationFunctional.cxx000066400000000000000000000103711276303427400313370ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2010 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 2398 $ // // $LastChangedDate: 2010-10-05 14:54:37 -0700 (Tue, 05 Oct 2010) $ // // $LastChangedBy: torstenrohlfing $ // */ #include "cmtkImagePairSymmetricNonrigidRegistrationFunctional.h" #include #include #include #include #include #include #include #include #include namespace cmtk { /** \addtogroup Registration */ //@{ template void ImagePairSymmetricNonrigidRegistrationFunctionalTemplate::SetWarpXform ( SplineWarpXform::SmartPtr& warpFwd, SplineWarpXform::SmartPtr& warpBwd ) { this->FwdFunctional.SetWarpXform( warpFwd ); this->FwdFunctional.SetInverseTransformation( warpBwd ); this->BwdFunctional.SetWarpXform( warpBwd ); this->BwdFunctional.SetInverseTransformation( warpFwd ); } template typename ImagePairSymmetricNonrigidRegistrationFunctionalTemplate::ReturnType ImagePairSymmetricNonrigidRegistrationFunctionalTemplate ::EvaluateWithGradient( CoordinateVector& v, CoordinateVector& g, const Types::Coordinate step ) { CoordinateVector vFwd( this->FwdFunctional.ParamVectorDim(), v.Elements, false /*freeElements*/ ); CoordinateVector gFwd( this->FwdFunctional.ParamVectorDim(), g.Elements, false /*freeElements*/ ); CoordinateVector vBwd( this->BwdFunctional.ParamVectorDim(), v.Elements+this->FwdFunctional.ParamVectorDim(), false /*freeElements*/ ); CoordinateVector gBwd( this->BwdFunctional.ParamVectorDim(), g.Elements+this->FwdFunctional.ParamVectorDim(), false /*freeElements*/ ); const typename Self::ReturnType result = this->FwdFunctional.EvaluateWithGradient( vFwd, gFwd, step ) + this->BwdFunctional.EvaluateWithGradient( vBwd, gBwd, step ); return result; } ImagePairSymmetricNonrigidRegistrationFunctional* ImagePairSymmetricNonrigidRegistrationFunctional ::Create( const int metric, UniformVolume::SmartPtr& refVolume, UniformVolume::SmartPtr& fltVolume, const Interpolators::InterpolationEnum interpolation ) { switch ( metric ) { case 0: return new ImagePairSymmetricNonrigidRegistrationFunctionalTemplate( refVolume, fltVolume, interpolation ); case 1: return new ImagePairSymmetricNonrigidRegistrationFunctionalTemplate( refVolume, fltVolume, interpolation ); case 2: return new ImagePairSymmetricNonrigidRegistrationFunctionalTemplate( refVolume, fltVolume, interpolation ); case 3: return NULL; // masked NMI retired case 4: return new ImagePairSymmetricNonrigidRegistrationFunctionalTemplate( refVolume, fltVolume, interpolation ); case 5: return new ImagePairSymmetricNonrigidRegistrationFunctionalTemplate( refVolume, fltVolume, interpolation ); default: return NULL; } return NULL; } } // namespace cmtk cmtk-3.3.1/libs/Registration/cmtkImagePairSymmetricNonrigidRegistrationFunctional.h000066400000000000000000000055201276303427400307640ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2012 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4364 $ // // $LastChangedDate: 2012-05-29 15:45:24 -0700 (Tue, 29 May 2012) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkImagePairSymmetricNonrigidRegistrationFunctional_h_included_ #define __cmtkImagePairSymmetricNonrigidRegistrationFunctional_h_included_ #include #include #include #include namespace cmtk { /** \addtogroup Registration */ //@{ /// Symmtric-consistent elastic registration functional. class ImagePairSymmetricNonrigidRegistrationFunctional : /** Inherit from generic functional. */ public Functional { public: /// This class. typedef ImagePairSymmetricNonrigidRegistrationFunctional Self; /// Smart pointer to this class. typedef SmartPointer SmartPtr; /// Superclass. typedef Functional Superclass; /// Set inverse consistency weight. virtual void SetInverseConsistencyWeight( const Self::ReturnType ) = 0; /// Set adaptive parameter fixing flag. virtual void SetAdaptiveFixParameters( const bool ) = 0; /// Set adaptive parameter fixing flag. virtual void SetAdaptiveFixThreshFactor( const Self::ReturnType ) = 0; /// Set Jacobian constraint weight. virtual void SetJacobianConstraintWeight( const Self::ReturnType ) = 0; /// Set smoothness constraint weight. virtual void SetGridEnergyWeight( const Self::ReturnType ) = 0; /// Set warp for forward and backward functional. virtual void SetWarpXform( SplineWarpXform::SmartPtr& warpFwd, SplineWarpXform::SmartPtr& warpBwd ) = 0; /// Constructor function. static ImagePairSymmetricNonrigidRegistrationFunctional* Create ( const int metric, UniformVolume::SmartPtr& refVolume, UniformVolume::SmartPtr& fltVolume, const Interpolators::InterpolationEnum interpolation ); }; //@} } // namespace cmtk #endif // #ifndef __cmtkImagePairSymmetricNonrigidRegistrationFunctional_h_included_ cmtk-3.3.1/libs/Registration/cmtkImagePairSymmetricNonrigidRegistrationFunctionalTemplate.h000066400000000000000000000142231276303427400324600ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2010 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 2398 $ // // $LastChangedDate: 2010-10-05 14:54:37 -0700 (Tue, 05 Oct 2010) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkImagePairSymmetricNonrigidRegistrationFunctionalTemplate_h_included_ #define __cmtkImagePairSymmetricNonrigidRegistrationFunctionalTemplate_h_included_ #include #include "cmtkImagePairSymmetricNonrigidRegistrationFunctionalTemplate.h" #include #include #include namespace cmtk { /** \addtogroup Registration */ //@{ /// Template for symmtric-consistent elastic registration functional. template class ImagePairSymmetricNonrigidRegistrationFunctionalTemplate : /** Inherit from non-template base functional class. */ public ImagePairSymmetricNonrigidRegistrationFunctional { public: /// This class. typedef ImagePairSymmetricNonrigidRegistrationFunctionalTemplate Self; /// Smart pointer to this class. typedef SmartPointer SmartPtr; /// Superclass. typedef ImagePairSymmetricNonrigidRegistrationFunctional Superclass; /// The forward functional. ImagePairNonrigidRegistrationFunctionalTemplate FwdFunctional; /// The backward functional. ImagePairNonrigidRegistrationFunctionalTemplate BwdFunctional; /// Constructor. ImagePairSymmetricNonrigidRegistrationFunctionalTemplate( UniformVolume::SmartPtr& reference, UniformVolume::SmartPtr& floating, const Interpolators::InterpolationEnum interpolation ) : FwdFunctional( reference, floating, interpolation ), BwdFunctional( floating, reference, interpolation ) {} /// Set inverse consistency weight. virtual void SetInverseConsistencyWeight( const typename Self::ReturnType inverseConsistencyWeight ) { this->FwdFunctional.SetInverseConsistencyWeight( inverseConsistencyWeight ); this->BwdFunctional.SetInverseConsistencyWeight( inverseConsistencyWeight ); } /// Set adaptive parameter fixing flag. virtual void SetAdaptiveFixParameters( const bool adaptiveFixParameters ) { this->FwdFunctional.SetAdaptiveFixParameters( adaptiveFixParameters ); this->BwdFunctional.SetAdaptiveFixParameters( adaptiveFixParameters ); } /// Set adaptive parameter fixing threshold. virtual void SetAdaptiveFixThreshFactor( const typename Self::ReturnType threshFactor ) { this->FwdFunctional.SetAdaptiveFixThreshFactor( threshFactor ); this->BwdFunctional.SetAdaptiveFixThreshFactor( threshFactor ); } /// Set Jacobian constraint weight. virtual void SetJacobianConstraintWeight( const typename Self::ReturnType jacobianConstraintWeight ) { this->FwdFunctional.SetJacobianConstraintWeight( jacobianConstraintWeight ); this->BwdFunctional.SetJacobianConstraintWeight( jacobianConstraintWeight ); } /// Set smoothness constraint weight. virtual void SetGridEnergyWeight( const typename Self::ReturnType gridEnergyWeight ) { this->FwdFunctional.SetGridEnergyWeight( gridEnergyWeight ); this->BwdFunctional.SetGridEnergyWeight( gridEnergyWeight ); } /// Set warp for forward and backward functional. virtual void SetWarpXform( SplineWarpXform::SmartPtr& warpFwd, SplineWarpXform::SmartPtr& warpBwd ); /// Return parameter vector. virtual void GetParamVector ( CoordinateVector& v ) { CoordinateVector vFwd, vBwd; this->FwdFunctional.GetParamVector( vFwd ); this->BwdFunctional.GetParamVector( vBwd ); v.SetDim( vFwd.Dim + vBwd.Dim ); v.CopyToOffset( vFwd ); v.CopyToOffset( vBwd, vFwd.Dim ); } /// Evaluate functional value and gradient. virtual typename Self::ReturnType EvaluateWithGradient( CoordinateVector& v, CoordinateVector& g, const Types::Coordinate step = 1 ); /// Evaluate functional value. virtual typename Self::ReturnType EvaluateAt ( CoordinateVector& v ) { CoordinateVector vFwd( this->FwdFunctional.ParamVectorDim(), v.Elements, false /*freeElements*/ ); CoordinateVector vBwd( this->BwdFunctional.ParamVectorDim(), v.Elements+this->FwdFunctional.ParamVectorDim(), false /*freeElements*/ ); return this->FwdFunctional.EvaluateAt( vFwd ) + this->BwdFunctional.EvaluateAt( vBwd ); } virtual typename Self::ReturnType Evaluate () { return this->FwdFunctional.Evaluate() + this->BwdFunctional.Evaluate(); } /// Get parameter stepping in milimeters. virtual Types::Coordinate GetParamStep( const size_t idx, const Types::Coordinate mmStep = 1 ) const { if ( idx < this->FwdFunctional.ParamVectorDim() ) return this->FwdFunctional.GetParamStep( idx, mmStep ); else return this->BwdFunctional.GetParamStep( idx - this->FwdFunctional.ParamVectorDim(), mmStep ); } /// Return the transformation's parameter vector dimension. virtual size_t ParamVectorDim() const { return this->FwdFunctional.ParamVectorDim() + this->BwdFunctional.ParamVectorDim(); } /// Return the number of variable parameters of the transformation. virtual size_t VariableParamVectorDim() const { return this->FwdFunctional.VariableParamVectorDim() + this->BwdFunctional.VariableParamVectorDim(); } }; //@} } // namespace cmtk #endif // #ifndef __cmtkImagePairSymmetricNonrigidRegistrationFunctionalTemplate_h_included_ cmtk-3.3.1/libs/Registration/cmtkImageSymmetryPlaneCommandLine.h000066400000000000000000000053001276303427400250000ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2010 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 2097 $ // // $LastChangedDate: 2010-07-28 14:12:50 -0700 (Wed, 28 Jul 2010) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkImageSymmetryPlaneCommandLine_h_included_ #define __cmtkImageSymmetryPlaneCommandLine_h_included_ #include #include "cmtkImageSymmetryPlaneCommandLineBase.h" namespace cmtk { /** \addtogroup Registration */ //@{ /** Class template for symmetry plane computation command line tools. * This is templated over the symmetry plane functional, which can be either * CPU-based or GPU-based. */ template class ImageSymmetryPlaneCommandLine : /// Inherit from non-template base class. public ImageSymmetryPlaneCommandLineBase { public: /// The functional type. typedef TFunctional FunctionalType; /// This class. typedef ImageSymmetryPlaneCommandLine Self; /// Parent class. typedef ImageSymmetryPlaneCommandLineBase Superclass; /// Smart pointer. typedef SmartPointer SmartPtr; /// Smart pointer to const. typedef SmartConstPointer SmartConstPtr; protected: /// Make a functional of the given template type using an image volume. virtual ImageSymmetryPlaneFunctionalBase::SmartPtr CreateFunctional( UniformVolume::SmartPtr& volume ) { return ImageSymmetryPlaneFunctionalBase::SmartPtr( new FunctionalType( volume ) ); } /// Make a functional of the given template type using an image volume and a value range. virtual ImageSymmetryPlaneFunctionalBase::SmartPtr CreateFunctional( UniformVolume::SmartPtr& volume, const cmtk::Types::DataItemRange& range ) { return ImageSymmetryPlaneFunctionalBase::SmartPtr( new FunctionalType( volume, range ) ); } }; } // namespace cmtk #endif // #ifndef __cmtkImageSymmetryPlaneCommandLine_h_included_ cmtk-3.3.1/libs/Registration/cmtkImageSymmetryPlaneCommandLineBase.cxx000066400000000000000000000440711276303427400261560ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2013 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4882 $ // // $LastChangedDate: 2013-09-27 15:16:36 -0700 (Fri, 27 Sep 2013) $ // // $LastChangedBy: torstenrohlfing $ // */ #include "cmtkImageSymmetryPlaneCommandLineBase.h" #include #include #include #include #include #include #include #include #include cmtk::ImageSymmetryPlaneCommandLineBase ::ImageSymmetryPlaneCommandLineBase() : m_MinValue( 0.0 ), m_MinValueSet( false ), m_MaxValue( 0.0 ), m_MaxValueSet( false ), m_Sampling( 1.0 ), m_Accuracy( 0.1 ), m_Interpolation( Interpolators::LINEAR ), m_Levels( 4 ), m_DisableOptimization( false ), m_Rho( 0.0 ), m_Theta( 0.0 ), m_Phi( 0.0 ), m_FixOffset( false ), m_MirrorOutFile( NULL ), m_AlignedOutFile( NULL ), m_MarkPlaneAligned( false ), m_MarkedOutFile( NULL ), m_DifferenceOutFile( NULL ), m_WriteXformPath( NULL ), m_MarkPlaneValue( 4095 ), m_PadOutValueSet( false ), m_SymmetryOutFileName( NULL ), m_SymmetryParameters( NULL ), m_SymmetryParametersFile( NULL ), m_InitialPlane( SYMPL_INIT_YZ ), m_CommandLine( CommandLine::PROPS_XML ) { this->m_CommandLine.SetProgramInfo( CommandLine::PRG_TITLE, "Symmetry plane computation" ); this->m_CommandLine.SetProgramInfo( CommandLine::PRG_DESCR, "Compute the approximate symmetry plane of an image to determine, for example, the mid-sagittal plane in human brain images. " "Various forms of output are supported, e.g., writing the input image with the symmetry plane drawn into it, or the input image realigned along the symmetry plane." ); this->m_CommandLine.SetProgramInfo( CommandLine::PRG_CATEG, "CMTK.Registration" ); typedef CommandLine::Key Key; this->m_CommandLine.BeginGroup( "Optimization", "Optimization" ); this->m_CommandLine.AddOption( Key( 'a', "accuracy" ), &this->m_Accuracy, "Accuracy (final optimization step size in [mm]." ); this->m_CommandLine.AddOption( Key( 's', "sampling" ), &this->m_Sampling, "Resampled image resolution. This is the resolution [in mm] of the first (finest) resampled image in the multi-scale pyramid, " "which is derived directly from the original full-resolution images."); this->m_CommandLine.AddOption( Key( 'l', "levels" ), &this->m_Levels, "Number of resolution levels. The algorithm will create (levels-1) resampled images with increasingly coarse resolution and use these " "in successive order of increasing resolution before using the original images at the final level." ); this->m_CommandLine.AddSwitch( Key( "fix-offset" ), &this->m_FixOffset, true, "Fix symmetry plane offset. Reduces computation time and forces symmetry plane to cross center of mass, but may lead to less-than-accurate result." ); this->m_CommandLine.EndGroup(); this->m_CommandLine.BeginGroup( "Initial", "Initial approximate symmetry plane orientation" ); CommandLine::EnumGroup::SmartPtr initialPlane = this->m_CommandLine.AddEnum( "initial-plane", &this->m_InitialPlane, "Initial orientation of symmetry plane. This should be the closest orthogonal plane to the expected actual symmetry plane." ); initialPlane->AddSwitch( Key( "initial-axial" ), SYMPL_INIT_XY, "Approximately axial symmetry" ); initialPlane->AddSwitch( Key( "initial-coronal" ), SYMPL_INIT_XZ, "Approximately coronal symmetry" ); initialPlane->AddSwitch( Key( "initial-sagittal" ), SYMPL_INIT_YZ, "Approximately sagittal symmetry" ); initialPlane->AddSwitch( Key( "initial-xy" ), SYMPL_INIT_XY, "Approximately XY plane symmetry" ); initialPlane->AddSwitch( Key( "initial-xz" ), SYMPL_INIT_XZ, "Approximately XZ plane symmetry" ); initialPlane->AddSwitch( Key( "initial-yz" ), SYMPL_INIT_YZ, "Approximately YZ plane symmetry" ); this->m_CommandLine.EndGroup(); this->m_CommandLine.BeginGroup( "Pre-computed", "Pre-computed symmetry" )->SetProperties( CommandLine::PROPS_ADVANCED | CommandLine::PROPS_NOXML ); this->m_CommandLine.AddOption( Key( "output-only" ), &this->m_SymmetryParameters, "Give symmetry parameters [Rho Theta Phi] as option, skip search.", &this->m_DisableOptimization ); this->m_CommandLine.AddOption( Key( "output-only-file" ), &this->m_SymmetryParametersFile, "Read symmetry parameters from file, skip search.", &this->m_DisableOptimization ); this->m_CommandLine.EndGroup(); this->m_CommandLine.BeginGroup( "Preprocessing", "Data pre-processing" )->SetProperties( CommandLine::PROPS_ADVANCED ); this->m_CommandLine.AddOption( Key( "min-value" ), &this->m_MinValue, "Force minumum data value.", &this->m_MinValueSet ); this->m_CommandLine.AddOption( Key( "max-value" ), &this->m_MaxValue, "Force maximum data value.", &this->m_MaxValueSet ); this->m_CommandLine.EndGroup(); this->m_CommandLine.BeginGroup( "OutputImages", "Output of Images" ); CommandLine::EnumGroup::SmartPtr interpGroup = this->m_CommandLine.AddEnum( "interpolation", &this->m_Interpolation, "Interpolation method used for reformatted output data" ); interpGroup->AddSwitch( Key( 'L', "linear" ), Interpolators::LINEAR, "Use linear image interpolation for output." ); interpGroup->AddSwitch( Key( 'C', "cubic" ), Interpolators::CUBIC, "Use cubic image interpolation for output." ); interpGroup->AddSwitch( Key( 'S', "sinc" ), Interpolators::COSINE_SINC, "Use cosine-windowed sinc image interpolation for output." ); this->m_CommandLine.AddOption( Key( 'P', "pad-out" ), &this->m_PadOutValue, "Padding value for output images.", &this->m_PadOutValueSet )->SetProperties( CommandLine::PROPS_ADVANCED ); this->m_CommandLine.AddOption( Key( "mark-value" ), &this->m_MarkPlaneValue, "Data value to mark (draw) symmetry plane." ); this->m_CommandLine.AddOption( Key( "write-marked" ), &this->m_MarkedOutFile, "File name for output image with marked symmetry plane." )->SetProperties( CommandLine::PROPS_IMAGE | CommandLine::PROPS_OUTPUT ); this->m_CommandLine.AddOption( Key( "write-aligned" ), &this->m_AlignedOutFile, "File name for symmetry plane-aligned output image." )->SetProperties( CommandLine::PROPS_IMAGE | CommandLine::PROPS_OUTPUT ); this->m_CommandLine.AddSwitch( Key( "mark-aligned" ), &this->m_MarkPlaneAligned, true, "Mark symmetry plane in aligned output image." ); this->m_CommandLine.AddOption( Key( "write-subtract" ), &this->m_DifferenceOutFile, "File name for mirror subtraction image." )->SetProperties( CommandLine::PROPS_IMAGE | CommandLine::PROPS_OUTPUT ); this->m_CommandLine.AddOption( Key( "write-mirror" ), &this->m_MirrorOutFile, "File name for image mirrored w.r.t. symmetry plane." )->SetProperties( CommandLine::PROPS_IMAGE | CommandLine::PROPS_OUTPUT ); this->m_CommandLine.EndGroup(); this->m_CommandLine.BeginGroup( "OutputParameters", "Output of Parameters" )->SetProperties( CommandLine::PROPS_ADVANCED ); this->m_CommandLine.AddOption( Key( 'o', "outfile" ), &this->m_SymmetryOutFileName, "File name for symmetry plane parameter output." )->SetProperties( CommandLine::PROPS_FILENAME | CommandLine::PROPS_OUTPUT ); this->m_CommandLine.AddOption( Key( "write-xform" ), &this->m_WriteXformPath, "Write affine alignment transformation to file" ) ->SetProperties( CommandLine::PROPS_XFORM | CommandLine::PROPS_OUTPUT ) ->SetAttribute( "reference", "InputImage" ); this->m_CommandLine.EndGroup(); this->m_CommandLine.AddParameter( &this->m_InFileName, "InputImage", "Input image path" )->SetProperties( CommandLine::PROPS_IMAGE ); } int cmtk::ImageSymmetryPlaneCommandLineBase ::Run( const int argc, const char* argv[] ) { if ( ! this->ParseCommandLine( argc, argv ) ) return 2; UniformVolume::SmartPtr originalVolume( VolumeIO::ReadOriented( this->m_InFileName ) ); if ( !originalVolume ) { StdErr.printf( "Could not read image file %s\n", this->m_InFileName.c_str() ); return 1; } CoordinateVector v( 6 ); // initialize plane as the mid-sagittal with respect to image orientation -- // distance from coordinate origin (image center) is 0: v[0] = 0; // and angles are chosen so that the plane normal is (1,0,0) switch ( this->m_InitialPlane ) { case SYMPL_INIT_XY: v[1] = 0; v[2] = 0; break; case SYMPL_INIT_XZ: v[1] = 90; v[2] = 90; break; default: case SYMPL_INIT_YZ: v[1] = 0; v[2] = 90; break; } // set center of volume (crop region) as coordinate origin. Vector3D center = originalVolume->GetCenterOfMass(); v[3] = center[0]; v[4] = center[1]; v[5] = center[2]; if ( this->m_DisableOptimization ) { v[0] = this->m_Rho; v[1] = this->m_Theta.Value(); v[2] = this->m_Phi.Value(); } else { BestNeighbourOptimizer optimizer; // Instantiate programm progress indicator. ProgressConsole progressIndicator( "Symmetry Plane Computation" ); Progress::Begin( 0, this->m_Levels, 1, "Symmetry Plane Computation" ); for ( int level = 0; level < this->m_Levels; ++level ) { UniformVolume::SmartPtr volume; if ( level < this->m_Levels-1 ) { Types::Coordinate voxelSize = this->m_Sampling * pow( 2.0, (this->m_Levels-level-2) ); DebugOutput( 1 ).GetStream().printf( "Entering level %d out of %d (%.2f mm voxel size)\n", level+1, this->m_Levels, voxelSize ); volume = UniformVolume::SmartPtr( originalVolume->GetResampled( voxelSize ) ); } else { DebugOutput( 1 ).GetStream().printf( "Entering level %d out of %d (original voxel size)\n", level+1, this->m_Levels ); volume = originalVolume; } ImageSymmetryPlaneFunctionalBase::SmartPtr functional; if ( this->m_MinValueSet || this->m_MaxValueSet ) { Types::DataItemRange valueRange = volume->GetData()->GetRange(); if ( this->m_MinValueSet ) valueRange.m_LowerBound = this->m_MinValue; if ( this->m_MaxValueSet ) valueRange.m_UpperBound = this->m_MaxValue; functional = this->CreateFunctional( volume, valueRange ); } else { functional = this->CreateFunctional( volume ); } functional->SetFixOffset( this->m_FixOffset ); optimizer.SetFunctional( functional ); optimizer.Optimize( v, pow( 2.0, this->m_Levels-level-1 ), this->m_Accuracy * pow( 2.0, this->m_Levels-level-1 ) ); Progress::SetProgress( level ); } Progress::Done(); DebugOutput( 1 ).GetStream().printf( "rho=%f, theta=%f, phi=%f\n", v[0], v[1], v[2] ); } this->m_SymmetryPlane.SetParameters( v ); if ( this->m_SymmetryOutFileName ) { ClassStreamOutput stream( this->m_SymmetryOutFileName, ClassStreamOutput::MODE_WRITE ); stream << this->m_SymmetryPlane; stream.Close(); } if ( this->m_AlignedOutFile ) this->WriteAligned( originalVolume ); if ( this->m_MarkedOutFile ) this->WriteMarkPlane( originalVolume ); if ( this->m_DifferenceOutFile ) this->WriteDifference( originalVolume ); if ( this->m_MirrorOutFile ) WriteMirror( originalVolume ); if ( this->m_WriteXformPath ) { AffineXform::SmartPtr alignment( this->m_SymmetryPlane.GetAlignmentXform( 0 ) ); XformIO::Write( alignment, this->m_WriteXformPath ); } return 0; } bool cmtk::ImageSymmetryPlaneCommandLineBase ::ParseCommandLine( const int argc, const char* argv[] ) { try { if ( ! this->m_CommandLine.Parse( argc, argv ) ) return false; if ( this->m_SymmetryParameters ) { double rho, theta, phi; if ( 3 == sscanf( this->m_SymmetryParameters, "%20lf %20lf %20lf", &rho, &theta, &phi ) ) { this->m_Rho = rho; this->m_Theta = Units::Degrees( theta ); this->m_Phi = Units::Degrees( phi ); } } if ( this->m_SymmetryParametersFile ) { ClassStreamInput inStream( this->m_SymmetryParametersFile ); if ( inStream.IsValid() ) { ParametricPlane *plane = NULL; inStream >> plane; this->m_Rho = plane->GetRho(); this->m_Theta = plane->GetTheta(); this->m_Phi = plane->GetPhi(); delete plane; } else { StdErr.printf( "ERROR: Could not open symmetry parameter file %s\n", this->m_SymmetryParametersFile ); } } } catch ( const CommandLine::Exception& ex ) { StdErr << ex << "\n"; return false; } return true; } void cmtk::ImageSymmetryPlaneCommandLineBase ::WriteDifference ( UniformVolume::SmartConstPtr& originalVolume ) const { UniformVolume::SmartPtr diffVolume( originalVolume->CloneGrid() ); const TypedArray* originalData = originalVolume->GetData(); TypedArray::SmartPtr diffData = TypedArray::SmartPtr( TypedArray::Create( GetSignedDataType( originalData->GetType() ), originalData->GetDataSize() ) ); diffVolume->SetData( diffData ); Types::DataItem dataV, dataW; const UniformVolumeInterpolatorBase::SmartPtr interpolator( ReformatVolume::CreateInterpolator( this->m_Interpolation, originalVolume ) );; int offset = 0; for ( int z = 0; z < originalVolume->GetDims()[2]; ++z ) for ( int y = 0; y < originalVolume->GetDims()[1]; ++y ) for ( int x = 0; x < originalVolume->GetDims()[0]; ++x, ++offset ) { if ( ! originalData->Get( dataV, offset ) ) { diffData->SetPaddingAt( offset ); continue; } UniformVolume::CoordinateVectorType w = originalVolume->GetGridLocation( x, y, z ); this->m_SymmetryPlane.MirrorInPlace( w ); if ( interpolator->GetDataAt( w, dataW ) ) { diffData->Set( fabs( dataV - dataW ), offset ); } else { diffData->SetPaddingAt( offset ); } } VolumeIO::Write( *diffVolume, this->m_DifferenceOutFile ); } void cmtk::ImageSymmetryPlaneCommandLineBase ::WriteMirror ( UniformVolume::SmartConstPtr& originalVolume ) const { TypedArray::SmartPtr mirrorData = TypedArray::Create( originalVolume->GetData()->GetType(), originalVolume->GetData()->GetDataSize() ); Types::DataItem data; const UniformVolumeInterpolatorBase::SmartPtr interpolator( ReformatVolume::CreateInterpolator( this->m_Interpolation, originalVolume ) );; int offset = 0; for ( int z = 0; z < originalVolume->GetDims()[2]; ++z ) { for ( int y = 0; y < originalVolume->GetDims()[1]; ++y ) for ( int x = 0; x < originalVolume->GetDims()[0]; ++x, ++offset ) { UniformVolume::CoordinateVectorType v = originalVolume->GetGridLocation( x, y, z ); this->m_SymmetryPlane.MirrorInPlace( v ); if ( interpolator->GetDataAt( v, data ) ) { mirrorData->Set( data, offset ); } else { mirrorData->SetPaddingAt( offset ); } } } UniformVolume::SmartPtr mirrorVolume( originalVolume->CloneGrid() ); mirrorVolume->SetData( mirrorData ); VolumeIO::Write( *mirrorVolume, this->m_MirrorOutFile ); } void cmtk::ImageSymmetryPlaneCommandLineBase ::WriteMarkPlane ( UniformVolume::SmartConstPtr& originalVolume ) const { UniformVolume::SmartPtr markVolume( originalVolume->CloneGrid() ); TypedArray::SmartPtr markData( originalVolume->GetData()->Clone() ); markVolume->SetData( markData ); int offset = 0; for ( int z = 0; z < originalVolume->GetDims()[2]; ++z ) { for ( int y = 0; y < originalVolume->GetDims()[1]; ++y ) { int currentSideOfPlane = 0; for ( int x = 0; x < originalVolume->GetDims()[0]; ++x, ++offset ) { int newSideOfPlane = this->m_SymmetryPlane.GetWhichSide( originalVolume->GetGridLocation( x, y, z ) ); if ( ( newSideOfPlane != currentSideOfPlane ) && x ) markData->Set( this->m_MarkPlaneValue, offset ); currentSideOfPlane = newSideOfPlane; } } } VolumeIO::Write( *markVolume, this->m_MarkedOutFile ); } void cmtk::ImageSymmetryPlaneCommandLineBase ::WriteAligned ( UniformVolume::SmartConstPtr& originalVolume ) const { const TypedArray* originalData = originalVolume->GetData(); TypedArray::SmartPtr alignData = TypedArray::Create( originalData->GetType(), originalData->GetDataSize() ); if ( this->m_PadOutValueSet ) { alignData->SetPaddingValue( this->m_PadOutValue ); } UniformVolume::SmartPtr alignVolume( originalVolume->CloneGrid() ); alignVolume->SetData( alignData ); const Types::DataItem maxData = originalData->GetRange().m_UpperBound; Types::DataItem data; int normalAxis = 0; switch ( this->m_InitialPlane ) { case SYMPL_INIT_XY: normalAxis = 2; break; case SYMPL_INIT_XZ: normalAxis = 1; break; case SYMPL_INIT_YZ: normalAxis = 0; break; } const UniformVolumeInterpolatorBase::SmartPtr interpolator( ReformatVolume::CreateInterpolator( this->m_Interpolation, originalVolume ) );; AffineXform::SmartPtr alignment( this->m_SymmetryPlane.GetAlignmentXform( normalAxis ) ); int offset = 0; for ( int z = 0; z < originalVolume->GetDims()[2]; ++z ) { for ( int y = 0; y < originalVolume->GetDims()[1]; ++y ) { for ( int x = 0; x < originalVolume->GetDims()[0]; ++x, ++offset ) { const UniformVolume::CoordinateVectorType v = alignment->Apply( originalVolume->GetGridLocation( x, y, z ) ); if ( interpolator->GetDataAt( v, data ) ) { if ( this->m_MarkPlaneAligned && (x == ( originalVolume->GetDims()[0] / 2 )) ) alignData->Set( 2 * maxData, offset ); else alignData->Set( data, offset ); } else { alignData->SetPaddingAt( offset ); } } } } VolumeIO::Write( *alignVolume, this->m_AlignedOutFile ); } cmtk-3.3.1/libs/Registration/cmtkImageSymmetryPlaneCommandLineBase.h000066400000000000000000000132101276303427400255720ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2013 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4834 $ // // $LastChangedDate: 2013-09-12 15:15:51 -0700 (Thu, 12 Sep 2013) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkImageSymmetryPlaneCommandLineBase_h_included_ #define __cmtkImageSymmetryPlaneCommandLineBase_h_included_ #include #include #include #include #include namespace cmtk { /** \addtogroup Registration */ //@{ /** Base class for symmetry plane computation command line tools. */ class ImageSymmetryPlaneCommandLineBase : private CannotBeCopied { public: /// Default constructor. ImageSymmetryPlaneCommandLineBase(); /** Run the symmetry plane computation based on provided command line arguments. *\return The return code for the symmetry plane program. This should be returned * from main() via either "return" or "exit()". */ int Run( const int argc, const char* argv[] ); /// Get reference to command line parser object. CommandLine& GetCommandLine() { return this->m_CommandLine; } protected: /// Create functional for volume. virtual ImageSymmetryPlaneFunctionalBase::SmartPtr CreateFunctional( UniformVolume::SmartPtr& volume ) = 0; /// Create functional for volume and value range. virtual ImageSymmetryPlaneFunctionalBase::SmartPtr CreateFunctional( UniformVolume::SmartPtr& volume, const Types::DataItemRange& range ) = 0; private: /// Parse the given command line. bool ParseCommandLine ( const int argc, const char* argv[] ); /// The symmetry plane object. ParametricPlane m_SymmetryPlane; /// Minimum data value (lower threshold). float m_MinValue; /// Flag for valid (user-set) minimum data value. bool m_MinValueSet; /// Minimum data value (lower threshold). float m_MaxValue; /// Flag for valid (user-set) maximum data value. bool m_MaxValueSet; /// Image sampling (highest resampled resolution). Types::Coordinate m_Sampling; /// Optimization "accuracy" (really, precision) Types::Coordinate m_Accuracy; /// Interpolation method for reformatted image generation. Interpolators::InterpolationEnum m_Interpolation; /// Number of multi-resolution levels. int m_Levels; /// Flag to disable optimization. bool m_DisableOptimization; /// Symmetry plane "Rho" parameter (offset). Types::Coordinate m_Rho; /// Symmetry plane "Theta" angle parameter. Units::Degrees m_Theta; /// Symmetry plane "Phi" angle parameter. Units::Degrees m_Phi; /// Flag to fix symmetry plane offset parameter (i.e., do not optimize "Rho") bool m_FixOffset; /// Optional output path for mirrored file. const char* m_MirrorOutFile; /// Optional output path for aligned file. const char* m_AlignedOutFile; /// Flag for marking the symmetry plane in the aligned file. bool m_MarkPlaneAligned; /// Optional output path for file with marked symmetry plane. const char* m_MarkedOutFile; /// Optional output path for file with subtraction between input and mirrored input. const char* m_DifferenceOutFile; /// Optional output path for the alignment transformation. const char* m_WriteXformPath; /// Data value used for marking the symmetry plane in output images. Types::DataItem m_MarkPlaneValue; /// Flag for user-provided padding value. bool m_PadOutValueSet; /// User-provided padding value. Types::DataItem m_PadOutValue; /// Optional output path for symmetry plane. const char* m_SymmetryOutFileName; /// Optional output path for symmetry plane parameters. const char* m_SymmetryParameters; /// Optional input path for previously computed symmetry plane parameters. const char* m_SymmetryParametersFile; /// Input image file path. std::string m_InFileName; /// Constants for initial plane orientation. typedef enum { /// XY plane (axial) SYMPL_INIT_XY, /// XZ plane (coronal) SYMPL_INIT_XZ, /// YZ plane (sagittal) SYMPL_INIT_YZ } InitialPlaneEnum; /// Initial plane orientation: default to sagittal for human images. InitialPlaneEnum m_InitialPlane; /// Write difference image between original and mirrored image. void WriteDifference( UniformVolume::SmartConstPtr& originalVolume ) const; /// Write mirrored image. void WriteMirror( UniformVolume::SmartConstPtr& originalVolume ) const; /// Write original image with marked symmetry plane. void WriteMarkPlane( UniformVolume::SmartConstPtr& originalVolume ) const; /// Write image aligned w.r.t. the symmetry plane. void WriteAligned( UniformVolume::SmartConstPtr& originalVolume ) const; private: CommandLine m_CommandLine; }; } // namespace cmtk #endif // #ifndef __cmtkImageSymmetryPlaneCommandLineBase_h_included_ cmtk-3.3.1/libs/Registration/cmtkImageSymmetryPlaneFunctional.cxx000066400000000000000000000061371276303427400253000ustar00rootroot00000000000000/* // // Copyright 2016 Google, Inc. // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2012 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 5399 $ // // $LastChangedDate: 2016-01-14 19:00:40 -0800 (Thu, 14 Jan 2016) $ // // $LastChangedBy: torstenrohlfing $ // */ #include "cmtkImageSymmetryPlaneFunctional.h" #include namespace cmtk { /** \addtogroup Registration */ //@{ ImageSymmetryPlaneFunctional::ImageSymmetryPlaneFunctional ( UniformVolume::SmartConstPtr& volume ) : ImageSymmetryPlaneFunctionalBase( volume ) { this->m_Metric = Self::MetricType::SmartPtr( new ImagePairSimilarityMeasureMSD( this->m_Volume, this->m_Volume ) ); } ImageSymmetryPlaneFunctional::ImageSymmetryPlaneFunctional ( UniformVolume::SmartConstPtr& volume, const Types::DataItemRange& valueRange ) : ImageSymmetryPlaneFunctionalBase( volume, valueRange ) { this->m_Metric = Self::MetricType::SmartPtr( new ImagePairSimilarityMeasureMSD( this->m_Volume, this->m_Volume ) ); } ImageSymmetryPlaneFunctional::ReturnType ImageSymmetryPlaneFunctional::Evaluate() { const TransformedVolumeAxes gridHash( *m_Volume, this->m_ParametricPlane, m_Volume->Deltas().begin() ); const Vector3D *HashX = gridHash[0], *HashY = gridHash[1], *HashZ = gridHash[2]; Vector3D pFloating; Self::MetricType& metric = *m_Metric; metric.Reset(); const DataGrid::IndexType& Dims = m_Volume->GetDims(); const Types::GridIndexType DimsX = Dims[0], DimsY = Dims[1], DimsZ = Dims[2]; Types::GridIndexType fltIdx[3]; Types::Coordinate fltFrac[3]; Vector3D planeStart, rowStart; Types::GridIndexType r = 0; for ( Types::GridIndexType pZ = 0; pZFindVoxelByIndex( pFloating, fltIdx, fltFrac ) ) { // Continue metric computation. metric.Increment( metric.GetSampleX( r ), metric.GetSampleY( fltIdx, fltFrac ) ); } } } } return metric.Get(); } } // namespace cmtk cmtk-3.3.1/libs/Registration/cmtkImageSymmetryPlaneFunctional.h000066400000000000000000000047001276303427400247170ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2010 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 2398 $ // // $LastChangedDate: 2010-10-05 14:54:37 -0700 (Tue, 05 Oct 2010) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkImageSymmetryPlaneFunctional_h_included_ #define __cmtkImageSymmetryPlaneFunctional_h_included_ #include #include "cmtkImageSymmetryPlaneFunctionalBase.h" #include #include namespace cmtk { /** \addtogroup Registration */ //@{ /** Functional for finding a symmetry plane in 3-D volumes. */ class ImageSymmetryPlaneFunctional : /// Inherit functional interface. public ImageSymmetryPlaneFunctionalBase { public: /// This class. typedef ImageSymmetryPlaneFunctional Self; /// Smart pointer to this class. typedef SmartPointer SmartPtr; /// Superclass typedef ImageSymmetryPlaneFunctionalBase Superclass; /// Type of metric we're using. typedef ImagePairSimilarityMeasureMSD MetricType; /// Constructor. ImageSymmetryPlaneFunctional( UniformVolume::SmartConstPtr& volume ); /// Constructor with value range limits. ImageSymmetryPlaneFunctional( UniformVolume::SmartConstPtr& volume, const Types::DataItemRange& valueRange ); /// Destructor. virtual ~ImageSymmetryPlaneFunctional() {} /// Compute functional value. virtual Self::ReturnType Evaluate(); private: /// Image similarity measure. Self::MetricType::SmartPtr m_Metric; }; //@} } // namespace cmtk #endif // #ifndef __cmtkImageSymmetryPlaneFunctional_h_included_ cmtk-3.3.1/libs/Registration/cmtkImageSymmetryPlaneFunctionalBase.cxx000066400000000000000000000046071276303427400260730ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2012 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4381 $ // // $LastChangedDate: 2012-05-30 14:18:15 -0700 (Wed, 30 May 2012) $ // // $LastChangedBy: torstenrohlfing $ // */ #include "cmtkImageSymmetryPlaneFunctionalBase.h" namespace cmtk { /** \addtogroup Registration */ //@{ ImageSymmetryPlaneFunctionalBase::ImageSymmetryPlaneFunctionalBase ( UniformVolume::SmartConstPtr& volume ) : m_Volume( volume ), m_FixOffset( false ) { } ImageSymmetryPlaneFunctionalBase::ImageSymmetryPlaneFunctionalBase ( UniformVolume::SmartConstPtr& volume, const Types::DataItemRange& valueRange ) : m_Volume( Self::ApplyThresholds( *volume, valueRange ) ), m_FixOffset( false ) { } Types::Coordinate ImageSymmetryPlaneFunctionalBase::GetParamStep ( const size_t idx, const Types::Coordinate mmStep ) const { switch ( idx ) { // plane offset is a translation case 0: if ( this->m_FixOffset ) return 0; else return mmStep; // the other two parameters are rotations case 1: case 2: return mmStep / sqrt( MathUtil::Square( 0.5 * m_Volume->m_Size[0] ) + MathUtil::Square( 0.5 * m_Volume->m_Size[1] ) + MathUtil::Square( 0.5 * m_Volume->m_Size[2] ) ) * 90/M_PI; } return mmStep; } UniformVolume::SmartPtr ImageSymmetryPlaneFunctionalBase::ApplyThresholds( const UniformVolume& volume, const Types::DataItemRange& valueRange ) { UniformVolume::SmartPtr result( volume.Clone() ); result->GetData()->Threshold( valueRange ); return result; } } // namespace cmtk cmtk-3.3.1/libs/Registration/cmtkImageSymmetryPlaneFunctionalBase.h000066400000000000000000000071041276303427400255130ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2010 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 2398 $ // // $LastChangedDate: 2010-10-05 14:54:37 -0700 (Tue, 05 Oct 2010) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkImageSymmetryPlaneFunctionalBase_h_included_ #define __cmtkImageSymmetryPlaneFunctionalBase_h_included_ #include #include #include #include namespace cmtk { /** \addtogroup Registration */ //@{ /** Base class for functionals to find a symmetry plane in 3-D volumes. * This class provides the interface and code that is independent of how the * symmetry plane is computed. */ class ImageSymmetryPlaneFunctionalBase : /// Inherit functional interface. public Functional { public: /// This class. typedef ImageSymmetryPlaneFunctionalBase Self; /// Smart pointer to this class. typedef SmartPointer SmartPtr; /// Superclass typedef Functional Superclass; /// Constructor. ImageSymmetryPlaneFunctionalBase( UniformVolume::SmartConstPtr& volume ); /// Constructor with value range limits. ImageSymmetryPlaneFunctionalBase( UniformVolume::SmartConstPtr& volume, const Types::DataItemRange& valueRange ); /// Destructor. virtual ~ImageSymmetryPlaneFunctionalBase() {} /// Get parameter vector. virtual void GetParamVector ( CoordinateVector& v ) { this->m_ParametricPlane.GetParameters( v ); } /// Compute functional value. virtual Self::ReturnType Evaluate() = 0; /// Compute functional value. virtual Self::ReturnType EvaluateAt( CoordinateVector& v ) { this->m_ParametricPlane.SetParameters( v ); return this->Evaluate(); } /// Return the symmetry plane's parameter vector dimension. virtual size_t ParamVectorDim() const { return 6; } /// Return the number of variable parameters of the transformation. virtual size_t VariableParamVectorDim() const { return 3; } /// Return the parameter stepping for 1 mm optimization steps. virtual Types::Coordinate GetParamStep( const size_t idx, const Types::Coordinate mmStep = 1 ) const; /// Set fix offset flag. void SetFixOffset( const bool fixOffset ) { this->m_FixOffset = fixOffset; } protected: /// Volume image. UniformVolume::SmartConstPtr m_Volume; /// The symmetry plane. ParametricPlane m_ParametricPlane; /// Flag for fixing offset parameter: resulting plane will go through volume center of mass. bool m_FixOffset; /// Apply thresholding to volume data. static UniformVolume::SmartPtr ApplyThresholds( const UniformVolume& volume, const Types::DataItemRange& valueRange ); }; //@} } // namespace cmtk #endif // #ifndef __cmtkImageSymmetryPlaneFunctionalBase_h_included_ cmtk-3.3.1/libs/Registration/cmtkImageXformDB.cxx000066400000000000000000000210341276303427400217360ustar00rootroot00000000000000/* // // Copyright 2010 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 2398 $ // // $LastChangedDate: 2010-10-05 14:54:37 -0700 (Tue, 05 Oct 2010) $ // // $LastChangedBy: torstenrohlfing $ // */ #include #include #include #include #include #include cmtk::ImageXformDB ::ImageXformDB( const std::string& dbPath, const bool readOnly ) : cmtk::SQLite( dbPath, readOnly ) { // create entity tables if ( ! this->TableExists( "images" ) ) { this->Exec( "CREATE TABLE images(id INTEGER PRIMARY KEY, space INTEGER, path TEXT)" ); } if ( ! this->TableExists( "xforms" ) ) { this->Exec( "CREATE TABLE xforms(id INTEGER PRIMARY KEY, path TEXT, invertible INTEGER, level INTEGER, spacefrom INTEGER, spaceto INTEGER)" ); } } void cmtk::ImageXformDB ::AddImage( const std::string& imagePath, const std::string& spacePath ) { PrimaryKeyType imageKey = this->FindImageSpaceID( imagePath ); if ( imageKey != Self::NOTFOUND ) return; if ( (spacePath == "") ) { this->Exec( "INSERT INTO images (path) VALUES ('"+imagePath+"')" ); this->Exec( "UPDATE images SET space=(SELECT id FROM images WHERE path='"+imagePath+"') WHERE path='"+imagePath+"'" ); } else { PrimaryKeyType spaceKey = this->FindImageSpaceID( spacePath ); if ( (spaceKey == Self::NOTFOUND) ) { this->Exec( "INSERT INTO images (path) VALUES ('"+spacePath+"')" ); this->Exec( "UPDATE images SET space=(SELECT id FROM images WHERE path='"+spacePath+"') WHERE path='"+spacePath+"'" ); spaceKey = this->FindImageSpaceID( spacePath ); } std::ostringstream sql; sql << "INSERT INTO images (space,path) VALUES ( " << spaceKey << ", '" << imagePath << "')"; this->Exec( sql.str() ); } } bool cmtk::ImageXformDB ::AddImagePairXform ( const std::string& xformPath, const bool invertible, const std::string& imagePathSrc, const std::string& imagePathTrg ) { PrimaryKeyType spaceKeySrc = this->FindImageSpaceID( imagePathSrc ); if ( spaceKeySrc == Self::NOTFOUND ) { this->AddImage( imagePathSrc ); spaceKeySrc = this->FindImageSpaceID( imagePathSrc ); assert( spaceKeySrc != Self::NOTFOUND ); } PrimaryKeyType spaceKeyTrg = this->FindImageSpaceID( imagePathTrg ); if ( spaceKeyTrg == Self::NOTFOUND ) { this->AddImage( imagePathTrg ); spaceKeyTrg = this->FindImageSpaceID( imagePathTrg ); assert( spaceKeyTrg != Self::NOTFOUND ); } if ( spaceKeyTrg == spaceKeySrc ) { StdErr << "WARNING - cmtk::ImageXformDB::AddXform - source and target image of transformation are in the same space; bailing out.\n"; return false; } std::ostringstream sql; sql << "INSERT INTO xforms (path,invertible,level,spacefrom,spaceto) VALUES ( '" << xformPath << "', " << (invertible ? 1 : 0) << ", 0, " << spaceKeySrc << ", " << spaceKeyTrg << ")"; this->Exec( sql.str() ); return true; } bool cmtk::ImageXformDB ::AddRefinedXform ( const std::string& xformPath, const bool invertible, const std::string& xformInitPath, const bool initInverse ) { const std::string sql = "SELECT level,spacefrom,spaceto FROM xforms WHERE ( path='" + xformInitPath + "' )"; SQLite::TableType table; this->Query( sql, table ); if ( !table.size() || !table[0].size() ) { return false; } const int level = 1 + atoi( table[0][0].c_str() ); const Self::PrimaryKeyType spacefrom = atoi( table[0][1].c_str() ); const Self::PrimaryKeyType spaceto = atoi( table[0][2].c_str() ); if ( spacefrom == Self::NOTFOUND || spaceto == Self::NOTFOUND ) { StdErr << "WARNING - cmtk::ImageXformDB::AddXform - given initializing transformation has invalid space ID(s). Bailing out.\n"; return false; } std::ostringstream sqlAdd; sqlAdd << "INSERT INTO xforms (path,invertible,level,spacefrom,spaceto) VALUES ( '" << xformPath << "', " << (invertible ? 1 : 0) << ", " << level << ", "; if ( initInverse ) sqlAdd << spaceto << ", " << spacefrom; else sqlAdd << spacefrom << ", " << spaceto; sqlAdd << ")"; this->Exec( sqlAdd.str() ); return true; } cmtk::ImageXformDB::PrimaryKeyType cmtk::ImageXformDB ::FindImageSpaceID( const std::string& imagePath ) const { if ( imagePath != "" ) { const std::string sql = "SELECT space FROM images WHERE path='"+imagePath+"'"; SQLite::TableType table; this->Query( sql, table ); if ( table.size() && table[0].size() ) return atoi( table[0][0].c_str() ); } return Self::NOTFOUND; } const std::vector cmtk::ImageXformDB ::GetSpaceImageList( const Self::PrimaryKeyType& spaceKey, const bool sortById ) { std::vector results; if ( spaceKey == Self::NOTFOUND ) { return results; } std::ostringstream sql; sql << "SELECT path FROM images WHERE space=" << spaceKey; if ( sortById ) { sql << " ORDER BY id ASC"; } SQLite::TableType table; this->Query( sql.str(), table ); for ( size_t i = 0; i < table.size(); ++i ) { if ( table[i].size() ) results.push_back( table[i][0] ); } return results; } bool cmtk::ImageXformDB ::FindXform( const std::string& imagePathSrc, const std::string& imagePathTrg, std::string& xformPath, bool& inverse ) const { const PrimaryKeyType spaceKeySrc = this->FindImageSpaceID( imagePathSrc ); const PrimaryKeyType spaceKeyTrg = this->FindImageSpaceID( imagePathTrg ); if ( (spaceKeySrc == Self::NOTFOUND) || (spaceKeyTrg == Self::NOTFOUND) ) { // if either space is not in the database, then clearly no transformation can reach it return false; } if ( spaceKeySrc == spaceKeyTrg ) { xformPath = ""; inverse = false; return true; } std::ostringstream sql; sql << "SELECT path FROM xforms WHERE ( spacefrom=" << spaceKeySrc << " AND spaceto=" << spaceKeyTrg << " ) ORDER BY level DESC, invertible ASC"; SQLite::TableType table; this->Query( sql.str(), table ); if ( table.size() && table[0].size() ) { inverse = false; xformPath = table[0][0]; return true; } sql.str( "" ); sql << "SELECT path FROM xforms WHERE ( spacefrom=" << spaceKeyTrg << " AND spaceto=" << spaceKeySrc << " ) ORDER BY level DESC, invertible ASC"; this->Query( sql.str(), table ); if ( table.size() && table[0].size() ) { inverse = true; xformPath = table[0][0]; return true; } return false; } const std::vector cmtk::ImageXformDB ::FindAllXforms( const std::string& imagePathSrc, const std::string& imagePathTrg ) const { std::vector result; const PrimaryKeyType spaceKeySrc = this->FindImageSpaceID( imagePathSrc ); const PrimaryKeyType spaceKeyTrg = this->FindImageSpaceID( imagePathTrg ); if ( (spaceKeySrc == Self::NOTFOUND) || (spaceKeyTrg == Self::NOTFOUND) ) { // if either space is not in the database, then clearly no transformation can reach it return result; } if ( spaceKeySrc == spaceKeyTrg ) { result.push_back( "" ); return result; } std::ostringstream sql; sql << "SELECT path FROM xforms WHERE ( spacefrom=" << spaceKeySrc << " AND spaceto=" << spaceKeyTrg << " ) ORDER BY level DESC, invertible ASC"; SQLite::TableType table; this->Query( sql.str(), table ); for ( size_t i = 0; i < table.size(); ++i ) { if ( table[i].size() ) result.push_back( table[i][0] ); } return result; } int cmtk::ImageXformDB ::FindXformLevel( const std::string& xformPath ) const { const std::string sql = "SELECT level FROM xforms WHERE ( path='" + xformPath + "' )"; SQLite::TableType table; this->Query( sql, table ); if ( table.size() && table[0].size() ) { return atoi( table[0][0].c_str() ); } return -1; } cmtk-3.3.1/libs/Registration/cmtkImageXformDB.h000066400000000000000000000172571276303427400213770ustar00rootroot00000000000000/* // // Copyright 2010-2011 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 2740 $ // // $LastChangedDate: 2011-01-14 10:50:48 -0800 (Fri, 14 Jan 2011) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkImageXformDB_h_included_ #define __cmtkImageXformDB_h_included_ #include #include namespace cmtk { /** \addtogroup Registration */ //@{ /** Class for image and transformation database. * The image and transformation database has three tables that store: * a) images and the coordinate spaces that they live in, b) coordinate * transformations, and from which source space to which target space they map. * * The "images" table stores all images and identifies which space they live in: * \code * CREATE TABLE images(id INTEGER PRIMARY KEY, space INTEGER, path TEXT); * \endcode * Each image is assigned a table-unique ID. All images that live in the same * coordinate space (i.e., the same acquisition of the same subject) share a * space ID, which is the unique image ID of the first image that was added to * this space. * * The "xforms" table stores the file system path and properties for each coordinate * transformation: * \code * CREATE TABLE xforms(id INTEGER PRIMARY KEY, path TEXT, invertible INTEGER, level INTEGER, spacefrom INTEGER, spaceto INTEGER); * \endcode * Each transformation is assigned a table-unique ID. * The field "invertible" is a flag that is set if the transformation has an explicit * inverse (i.e., if it is affine). All transformations can be inverted nuerically, but * we usually prefer explicit inverses for speed and accuracy. * The field "level" gives the refinement level of the transformation: a transformation * computed from two images with no initialization has level 0. A transformation computed * with initialization from an existing transformation has a level of that transformation * plus one. */ class ImageXformDB /// Inherit from SQLite wrapper class. : public SQLite { public: /// This class. typedef ImageXformDB Self; /// Parent class. typedef SQLite Superclass; /// Constructor: open ImageXformDB database. ImageXformDB( const std::string& dbPath /*!< Path to the database file. */, const bool readOnly = false /*!< If this flag is set, the database is opened read-only. If false, the database is opened for read/write, and a non-existing database will be created. */); /** Add an image to a coordinate space, each identified by its file system path. * If the given image already exists in the database, no change is made. *\warning If the given image already exists in the database, no change is made even * if a new space is assigned to it with this call. The reason for this behaviour is * that if we assign the new space, it is unclear what to do with other images that potentially * live in the old image space, especially if that space was defined by the reassigned image * in the first place. */ void AddImage( const std::string& imagePath /*!< File system path of the new image*/, const std::string& spacePath = "" /*!< File system path of an existing image that lives in the same space*/ ); /** Add a transformation between two images. *\return True if the operation was successful, false otherwise. Failure may be due to source and target image being in the same * space to begin with. */ bool AddImagePairXform( const std::string& xformPath /*!< File system path of the tranformation */, const bool invertible /** GetSpaceImageList( const Self::PrimaryKeyType& spaceKey, const bool sortById = false ); /** Find transformation between two images. * Only one transformation is returned, even if more than one transformation * connects the two spaces. * * Forward non-invertible (i.e., nonrigid) transformations are preferred, * followed by forward explicitly invertible (i.e., affine) transformations, * then inverses of nonrigid transformations. and finally inverses of affine * transformations. * *\return True if transformation exists. If false, the two given images may still be connected via a chain of * multiple, concatenated transformations. */ bool FindXform( const std::string& imagePathSrc /*!< File system path of the source image */, const std::string& imagePathTrg /*!< File system path of the target image */, std::string& xformPath /*!< File system path of the transformation. Only valid if function returns "true." Path can be empty if both images are already in the same space. */, bool& inverse /*!< If this is set, the given transformation needs to be inverted. */) const; /** Find all transformations between two images. * Only forward transformations are returned. To find inverse transformations, * call this function with source and target images reversed. * *\return List of transformations that map from source to target. Non-invertible * (i.e., nonrigid) transformations are listed first, followed by explicitly invertible * (i.e., affine) transformations.mations. */ const std::vector FindAllXforms( const std::string& imagePathSrc /*!< File system path of the source image */, const std::string& imagePathTrg /*!< File system path of the target image */ ) const; /** Get the refinement level of a transformation in the database. *\return The level of the given transformation: 0 for an original transformation, * positive for refined transformation, or -1 is transformation is not in the * database. */ int FindXformLevel( const std::string& xformPath /*!< Path of the transformation to find and inspect.*/ ) const; }; //@} } // namespace cmtk #endif // #ifndef __cmtkImageXformDB_h_included_ cmtk-3.3.1/libs/Registration/cmtkMakeInitialAffineTransformation.cxx000066400000000000000000000131641276303427400257260ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2013 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4812 $ // // $LastChangedDate: 2013-09-09 14:26:32 -0700 (Mon, 09 Sep 2013) $ // // $LastChangedBy: torstenrohlfing $ // */ #include "cmtkMakeInitialAffineTransformation.h" #include namespace cmtk { /** \addtogroup Registration */ //@{ const std::string MakeInitialAffineTransformation ::GetModeName( const Self::Mode mode ) { switch ( mode ) { case Self::NONE: return std::string( "none" ); case Self::FOV: return std::string( "FieldsOfView" ); case Self::COM: return std::string( "CentersOfMass" ); case Self::PAX: return std::string( "PrincipalAxes" ); case Self::PHYS: return std::string( "PhysicalCoordinates" ); default: break; } return std::string( "unknown" ); } AffineXform* MakeInitialAffineTransformation ::Create( const UniformVolume& referenceImage, const UniformVolume& floatingImage, const Self::Mode mode ) { switch ( mode ) { case Self::FOV: return Self::AlignFieldsOfView( referenceImage, floatingImage ); case Self::COM: return Self::AlignCentersOfMass( referenceImage, floatingImage ); case Self::PAX: return Self::AlignPrincipalAxes( referenceImage, floatingImage ); case Self::PHYS: return Self::AlignDirectionVectors( referenceImage, floatingImage ); case Self::NONE: default: break; } return new AffineXform; } AffineXform* MakeInitialAffineTransformation ::AlignDirectionVectors( const UniformVolume& referenceImage, const UniformVolume& floatingImage, const bool centerXform ) { if ( referenceImage.GetMetaInfo( META_SPACE ) != floatingImage.GetMetaInfo( META_SPACE ) ) { StdErr << "ERROR: coordinate spaces '" << referenceImage.GetMetaInfo( META_SPACE ) << "' and '" << floatingImage.GetMetaInfo( META_SPACE ) << "' do not match.\n"; return NULL; } if ( referenceImage.GetMetaInfo( META_EXTERNAL_SPACE_ID ) != floatingImage.GetMetaInfo( META_EXTERNAL_SPACE_ID ) ) { StdErr << "ERROR: semantic coordinate spaces '" << referenceImage.GetMetaInfo( META_EXTERNAL_SPACE_ID ) << "' and '" << floatingImage.GetMetaInfo( META_EXTERNAL_SPACE_ID ) << "' do not match.\n"; return NULL; } const AffineXform::MatrixType refMatrix = referenceImage.GetImageToPhysicalMatrix(); AffineXform referenceXform( refMatrix ); const AffineXform::MatrixType fltMatrix = floatingImage.GetImageToPhysicalMatrix(); AffineXform floatingXform( fltMatrix ); AffineXform* xform = new AffineXform( referenceXform ); xform->Concat( *floatingXform.GetInverse() ); if ( centerXform ) { const Vector3D center = referenceImage.GetCenterCropRegion(); xform->ChangeCenter( center ); } return xform; } AffineXform* MakeInitialAffineTransformation ::AlignFieldsOfView( const UniformVolume& referenceImage, const UniformVolume& floatingImage ) { AffineXform* xform = new AffineXform; const Vector3D translation = floatingImage.GetCenterCropRegion() - referenceImage.GetCenterCropRegion(); xform->SetXlate( translation.begin() ); return xform; } AffineXform* MakeInitialAffineTransformation ::AlignCentersOfMass( const UniformVolume& referenceImage, const UniformVolume& floatingImage ) { AffineXform* xform = new AffineXform; const Vector3D translation = floatingImage.GetCenterOfMass() - referenceImage.GetCenterOfMass(); xform->SetXlate( translation.begin() ); return xform; } AffineXform* MakeInitialAffineTransformation ::AlignPrincipalAxes( const UniformVolume& referenceImage, const UniformVolume& floatingImage ) { // get principal axes Matrix3x3 pAxesRef, pAxesFlt; Vector3D centerOfMassRef, centerOfMassFlt; referenceImage.GetPrincipalAxes( pAxesRef, centerOfMassRef ); floatingImage.GetPrincipalAxes( pAxesFlt, centerOfMassFlt ); pAxesRef = pAxesRef.GetTranspose(); pAxesFlt = pAxesFlt.GetTranspose(); // Now compute transformation const Matrix3x3 xform3x3 = (pAxesRef.GetInverse() * pAxesFlt); const Vector3D xlation = centerOfMassFlt - (centerOfMassRef * xform3x3); // Assign xform3x3 as a submatrix of a 4x4 Matrix4x4 xform4x4 = xform3x3; // Turn xform4x4 into homogenized matrix with xlation as the 4th column. for ( int i = 0; i < 3; i++ ) { xform4x4[3][i] = xlation[i]; xform4x4[i][3] = 0; } xform4x4[3][3] = 1; AffineXform* xform = new AffineXform( xform4x4 ); xform->ChangeCenter( centerOfMassRef ); Types::Coordinate* angles = xform->RetAngles(); for ( int i = 0; i < 3; ++i ) { if ( angles[i] > 90 ) angles[i] -= 180; else if ( angles[i] < -90 ) angles[i] += 180; } xform->SetAngles( angles ); return xform; } } // namespace cmtk cmtk-3.3.1/libs/Registration/cmtkMakeInitialAffineTransformation.h000066400000000000000000000077301276303427400253550ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2011 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 2994 $ // // $LastChangedDate: 2011-03-14 11:27:30 -0700 (Mon, 14 Mar 2011) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkMakeInitialAffineTransformation_h_included_ #define __cmtkMakeInitialAffineTransformation_h_included_ #include #include #include #include namespace cmtk { /** \addtogroup Registration */ //@{ /** Class for generating initial affine coordinate transformations between two images */ class MakeInitialAffineTransformation { public: /// This class. typedef MakeInitialAffineTransformation Self; /// Enum that defines all initialization modes supported by this class. typedef enum { /// No initialization. Usually means use identity transformation. NONE = 0, /// Align centers of fields of view. FOV = 1, /// Align centers of mass. COM = 2, /// Align using principal axes. PAX = 3, /// Align using physical coordinates, ie., image origins and direction vectors. PHYS = 4 } Mode; /// Return a name for each initialization mode. static const std::string GetModeName( const Self::Mode mode ); /// Create an initial affine transformation for two images based on a selected mode. static AffineXform* Create( const UniformVolume& referenceImage /*!< The reference (fixed) image*/, const UniformVolume& floatingImage /*!< The floating (moving) image*/, const Self::Mode mode /*!< Selected initialization method.*/ ); /** Align images based on their direction vectors. * The direction vectors are encoded in each volume's "AffineXform" field. */ static AffineXform* AlignDirectionVectors( const UniformVolume& referenceImage /*!< The reference (fixed) image*/, const UniformVolume& floatingImage /*!< The floating (moving) image*/, const bool centerXform = false /*!< If this flag is set, the rotation center of the transformation is set to the center of the reference image.*/ ); /** Align images based on fields of view. *\return This function returns a transformation with three degrees of freedom for a translation only, which * aligns the centers of field of view for the two input images. If a crop region is defined in an image, the * crop region center is used, otherwise the bounding box center. */ static AffineXform* AlignFieldsOfView( const UniformVolume& referenceImage, const UniformVolume& floatingImage ); /** Align images based on center of mass. *\return This function returns a transformation with three degrees of freedom for a translation only. */ static AffineXform* AlignCentersOfMass( const UniformVolume& referenceImage, const UniformVolume& floatingImage ); /** Rigidly align images based on principal axes. * This function implies alignment by translation according to the centers of mass. */ static AffineXform* AlignPrincipalAxes( const UniformVolume& referenceImage, const UniformVolume& floatingImage ); }; //@} } // namespace cmtk #endif // #ifndef __cmtkMakeInitialAffineTransformation_h_included_ cmtk-3.3.1/libs/Registration/cmtkMultiChannelHistogramRegistrationFunctional.h000066400000000000000000000127531276303427400300060ustar00rootroot00000000000000/* // // Copyright 1997-2010 Torsten Rohlfing // // Copyright 2004-2010, 2013 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4882 $ // // $LastChangedDate: 2013-09-27 15:16:36 -0700 (Fri, 27 Sep 2013) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkMultiChannelHistogramRegistrationFunctional_h_included_ #define __cmtkMultiChannelHistogramRegistrationFunctional_h_included_ #include #include #include #include #include #include #include #include namespace cmtk { /** \addtogroup Registration */ //@{ /** Base class for multi-channel registration functionals using the Histogram metric. */ template, class THashKeyType = unsigned int, char NBitsPerChannel=6> class MultiChannelHistogramRegistrationFunctional : /** Inherit functional interface. */ public MultiChannelRegistrationFunctional { public: /** Number of bits per channel in the histogram bin index type. */ static const char m_HistogramBitsPerChannel = NBitsPerChannel; /** This class. */ typedef MultiChannelHistogramRegistrationFunctional Self; /** Smart pointer. */ typedef SmartPointer SmartPtr; /** Superclass. */ typedef MultiChannelRegistrationFunctional Superclass; /** Real value type for data representation. */ typedef TDataType DataType; /** Hash key type. */ typedef THashKeyType HashKeyType; /// Default constructor. MultiChannelHistogramRegistrationFunctional() : m_HashKeyShiftRef( 0 ) {} /** Add reference channel. */ virtual void AddReferenceChannel( UniformVolume::SmartPtr& channel ); /** Add floating channel. */ virtual void AddFloatingChannel( UniformVolume::SmartPtr& channel ); /** Reset channels, clear all images. */ virtual void ClearAllChannels(); protected: /** Local class for data needed to compute similarity metric. */ class MetricData { private: /** Typedef of parent class. */ typedef MultiChannelHistogramRegistrationFunctional Parent; /** Parent object. */ Parent* m_Parent; public: /** This class type. */ typedef MetricData Self; /// Default constructor. MetricData() : m_Parent( NULL ), m_TotalNumberOfSamples( 0 ) {} /** Initialize metric object and local storage. */ void Init( Parent *const parent ); /** Hash table type. */ typedef HashMapSTL HashTableType; /** Joint haash table for reference and floating channels. */ HashTableType m_JointHash; /** Hash table for reference channels. */ HashTableType m_ReferenceHash; /** Hash table for floating channels. */ HashTableType m_FloatingHash; /** Total number of samples (pixels) under current transformation. */ size_t m_TotalNumberOfSamples; /** Assignment operator. */ Self& operator=( const Self& source ); /** In-place addition operator. */ Self& operator+=( const Self& other ); /** In-place subtraction operator. */ Self& operator-=( const Self& other ); /** In-place single sample addition operator. */ void operator+=( const std::vector& values ); /** In-place single sample subtraction operator. */ void operator-=( const std::vector& values ); }; /// Global data structure for metric computation. MetricData m_MetricData; /** Continue metric computation. */ virtual void ContinueMetric( MetricData& metricData, const size_t rindex, const Vector3D& fvector ); /** Get metric value. */ virtual Functional::ReturnType GetMetric( const MetricData& metricData ) const; private: /** Scale factors for reference values to histogram "bins". */ std::vector m_HashKeyScaleRef; /** Scale factors for floating values to histogram "bins". */ std::vector m_HashKeyScaleFlt; /** Offsets for reference values to histogram "bins". */ std::vector m_HashKeyOffsRef; /** Offsets for reference values to histogram "bins". */ std::vector m_HashKeyOffsFlt; /** Bit shift count to combine floating and reference hash keys into joint keys. */ size_t m_HashKeyShiftRef; }; //@} } // namespace cmtk #include "cmtkMultiChannelHistogramRegistrationFunctional.txx" #include "cmtkMultiChannelHistogramRegistrationFunctionalMetricData.txx" #endif // #ifndef __cmtkMultiChannelHistogramRegistrationFunctional_h_included_ cmtk-3.3.1/libs/Registration/cmtkMultiChannelHistogramRegistrationFunctional.txx000066400000000000000000000137761276303427400304100ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2010 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4882 $ // // $LastChangedDate: 2013-09-27 15:16:36 -0700 (Fri, 27 Sep 2013) $ // // $LastChangedBy: torstenrohlfing $ // */ #include #include #include #include namespace cmtk { /** \addtogroup Registration */ //@{ template void MultiChannelHistogramRegistrationFunctional ::ClearAllChannels() { this->m_HashKeyScaleRef.resize( 0 ); this->m_HashKeyOffsRef.resize( 0 ); this->m_HashKeyScaleFlt.resize( 0 ); this->m_HashKeyOffsFlt.resize( 0 ); this->Superclass::ClearAllChannels(); } template void MultiChannelHistogramRegistrationFunctional ::AddReferenceChannel( UniformVolume::SmartPtr& channel ) { const Types::DataItem maxBinIndex = (1<GetData()->GetRange(); const Types::DataItem scale = maxBinIndex / range.Width(); const Types::DataItem offset = -(range.m_LowerBound/scale); this->m_HashKeyScaleRef.push_back( static_cast( scale ) ); this->m_HashKeyOffsRef.push_back( static_cast( offset ) ); this->m_HashKeyShiftRef = NBitsPerChannel*this->m_ReferenceChannels.size(); this->Superclass::AddReferenceChannel( channel ); const size_t hashKeyBits = 8 * sizeof( THashKeyType ); if ( this->m_NumberOfChannels * NBitsPerChannel > hashKeyBits ) { StdErr << "ERROR in MultiChannelHistogramRegistrationFunctional:\n" << " Cannot represent total of " << this->m_NumberOfChannels << " channels with " << NBitsPerChannel << " bits per channel using hash key type with " << hashKeyBits << "bits.\n"; exit( 1 ); } } template void MultiChannelHistogramRegistrationFunctional ::AddFloatingChannel( UniformVolume::SmartPtr& channel ) { const Types::DataItem maxBinIndex = (1<GetData()->GetRange(); const Types::DataItem scale = maxBinIndex / range.Width(); const Types::DataItem offset = -(range.m_LowerBound/scale); this->m_HashKeyScaleFlt.push_back( static_cast( scale ) ); this->m_HashKeyOffsFlt.push_back( static_cast( offset ) ); this->Superclass::AddFloatingChannel( channel ); const size_t hashKeyBits = 8 * sizeof( THashKeyType ); if ( this->m_NumberOfChannels * NBitsPerChannel > hashKeyBits ) { StdErr << "ERROR in MultiChannelHistogramRegistrationFunctional:\n" << " Cannot represent total of " << this->m_NumberOfChannels << " channels with " << this->m_HistogramBitsPerChannel << " bits per channel using hash key type with " << hashKeyBits << "bits.\n"; exit( 1 ); } } template void MultiChannelHistogramRegistrationFunctional ::ContinueMetric( MetricData& metricData, const size_t rindex, const Vector3D& fvector ) { std::vector values( this->m_NumberOfChannels ); size_t idx = 0; for ( size_t ref = 0; ref < this->m_ReferenceChannels.size(); ++ref ) { if ( !this->m_ReferenceChannels[ref]->GetDataAt( values[idx++], rindex ) ) return; } for ( size_t flt = 0; flt < this->m_FloatingChannels.size(); ++flt ) { if ( !this->m_FloatingInterpolators[flt]->GetDataAt( fvector, values[idx++] ) ) return; } metricData += values; } template Functional::ReturnType MultiChannelHistogramRegistrationFunctional ::GetMetric( const MetricData& metricData ) const { if ( metricData.m_TotalNumberOfSamples ) { const double norm = 1.0 / metricData.m_TotalNumberOfSamples; double hXY = 0; typename MetricData::HashTableType::const_iterator it = metricData.m_JointHash.begin(); for ( ; it != metricData.m_JointHash.end(); ++it ) { if ( it->second ) { const double p = norm * it->second; hXY -= p * log( p ); } } double hX = 0; it = metricData.m_ReferenceHash.begin(); for ( ; it != metricData.m_ReferenceHash.end(); ++it ) { if ( it->second ) { const double p = norm * it->second; hX -= p * log( p ); } } double hY = 0; it = metricData.m_FloatingHash.begin(); for ( ; it != metricData.m_FloatingHash.end(); ++it ) { if ( it->second ) { const double p = norm * it->second; hY -= p * log( p ); } } if ( this->m_NormalizedMI ) return static_cast( (hX + hY) / hXY ); else return static_cast( hX + hY - hXY ); } return static_cast( -FLT_MAX ); } } // namespace cmtk cmtk-3.3.1/libs/Registration/cmtkMultiChannelHistogramRegistrationFunctionalMetricData.txx000066400000000000000000000150271276303427400323350ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2013 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4882 $ // // $LastChangedDate: 2013-09-27 15:16:36 -0700 (Fri, 27 Sep 2013) $ // // $LastChangedBy: torstenrohlfing $ // */ #include "cmtkMultiChannelHistogramRegistrationFunctional.h" #include namespace cmtk { /** \addtogroup Registration */ //@{ template void MultiChannelHistogramRegistrationFunctional::MetricData ::Init( Parent *const parent ) { this->m_Parent = parent; this->m_JointHash.clear(); this->m_ReferenceHash.clear(); this->m_FloatingHash.clear(); this->m_TotalNumberOfSamples = 0; } template typename MultiChannelHistogramRegistrationFunctional::MetricData& MultiChannelHistogramRegistrationFunctional::MetricData::operator= ( const typename MultiChannelHistogramRegistrationFunctional::MetricData& source ) { this->m_JointHash = source.m_JointHash; this->m_ReferenceHash = source.m_ReferenceHash; this->m_FloatingHash = source.m_FloatingHash; this->m_TotalNumberOfSamples = source.m_TotalNumberOfSamples; return *this; } template typename MultiChannelHistogramRegistrationFunctional::MetricData& MultiChannelHistogramRegistrationFunctional::MetricData::operator+= ( const typename MultiChannelHistogramRegistrationFunctional::MetricData& other ) { for ( typename HashTableType::const_iterator it = other.m_JointHash.begin(); it != other.m_JointHash.end(); ++it ) { this->m_JointHash[it->first] += it->second; } for ( typename HashTableType::const_iterator it = other.m_ReferenceHash.begin(); it != other.m_ReferenceHash.end(); ++it ) { this->m_ReferenceHash[it->first] += it->second; } for ( typename HashTableType::const_iterator it = other.m_FloatingHash.begin(); it != other.m_FloatingHash.end(); ++it ) { this->m_FloatingHash[it->first] += it->second; } this->m_TotalNumberOfSamples += other.m_TotalNumberOfSamples; return *this; } template typename MultiChannelHistogramRegistrationFunctional::MetricData& MultiChannelHistogramRegistrationFunctional::MetricData::operator-= ( const typename MultiChannelHistogramRegistrationFunctional::MetricData& other ) { for ( typename HashTableType::const_iterator it = other.m_JointHash.begin(); it != other.m_JointHash.end(); ++it ) { this->m_JointHash[it->first] -= it->second; } for ( typename HashTableType::const_iterator it = other.m_ReferenceHash.begin(); it != other.m_ReferenceHash.end(); ++it ) { this->m_ReferenceHash[it->first] -= it->second; } for ( typename HashTableType::const_iterator it = other.m_FloatingHash.begin(); it != other.m_FloatingHash.end(); ++it ) { this->m_FloatingHash[it->first] -= it->second; } this->m_TotalNumberOfSamples -= other.m_TotalNumberOfSamples; return *this; } template void MultiChannelHistogramRegistrationFunctional::MetricData::operator+= ( const std::vector& values ) { THashKeyType hashKeyRef = 0, hashKeyFlt = 0; size_t idx = 0; for ( size_t ref = 0; ref < m_Parent->m_ReferenceChannels.size(); ++ref, ++idx ) { hashKeyRef |= static_cast(m_Parent->m_HashKeyScaleRef[ref] * values[idx] + m_Parent->m_HashKeyOffsRef[ref] ) << (NBitsPerChannel*ref); } for ( size_t flt = 0; flt < m_Parent->m_FloatingChannels.size(); ++flt, ++idx ) { hashKeyFlt |= static_cast(m_Parent->m_HashKeyScaleFlt[flt] * values[idx] + m_Parent->m_HashKeyOffsFlt[flt] ) <<( NBitsPerChannel*flt); } THashKeyType hashKeyJnt = (hashKeyFlt << m_Parent->m_HashKeyShiftRef) + hashKeyRef; ++this->m_ReferenceHash[hashKeyRef]; ++this->m_FloatingHash[hashKeyFlt]; ++this->m_JointHash[hashKeyJnt]; ++this->m_TotalNumberOfSamples; } template void MultiChannelHistogramRegistrationFunctional::MetricData::operator-= ( const std::vector& values ) { THashKeyType hashKeyRef = 0, hashKeyFlt = 0; size_t idx = 0; for ( size_t ref = 0; ref < m_Parent->m_ReferenceChannels.size(); ++ref, ++idx ) { hashKeyRef |= static_cast(m_Parent->m_HashKeyScaleRef[ref] * values[idx] + m_Parent->m_HashKeyOffsRef[ref] ) << (NBitsPerChannel*ref); } for ( size_t flt = 0; flt < m_Parent->m_FloatingChannels.size(); ++flt, ++idx ) { hashKeyFlt |= static_cast(m_Parent->m_HashKeyScaleFlt[flt] * values[idx] + m_Parent->m_HashKeyOffsFlt[flt] ) << (NBitsPerChannel*flt); } THashKeyType hashKeyJnt = (hashKeyFlt << m_Parent->m_HashKeyShiftRef) + hashKeyRef; --this->m_ReferenceHash[hashKeyRef]; --this->m_FloatingHash[hashKeyFlt]; --this->m_JointHash[hashKeyJnt]; --this->m_TotalNumberOfSamples; } } // namespace cmtk cmtk-3.3.1/libs/Registration/cmtkMultiChannelRMIRegistrationFunctional.h000066400000000000000000000104451276303427400264740ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2013 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4882 $ // // $LastChangedDate: 2013-09-27 15:16:36 -0700 (Fri, 27 Sep 2013) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkMultiChannelRMIRegistrationFunctional_h_included_ #define __cmtkMultiChannelRMIRegistrationFunctional_h_included_ #include #include #include #include #include #include namespace cmtk { /** \addtogroup Registration */ //@{ /** Base class for multi-channel registration functionals using the RMI metric. */ template > class MultiChannelRMIRegistrationFunctional : /** Inherit functional interface. */ public MultiChannelRegistrationFunctional { public: /** This class. */ typedef MultiChannelRMIRegistrationFunctional Self; /** Smart pointer. */ typedef SmartPointer SmartPtr; /** Superclass. */ typedef MultiChannelRegistrationFunctional Superclass; /** Real value type for internal computations. */ typedef TRealType RealType; /** Real value type for data representation. */ typedef TDataType DataType; protected: /** Local class for data needed to compute similarity metric. */ class MetricData { private: /** Typedef of parent class. */ typedef MultiChannelRMIRegistrationFunctional Parent; public: /** This class type. */ typedef MetricData Self; /** Initialize metric object and local storage. */ void Init( Parent *const parent ); /** Vector of pixel value sums. */ std::vector m_Sums; /** Vector (actually matrix) of pairwise pixel value products. */ std::vector m_Products; /** Covariance matrix for joint entropy computation. */ SymmetricMatrix m_CovarianceMatrix; /** Covariance matrix for reference channels entropy computation. */ SymmetricMatrix m_CovarianceMatrixRef; /** Covariance matrix for floating channels entropy computation. */ SymmetricMatrix m_CovarianceMatrixFlt; /** Total number of samples (pixels) under current transformation. */ size_t m_TotalNumberOfSamples; /** Assignment operator. */ Self& operator=( const Self& source ); /** In-place addition operator. */ Self& operator+=( const Self& other ); /** In-place subtraction operator. */ Self& operator-=( const Self& other ); /** In-place single sample addition operator. */ void operator+=( const std::vector& values ); /** In-place single sample subtraction operator. */ void operator-=( const std::vector& values ); }; /// Global data structure for metric computation. MetricData m_MetricData; /** Continue metric computation. */ virtual void ContinueMetric( MetricData& metricData, const size_t rindex, const Vector3D& fvector ); /** Get metric value. */ virtual RealType GetMetric( MetricData& metricData ) const; }; //@} } // namespace cmtk #include "cmtkMultiChannelRMIRegistrationFunctional.txx" #include "cmtkMultiChannelRMIRegistrationFunctionalMetricData.txx" #endif // #ifndef __cmtkMultiChannelRMIRegistrationFunctional_h_included_ cmtk-3.3.1/libs/Registration/cmtkMultiChannelRMIRegistrationFunctional.txx000066400000000000000000000110351276303427400270640ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2013 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4882 $ // // $LastChangedDate: 2013-09-27 15:16:36 -0700 (Fri, 27 Sep 2013) $ // // $LastChangedBy: torstenrohlfing $ // */ #include #include #include #include #include namespace cmtk { /** \addtogroup Registration */ //@{ template void MultiChannelRMIRegistrationFunctional ::ContinueMetric( MetricData& metricData, const size_t rindex, const Vector3D& fvector ) { std::vector values( this->m_NumberOfChannels ); size_t idx = 0; for ( size_t ref = 0; ref < this->m_ReferenceChannels.size(); ++ref ) { if ( !this->m_ReferenceChannels[ref]->GetDataAt( values[idx++], rindex ) ) return; } for ( size_t flt = 0; flt < this->m_FloatingChannels.size(); ++flt ) { if ( !this->m_FloatingInterpolators[flt]->GetDataAt( fvector, values[idx++] ) ) return; } metricData += values; } template TRealType MultiChannelRMIRegistrationFunctional ::GetMetric( MetricData& metricData ) const { const size_t nRefs = this->m_ReferenceChannels.size(); const size_t nFlts = this->m_FloatingChannels.size(); size_t idx = 0; for ( size_t j = 0; j < this->m_NumberOfChannels; ++j ) { const RealType muj = metricData.m_Sums[j] / metricData.m_TotalNumberOfSamples; for ( size_t i = 0; i <= j; ++i, ++idx ) { const RealType mui = metricData.m_Sums[i] / metricData.m_TotalNumberOfSamples; metricData.m_CovarianceMatrix(i,j) = (metricData.m_Products[idx] / metricData.m_TotalNumberOfSamples) - mui * muj; } } for ( size_t j = 0; j < nRefs; ++j ) { for ( size_t i = 0; i <= j; ++i ) { metricData.m_CovarianceMatrixRef(i,j) = metricData.m_CovarianceMatrix(i,j); } } for ( size_t j = 0; j < nFlts; ++j ) { for ( size_t i = 0; i <= j; ++i ) { metricData.m_CovarianceMatrixFlt(i,j) = metricData.m_CovarianceMatrix(nRefs+i,nRefs+j); } } const std::vector eigenvalues = EigenValuesSymmetricMatrix( metricData.m_CovarianceMatrix ).GetEigenvalues(); const std::vector eigenvaluesRef = EigenValuesSymmetricMatrix( metricData.m_CovarianceMatrixRef ).GetEigenvalues(); const std::vector eigenvaluesFlt = EigenValuesSymmetricMatrix( metricData.m_CovarianceMatrixFlt ).GetEigenvalues(); const double EIGENVALUE_THRESHOLD = 1e-6; double determinant = 1.0, determinantRef = 1.0, determinantFlt = 1.0; for ( size_t i = 0; i < this->m_NumberOfChannels; ++i ) { if ( eigenvalues[i] > EIGENVALUE_THRESHOLD ) determinant *= eigenvalues[i]; } for ( size_t i = 0; i < nRefs; ++i ) { if ( eigenvaluesRef[i] > EIGENVALUE_THRESHOLD ) determinantRef *= eigenvaluesRef[i]; } for ( size_t i = 0; i < nFlts; ++i ) { if ( eigenvaluesFlt[i] > EIGENVALUE_THRESHOLD ) determinantFlt *= eigenvaluesFlt[i]; } if ( (determinant > 0) && (determinantRef > 0) && (determinantFlt > 0) ) { const static double alpha = 1.41893853320467; const double hxy = this->m_NumberOfChannels*alpha + .5*log( determinant ); const double hx = nRefs*alpha + .5*log( determinantRef ); const double hy = nFlts*alpha + .5*log( determinantFlt ); if ( this->m_NormalizedMI ) return static_cast( (hx+hy) / hxy ); else return static_cast( hx+hy-hxy ); } return -FLT_MAX; } } // namespace cmtk cmtk-3.3.1/libs/Registration/cmtkMultiChannelRMIRegistrationFunctionalMetricData.txx000066400000000000000000000135641276303427400310330ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2013 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4882 $ // // $LastChangedDate: 2013-09-27 15:16:36 -0700 (Fri, 27 Sep 2013) $ // // $LastChangedBy: torstenrohlfing $ // */ #include "cmtkMultiChannelRMIRegistrationFunctional.h" #include namespace cmtk { /** \addtogroup Registration */ //@{ template void MultiChannelRMIRegistrationFunctional::MetricData ::Init( Parent *const parent ) { const size_t nref = parent->m_ReferenceChannels.size(); const size_t nflt = parent->m_FloatingChannels.size(); this->m_Sums.resize( nref + nflt ); std::fill( this->m_Sums.begin(), this->m_Sums.end(), static_cast( 0.0 ) ); this->m_Products.resize( ((nref+nflt) * (nref+nflt+1)) / 2 ); std::fill( this->m_Products.begin(), this->m_Products.end(), static_cast( 0.0 ) ); this->m_CovarianceMatrix.Resize( nref+nflt ); // needs no reset this->m_CovarianceMatrixRef.Resize( nref ); // needs no reset this->m_CovarianceMatrixFlt.Resize( nflt ); // needs no reset this->m_TotalNumberOfSamples = 0; } template typename MultiChannelRMIRegistrationFunctional::MetricData& MultiChannelRMIRegistrationFunctional::MetricData::operator= ( const typename MultiChannelRMIRegistrationFunctional::MetricData& source ) { this->m_Sums.resize( source.m_Sums.size() ); std::copy( source.m_Sums.begin(), source.m_Sums.end(), this->m_Sums.begin() ); this->m_Products.resize( source.m_Products.size() ); std::copy( source.m_Products.begin(), source.m_Products.end(), this->m_Products.begin() ); // covariance matrices need not be copied as they only provide temporary storage for GetMetric() this->m_TotalNumberOfSamples = source.m_TotalNumberOfSamples; return *this; } template typename MultiChannelRMIRegistrationFunctional::MetricData& MultiChannelRMIRegistrationFunctional::MetricData::operator+= ( const typename MultiChannelRMIRegistrationFunctional::MetricData& other ) { assert( this->m_Sums.size() == other.m_Sums.size() ); assert( this->m_Products.size() == other.m_Products.size() ); for ( size_t idx = 0; idx < this->m_Sums.size(); ++idx ) this->m_Sums[idx] += other.m_Sums[idx]; for ( size_t idx = 0; idx < this->m_Products.size(); ++idx ) this->m_Products[idx] += other.m_Products[idx]; // covariance matrices need not be treated as they only provide temporary storage for GetMetric() this->m_TotalNumberOfSamples += other.m_TotalNumberOfSamples; return *this; } template typename MultiChannelRMIRegistrationFunctional::MetricData& MultiChannelRMIRegistrationFunctional::MetricData::operator-= ( const typename MultiChannelRMIRegistrationFunctional::MetricData& other ) { assert( this->m_Sums.size() == other.m_Sums.size() ); assert( this->m_Products.size() == other.m_Products.size() ); for ( size_t idx = 0; idx < this->m_Sums.size(); ++idx ) this->m_Sums[idx] -= other.m_Sums[idx]; for ( size_t idx = 0; idx < this->m_Products.size(); ++idx ) this->m_Products[idx] -= other.m_Products[idx]; // covariance matrices need not be treated as they only provide temporary storage for GetMetric() this->m_TotalNumberOfSamples -= other.m_TotalNumberOfSamples; return *this; } template void MultiChannelRMIRegistrationFunctional::MetricData::operator+= ( const std::vector& values ) { const size_t numberOfChannels = this->m_Sums.size(); for ( size_t j = 0; j < numberOfChannels; ++j ) { this->m_Sums[j] += static_cast( values[j] ); } size_t idx = 0; for ( size_t j = 0; j < numberOfChannels; ++j ) { for ( size_t i = 0; i <= j; ++i, ++idx ) { this->m_Products[idx] += static_cast( values[i] * values[j] ); } } ++this->m_TotalNumberOfSamples; } template void MultiChannelRMIRegistrationFunctional::MetricData::operator-= ( const std::vector& values ) { const size_t numberOfChannels = this->m_Sums.size(); for ( size_t j = 0; j < numberOfChannels; ++j ) { this->m_Sums[j] -= static_cast( values[j] ); } size_t idx = 0; for ( size_t j = 0; j < numberOfChannels; ++j ) { for ( size_t i = 0; i <= j; ++i, ++idx ) { this->m_Products[idx] -= static_cast( values[i] * values[j] ); } } --this->m_TotalNumberOfSamples; } } // namespace cmtk cmtk-3.3.1/libs/Registration/cmtkMultiChannelRegistrationFunctional.h000066400000000000000000000046531276303427400261300ustar00rootroot00000000000000/* // // Copyright 1997-2010 Torsten Rohlfing // // Copyright 2004-2010 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 2398 $ // // $LastChangedDate: 2010-10-05 14:54:37 -0700 (Tue, 05 Oct 2010) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkMultiChannelRegistrationFunctional_h_included_ #define __cmtkMultiChannelRegistrationFunctional_h_included_ #include #include #include #include #include #include #include namespace cmtk { /** \addtogroup Registration */ //@{ /** Base class for multi-channel registration functionals. */ template > class MultiChannelRegistrationFunctional : /** Inherit non-template implementation and interface. */ public MultiChannelRegistrationFunctionalBase { public: /** This class. */ typedef MultiChannelRegistrationFunctional Self; /** Smart pointer. */ typedef SmartPointer SmartPtr; /** Superclass. */ typedef MultiChannelRegistrationFunctionalBase Superclass; /** Add floating channel. */ virtual void AddFloatingChannel( UniformVolume::SmartPtr& channel ); protected: /// Interpolators for the floating image channels. std::vector m_FloatingInterpolators; }; //@} } // namespace cmtk #include "cmtkMultiChannelRegistrationFunctional.txx" #endif // #ifndef __cmtkMultiChannelRegistrationFunctional_h_included_ cmtk-3.3.1/libs/Registration/cmtkMultiChannelRegistrationFunctional.txx000066400000000000000000000030641276303427400265170ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2010 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 2398 $ // // $LastChangedDate: 2010-10-05 14:54:37 -0700 (Tue, 05 Oct 2010) $ // // $LastChangedBy: torstenrohlfing $ // */ #include #include #include #include namespace cmtk { /** \addtogroup Registration */ //@{ template void MultiChannelRegistrationFunctional ::AddFloatingChannel( UniformVolume::SmartPtr& channel ) { this->Superclass::AddFloatingChannel( channel ); this->m_FloatingInterpolators.push_back( typename TInterpolator::SmartPtr( new TInterpolator( *channel ) ) ); } } // namespace cmtk cmtk-3.3.1/libs/Registration/cmtkMultiChannelRegistrationFunctionalBase.cxx000066400000000000000000000064341276303427400272750ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2012 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4381 $ // // $LastChangedDate: 2012-05-30 14:18:15 -0700 (Wed, 30 May 2012) $ // // $LastChangedBy: torstenrohlfing $ // */ #include "cmtkMultiChannelRegistrationFunctionalBase.h" #include #include #include #include namespace cmtk { /** \addtogroup Registration */ //@{ void MultiChannelRegistrationFunctionalBase ::ClearAllChannels() { this->m_ReferenceChannels.resize( 0 ); this->m_FloatingChannels.resize( 0 ); } void MultiChannelRegistrationFunctionalBase ::AddReferenceChannel( UniformVolume::SmartPtr& channel ) { if ( this->m_ReferenceChannels.size() ) { this->VerifyImageSize( this->m_ReferenceChannels[0], channel ); } else { this->m_ReferenceDims = channel->GetDims(); this->m_ReferenceSize = channel->m_Size; this->m_ReferenceCropRegion = channel->CropRegion(); } this->m_ReferenceChannels.push_back( channel ); this->m_NumberOfChannels = this->m_ReferenceChannels.size() + this->m_FloatingChannels.size(); if ( this->m_ReferenceChannels.size() == 1 ) { this->NewReferenceChannelGeometry(); } } void MultiChannelRegistrationFunctionalBase ::AddFloatingChannel( UniformVolume::SmartPtr& channel ) { if ( this->m_FloatingChannels.size() ) { this->VerifyImageSize( this->m_FloatingChannels[0], channel ); } else { this->m_FloatingDims = channel->GetDims(); this->m_FloatingSize = channel->m_Size; this->m_FloatingCropRegion = channel->GetHighResCropRegion(); for ( int dim = 0; dim < 3; ++dim ) { this->m_FloatingInverseDelta[dim] = 1.0 / channel->m_Delta[dim]; } } this->m_FloatingChannels.push_back( channel ); this->m_NumberOfChannels = this->m_ReferenceChannels.size() + this->m_FloatingChannels.size(); } void MultiChannelRegistrationFunctionalBase ::VerifyImageSize( const UniformVolume* imgA, const UniformVolume* imgB ) { for ( int dim = 0; dim < 3; ++dim ) { if ( imgA->GetDims()[dim] != imgB->GetDims()[dim] ) { throw Exception( "MultiChannelRegistrationFunctionalBase::VerifyImageSize(): Image dimension mismatch" ); } if ( fabs( imgA->m_Size[dim] - imgB->m_Size[dim] ) > 1e-6 ) { throw Exception( "MultiChannelRegistrationFunctionalBase::VerifyImageSize(): Image size mismatch" ); } } } } // namespace cmtk cmtk-3.3.1/libs/Registration/cmtkMultiChannelRegistrationFunctionalBase.h000066400000000000000000000131541276303427400267170ustar00rootroot00000000000000/* // // Copyright 1997-2010 Torsten Rohlfing // // Copyright 2004-2010 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 2398 $ // // $LastChangedDate: 2010-10-05 14:54:37 -0700 (Tue, 05 Oct 2010) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkMultiChannelRegistrationFunctionalBase_h_included_ #define __cmtkMultiChannelRegistrationFunctionalBase_h_included_ #include #include #include #include #include #include #include namespace cmtk { /** \addtogroup Registration */ //@{ /** Base class for multi-channel registration functionals. */ class MultiChannelRegistrationFunctionalBase : /** Inherit functional interface. */ public Functional { public: /** This class. */ typedef MultiChannelRegistrationFunctionalBase Self; /** Smart pointer. */ typedef SmartPointer SmartPtr; /** Superclass. */ typedef Functional Superclass; /** Default constructor. */ MultiChannelRegistrationFunctionalBase() : m_NumberOfChannels( 0 ), m_NormalizedMI( false ) {} /** Destructor: free all converted image arrays. */ virtual ~MultiChannelRegistrationFunctionalBase() { this->ClearAllChannels(); } /** Set flag for normalized vs. standard MI */ void SetNormalizedMI( const bool nmi = true ) { this->m_NormalizedMI = nmi; } /** Reset channels, clear all images. */ virtual void ClearAllChannels(); /** Add reference channel. */ virtual void AddReferenceChannel( UniformVolume::SmartPtr& channel ); /** Add reference channels from stl container. */ template void AddReferenceChannels( ForwardIterator first, ForwardIterator last ) { while ( first != last ) { this->AddReferenceChannel( *first ); ++first; } } /** Get number of reference channels. */ size_t GetNumberOfReferenceChannels() const { return this->m_ReferenceChannels.size(); } /** Get a reference channel image. */ UniformVolume::SmartPtr& GetReferenceChannel( const size_t idx ) { return this->m_ReferenceChannels[idx]; } /** Get constant pointer to reference channel image. */ const UniformVolume* GetReferenceChannel( const size_t idx ) const { return this->m_ReferenceChannels[idx]; } /** Add floating channel. */ virtual void AddFloatingChannel( UniformVolume::SmartPtr& channel ); /** Add floating channels from stl container. */ template void AddFloatingChannels( ForwardIterator first, ForwardIterator last ) { while ( first != last ) { this->AddFloatingChannel( *first ); ++first; } } /** Get number of floating channels. */ size_t GetNumberOfFloatingChannels() const { return this->m_FloatingChannels.size(); } /** Get a floating channel image. */ UniformVolume::SmartPtr& GetFloatingChannel( const size_t idx ) { return this->m_FloatingChannels[idx]; } /** Get constant pointer to floating channel image. */ const UniformVolume* GetFloatingChannel( const size_t idx ) const { return this->m_FloatingChannels[idx]; } /** Vector of reference images. */ std::vector m_ReferenceChannels; /** Vector of floating images. */ std::vector m_FloatingChannels; protected: /** Total number of channels. This is the sum of the floating and reference channel vector sizes. */ size_t m_NumberOfChannels; /// Grid dimensions of the reference volume. DataGrid::IndexType m_ReferenceDims; /// Extents of the reference volume in real-world coordinates. UniformVolume::CoordinateVectorType m_ReferenceSize; /// Inverse pixel deltas of the reference volume. UniformVolume::CoordinateVectorType m_ReferenceInvDelta; /// Rectangular crop region in the reference volume. DataGrid::RegionType m_ReferenceCropRegion; /// Grid dimensions of the floating volume. DataGrid::IndexType m_FloatingDims; /// Extents of the floating volume in real-world coordinates. UniformVolume::CoordinateVectorType m_FloatingSize; /// Inverse pixel deltas of the floating volume. UniformVolume::CoordinateVectorType m_FloatingInverseDelta; /// Coordinates of the floating image cropping region. UniformVolume::CoordinateRegionType m_FloatingCropRegion; /** Update all transformation-related data after init or refine. */ virtual void NewReferenceChannelGeometry() {} protected: /** Flag for normalized vs. standard mutual information. */ bool m_NormalizedMI; private: /** Verify size and geometry of newly added image channels against already added channels. */ void VerifyImageSize( const UniformVolume* imgA, const UniformVolume* imgB ); }; //@} } // namespace cmtk #endif // #ifndef __cmtkMultiChannelRegistrationFunctionalBase_h_included_ cmtk-3.3.1/libs/Registration/cmtkOptimizer.h000066400000000000000000000121351276303427400211030ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2011, 2013 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4824 $ // // $LastChangedDate: 2013-09-11 12:13:30 -0700 (Wed, 11 Sep 2013) $ // // $LastChangedBy: torsten_at_home $ // */ #ifndef __cmtkOptimizer_h_included_ #define __cmtkOptimizer_h_included_ #include #include #include #include #include #include #include #include namespace cmtk { /** \addtogroup Registration */ //@{ /// Abstract optimizer. class Optimizer : /// Inherit from optimizer base class. public OptimizerBase { public: /// This class. typedef Optimizer Self; /// Superclass. typedef OptimizerBase Superclass; /// Smart pointer to Optimizer. typedef SmartPointer SmartPtr; /** This flag determines whether the vector of step sizes is updated. * For some optimization problems, such as 2-D projection to 3-D image * registration, the current parameter vector determines the optimum step * sizes for the transformation parameters. In such cases, this flag can be * set to "true" so that after each optimization step the functional is * queried for updated steppings. */ cmtkGetSetMacro(bool,UpdateStepScaleVector); /** External callback object. * This object is called during the optimization, reporting optimization * progress to the user and checking for user interrupts. */ cmtkGetSetMacro(RegistrationCallback::SmartPtr,Callback); /** Optimization functional. */ cmtkGetSetMacro(Functional::SmartPtr,Functional); /// Set DeltaF threshold. virtual void SetDeltaFThreshold( const Self::ReturnType value ) { this->m_DeltaFThreshold = value; } /// Execute callback if one was set. virtual CallbackResult CallbackExecuteWithData( const CoordinateVector &v, const Self::ReturnType metric ) { if ( m_Callback ) return m_Callback->ExecuteWithData( v, metric ); return CALLBACK_OK; } /// Execute callback if one was set. virtual CallbackResult CallbackExecute() { if ( m_Callback ) m_Callback->Execute(); return CALLBACK_OK; } /// Notify callback of an annotation if one exists. virtual void CallbackComment ( const char* comment = NULL ) { if ( m_Callback ) m_Callback->Comment( comment ); } /// Return dimension of search space. virtual unsigned int GetSearchSpaceDimension() const { return this->m_Functional->VariableParamVectorDim(); } /// Return parameter stepping. virtual Self::ParameterType GetParamStep( unsigned int idx, const Self::ParameterType mmStep = 1.0 ) const { return this->m_Functional->GetParamStep( idx, mmStep ); } /// Return functional value. virtual Self::ReturnType Evaluate ( CoordinateVector& v ) { return this->m_Functional->EvaluateAt( v ); } /// Evaluate functional and also return its gradient. virtual Self::ReturnType EvaluateWithGradient( CoordinateVector& v, CoordinateVector& directionVector, const Self::ParameterType step = 1 ) { return this->m_Functional->EvaluateWithGradient( v, directionVector, step ); } /// Default constructor. Optimizer() : m_Callback( NULL ), m_Functional( NULL ), m_LastOptimizeChangedParameters( false ), m_DeltaFThreshold( 0.0 ) { this->m_UpdateStepScaleVector = false; } /// Virtual destructor. virtual ~Optimizer () {} /// Interface: Optimize functional. virtual CallbackResult Optimize( CoordinateVector&, const Self::ParameterType = 1, const Self::ParameterType = 0 ) = 0; /// Get flag to check whether previous call to Optimize() changed parameters. bool GetLastOptimizeChangedParameters() const { return this->m_LastOptimizeChangedParameters; } protected: /// Flag whether the last call to Optimize() made any changes to functional parameters. bool m_LastOptimizeChangedParameters; /** Threshold for termination based on change of target function. * Optimization should terminate if the relative change of the target function in one step * falls below this threshold. */ Self::ReturnType m_DeltaFThreshold; }; //@} } // namespace cmtk #endif // #ifdef __cmtkOptimizer_h_included_ cmtk-3.3.1/libs/Registration/cmtkOptimizerBase.h000066400000000000000000000042441276303427400217000ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2010 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 2398 $ // // $LastChangedDate: 2010-10-05 14:54:37 -0700 (Tue, 05 Oct 2010) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkOptimizerBase_h_included_ #define __cmtkOptimizerBase_h_included_ #include #include #include namespace cmtk { /** \addtogroup Registration */ //@{ /// Base class for all optimizers and meta optimizers. class OptimizerBase : /// Inherit to prevent object copying. private CannotBeCopied { public: /// This class. typedef OptimizerBase Self; /// Return type. typedef Functional::ReturnType ReturnType; /// Parameter type. typedef Functional::ParameterType ParameterType; /// Default constructor. OptimizerBase() : m_FinalValue( 0.0 ) {}; /// Virtual destructor. virtual ~OptimizerBase() {} /// Get final functional value. Self::ReturnType GetFinalValue() const { return this->m_FinalValue; } protected: /// Set final functional value. void SetFinalValue( const Self::ReturnType finalValue ) { this->m_FinalValue = finalValue; } private: /// Final functional value. Self::ReturnType m_FinalValue; }; //@} } // namespace cmtk #endif // #ifdef __cmtkOptimizerBase_h_included_ cmtk-3.3.1/libs/Registration/cmtkProtocolCallback.cxx000066400000000000000000000051631276303427400227150ustar00rootroot00000000000000/* // // Copyright 2016 Google, Inc. // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2009, 2013 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 5418 $ // // $LastChangedDate: 2016-01-20 20:15:12 -0800 (Wed, 20 Jan 2016) $ // // $LastChangedBy: torstenrohlfing $ // */ #include #include namespace cmtk { /** \addtogroup Registration */ //@{ ProtocolCallback::ProtocolCallback ( const std::string& filename, const bool debug ) { if ( !filename.empty() ) { if ( (fp = fopen( filename.c_str(), "w" )) ) { fputs( "4\n1 3 3 3\n", fp ); fflush( fp ); } } else fp = NULL; Debug = debug; } ProtocolCallback::~ProtocolCallback () { if (fp) fclose(fp); } CallbackResult ProtocolCallback::ExecuteWithData ( const CoordinateVector& v, const double metric ) { size_t dim = std::min( 20, v.Dim ); if (fp) { fprintf( fp, "%f", metric ); for ( size_t i = 0; i < dim; ++i ) fprintf( fp, " %f", (float) v[i] ); if ( v.Dim > 20 ) fputs( " ...", fp ); fputs( "\n", fp ); fflush( fp ); } if ( Debug ) { fprintf( stderr, "%f", metric ); for ( size_t i = 0; i < dim; ++i ) fprintf( stderr, " %f", (float) v[i] ); fputs( "\n", stderr ); } return this->Superclass::ExecuteWithData( v, metric ); } void ProtocolCallback::Comment ( const char* comment ) { if ( fp ) { if ( comment != NULL ) { fprintf( fp, "# %s\n", comment ); fflush( fp ); } else { fputs( "#\n", fp ); fflush( fp ); } } if ( Debug ) { if ( comment != NULL ) fprintf( stderr, "# %s\n", comment ); else fputs( "#\n", stderr ); } } } // namespace cmtk cmtk-3.3.1/libs/Registration/cmtkProtocolCallback.h000066400000000000000000000041351276303427400223400ustar00rootroot00000000000000/* // // Copyright 2016 Google, Inc. // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2009, 2013 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 5418 $ // // $LastChangedDate: 2016-01-20 20:15:12 -0800 (Wed, 20 Jan 2016) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkProtocolCallback_h_included_ #define __cmtkProtocolCallback_h_included_ #include #include namespace cmtk { /** \addtogroup Registration */ //@{ /// Callback object with protocol functionality. class ProtocolCallback : public RegistrationCallback { public: /// Open protocol stream if filename is given. ProtocolCallback ( const std::string& filename = "", const bool debug = false ); /** Destructor. * Closes protocol stream. */ virtual ~ProtocolCallback (); /// Execute callback action. virtual CallbackResult ExecuteWithData ( const CoordinateVector& v, const double metric ); /// Write comment to protocol file. virtual void Comment ( const char* comment = NULL ); private: /// Protocol stream. FILE *fp; /// Debug flag. bool Debug; /// Convenience type definition. typedef RegistrationCallback Superclass; }; //@} } // namespace cmtk #endif // #ifndef __cmtkProtocolCallback_h_included_ cmtk-3.3.1/libs/Registration/cmtkReformatVolume.cxx000066400000000000000000000451561276303427400224540ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2013 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4882 $ // // $LastChangedDate: 2013-09-27 15:16:36 -0700 (Fri, 27 Sep 2013) $ // // $LastChangedBy: torstenrohlfing $ // */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace cmtk { /** \addtogroup Registration */ //@{ ReformatVolume::ReformatVolume() : m_LowerThresholdReference( 0.0 ), m_LowerThresholdFloating( 0.0 ), m_UpperThresholdReference( 0.0 ), m_UpperThresholdFloating( 0.0 ), m_UsePaddingValue( false ), m_PaddingValue( 0.0 ), Interpolation( cmtk::Interpolators::LINEAR ), m_UserDataType( TYPE_NONE ), ReferenceVolume( NULL ), FloatingVolume( NULL ), m_AffineXform( NULL ), m_WarpXform( NULL ) { } void ReformatVolume::SetReferenceVolume ( const UniformVolume::SmartConstPtr& referenceVolume ) { this->ReferenceVolume = referenceVolume; } void ReformatVolume::SetFloatingVolume ( const UniformVolume::SmartConstPtr& floatingVolume ) { FloatingVolume = floatingVolume; } void ReformatVolume::SetAffineXform( const AffineXform::SmartPtr& affineXform ) { this->m_AffineXform = affineXform; } void ReformatVolume::SetWarpXform( const WarpXform::SmartPtr& warpXform ) { this->m_WarpXform = warpXform; } const UniformVolume::SmartPtr ReformatVolume::MakeTargetVolume() const { return UniformVolume::SmartPtr( ReferenceVolume->CloneGrid() ); } const UniformVolume::SmartPtr ReformatVolume::PlainReformat() { UniformVolume::SmartPtr targetVolume = this->MakeTargetVolume(); if ( targetVolume ) { Progress::Begin( 0, targetVolume->GetDims()[AXIS_Z], 1, "Volume reformatting" ); TypedArray::SmartPtr targetData( TypedArray::Create( FloatingVolume->GetData()->GetType(), targetVolume->GetNumberOfPixels() ) ); if ( this->m_UsePaddingValue ) { targetData->SetPaddingValue( this->m_PaddingValue ); } else { if ( FloatingVolume->GetData()->GetPaddingFlag() ) { targetData->SetPaddingValue( FloatingVolume->GetData()->GetPaddingValue() ); } } UniformVolumeInterpolatorBase::SmartPtr interpolator( this->CreateInterpolator( this->FloatingVolume ) ); Vector3D pFlt; const DataGrid::IndexType dims = targetVolume->GetDims(); size_t offset = 0; for ( int pZ = 0; pZ < dims[2]; ++pZ ) { Types::DataItem value = 0; const SplineWarpXform::SmartConstPtr& splineWarp = SplineWarpXform::SmartConstPtr::DynamicCastFrom( this->m_WarpXform ); if ( splineWarp ) { SplineWarpXformUniformVolume xformVolume( *(this->ReferenceVolume), splineWarp ); for ( int pY = 0; pYGetDataAt( pFlt, value ) ) targetData->Set( value, offset ); else targetData->SetPaddingAt( offset ); } } } else { for ( int pY = 0; pYm_AffineXform->Apply( ReferenceVolume->GetGridLocation( pX, pY, pZ ) ); if ( interpolator->GetDataAt( pFlt, value ) ) targetData->Set( value, offset ); else targetData->SetPaddingAt( offset ); } } } Progress::SetProgress( pZ ); } targetVolume->SetData( targetData ); } return targetVolume; } TypedArray::SmartPtr ReformatVolume::PlainReformat ( const int plane, TypedArray::SmartPtr& target, const size_t targetOffset ) { const DataGrid::IndexType& Dims = ReferenceVolume->GetDims(); const int DimsX = Dims[0], DimsY = Dims[1]; const int DataSize = DimsX * DimsY; TypedArray::SmartPtr result = target; if ( ! result ) { result = TypedArray::Create( FloatingVolume->GetData()->GetType(), DataSize ); if ( this->m_UsePaddingValue ) result->SetPaddingValue( this->m_PaddingValue ); } if ( ! result ) return result; Vector3D pMod; Types::DataItem value = 0; int offset = targetOffset; UniformVolumeInterpolatorBase::SmartPtr interpolator( this->CreateInterpolator( this->FloatingVolume ) ); const SplineWarpXform::SmartConstPtr splineWarp = SplineWarpXform::SmartConstPtr::DynamicCastFrom( this->m_WarpXform ); if ( splineWarp ) { const SplineWarpXformUniformVolume xformVolume( *(this->ReferenceVolume), splineWarp ); for ( int pY = 0; pYGetDataAt( pMod, value ) ) result->Set( value, offset ); else result->SetPaddingAt( offset ); } } } else { if ( ! this->m_AffineXform ) return result; for ( int pY = 0; pYm_AffineXform->Apply( ReferenceVolume->GetGridLocation( pX, pY, plane ) ); if ( interpolator->GetDataAt( pMod, value ) ) result->Set( value, offset ); else result->SetPaddingAt( offset ); } } } return result; } CMTK_THREAD_RETURN_TYPE ReformatVolume::GetTransformedReferenceGrey( void *const arg ) { GetTransformedReferenceTP* params = static_cast( arg ); TypedArray::SmartPtr dataArray = params->dataArray; const SplineWarpXform* splineXform = params->splineXform; const UniformVolumeInterpolatorBase* interpolator = params->referenceInterpolator; const Types::Coordinate* delta = params->delta; const Types::Coordinate* bbFrom = params->bbFrom; const DataGrid::IndexType& dims = params->dims; const Types::Coordinate minDelta = MathUtil::Min( 3, delta ); Types::DataItem value; UniformVolume::CoordinateVectorType u, v; v[2] = bbFrom[2]; size_t offset = 0; for ( int cz = 0; cz < dims[2]; ++cz, v[2] += delta[2] ) { if ( ! params->ThisThreadIndex ) Progress::SetProgress( cz ); v[1] = bbFrom[1]; for ( int cy = 0; cy < dims[1]; ++cy, v[1] += delta[1] ) { v[0] = bbFrom[0]; for ( int cx = 0; cx < dims[0]; ++cx, v[0] += delta[0], ++offset ) { const bool success = splineXform->ApplyInverse( v, u, 0.1 * minDelta ); if ( success ) { if ( interpolator->GetDataAt( u, value ) ) dataArray->Set( value, offset ); else dataArray->SetPaddingAt( offset ); } } } } return CMTK_THREAD_RETURN_VALUE; } CMTK_THREAD_RETURN_TYPE ReformatVolume::GetTransformedReferenceLabel( void *const arg ) { GetTransformedReferenceTP* params = static_cast( arg ); const ReformatVolume* thisObject = params->thisObject; TypedArray::SmartPtr dataArray = params->dataArray; const SplineWarpXform* splineXform = params->splineXform; const Types::Coordinate* delta = params->delta; const Types::Coordinate* bbFrom = params->bbFrom; const DataGrid::IndexType& dims = params->dims; const std::vector* xformList = params->xformList; const std::vector* volumeList = params->volumeList; const Types::Coordinate minDelta = MathUtil::Min( 3, delta ); UniformVolume::CoordinateVectorType u, v, xyz; std::vector probe( params->numberOfImages ); std::vector labelCount( params->maxLabel+1 ); xyz[2] = bbFrom[2]; size_t offset = 0; for ( int cz = 0; cz < dims[2]; ++cz, xyz[2] += delta[2] ) { if ( ! params->ThisThreadIndex ) Progress::SetProgress( cz ); xyz[1] = bbFrom[1]; for ( int cy = 0; cy < dims[1]; ++cy, xyz[1] += delta[1] ) { xyz[0] = bbFrom[0]; for ( int cx = 0; cx < dims[0]; ++cx, xyz[0] += delta[0], ++offset ) { const bool success = splineXform->ApplyInverse( xyz, v, 0.1 * minDelta ); u = v; unsigned int toIdx = 0; if ( success ) { bool valid = false; if ( params->IncludeReferenceData ) { valid = thisObject->ReferenceVolume->ProbeNoXform( probe[toIdx], v ); if ( valid ) ++toIdx; } for ( unsigned int img = 0; img < params->numberOfImages-1; ++img ) { v = (*xformList)[img]->Apply( u ); valid = (*volumeList)[img]->ProbeNoXform( probe[toIdx], v ); if ( valid ) ++toIdx; } } if ( toIdx && success ) { std::fill( labelCount.begin(), labelCount.end(), 0 ); for ( unsigned int idx = 0; idx < toIdx; ++idx ) { for ( unsigned int corner = 0; corner < 8; ++corner ) { labelCount[static_cast( probe[idx].Values[corner] )] += probe[idx].GetWeight( corner ); } } unsigned int winner = 0; Types::Coordinate winnerWeight = labelCount[0]; for ( int label = 1; label < params->maxLabel; ++label ) { if ( labelCount[label] > winnerWeight ) { winnerWeight = labelCount[label]; winner = label; } } dataArray->Set( static_cast( winner ), offset ); } else dataArray->SetPaddingAt( offset ); } } } return CMTK_THREAD_RETURN_VALUE; } UniformVolume* ReformatVolume::GetTransformedReferenceJacobianAvg ( const std::vector* xformList, Types::Coordinate *const volumeOffset, const bool includeReferenceData ) { const SplineWarpXform* splineXform = dynamic_cast( this->m_WarpXform.GetConstPtr() ); if ( ! splineXform ) { StdErr << "ERROR: ReformatVolume::GetTransformedReferenceJacobian supports spline warp only.\n"; return NULL; } // bounding box for reformatted volume. Types::Coordinate bbFrom[3], delta[3]; UniformVolume* result = this->CreateTransformedReference( bbFrom, delta, volumeOffset ); TypedArray::SmartPtr dataArray( TypedArray::Create( TYPE_FLOAT, result->GetNumberOfPixels() ) ); if ( this->m_UsePaddingValue ) dataArray->SetPaddingValue( this->m_PaddingValue ); result->SetData( dataArray ); const size_t numberOfThreads = Threads::GetNumberOfThreads(); std::vector params( numberOfThreads ); for ( size_t thr = 0; thr < numberOfThreads; ++thr ) { params[thr].thisObject = this; params[thr].ThisThreadIndex = thr; params[thr].NumberOfThreads = numberOfThreads; params[thr].dims = result->GetDims(); params[thr].bbFrom = bbFrom; params[thr].delta = delta; params[thr].splineXform = splineXform; params[thr].xformList = xformList; params[thr].dataArray = dataArray; params[thr].avgMode = MODE_MEAN; params[thr].IncludeReferenceData = includeReferenceData; } Threads::RunThreads( GetTransformedReferenceJacobianAvgThread, numberOfThreads, ¶ms[0] ); return result; } CMTK_THREAD_RETURN_TYPE ReformatVolume::GetTransformedReferenceJacobianAvgThread ( void *const arg ) { GetTransformedReferenceTP* params = static_cast( arg ); TypedArray::SmartPtr dataArray = params->dataArray; const SplineWarpXform* splineXform = params->splineXform; const Types::Coordinate* delta = params->delta; const Types::Coordinate* bbFrom = params->bbFrom; const DataGrid::IndexType& dims = params->dims; const std::vector* xformList = params->xformList; const Types::Coordinate minDelta = MathUtil::Min( 3, delta ); UniformVolume::CoordinateVectorType u, v, xyz; const size_t numberOfXforms = xformList->size(); std::vector xforms( numberOfXforms ); for ( unsigned int img = 0; img < numberOfXforms; ++img ) xforms[img] = (*xformList)[img]; const int czFrom = params->ThisThreadIndex * dims[2] / params->NumberOfThreads; const int czTo = std::min( dims[2], (1+params->ThisThreadIndex) * dims[2] / params->NumberOfThreads ); Vector values( params->IncludeReferenceData ? numberOfXforms+1 : numberOfXforms ); const size_t margin = numberOfXforms / 20; // margin for center 90% xyz[2] = bbFrom[2] + czFrom * delta[2]; size_t offset = czFrom * dims[0] * dims[1]; for ( int cz = czFrom; cz < czTo; ++cz, xyz[2] += delta[2] ) { if ( ! params->ThisThreadIndex ) Progress::SetProgress( cz ); xyz[1] = bbFrom[1]; for ( int cy = 0; cy < dims[1]; ++cy, xyz[1] += delta[1] ) { xyz[0] = bbFrom[0]; for ( int cx = 0; cx < dims[0]; ++cx, xyz[0] += delta[0], ++offset ) { const bool success = splineXform->ApplyInverse( xyz, v, 0.1 * minDelta ); u = v; if ( success ) { const Types::Coordinate refJacobian = splineXform->GetGlobalScaling() / splineXform->GetJacobianDeterminant( u ); switch ( params->avgMode ) { case MODE_MEAN: { // average Types::Coordinate sum = params->IncludeReferenceData ? 1.0 : 0.0; for ( unsigned int img = 0; img < numberOfXforms; ++img ) sum += xforms[img]->GetJacobianDeterminant( u ) / xforms[img]->GetGlobalScaling(); dataArray->Set( static_cast( refJacobian * sum / numberOfXforms ), offset ); break; } case MODE_MEDIAN: case MODE_ROBUST90: { for ( unsigned int img = 0; img < numberOfXforms; ++img ) values[img] = xforms[img]->GetJacobianDeterminant( u ) / xforms[img]->GetGlobalScaling(); if ( params->IncludeReferenceData ) values[numberOfXforms] = 1.0; values.Sort(); if ( params->avgMode == MODE_MEDIAN ) { // median if ( numberOfXforms & 1 ) dataArray->Set( static_cast( refJacobian * values[numberOfXforms/2+1] ), offset ); else dataArray->Set( static_cast( 0.5 * refJacobian * (values[numberOfXforms/2]+values[numberOfXforms/2+1]) ), offset ); } else { // robust average of center 90% average Types::Coordinate sum = 0; for ( unsigned int img = margin; img < numberOfXforms - margin; ++img ) sum += values[img]; dataArray->Set( static_cast( refJacobian * sum / ( numberOfXforms - 2 * margin ) ), offset ); } break; } } } else { dataArray->SetPaddingAt( offset ); } } } } return CMTK_THREAD_RETURN_VALUE; } UniformVolume* ReformatVolume::CreateTransformedReference ( Types::Coordinate *const bbFrom, Types::Coordinate *const delta, Types::Coordinate *const volumeOffset ) { // default: no offset output desired, // that means, reformat to original image domain UniformVolume::CoordinateVectorType bbTo; for ( unsigned int axis = 0; axis < 3; ++axis ) { bbFrom[axis] = 0; bbTo[axis] = this->ReferenceVolume->m_Size[axis]; } if ( volumeOffset ) { Vector3D u, v; for ( unsigned int z = 0; z < 2; ++z ) { if ( z ) u[AXIS_Z] = this->ReferenceVolume->m_Size[AXIS_Z]; else u[AXIS_Z] = 0; for ( unsigned int y = 0; y < 2; ++y ) { if ( y ) u[AXIS_Y] = this->ReferenceVolume->m_Size[AXIS_Y]; else u[AXIS_Y] = 0; for ( unsigned int x = 0; x < 2; ++x ) { if ( x ) u[AXIS_X] = this->ReferenceVolume->m_Size[AXIS_X]; else u[AXIS_X] = 0; v = this->m_WarpXform->Apply( u ); for ( unsigned int axis = 0; axis < 3; ++axis ) { bbFrom[axis] = std::min( bbFrom[axis], v[axis] ); bbTo[axis] = std::max( bbTo[axis], v[axis] ); } } } } // report new volume offset, if so desired by the caller for ( unsigned int axis = 0; axis < 3; ++axis ) volumeOffset[axis] = bbFrom[axis]; } UniformVolume::IndexType dims; for ( int dim = 0; dim < 3; ++dim ) { delta[dim] = this->ReferenceVolume->m_Delta[dim]; bbTo[dim] -= bbFrom[dim]; dims[dim] = 1 + static_cast( bbTo[dim] / delta[dim] ); } return new UniformVolume( dims, bbTo ); } UniformVolumeInterpolatorBase::SmartPtr ReformatVolume::CreateInterpolator ( const cmtk::Interpolators::InterpolationEnum interpolation, const UniformVolume::SmartConstPtr& volume ) { switch ( interpolation ) { default: case cmtk::Interpolators::LINEAR: { typedef UniformVolumeInterpolator TInterpolator; return TInterpolator::SmartPtr( new TInterpolator( *volume ) ); } case cmtk::Interpolators::NEAREST_NEIGHBOR: { typedef UniformVolumeInterpolator TInterpolator; return TInterpolator::SmartPtr( new TInterpolator( *volume ) ); } case cmtk::Interpolators::CUBIC: { typedef UniformVolumeInterpolator TInterpolator; return TInterpolator::SmartPtr( new TInterpolator( *volume ) ); } case cmtk::Interpolators::COSINE_SINC: { typedef UniformVolumeInterpolator< cmtk::Interpolators::CosineSinc<> > TInterpolator; return TInterpolator::SmartPtr( new TInterpolator( *volume ) ); } case cmtk::Interpolators::PARTIALVOLUME: { typedef UniformVolumeInterpolatorPartialVolume TInterpolator; return TInterpolator::SmartPtr( new TInterpolator( *volume ) ); } } return UniformVolumeInterpolatorBase::SmartPtr( NULL ); } UniformVolumeInterpolatorBase::SmartPtr ReformatVolume::CreateInterpolator ( const UniformVolume::SmartConstPtr& volume ) { return Self::CreateInterpolator( this->Interpolation, volume ); } } // end namespace cmtk #include "cmtkReformatVolume.txx" cmtk-3.3.1/libs/Registration/cmtkReformatVolume.h000066400000000000000000000265651276303427400221040ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2011, 2013 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4882 $ // // $LastChangedDate: 2013-09-27 15:16:36 -0700 (Fri, 27 Sep 2013) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkReformatVolume_h_included_ #define __cmtkReformatVolume_h_included_ #include #ifdef HAVE_VALUES_H # include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace cmtk { /** \addtogroup Registration */ //@{ /** Utility class for reformatting volume data. * This class takes two volume data sets, and affine and an (optional) local * deformation. It provides member functions to reformat from one of the * images a slice plane corresponding exactly to one of the original planes * from the respective other images. The class provides optional checkerboard filling * of areas with no valid original image data present. */ class ReformatVolume { public: /// This class. typedef ReformatVolume Self; /// Smart pointer to this class. typedef SmartPointer SmartPtr; /// Threshold for the reference image. cmtkGetSetMacroDefault(float,LowerThresholdReference,FLT_MIN); /// Threshold for the floating image. cmtkGetSetMacroDefault(float,LowerThresholdFloating,FLT_MIN); /// Threshold for the reference image. cmtkGetSetMacroDefault(float,UpperThresholdReference,FLT_MAX); /// Threshold for the floating image. cmtkGetSetMacroDefault(float,UpperThresholdFloating,FLT_MAX); /** Flag whether the PADDING value is defined by client. */ cmtkGetSetMacroDefault(bool,UsePaddingValue,true); /** PADDING value as defined by client. */ cmtkGetSetMacro(Types::DataItem,PaddingValue); /// Default constructor. ReformatVolume(); /// Set interpolation mode. void SetInterpolation( const cmtk::Interpolators::InterpolationEnum interpolation ) { Interpolation = interpolation; } /// Create interpolator object for given volume according to interpolation mode set in this object. UniformVolumeInterpolatorBase::SmartPtr CreateInterpolator( const UniformVolume::SmartConstPtr& volume ); /// Create interpolator object for given volume according to interpolation mode set in this object. static UniformVolumeInterpolatorBase::SmartPtr CreateInterpolator( const cmtk::Interpolators::InterpolationEnum interpolation, const UniformVolume::SmartConstPtr& volume ); /// Set user-defined data type. void SetUserDataType( const ScalarDataType dataType ) { this->m_UserDataType = dataType; } /// Set the reference volume for reformatting. void SetReferenceVolume( const UniformVolume::SmartConstPtr& referenceVolume ); /// Set the floating (transformed) volume for reformatting. void SetFloatingVolume( const UniformVolume::SmartConstPtr& floatingVolume ); /// Set affine transformation to be applied to the floating volume. void SetAffineXform( const AffineXform::SmartPtr& affineXform ); /// Set the local deformation to be applied to the reference grid. void SetWarpXform( const WarpXform::SmartPtr& warpXform ); /** Plain reformatting. */ const UniformVolume::SmartPtr PlainReformat(); /** Plain reformatting of a single plane. * This function reformats the floating data to a plane spatially identical * to the given plane in the reference image. This is useful for interactive * reformatting, where we want a single plane reformatted as fast as possible. */ TypedArray::SmartPtr PlainReformat( const int plane, TypedArray::SmartPtr& target = TypedArray::SmartPtr::Null(), const size_t targetOffset = 0 ); /// Constants for transformation field mode. typedef enum { MODE_MEAN, MODE_MEDIAN, MODE_ROBUST90 } AveragingMode; /// Apply forward warp transformation to reference volume. UniformVolume* GetTransformedReference ( const std::vector* xformList, std::vector* volumeList, Types::Coordinate *const volumeOffset = NULL, const bool includeReferenceData = true ); /// Average Jacobians into deformed reference coordinate system. UniformVolume* GetTransformedReferenceJacobianAvg ( const std::vector* xformList, Types::Coordinate *const volumeOffset = NULL, const bool includeReferenceData = true ); /// Complex reformat using target data as the mask. template static TypedArray::SmartPtr ReformatMasked ( const UniformVolume* target, const cmtk::XformList& targetToRef, const cmtk::XformList& refToFloat, Fct& fct, const UniformVolume* floating = NULL, TInterpolator& interpolator = TInterpolator::Null ); /// Complex reformat without mask. template static TypedArray::SmartPtr ReformatUnmasked ( const UniformVolume* target, const cmtk::XformList& targetToRef, const cmtk::XformList& refToFloat, Fct& fct, const UniformVolume* floating = NULL, TInterpolator& interpolator = TInterpolator::Null ); /// Constants for extended reformatting mode. typedef enum { REFORMAT_PLAIN, REFORMAT_JACOBIAN } Mode; /// Function class for plain reformating. class Plain { public: /** Constructor. */ Plain( const ScalarDataType dataType = TYPE_NONE ) : DataType( dataType ), PaddingValue( 0 ), UsePaddingValue( false ) {}; /// Set output padding value. void SetPaddingValue( const Types::DataItem paddingValue ) { this->PaddingValue = paddingValue; this->UsePaddingValue = true; } /** Query operator. */ template bool operator()( Types::DataItem& value, const Vector3D& inRef, const cmtk::XformList& refToFloat, TInterpolatorInstantiationPtr& interpolator ); /// Return preferred data type for reformatted data. ScalarDataType GetDataType( const UniformVolume& fltImage ) const { if ( this->DataType != TYPE_NONE ) return this->DataType; else return fltImage.GetData()->GetType(); } protected: /** Data type for reformated image. */ ScalarDataType DataType; public: /** Padding value for output. */ Types::DataItem PaddingValue; /** Use padding value. */ bool UsePaddingValue; }; /// Function class for reformating of a Jacobian map. class Jacobian : /// Inherit from plain reformatter for interpolation etc. public ReformatVolume::Plain { public: /** Constructor. */ Jacobian( const ScalarDataType dataType = TYPE_FLOAT, const bool correctGlobalScale = true ) : Plain( dataType ), CorrectGlobalScale( correctGlobalScale ) {}; /** Query operator. */ template bool operator()( Types::DataItem& value, const Vector3D& inRef, const cmtk::XformList& refToFloat, TInterpolatorInstantiationPtr& interpolator ); /** Return preferred data type for reformatted data. */ ScalarDataType GetDataType( const UniformVolume& ) const { if ( this->DataType != TYPE_NONE ) return this->DataType; else return TYPE_FLOAT; } private: /** Flag for correction of global scale. * If this is true, all Jacobian determinants are divided (forward * transformation) or multiplied (inverse transformation) by the * global affine scale factor of the transformation. */ bool CorrectGlobalScale; }; private: /// Interpolation mode. cmtk::Interpolators::InterpolationEnum Interpolation; /// User-selected data type for reformatted data. ScalarDataType m_UserDataType; /// Pointer to the reference volume. UniformVolume::SmartConstPtr ReferenceVolume; /// Pointer to the floating volume. UniformVolume::SmartConstPtr FloatingVolume; /// Make target volume matching (reoriented) reference volume. const UniformVolume::SmartPtr MakeTargetVolume() const; /// Pointer to the affine transformation of the floating volume. AffineXform::SmartConstPtr m_AffineXform; /// Pointer to the local deformation of the reference grid. WarpXform::SmartConstPtr m_WarpXform; class GetTransformedReferenceTP : public ThreadParameters { public: GetTransformedReferenceTP() : splineXform( NULL ), m_Offset( 0 ), m_Stride( 1 ), delta( NULL ), bbFrom( NULL ), numberOfImages( 0 ), xformList( NULL ), volumeList( NULL ), interpolatorList( NULL ), referenceInterpolator( NULL ), maxLabel( 0 ), avgMode( MODE_MEAN ), IncludeReferenceData( false ) {} TypedArray::SmartPtr dataArray; const SplineWarpXform* splineXform; DataGrid::IndexType dims; /// Offset for blockwise distributed computation. size_t m_Offset; size_t m_Stride; const Types::Coordinate* delta; const Types::Coordinate* bbFrom; unsigned int numberOfImages; const std::vector* xformList; const std::vector* volumeList; const std::vector* interpolatorList; const UniformVolumeInterpolatorBase* referenceInterpolator; int maxLabel; AveragingMode avgMode; bool IncludeReferenceData; }; /// Apply forward warp transformation to grey-level reference volume. static CMTK_THREAD_RETURN_TYPE GetTransformedReferenceGrey( void *const arg ); /// Apply forward warp transformation to average grey-level reference volume. static CMTK_THREAD_RETURN_TYPE GetTransformedReferenceGreyAvg( void *const arg ); /// Apply forward warp transformation to label reference volume. static CMTK_THREAD_RETURN_TYPE GetTransformedReferenceLabel( void *const arg ); /// Thread function: average Jacobians into deformed reference system. static CMTK_THREAD_RETURN_TYPE GetTransformedReferenceJacobianAvgThread( void *const arg ); /// Create uniform volume with correct dimensions for reformatted reference. UniformVolume* CreateTransformedReference( Types::Coordinate *const bbFrom, Types::Coordinate *const delta, Types::Coordinate *const volumeOffset = NULL ); }; //@} } // namespace cmtk #include "cmtkReformatVolumeReformat.txx" #include "cmtkReformatVolumePlain.txx" #include "cmtkReformatVolumeJacobian.txx" #endif // #ifndef __cmtkReformatVolume_h_included_ cmtk-3.3.1/libs/Registration/cmtkReformatVolume.txx000066400000000000000000000162231276303427400224660ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2013 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4907 $ // // $LastChangedDate: 2013-10-01 11:49:29 -0700 (Tue, 01 Oct 2013) $ // // $LastChangedBy: torstenrohlfing $ // */ namespace cmtk { /** \addtogroup Registration */ //@{ UniformVolume* ReformatVolume::GetTransformedReference ( const std::vector* xformList, std::vector* volumeList, Types::Coordinate *const volumeOffset, const bool includeReferenceData ) { UniformVolume* result = NULL; unsigned int numberOfImages = 0; std::vector interpolatorList; interpolatorList.push_back( this->CreateInterpolator( this->ReferenceVolume ) ); if ( volumeList ) { numberOfImages = 1 + volumeList->size(); for ( unsigned int img = 0; img < numberOfImages-1; ++img ) { interpolatorList.push_back( this->CreateInterpolator( (*volumeList)[img] ) ); } } const SplineWarpXform* splineXform = dynamic_cast( this->m_WarpXform.GetConstPtr() ); if ( ! splineXform ) { StdErr << "ERROR: ReformatVolume::GetTransformedReference supports spline warp only.\n"; return NULL; } DataClass dataClass = ReferenceVolume->GetData()->GetDataClass(); int maxLabel = 0; if ( dataClass == DATACLASS_LABEL ) { const Types::DataItemRange rangeRef = ReferenceVolume->GetData()->GetRange(); maxLabel = static_cast( rangeRef.m_UpperBound ); if ( volumeList ) { for ( unsigned int img = 0; img < numberOfImages-1; ++img ) { const Types::DataItemRange rangeFlt = (*volumeList)[img]->GetData()->GetRange(); maxLabel = std::max( maxLabel, static_cast( rangeFlt.m_UpperBound ) ); } } } // bounding box for reformatted volume. Types::Coordinate bbFrom[3], delta[3]; result = this->CreateTransformedReference( bbFrom, delta, volumeOffset ); const ScalarDataType dtype = (this->m_UserDataType != TYPE_NONE) ? this->m_UserDataType : ReferenceVolume->GetData()->GetType(); TypedArray::SmartPtr dataArray( TypedArray::Create( dtype, result->GetNumberOfPixels() ) ); if ( this->m_UsePaddingValue ) dataArray->SetPaddingValue( this->m_PaddingValue ); result->SetData( dataArray ); const size_t numberOfThreads = Threads::GetNumberOfThreads(); std::vector params( numberOfThreads ); for ( size_t thr = 0; thr < numberOfThreads; ++thr ) { params[thr].thisObject = this; params[thr].ThisThreadIndex = thr; params[thr].NumberOfThreads = numberOfThreads; params[thr].dims = result->GetDims(); params[thr].bbFrom = bbFrom; params[thr].delta = delta; params[thr].splineXform = splineXform; params[thr].numberOfImages = numberOfImages; params[thr].xformList = xformList; params[thr].volumeList = volumeList; params[thr].interpolatorList = &interpolatorList; params[thr].dataArray = dataArray; params[thr].maxLabel = maxLabel; params[thr].IncludeReferenceData = includeReferenceData; } switch ( dataClass ) { default: case DATACLASS_GREY: { if ( xformList && !xformList->empty() ) Threads::RunThreads( GetTransformedReferenceGreyAvg, numberOfThreads, ¶ms[0] ); else Threads::RunThreads( GetTransformedReferenceGrey, numberOfThreads, ¶ms[0] ); } break; case DATACLASS_LABEL: { Threads::RunThreads( GetTransformedReferenceLabel, numberOfThreads, ¶ms[0] ); } break; } return result; } CMTK_THREAD_RETURN_TYPE ReformatVolume::GetTransformedReferenceGreyAvg( void *const arg ) { GetTransformedReferenceTP* params = static_cast( arg ); TypedArray::SmartPtr dataArray = params->dataArray; const SplineWarpXform* splineXform = params->splineXform; const Types::Coordinate* delta = params->delta; const Types::Coordinate* bbFrom = params->bbFrom; const DataGrid::IndexType& dims = params->dims; const std::vector* xformList = params->xformList; const std::vector* interpolatorList = params->interpolatorList; Types::Coordinate minDelta = std::min( delta[0], std::min( delta[1], delta[2] ) ); std::vector value( params->numberOfImages ); std::vector xforms( params->numberOfImages-1 ); for ( unsigned int img = 0; img < params->numberOfImages-1; ++img ) { xforms[img] = (*xformList)[img]; } int cx = params->ThisThreadIndex % dims[0]; int cy = (params->ThisThreadIndex / dims[0]) % dims[1]; int cz = params->ThisThreadIndex / (dims[0] * dims[1]) ; UniformVolume::CoordinateVectorType xyz; xyz[0] = bbFrom[0] + cx * delta[0]; xyz[1] = bbFrom[1] + cy * delta[1]; xyz[2] = bbFrom[2] + cz * delta[2]; const size_t numberOfPixels = dims[0] * dims[1] * dims[2]; const size_t statusUpdateIncrement = std::max( 1, numberOfPixels / 100 ); UniformVolume::CoordinateVectorType u, v; for ( size_t offset = params->ThisThreadIndex; offset < numberOfPixels; offset += params->NumberOfThreads ) { if ( ! params->ThisThreadIndex && ! (offset % statusUpdateIncrement) ) Progress::SetProgress( offset ); const bool success = splineXform->ApplyInverse( xyz, v, 0.1 * minDelta ); u = v; unsigned int toIdx = 0; if ( success ) { if ( params->IncludeReferenceData ) { if ( (*interpolatorList)[0]->GetDataAt( v, value[toIdx] ) ) ++toIdx; } for ( unsigned int img = 0; img < params->numberOfImages-1; ++img ) { v = xforms[img]->Apply( u ); if ( (*interpolatorList)[img+1]->GetDataAt( v, value[toIdx] ) ) ++toIdx; } } if ( toIdx && success ) { Types::DataItem avg = value[0]; for ( unsigned int idx = 1; idx < toIdx; ++idx ) avg += value[idx]; dataArray->Set( avg / toIdx, offset ); } else dataArray->SetPaddingAt( offset ); cx += params->NumberOfThreads; if ( cx >= dims[0] ) { cy += cx / dims[0]; cx %= dims[0]; if ( cy >= dims[1] ) { cz += cy / dims[1]; cy %= dims[1]; xyz[2] = bbFrom[2] + cz * delta[2]; } xyz[1] = bbFrom[1] + cy * delta[1]; } xyz[0] = bbFrom[0] + cx * delta[0]; } return CMTK_THREAD_RETURN_VALUE; } } // namespace cmtk cmtk-3.3.1/libs/Registration/cmtkReformatVolumeJacobian.txx000066400000000000000000000026571276303427400241230ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2010 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 2398 $ // // $LastChangedDate: 2010-10-05 14:54:37 -0700 (Tue, 05 Oct 2010) $ // // $LastChangedBy: torstenrohlfing $ // */ #include namespace cmtk { /** \addtogroup Registration */ //@{ template bool ReformatVolume ::Jacobian::operator()( Types::DataItem& value, const Vector3D& inRef, const XformList& refToFloat, TInterpolator& ) { return refToFloat.GetJacobian( inRef, value, this->CorrectGlobalScale ); } } // namespace cmtk cmtk-3.3.1/libs/Registration/cmtkReformatVolumePlain.txx000066400000000000000000000030571276303427400234530ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2010 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 2220 $ // // $LastChangedDate: 2010-08-16 15:53:13 -0700 (Mon, 16 Aug 2010) $ // // $LastChangedBy: torstenrohlfing $ // */ #include #include namespace cmtk { /** \addtogroup Registration */ //@{ template bool ReformatVolume::Plain::operator() ( Types::DataItem& value, const Vector3D& inRef, const XformList& refToFloat, TInterpolator& interpolator ) { Vector3D inFlt( inRef ); if ( ! refToFloat.ApplyInPlace( inFlt ) ) return false; return interpolator->GetDataAt( inFlt, value ); } } // namespace cmtk cmtk-3.3.1/libs/Registration/cmtkReformatVolumeReformat.txx000066400000000000000000000102641276303427400241650ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2011, 2014 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 5238 $ // // $LastChangedDate: 2014-03-18 12:05:27 -0700 (Tue, 18 Mar 2014) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifdef _OPENMP # include #endif namespace cmtk { /** \addtogroup Registration */ //@{ template TypedArray::SmartPtr ReformatVolume::ReformatMasked ( const UniformVolume* target, const cmtk::XformList& targetToRef, const cmtk::XformList& refToFloat, Fct& fct, const UniformVolume* floating, TInterpolator& interpolator ) { const DataGrid::IndexType& dims = target->GetDims(); TypedArray::SmartPtr result = TypedArray::Create( fct.GetDataType( *floating ), target->GetNumberOfPixels() ); if ( fct.UsePaddingValue ) result->SetPaddingValue( fct.PaddingValue ); // If there is floating image data (may not be, for Jacobian mapping etc.), then copy its data class if ( floating ) result->SetDataClass( floating->GetData()->GetDataClass() ); const TypedArray* targetData = target->GetData(); Progress::Begin( 0, dims[2], 1, "Volume reformatting" ); #pragma omp parallel for for ( int z = 0; z < dims[2]; z++ ) { Vector3D vRef; Types::DataItem value, targetValue; size_t offset = z * dims[0] * dims[1]; #ifdef _OPENMP if ( ! omp_get_thread_num() ) #endif Progress::SetProgress( z ); for ( int y = 0; y < dims[1]; y++ ) { for ( int x = 0; x < dims[0]; x++, offset++ ) { if ( !targetData || (targetData->Get( targetValue, offset ) && (targetValue != 0))) { vRef = target->GetGridLocation( x, y, z ); if ( targetToRef.ApplyInPlace( vRef ) && fct( value, vRef, refToFloat, interpolator ) ) { result->Set( value, offset ); } else { result->SetPaddingAt( offset ); } } else { result->SetPaddingAt( offset ); } } } } Progress::Done(); return result; } template TypedArray::SmartPtr ReformatVolume::ReformatUnmasked ( const UniformVolume* target, const cmtk::XformList& targetToRef, const cmtk::XformList& refToFloat, Fct& fct, const UniformVolume* floating, TInterpolator& interpolator ) { const DataGrid::IndexType& dims = target->GetDims(); TypedArray::SmartPtr result = TypedArray::Create( fct.GetDataType( *floating ), target->GetNumberOfPixels() ); if ( fct.UsePaddingValue ) result->SetPaddingValue( fct.PaddingValue ); // If there is floating image data (may not be, for Jacobian mapping etc.), then copy its data class if ( floating ) result->SetDataClass( floating->GetData()->GetDataClass() ); Progress::Begin( 0, dims[2], 1, "Volume reformatting" ); #pragma omp parallel for for ( int z = 0; z < dims[2]; z++ ) { Vector3D vRef; Types::DataItem value; size_t offset = z * dims[0] * dims[1]; #ifdef _OPENMP if ( ! omp_get_thread_num() ) #endif Progress::SetProgress( z ); for ( int y = 0; y < dims[1]; y++ ) { for ( int x = 0; x < dims[0]; x++, offset++ ) { vRef = target->GetGridLocation( x, y, z ); if ( targetToRef.ApplyInPlace( vRef ) && fct( value, vRef, refToFloat, interpolator ) ) { result->Set( value, offset ); } else { result->SetPaddingAt( offset ); } } } } Progress::Done(); return result; } } // namespace cmtk cmtk-3.3.1/libs/Registration/cmtkRegistrationCallback.cxx000066400000000000000000000047111276303427400235640ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2010 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4918 $ // // $LastChangedDate: 2013-10-03 13:01:32 -0700 (Thu, 03 Oct 2013) $ // // $LastChangedBy: torstenrohlfing $ // */ #include #include #include #include namespace cmtk { /** \addtogroup Registration */ //@{ /// Flag that is set upon SIGINT signal. static bool InterruptSignalReceived; RegistrationCallback::RegistrationCallback() { #ifndef DEBUG InterruptSignalReceived = false; #ifndef _MSC_VER signal( SIGINT, cmtkRegistrationCallbackDispatchSIGINT ); #endif #endif } RegistrationCallback::~RegistrationCallback() { #ifndef DEBUG # ifdef HAVE_SIGRELSE sigrelse( SIGINT ); # endif #endif } CallbackResult RegistrationCallback::ExecuteWithData ( const CoordinateVector&, const double ) { return InterruptSignalReceived ? CALLBACK_INTERRUPT : CALLBACK_OK; } CallbackResult RegistrationCallback::Execute () { return InterruptSignalReceived ? CALLBACK_INTERRUPT : CALLBACK_OK; } void RegistrationCallback::Comment ( const char* ) { return; } } // namespace cmtk void cmtkRegistrationCallbackDispatchSIGINT( int sig ) { if ( cmtk::InterruptSignalReceived ) { cmtk::StdErr.printf( "Received repeated INT signal... exiting.\n" ); exit( 3 ); } #ifndef DEBUG cmtk::InterruptSignalReceived = true; #ifndef _MSC_VER signal( sig, cmtkRegistrationCallbackDispatchSIGINT ); #endif #endif cmtk::StdErr.printf( "Received INT (%d) signal... preparing exit. Press Ctrl-C again to abort immediately.\n", sig ); } cmtk-3.3.1/libs/Registration/cmtkRegistrationCallback.h000066400000000000000000000053141276303427400232110ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2009, 2013 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4918 $ // // $LastChangedDate: 2013-10-03 13:01:32 -0700 (Thu, 03 Oct 2013) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkRegistrationCallback_h_included_ #define __cmtkRegistrationCallback_h_included_ #include #include #include #include #include namespace cmtk { /** \addtogroup Registration */ //@{ /// Status code returned by Execute() methods. typedef enum { /// Everything okay; continue as usual. CALLBACK_OK = 0, /// User requests interrupt of operation. CALLBACK_INTERRUPT = 1, /// Interrupt generated by timeout. CALLBACK_TIMEOUT = 2, /// Something went wrong. CALLBACK_FAILED = 3 } CallbackResult; /** Generic callback class. * Callbacks define user specific actions during optimization, i.e. update of * progress indicators, protocoling, etc. This particular class is a "dummy" * callback, providing a common interface but taking no actions on any * member function calls. */ class RegistrationCallback { public: /// SMart pointer. typedef SmartPointer SmartPtr; /// Interface: Execute callback action virtual CallbackResult ExecuteWithData( const CoordinateVector& v, const double metric ); /// Execute callback action without interim result. virtual CallbackResult Execute(); /// Notify callback of an annotation. virtual void Comment ( const char* comment = NULL ); /// Default constructor. RegistrationCallback(); /// Virtual destructor. virtual ~RegistrationCallback(); }; //@} } // namespace cmtk /// Handler function for SIGINT interrupt signal. extern "C" void cmtkRegistrationCallbackDispatchSIGINT( int sig ); #endif // #ifndef __cmtkRegistrationCallback_h_included_ cmtk-3.3.1/libs/Registration/cmtkRegistrationJointHistogram.cxx000066400000000000000000000037211276303427400250310ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2010 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 2311 $ // // $LastChangedDate: 2010-08-26 14:41:39 -0700 (Thu, 26 Aug 2010) $ // // $LastChangedBy: torstenrohlfing $ // */ #include "cmtkRegistrationJointHistogram.h" namespace cmtk { /** \addtogroup Registration */ //@{ template RegistrationJointHistogram::RegistrationJointHistogram ( const UniformVolume* refVolume, const UniformVolume* fltVolume, const unsigned int numBinsX, const unsigned int numBinsY, const Types::DataItemRange& boundsX, const Types::DataItemRange& boundsY ) : #ifdef CMTK_PVI_HISTOGRAMS JointHistogram(), #else JointHistogram(), #endif VoxelMatchingMetric( refVolume, fltVolume, false /* initData */ ) { this->Resize( this->DataX.Init( refVolume, numBinsX, boundsX ), this->DataY.Init( fltVolume, numBinsY, boundsY ) ); } // instantiate required templates template class RegistrationJointHistogram; template class RegistrationJointHistogram; } // namespace cmtk cmtk-3.3.1/libs/Registration/cmtkRegistrationJointHistogram.h000066400000000000000000000106511276303427400244560ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2011 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 2752 $ // // $LastChangedDate: 2011-01-17 11:33:31 -0800 (Mon, 17 Jan 2011) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkRegistrationJointHistogram_h_included_ #define __cmtkRegistrationJointHistogram_h_included_ #include #include #include #include #ifndef CMTK_HISTOGRAM_AUTOBINS /// Constant for number of bins to be determined automatically. #define CMTK_HISTOGRAM_AUTOBINS 0 #endif namespace cmtk { /** \addtogroup Registration */ //@{ /** 2-D histogram for entropy-based image similarity measures. */ template class RegistrationJointHistogram : /// Inherit histogram with integral bins. public JointHistogram, /// Inherit from generic voxel metric with internal byte data. public VoxelMatchingMetric { public: /// This class. typedef RegistrationJointHistogram Self; /// Smart pointer. typedef SmartPointer SmartPtr; /** Constructor. *\param refVolume The reference (fixed) volume. *\param fltVolume The floating (transformed) volume. *\param numBinsX The desired number of bins to classify the * reference data. If this parameter is zero (default), a suitable value * is automatically determined. *\param numBinsY The desired number of bins to classify the * floating data. If this parameter is zero (default), a suitable value * is automatically determined. *\param boundsX Value range for the X data distribution. Values outside this range will be assigned to the first and last histogram bins, respectively. *\param boundsY Value range for the Y data distribution. Values outside this range will be assigned to the first and last histogram bins, respectively. */ RegistrationJointHistogram ( const UniformVolume* refVolume, const UniformVolume* fltVolume, const unsigned int numBinsX = CMTK_HISTOGRAM_AUTOBINS, const unsigned int numBinsY = CMTK_HISTOGRAM_AUTOBINS, const Types::DataItemRange& boundsX = Types::DataItemRange( -HUGE_VAL, HUGE_VAL ), const Types::DataItemRange& boundsY = Types::DataItemRange( -HUGE_VAL, HUGE_VAL ) ); /** Continue incremental calculation by fractional voxel index. * For a given pair of reference and floating sample, the computation * proceeds. This means the bin counters for both given samples are * incremented. Important is that for increased efficiency, only the indices * of the samples in the original volume data are given. The bin indices of * the respective data values are then retrieved from the pre-calculated raw * byte array (see InitDataset). *\param refIdx Index of the current reference data sample. *\param fltIdx Index of the current floating data sample. *\param frac Fractional voxel coordinate of the probed floating data * value. */ inline void Proceed( const size_t refIdx, const size_t fltIdx, const Types::Coordinate* frac ) { this->Increment( this->GetSampleX( refIdx ), this->GetSampleY( fltIdx, frac ) ); } /// Add another metric object, e.g., from distributed computation. void AddMetric ( const Self& other ) { this->AddJointHistogram( other ); } /// Remove another metric, e.g., to undo results from an ROI in local computation. void RemoveMetric ( const Self& other ) { this->RemoveJointHistogram( other ); } }; //@} } // namespace cmtk #endif // #ifndef __cmtkRegistrationJointHistogram_h_included_ cmtk-3.3.1/libs/Registration/cmtkScalarImageSimilarityRMI.cxx000066400000000000000000000102651276303427400242650ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2010 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 3517 $ // // $LastChangedDate: 2011-10-27 12:42:15 -0700 (Thu, 27 Oct 2011) $ // // $LastChangedBy: torstenrohlfing $ // */ #include "cmtkScalarImageSimilarity.h" #include namespace cmtk { /** \addtogroup Registration */ //@{ ScalarImageSimilarity::ReturnType ScalarImageSimilarity::GetRegionalMutualInformation ( const ScalarImage* image0, const ScalarImage* image1, const int radius ) { // printf("REGIONAL MUTUAL INFORMATION\n"); const ScalarImage::IndexType& dims = image0->GetDims(); int nrows = dims[1]; int ncols = dims[0]; int d = 2*radius+1; int N = (ncols-2*radius)*(nrows-2*radius); int d2 = d*d; int dim = 2*d2; Types::DataItem* pts = Memory::ArrayC::Allocate( N*dim ); Types::DataItem tmp1, tmp2; const TypedArray *data1 = image0->GetPixelData(); const TypedArray *data2 = image1->GetPixelData(); int idx,nidx,lidx; nidx = 0; idx = 0; lidx = 0; // printf("creating neighborhood...\n"); // CREATE NEIGHBORHOOD VECTORS for (int i=radius; iGet(tmp1,idx); data2->Get(tmp2,idx); pts[lidx*N + nidx] = tmp1; pts[(lidx+d2)*N + nidx] = tmp2; lidx++; } } nidx++; } } // printf("done.\n"); // SUBTRACT MEAN // printf("subtracting mean...\n"); std::vector mean(dim,0.0); for (int i=0; i cM( dim, dim ); ScalarImageSimilarity::ReturnType sum; int iN, jN; for (int i=0; i mcM( dim/2, dim/2 ); // image0's bloc for (int i=0; i. // // $Revision: 3975 $ // // $LastChangedDate: 2012-03-06 15:04:42 -0800 (Tue, 06 Mar 2012) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkScaleHistogramValueTrait_h_included_ #define __cmtkScaleHistogramValueTrait_h_included_ #include namespace cmtk { /** \addtogroup Registration */ //@{ /// Histogram kernel scaling traits. template class ScaleHistogramValueTrait { public: /// In general return original value as scaled value. static T Scale( const Types::DataItem value ) { return value; } }; template<> class ScaleHistogramValueTrait { public: /// For integer bins, scale with factor 256 for added resolution. static int Scale( const Types::DataItem value ) { return static_cast( 256 * value ); } }; template<> class ScaleHistogramValueTrait { public: /// For integer bins, scale with factor 256 for added resolution. static unsigned int Scale( const Types::DataItem value ) { return static_cast( 256 * value ); } }; //@} } // namespace cmtk #endif cmtk-3.3.1/libs/Registration/cmtkSearchTrace.h000066400000000000000000000136701276303427400213120ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2011 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 3517 $ // // $LastChangedDate: 2011-10-27 12:42:15 -0700 (Thu, 27 Oct 2011) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkSearchTrace_h_included_ #define __cmtkSearchTrace_h_included_ #include #include namespace cmtk { /** \addtogroup Registration */ //@{ /** Class for traces in the optimization search space. * An object of this class runs a list of those locations in search space that * have already been visited during the optimization. This may save evaluations * of the target function when these locations are approached again later in * the optimum search. The type of relative vectors is defined by a template * argument R. For one step size only, the default setting of "short", even * "signed char" may be enough. To keep track of multiresolution schemes, * however, we shall need to use "float" or "double". */ template class SearchTrace { private: typedef struct _TraceListEntry { /// Vector pointing to a relative location that has already been visited. R *RelativePosition; /// Value of the target function that was found there. double FunctionValue; /// Pointer to the next entry in the linked list. struct _TraceListEntry *Next; } TraceListEntry; /** Dimension of the search space. */ int DOF; /** Pointer to the first element of the list of visited locations. */ TraceListEntry *List; /** Compare an item from the track list to a given location. * As with every move in the search space, relative locations of the * previously seen samples are updated, all components of the tested * location must be zero except for the one giving the current search * direction. *\param entry The entry in the track list to be tested. *\param dir Direction, i.e. index of the parameter, in which we are moving. *\param step Size of the intended step. *\return 1, if the list entry pointed to by "entry" is the location we * would be in when making the step defined by "dir" and "step", 0 otherwise. */ int IsHit ( const TraceListEntry* entry, const int dir, const R step ) const { for ( int idx=0; idxRelativePosition[idx] && ( (dir != idx) || (entry->RelativePosition[idx] != step) ) ) return 0; return 1; } public: /** Constructor. * Set dimension of search space and initialize trace list. */ SearchTrace ( const int _DOF ) { DOF = _DOF; List = NULL; } /** Destructor. * Call Clear() to remove list from memory. */ ~SearchTrace () { Clear(); } /** Add a location to the trace list. *\param value The value of the target function at the location to be * added to the list. *\param dir Direction of the location to add with respect to the current * position in search space. This is the index of the parameter we are * modifying. *\param step Size of the step, ie. distance of the new location from the current * position in search space. */ void Add ( const double value, const int dir = 0, const R step = 0 ) { TraceListEntry *add = new TraceListEntry; add->RelativePosition = Memory::ArrayC::Allocate( DOF ); memset( add->RelativePosition, 0, sizeof(R) ); add->RelativePosition[dir] += step; add->FunctionValue = value; add->Next = List; List = add; } /** Get a previously visited location from the list. *\param value This reference is used to return the target function's value * at the location that was asked for. This value is only valid, if the * location was in the list. In case the function returns 0, value is * undefined. *\param dir Direction in search space towards the location we ask for. *\param step Size of the step to make in the given direction. *\return 1 if the desired location was in the list, 0 otherwise. */ int Get ( double& value, const int dir = 0, const R step = 0 ) const { TraceListEntry *cursor = List; while ( cursor ) { if ( IsHit( cursor, dir, step ) ) { value = cursor->FunctionValue; return 1; } cursor = cursor ->Next; } return 0; } /** Move current position in search space. * All entries in the table of visited positions are modified accordingly to * keep their relative positions up-to-date. *\param dir Parameter modified to do the move. *\param step Size of the update step in the direction defined by 'dir'. */ void Move ( const int dir, const R step ) { TraceListEntry *cursor = List; while ( cursor ) { cursor->RelativePosition[dir] -= step; cursor = cursor ->Next; } } /** Clear list from memory. * All list entries as well as the location vectors stored in them are * deleted and the list pointer is reset to NULL. */ void Clear () { while ( List ) { TraceListEntry *save = List->Next; Memory::ArrayC::Delete( List->RelativePosition ); delete List; List = save; } } }; //@} } // namespace cmtk #endif // #ifndef __cmtkSearchTrace_h_included_ cmtk-3.3.1/libs/Registration/cmtkSplineWarpCongealingFunctional.cxx000066400000000000000000000176731276303427400256060ustar00rootroot00000000000000/* // // Copyright 2016 Google, Inc. // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2013 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 5412 $ // // $LastChangedDate: 2016-01-16 15:09:52 -0800 (Sat, 16 Jan 2016) $ // // $LastChangedBy: torstenrohlfing $ // */ #include "cmtkSplineWarpCongealingFunctional.h" #include #include #include #include #include #include #include namespace cmtk { /** \addtogroup Registration */ //@{ void SplineWarpCongealingFunctional ::SetTemplateGrid ( UniformVolume::SmartPtr& templateGrid, const Types::GridIndexType downsample, const bool useTemplateData ) { this->Superclass::SetTemplateGrid( templateGrid, downsample, useTemplateData ); // clear thread storage because we need to re-initialize these. this->m_StaticThreadStorage.resize(0); } void SplineWarpCongealingFunctional ::InitializeXformsFromAffine ( const Types::Coordinate gridSpacing, std::vector initialAffineXformsVector, const bool exactSpacing ) { this->Superclass::InitializeXformsFromAffine( gridSpacing, initialAffineXformsVector, exactSpacing ); // clear thread storage because we need to re-initialize these. this->m_StaticThreadStorage.resize(0); } void SplineWarpCongealingFunctional ::RefineTransformationGrids() { this->Superclass::RefineTransformationGrids(); // clear thread storage because we need to re-initialize these. this->m_StaticThreadStorage.resize(0); } void SplineWarpCongealingFunctional ::UpdateStandardDeviationByPixel() { this->Superclass::UpdateStandardDeviationByPixel(); this->UpdateActiveControlPoints(); } void SplineWarpCongealingFunctional ::UpdateActiveControlPoints() { Superclass::UpdateActiveControlPoints(); if ( this->m_DeactivateUninformativeMode ) { const size_t numberOfControlPoints = this->m_VolumeOfInfluenceArray.size(); const Vector3D templateFrom( this->m_TemplateGrid->m_Offset ); const Vector3D templateTo( this->m_TemplateGrid->m_Offset + this->m_TemplateGrid->m_Size ); Vector3D fromVOI, toVOI; std::vector::const_iterator voi = this->m_VolumeOfInfluenceArray.begin(); for ( size_t cp = 0; cp < numberOfControlPoints; ++cp, ++voi ) { bool active = false; if ( this->m_ActiveControlPointFlags[cp] ) { for ( Types::GridIndexType z = voi->From()[2]; (z < voi->To()[2]) && !active; ++z ) { for ( Types::GridIndexType y = voi->From()[1]; (y < voi->To()[1]) && !active; ++y ) { size_t ofs = this->m_TemplateGrid->GetOffsetFromIndex( voi->From()[0], y, z ); for ( Types::GridIndexType x = voi->From()[0]; (x < voi->To()[0]) && !active; ++x, ++ofs ) { if ( this->m_StandardDeviationByPixel[ofs] > 0 ) { active = true; } } } } } this->m_ActiveControlPointFlags[cp] = active; if ( !active ) --this->m_NumberOfActiveControlPoints; } DebugOutput( 2 ) << "Enabled " << this->m_NumberOfActiveControlPoints << "/" << this->m_ParametersPerXform / 3 << " control points as informative.\n"; } this->UpdateParamStepArray(); } SplineWarpCongealingFunctional::ReturnType SplineWarpCongealingFunctional ::Evaluate() { if ( this->m_NeedsUpdateStandardDeviationByPixel ) this->UpdateStandardDeviationByPixel(); const size_t numberOfPixels = this->m_TemplateNumberOfPixels; this->m_EntropyByPixel.resize( numberOfPixels ); double entropy = 0; unsigned int count = 0; ThreadPool& threadPool = ThreadPool::GetGlobalThreadPool(); const size_t numberOfThreads = threadPool.GetNumberOfThreads(); this->m_ThreadHistograms.resize( numberOfThreads ); std::vector< EvaluateThreadParameters> params( numberOfThreads ); for ( size_t taskIdx = 0; taskIdx < numberOfThreads; ++taskIdx ) { params[taskIdx].thisObject = this; } threadPool.Run( EvaluateThread, params ); // gather partial entropies from threads for ( size_t taskIdx = 0; taskIdx < numberOfThreads; ++taskIdx ) { entropy += params[taskIdx].m_Entropy; count += params[taskIdx].m_Count; } if ( count ) { const double result = entropy / count; double constraint = 0; if ( this->m_JacobianConstraintWeight > 0 ) { for ( size_t i = 0; i < this->m_XformVector.size(); ++i ) { const SplineWarpXform* splineWarp = dynamic_cast( this->m_XformVector[i].GetPtr() ); if ( ! splineWarp ) { StdErr << "ERROR: dynamic_cast to SplineWarpXform failed in SplineWarpCongealingFunctional::Evaluate()\n"; throw ExitException( 1 ); } constraint += splineWarp->GetJacobianConstraint(); } } return result - this->m_JacobianConstraintWeight * constraint; } else return -FLT_MAX; } void SplineWarpCongealingFunctional ::EvaluateThread ( void *args, const size_t taskIdx, const size_t taskCnt, const size_t threadIdx, const size_t ) { EvaluateThreadParameters* threadParameters = static_cast( args ); Self* This = threadParameters->thisObject; const Self* ThisConst = threadParameters->thisObject; HistogramType& histogram = This->m_ThreadHistograms[threadIdx]; histogram.Resize( ThisConst->m_HistogramBins + 2 * ThisConst->m_HistogramKernelRadiusMax, false /*reset*/ ); double totalEntropy = 0; size_t count = 0; const size_t numberOfPixels = ThisConst->m_TemplateNumberOfPixels; const size_t pixelsPerThread = (numberOfPixels / taskCnt); const size_t pixelFrom = taskIdx * pixelsPerThread; const size_t pixelTo = std::min( numberOfPixels, pixelFrom + pixelsPerThread ); const size_t imagesFrom = ThisConst->m_ActiveImagesFrom; const size_t imagesTo = ThisConst->m_ActiveImagesTo; const byte paddingValue = ThisConst->m_PaddingValue; for ( size_t ofs = pixelFrom; ofs < pixelTo; ++ofs ) { histogram.Reset(); const size_t kernelIdx = ThisConst->m_StandardDeviationByPixel[ofs]; const size_t kernelRadius = ThisConst->m_HistogramKernelRadius[kernelIdx]; const HistogramBinType* kernel = ThisConst->m_HistogramKernel[kernelIdx]; bool fullCount = true; if ( ThisConst->m_UseTemplateData ) { const byte templateValue = ThisConst->m_TemplateData[ofs]; if ( (fullCount = (templateValue != paddingValue)) ) { histogram.AddWeightedSymmetricKernel( templateValue, kernelRadius, kernel ); } } for ( size_t idx = imagesFrom; (idx < imagesTo) && fullCount; ++idx ) { const byte value = ThisConst->m_Data[idx][ofs]; if ( value != paddingValue ) { histogram.AddWeightedSymmetricKernel( value, kernelRadius, kernel ); } else { fullCount = false; } } if ( fullCount ) { const double entropy = histogram.GetEntropy(); This->m_EntropyByPixel[ofs] = entropy; totalEntropy -= entropy; ++count; } else { This->m_EntropyByPixel[ofs] = 0; } } threadParameters->m_Entropy = totalEntropy; threadParameters->m_Count = count; } //@} } // namespace cmtk #include "cmtkSplineWarpCongealingFunctional.txx" cmtk-3.3.1/libs/Registration/cmtkSplineWarpCongealingFunctional.h000066400000000000000000000164511276303427400252240ustar00rootroot00000000000000/* // // Copyright 2016 Google, Inc. // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2011, 2013 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 5415 $ // // $LastChangedDate: 2016-01-16 15:13:11 -0800 (Sat, 16 Jan 2016) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkSplineWarpCongealingFunctional_h_included_ #define __cmtkSplineWarpCongealingFunctional_h_included_ #include #include #include #include #include #include #include #include #include #include namespace cmtk { /** \addtogroup Registration */ //@{ /** Functional for spline warp congealing. * This functional evaluates Lilla Zollei's entropy criterion for massively * groupwise image registration. * *\section refs References * * [1] L . Zoellei, E. Learned-Miller, E. Grimson, W.M. Wells III: "Efficient * Population Registration of 3D Data", ICCV 2005, Computer Vision for * Biomedical Image Applications; Beijing, China */ class SplineWarpCongealingFunctional : public CongealingFunctional { public: /// Type of parent class. typedef CongealingFunctional Superclass; /// Superclass histogram type. typedef Superclass::HistogramType HistogramType; /// Type of this class. typedef SplineWarpCongealingFunctional Self; /// Smart pointer. typedef SmartPointer SmartPtr; /// Default constructor. SplineWarpCongealingFunctional() : m_ControlPointIndexNext( 0 ), m_ControlPointIndexLast( 0 ) {} /** Initialize spline warp transformations. */ virtual void InitializeXformsFromAffine( const Types::Coordinate gridSpacing /*!< Control point grid spacing in real-world units*/, std::vector initialAffineXformsVector /*!< Vector of initial affine coordinate transformations*/, const bool exactSpacing = true /*!< If set, the control point spacing will be exactly as given in the first parameter*/ ); /// Refine transformation control point grids. virtual void RefineTransformationGrids(); /// Call inherited function and allocate local storage. virtual void SetTemplateGrid( UniformVolume::SmartPtr& templateGrid, const Types::GridIndexType downsample = 1, const bool useTemplateData = false ); /// Evaluate functional with currently set parameters. virtual Self::ReturnType Evaluate(); /// Evaluate functional and set parameters. virtual Self::ReturnType EvaluateAt( CoordinateVector& v ) { return this->Superclass::EvaluateAt( v ); } /** Compute functional value and gradient. *\param v Parameter vector. *\param g The extimated gradient of the functional is stored in this vector. *\param step Step size for finite difference gradient approximation. Default * is 1 mm. *\return Const function value for given parameters. */ virtual Self::ReturnType EvaluateWithGradient( CoordinateVector& v, CoordinateVector& g, const Types::Coordinate step = 1 ); private: /// Update deactivated control points. virtual void UpdateActiveControlPoints(); /// Update standard deviation by pixel. virtual void UpdateStandardDeviationByPixel(); /// Entropies over all images by pixel for fast local recomputation. std::vector m_EntropyByPixel; /// Thread parameter for entropy evaluation. class EvaluateThreadParameters : /// Inherit from generic thread parameter class. public ThreadParameters { public: /// Upon return from the thread function, this holds the partial entropy. double m_Entropy; /** Upon return from the thread function, this holds the number of * pixels with full image count, i.e., pixels that are within all * target images. */ unsigned int m_Count; }; /// Evaluate functional with currently set parameters. static void EvaluateThread( void* args, const size_t taskIdx, const size_t taskCnt, const size_t threadIdx, const size_t ); /// Thread function parameters for image interpolation. class EvaluateLocalGradientThreadParameters : /// Inherit from generic thread parameters. public ThreadParameters { public: /// Global parameter step scale factor. Types::Coordinate m_Step; /// Pointer to array with computed local gradient components. Types::Coordinate* m_Gradient; }; /// Index of next control point to be processed by an available thread. size_t m_ControlPointIndexNext; /// Index of (last control point + 1) to be processed by an available thread. size_t m_ControlPointIndexLast; /// Mutex lock for control point queue. MutexLock m_ControlPointIndexLock; /// Class for static thread storage to avoid recurring memory allocations. class StaticThreadStorage { public: /// Initialize thread storage based on parent functional ("This"). void Initialize( const Self* This ); /// Function values evaluated at x+delta. std::vector m_FPlus; /// Function values evaluated at x-delta. std::vector m_FMinus; /// Pixel count for transformation +delta. std::vector m_CountByParameterPlus; /// Pixel count for transformation -delta. std::vector m_CountByParameterMinus; /// Copies of transformation objects. std::vector m_Xforms; /// List of transformed vectors. std::vector m_VectorList; /// Floating pixel count per template pixel. std::vector m_Count; /// Stack histograms per pixel. std::vector m_Histogram; /// Flag: do transformation parameters need to be copied from functional? bool m_NeedToCopyXformParameters; }; /// Static thread storage array. std::vector m_StaticThreadStorage; /** Task function: Compute local gradient of the cost function for gradient approximation. * This function takes into consideration that in a spline warp, each control point * effects only a local neighborhood. It also groups the parameters by control * point and works over all images and x,y,z to speed things up substantially. */ static void EvaluateLocalGradientThreadFunc( void* args, const size_t taskIdx, const size_t taskCnt, const size_t threadIdx, const size_t ); }; //@} } // namespace cmtk #endif // #ifndef __cmtkSplineWarpCongealingFunctional_h_included_ cmtk-3.3.1/libs/Registration/cmtkSplineWarpCongealingFunctional.txx000066400000000000000000000262121276303427400256140ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2011, 2013 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4857 $ // // $LastChangedDate: 2013-09-17 21:38:07 -0700 (Tue, 17 Sep 2013) $ // // $LastChangedBy: torstenrohlfing $ // */ namespace cmtk { /** \addtogroup Registration */ //@{ SplineWarpCongealingFunctional::ReturnType SplineWarpCongealingFunctional ::EvaluateWithGradient ( CoordinateVector& v, CoordinateVector& g, const Types::Coordinate step ) { ThreadPool& threadPool = ThreadPool::GetGlobalThreadPool(); const size_t numberOfThreads = Threads::GetNumberOfThreads(); this->m_ThreadHistograms.resize( numberOfThreads ); const Self::ReturnType baseValue = this->EvaluateAt( v ); this->m_ControlPointIndexNext = 0; this->m_ControlPointIndexLast = this->m_ParametersPerXform / 3; if ( this->m_StaticThreadStorage.size() != numberOfThreads ) { this->m_StaticThreadStorage.resize( numberOfThreads ); for ( size_t thread = 0; thread < numberOfThreads; ++thread ) { this->m_StaticThreadStorage[thread].Initialize( this ); } } else { for ( size_t thread = 0; thread < numberOfThreads; ++thread ) { this->m_StaticThreadStorage[thread].m_NeedToCopyXformParameters = true; } } std::vector params( 4 * numberOfThreads - 3 ); for ( size_t taskIdx = 0; taskIdx < params.size(); ++taskIdx ) { params[taskIdx].thisObject = this; params[taskIdx].m_Step = step; params[taskIdx].m_Gradient = g.Elements; } threadPool.Run( EvaluateLocalGradientThreadFunc, params ); if ( this->m_PartialGradientMode ) { const Types::Coordinate gthresh = g.MaxNorm() * this->m_PartialGradientThreshold; #pragma omp parallel for for ( int param = 0; param < static_cast( g.Dim ); ++param ) if ( fabs( g[param] ) < gthresh ) { g[param] = this->m_ParamStepArray[param] = 0.0; } } if ( this->m_ForceZeroSum ) { this->ForceZeroSumGradient( g ); } return baseValue; } void SplineWarpCongealingFunctional ::EvaluateLocalGradientThreadFunc ( void* args, const size_t taskIdx, const size_t taskCnt, const size_t threadIdx, const size_t ) { EvaluateLocalGradientThreadParameters* threadParameters = static_cast( args ); Self* This = threadParameters->thisObject; const Self* ThisConst = This; const size_t numberOfXforms = ThisConst->m_XformVector.size(); const size_t parametersPerXform = ThisConst->m_ParametersPerXform; const size_t paramVectorDim = ThisConst->ParamVectorDim(); const byte paddingValue = ThisConst->m_PaddingValue; const size_t imagesFrom = ThisConst->m_ActiveImagesFrom; const size_t imagesTo = ThisConst->m_ActiveImagesTo; const size_t numberOfImages = imagesTo - imagesFrom; const size_t numberOfImagesIncludingTemplate = ThisConst->m_UseTemplateData ? numberOfImages+1 : numberOfImages; Self::StaticThreadStorage* threadStorage = (&This->m_StaticThreadStorage[threadIdx]); if ( threadStorage->m_NeedToCopyXformParameters ) { for ( size_t xi = 0; xi < numberOfXforms; ++xi ) { threadStorage->m_Xforms[xi]->CopyParamVector( This->GetXformByIndex(xi) ); threadStorage->m_NeedToCopyXformParameters = false; } } const UniformVolume* templateGrid = ThisConst->m_TemplateGrid; const size_t numberOfControlPoints = ThisConst->m_ParametersPerXform / 3; for ( size_t cpIndex = taskIdx; cpIndex < This->m_ControlPointIndexLast; cpIndex += taskCnt ) { if ( !(cpIndex % 1000) ) { std::cerr << cpIndex << " / " << numberOfControlPoints << "\r"; } std::vector::const_iterator voi = ThisConst->m_VolumeOfInfluenceArray.begin() + cpIndex; const size_t pixelsPerLineVOI = (voi->To()[0]-voi->From()[0]); std::fill( threadStorage->m_FPlus.begin(), threadStorage->m_FPlus.end(), static_cast( 0 ) ); std::fill( threadStorage->m_FMinus.begin(), threadStorage->m_FMinus.end(), static_cast( 0 ) ); std::fill( threadStorage->m_CountByParameterPlus.begin(), threadStorage->m_CountByParameterPlus.end(), 0 ); std::fill( threadStorage->m_CountByParameterMinus.begin(), threadStorage->m_CountByParameterMinus.end(), 0 ); for ( int z = voi->From()[2]; (z < voi->To()[2]); ++z ) { for ( int y = voi->From()[1]; (y < voi->To()[1]); ++y ) { // evaluate baseline entropies for this row const size_t rowofs = templateGrid->GetOffsetFromIndex( voi->From()[0], y, z ); size_t ofs = rowofs; for ( size_t x = 0; x < pixelsPerLineVOI; ++x, ++ofs ) { threadStorage->m_Histogram[x].Reset(); threadStorage->m_Count[x] = 0; const size_t kernelIdx = ThisConst->m_StandardDeviationByPixel[ofs]; const size_t kernelRadius = ThisConst->m_HistogramKernelRadius[kernelIdx]; const HistogramBinType* kernel = ThisConst->m_HistogramKernel[kernelIdx]; if ( ThisConst->m_UseTemplateData ) { const byte templateValue = ThisConst->m_TemplateData[ofs]; if ( templateValue != paddingValue ) { threadStorage->m_Histogram[x].AddWeightedSymmetricKernel( templateValue, kernelRadius, kernel ); ++threadStorage->m_Count[x]; } } for ( size_t img = imagesFrom; img < imagesTo; ++img ) { const byte dataThisPixel = ThisConst->m_Data[img][ofs]; if ( dataThisPixel != paddingValue ) { threadStorage->m_Histogram[x].AddWeightedSymmetricKernel( dataThisPixel, kernelRadius, kernel ); ++threadStorage->m_Count[x]; } } } size_t cparam = 3 * cpIndex; size_t currentParameter = 0; for ( size_t imageIdx = 0; imageIdx < numberOfXforms; ++imageIdx, cparam += parametersPerXform ) { SplineWarpXform::SmartPtr xform = threadStorage->m_Xforms[imageIdx]; const UniformVolume* target = ThisConst->m_ImageVector[imageIdx]; const byte* dataPtr = static_cast( target->GetData()->GetDataPtr() ); for ( size_t dim = 0; dim < 3; ++dim, ++currentParameter ) { const size_t cdparam = cparam + dim; const size_t xfparam = 3 * cpIndex + dim; const Types::Coordinate pStep = ThisConst->m_ParamStepArray[cdparam] * threadParameters->m_Step; if ( pStep > 0 ) { const Types::Coordinate v0 = xform->GetParameter( xfparam ); for ( int delta = 0; delta < 2; ++delta ) { Types::Coordinate vTest = v0 + (2*delta-1) * pStep; xform->SetParameter( xfparam, vTest ); xform->GetTransformedGridRow( pixelsPerLineVOI, &(threadStorage->m_VectorList[0]), voi->From()[0], y, z ); ofs = rowofs; for ( size_t x = 0; x < pixelsPerLineVOI; ++x, ++ofs ) { if ( threadStorage->m_Count[x] == numberOfImagesIncludingTemplate ) // full count? { const byte baselineData = ThisConst->m_Data[imageIdx][ofs]; HistogramType& workingHistogram = threadStorage->m_Histogram[x]; const size_t kernelIdx = ThisConst->m_StandardDeviationByPixel[ofs]; const size_t kernelRadius = ThisConst->m_HistogramKernelRadius[kernelIdx]; const HistogramBinType* kernel = ThisConst->m_HistogramKernel[kernelIdx]; byte value; if ( (baselineData != paddingValue) && target->ProbeData( value, dataPtr, threadStorage->m_VectorList[x] ) ) { if ( value != paddingValue ) { if ( delta ) ++threadStorage->m_CountByParameterPlus[currentParameter]; else ++threadStorage->m_CountByParameterMinus[currentParameter]; if ( value != baselineData ) { double fd = 0; double invsamples = 1.0 / workingHistogram.SampleCount(); const size_t idxMin = std::min( value, baselineData ) - kernelRadius; const size_t idxMax = std::max( value, baselineData ) + kernelRadius + 1; for ( size_t idx = idxMin; idx < idxMax; ++idx ) { const size_t idxV = abs((int)(idx - value)); const double kernelIn = ( idxV < kernelRadius ) ? kernel[idxV] : 0; const size_t idxB = abs((int)(idx - baselineData)); const double kernelOut = ( idxB < kernelRadius ) ? kernel[idxB] : 0; if ( (kernelIn > 0) || (kernelOut > 0) ) { fd += MathUtil::plogp( invsamples * (workingHistogram[idx] - kernelOut + kernelIn) ) - MathUtil::plogp( invsamples * workingHistogram[idx] ); } } if ( delta ) threadStorage->m_FPlus[currentParameter] += fd; else threadStorage->m_FMinus[currentParameter] += fd; } } } } } } xform->SetParameter( xfparam, v0 ); } } } } } size_t imageDimIdx = 0; for ( size_t cparam = 3*cpIndex; cparam < paramVectorDim; cparam += parametersPerXform ) { for ( size_t dim = 0; dim < 3; ++dim, ++imageDimIdx ) { const size_t cdparam = cparam+dim; threadParameters->m_Gradient[cdparam] = 0.0; if ( threadStorage->m_CountByParameterPlus[imageDimIdx] && threadStorage->m_CountByParameterMinus[imageDimIdx] ) { double upper = threadStorage->m_FPlus[imageDimIdx] / threadStorage->m_CountByParameterPlus[imageDimIdx]; double lower = threadStorage->m_FMinus[imageDimIdx] / threadStorage->m_CountByParameterMinus[imageDimIdx]; if ( (ThisConst->m_JacobianConstraintWeight > 0) || (ThisConst->m_BendingEnergyWeight > 0) ) { const size_t warpParam = cdparam % parametersPerXform; const size_t imageIdx = cdparam / parametersPerXform; const SplineWarpXform* xform = ThisConst->GetXformByIndex(imageIdx); const Types::Coordinate pStep = ThisConst->m_ParamStepArray[cdparam] * threadParameters->m_Step; if ( ThisConst->m_JacobianConstraintWeight > 0 ) { double upperJ, lowerJ; xform->GetJacobianConstraintDerivative( upperJ, lowerJ, warpParam, *(voi+warpParam), pStep ); upper -= ThisConst->m_JacobianConstraintWeight * upperJ; lower -= ThisConst->m_JacobianConstraintWeight * lowerJ; } if ( ThisConst->m_BendingEnergyWeight > 0 ) { double upperE, lowerE; xform->GetGridEnergyDerivative( upperE, lowerE, warpParam, pStep ); upper -= ThisConst->m_BendingEnergyWeight * upperE; lower -= ThisConst->m_BendingEnergyWeight * lowerE; } } if ( (upper > 0) || (lower > 0)) { threadParameters->m_Gradient[cparam+dim] = upper - lower; } } } } } } } // namespace cmtk cmtk-3.3.1/libs/Registration/cmtkSplineWarpCongealingFunctionalStaticThreadStorage.cxx000066400000000000000000000043031276303427400314150ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2010 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 2022 $ // // $LastChangedDate: 2010-07-21 15:26:03 -0700 (Wed, 21 Jul 2010) $ // // $LastChangedBy: torstenrohlfing $ // */ #include "cmtkSplineWarpCongealingFunctional.h" namespace cmtk { /** \addtogroup Registration */ //@{ void SplineWarpCongealingFunctional::StaticThreadStorage::Initialize( const Self* This ) { const size_t numberOfXforms = This->m_XformVector.size(); this->m_FPlus.resize( 3 * numberOfXforms ); this->m_FMinus.resize( 3 * numberOfXforms ); this->m_CountByParameterPlus.resize( 3 * numberOfXforms ); this->m_CountByParameterMinus.resize( 3 * numberOfXforms ); this->m_Xforms.resize( numberOfXforms ); for ( size_t xi = 0; xi < numberOfXforms; ++xi ) { this->m_Xforms[xi] = SplineWarpXform::SmartPtr( This->GetXformByIndex(xi)->Clone() ); } this->m_VectorList.resize( This->m_MaximumNumberOfPixelsPerLineVOI ); this->m_Count.resize( This->m_MaximumNumberOfPixelsPerLineVOI ); this->m_Histogram.resize( This->m_MaximumNumberOfPixelsPerLineVOI ); for ( size_t x = 0; x < This->m_MaximumNumberOfPixelsPerLineVOI; ++x ) { this->m_Histogram[x].Resize( This->m_HistogramBins + 2 * This->m_HistogramKernelRadiusMax, false /*reset*/ ); } this->m_NeedToCopyXformParameters = true; } } // namespace cmtk cmtk-3.3.1/libs/Registration/cmtkSplineWarpGroupwiseRegistrationRMIFunctional.cxx000066400000000000000000000120001276303427400304420ustar00rootroot00000000000000/* // // Copyright 2016 Google, Inc. // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2012 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 5413 $ // // $LastChangedDate: 2016-01-16 15:10:49 -0800 (Sat, 16 Jan 2016) $ // // $LastChangedBy: torstenrohlfing $ // */ #include "cmtkSplineWarpGroupwiseRegistrationRMIFunctional.h" #include #include #include #include #include namespace cmtk { /** \addtogroup Registration */ //@{ void SplineWarpGroupwiseRegistrationRMIFunctional ::UpdateInformationByControlPoint() { this->m_NeedsUpdateInformationByControlPoint = false; const size_t numberOfControlPoints = this->m_VolumeOfInfluenceArray.size(); this->m_InformationByControlPoint.resize( numberOfControlPoints ); const byte paddingValue = this->m_PaddingValue; const size_t beginCP = 0; const size_t endCP = numberOfControlPoints; for ( size_t cp = beginCP; cp < endCP; ++cp ) { this->m_InformationByControlPoint[cp] = 0; std::vector::const_iterator voi = this->m_VolumeOfInfluenceArray.begin() + cp; for ( size_t img = this->m_ActiveImagesFrom; img < this->m_ActiveImagesTo; ++img ) { const byte* dataPtrImg = this->m_Data[img]; byte voiMin = 255, voiMax = 0; for ( Types::GridIndexType z = voi->From()[2]; z < voi->To()[2]; ++z ) { for ( Types::GridIndexType y = voi->From()[1]; y < voi->To()[1]; ++y ) { size_t ofs = this->m_TemplateGrid->GetOffsetFromIndex( voi->From()[0], y, z ); for ( Types::GridIndexType x = voi->From()[0]; x < voi->To()[0]; ++x, ++ofs ) { const byte data = dataPtrImg[ofs]; if ( data != paddingValue ) { voiMin = std::min( data, voiMin ); voiMax = std::max( data, voiMax ); } } } } this->m_InformationByControlPoint[cp] = std::max( (byte)(voiMax-voiMin), this->m_InformationByControlPoint[cp] ); } } this->UpdateActiveControlPoints(); } void SplineWarpGroupwiseRegistrationRMIFunctional::UpdateControlPointSchedule() { const SplineWarpXform* xform0 = this->GetXformByIndex(0); this->m_ControlPointSchedule.resize( xform0->GetNumberOfControlPoints() ); this->m_ControlPointScheduleOverlapFreeMaxLength = (xform0->m_Dims[0] / 4) * (xform0->m_Dims[1] / 4) * (xform0->m_Dims[2] / 4); size_t ofs = 0; for ( int z = 0; z < 4; ++z ) { for ( int y = 0; y < 4; ++y ) { for ( int x = 0; x < 4; ++x ) { for ( int k = z; k < xform0->m_Dims[2]; k += 4 ) { for ( int j = y; j < xform0->m_Dims[1]; j += 4 ) { for ( int i = x; i < xform0->m_Dims[0]; i += 4, ++ofs ) { this->m_ControlPointSchedule[ofs] = i + xform0->m_Dims[0] * ( j + xform0->m_Dims[1] * k ); } } } } } } } void SplineWarpGroupwiseRegistrationRMIFunctional::UpdateActiveControlPoints() { Superclass::UpdateActiveControlPoints(); if ( this->m_DeactivateUninformativeMode ) { const size_t numberOfControlPoints = this->m_VolumeOfInfluenceArray.size(); const Vector3D templateFrom( this->m_TemplateGrid->m_Offset ); const Vector3D templateTo( this->m_TemplateGrid->m_Offset + this->m_TemplateGrid->m_Size ); Vector3D fromVOI, toVOI; std::vector::const_iterator voi = this->m_VolumeOfInfluenceArray.begin(); for ( size_t cp = 0; cp < numberOfControlPoints; ++cp, ++voi ) { if ( this->m_ActiveControlPointFlags[cp] ) { this->m_ActiveControlPointFlags[cp] = (this->m_InformationByControlPoint[cp] > (this->m_HistogramBins / 4) ); } if ( !this->m_ActiveControlPointFlags[cp] ) --this->m_NumberOfActiveControlPoints; } DebugOutput( 2 ) << "Enabled " << this->m_NumberOfActiveControlPoints << "/" << this->m_ParametersPerXform / 3 << " control points as informative.\n"; } this->UpdateParamStepArray(); this->UpdateControlPointSchedule(); } SplineWarpGroupwiseRegistrationRMIFunctional::ReturnType SplineWarpGroupwiseRegistrationRMIFunctional::Evaluate() { return this->Superclass::Evaluate(); } //@} } // namespace cmtk #include "cmtkSplineWarpGroupwiseRegistrationRMIFunctional.txx" cmtk-3.3.1/libs/Registration/cmtkSplineWarpGroupwiseRegistrationRMIFunctional.h000066400000000000000000000115601276303427400301010ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2012 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4344 $ // // $LastChangedDate: 2012-05-11 14:51:08 -0700 (Fri, 11 May 2012) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkSplineWarpGroupwiseRegistrationRMIFunctional_h_included_ #define __cmtkSplineWarpGroupwiseRegistrationRMIFunctional_h_included_ #include #include #include #include #include #include #include #include namespace cmtk { /** \addtogroup Registration */ //@{ /** Functional for spline warp groupwise registration. */ class SplineWarpGroupwiseRegistrationRMIFunctional : public GroupwiseRegistrationRMIFunctional { public: /// Type of parent class. typedef GroupwiseRegistrationRMIFunctional Superclass; /// Type of this class. typedef SplineWarpGroupwiseRegistrationRMIFunctional Self; /// Smart pointer. typedef SmartPointer SmartPtr; /// Constructor. SplineWarpGroupwiseRegistrationRMIFunctional() : m_NeedsUpdateInformationByControlPoint( true ), m_ControlPointScheduleOverlapFreeMaxLength( 0 ) {} /// Refine transformation control point grids. virtual void RefineTransformationGrids() { this->Superclass::RefineTransformationGrids(); this->m_NeedsUpdateInformationByControlPoint = true; } /// Evaluate functional with currently set parameters. virtual Self::ReturnType Evaluate(); /// Evaluate functional and set parameters. virtual Self::ReturnType EvaluateAt( CoordinateVector& v ) { return this->Superclass::EvaluateAt( v ); } /** Compute functional value and gradient. *\param v Parameter vector. *\param g The extimated gradient of the functional is stored in this vector. *\param step Step size for finite difference gradient approximation. Default * is 1 mm. *\return Const function value for given parameters. */ virtual Self::ReturnType EvaluateWithGradient( CoordinateVector& v, CoordinateVector& g, const Types::Coordinate step = 1 ); private: /// Update deactivated control points. virtual void UpdateActiveControlPoints(); /// Local information measure for neighborhood of each control point. std::vector m_InformationByControlPoint; /// Flag whether information by control point needs to be updated. bool m_NeedsUpdateInformationByControlPoint; /// Update local information by control point. virtual void UpdateInformationByControlPoint(); /// Update control point schedule for gradient approximation. virtual void UpdateControlPointSchedule(); /// Processing schedule for overlap-free parallel processing of control points. std::vector m_ControlPointSchedule; /// Maximum number of concurrent jobs working on warps that is still overlap-free. size_t m_ControlPointScheduleOverlapFreeMaxLength; /// Thread function parameters for image interpolation. class EvaluateLocalGradientThreadParameters : /// Inherit from generic thread parameters. public ThreadParameters { public: /// Unique thread storage index. size_t m_ThreadStorageIndex; /// Global parameter step scale factor. Types::Coordinate m_Step; /// Pointer to array with computed local gradient components. Types::Coordinate* m_Gradient; /// Current metric value. Self::ReturnType m_MetricBaseValue; }; /** Thread function: Compute local gradient of the cost function for gradient approximation. * This function takes into consideration that in a spline warp, each control point * effects only a local neighborhood. It also groups the parameters by control * point and works over all images and x,y,z to speed things up substantially. */ static CMTK_THREAD_RETURN_TYPE EvaluateLocalGradientThreadFunc( void* args ); }; //@} } // namespace cmtk #endif // #ifndef __cmtkSplineWarpGroupwiseRegistrationRMIFunctional_h_included_ cmtk-3.3.1/libs/Registration/cmtkSplineWarpGroupwiseRegistrationRMIFunctional.txx000066400000000000000000000252441276303427400305010ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2013 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4982 $ // // $LastChangedDate: 2013-10-14 12:54:26 -0700 (Mon, 14 Oct 2013) $ // // $LastChangedBy: torstenrohlfing $ // */ namespace cmtk { /** \addtogroup Registration */ //@{ SplineWarpGroupwiseRegistrationRMIFunctional::ReturnType SplineWarpGroupwiseRegistrationRMIFunctional::EvaluateWithGradient ( CoordinateVector& v, CoordinateVector& g, const Types::Coordinate step ) { const size_t numberOfThreads = Threads::GetNumberOfThreads(); const size_t numberOfXforms = this->m_XformVector.size(); const Self::ReturnType baseValue = this->EvaluateAt( v ); if ( this->m_NeedsUpdateInformationByControlPoint ) { this->UpdateInformationByControlPoint(); } // allocate sufficiently many local thread data const size_t safeNumberOfThreads = std::min( numberOfThreads, this->m_ControlPointScheduleOverlapFreeMaxLength ); if ( this->m_ThreadSumOfProductsMatrix.size() < (6 * numberOfXforms * safeNumberOfThreads) ) { this->m_ThreadSumOfProductsMatrix.resize( 6 * numberOfXforms * safeNumberOfThreads ); } if ( this->m_ThreadSumsVector.size() < (6 * numberOfXforms * safeNumberOfThreads) ) { this->m_ThreadSumsVector.resize( 6 * numberOfXforms * safeNumberOfThreads ); } ThreadParameterArray threadParams( this, safeNumberOfThreads ); for ( size_t thread = 0; thread < safeNumberOfThreads; ++thread ) { threadParams[thread].m_ThreadStorageIndex = thread; threadParams[thread].m_Step = step; threadParams[thread].m_Gradient = g.Elements; threadParams[thread].m_MetricBaseValue = baseValue; } threadParams.RunInParallelFIFO( EvaluateLocalGradientThreadFunc, this->m_ControlPointSchedule.size() ); if ( this->m_PartialGradientMode ) { const Types::Coordinate gthresh = g.MaxNorm() * this->m_PartialGradientThreshold; for ( size_t param = 0; param < g.Dim; ++param ) if ( fabs( g[param] ) < gthresh ) { g[param] = this->m_ParamStepArray[param] = 0.0; } } if ( this->m_ForceZeroSum ) { this->ForceZeroSumGradient( g ); } return baseValue; } CMTK_THREAD_RETURN_TYPE SplineWarpGroupwiseRegistrationRMIFunctional::EvaluateLocalGradientThreadFunc ( void* args ) { EvaluateLocalGradientThreadParameters* threadParameters = static_cast( args ); Self* This = threadParameters->thisObject; const Self* ThisConst = threadParameters->thisObject; const size_t threadID = threadParameters->ThisThreadIndex; const size_t threadStorageIndex = threadParameters->m_ThreadStorageIndex; if ( !(threadID % 100) ) { std::cerr << threadID << " / " << ThisConst->m_ControlPointSchedule.size() << "\r"; } const size_t cpIndex = ThisConst->m_ControlPointSchedule[threadID]; std::vector::const_iterator voi = ThisConst->m_VolumeOfInfluenceArray.begin() + cpIndex; const size_t pixelsPerLineVOI = (voi->To()[0]-voi->From()[0]); std::vector vectorList( pixelsPerLineVOI ); std::vector count( pixelsPerLineVOI ); const size_t numberOfXforms = ThisConst->m_XformVector.size(); std::vector totalNumberOfSamples( 6 * numberOfXforms ); std::fill( totalNumberOfSamples.begin(), totalNumberOfSamples.end(), ThisConst->m_TotalNumberOfSamples ); const size_t parametersPerXform = ThisConst->m_ParametersPerXform; const size_t paramVectorDim = ThisConst->ParamVectorDim(); const byte paddingValue = ThisConst->m_PaddingValue; const size_t imagesFrom = ThisConst->m_ActiveImagesFrom; const size_t imagesTo = ThisConst->m_ActiveImagesTo; const size_t numberOfImages = imagesTo - imagesFrom; const UniformVolume* templateGrid = ThisConst->m_TemplateGrid; const size_t threadDataIdx = 6 * threadStorageIndex * numberOfXforms; for ( size_t image = 0; image < 6 * numberOfXforms; ++image ) { const SumsAndProductsVectorType& srcSumOfProducts = ThisConst->m_SumOfProductsMatrix; SumsAndProductsVectorType& dstSumOfProducts = This->m_ThreadSumOfProductsMatrix[threadDataIdx + image]; dstSumOfProducts.resize( srcSumOfProducts.size() ); std::copy( srcSumOfProducts.begin(), srcSumOfProducts.end(), dstSumOfProducts.begin() ); const SumsAndProductsVectorType& srcSumsVector = ThisConst->m_SumsVector; SumsAndProductsVectorType& dstSumsVector = This->m_ThreadSumsVector[threadDataIdx + image]; dstSumsVector.resize( srcSumsVector.size() ); std::copy( srcSumsVector.begin(), srcSumsVector.end(), dstSumsVector.begin() ); } for ( int z = voi->From()[2]; (z < voi->To()[2]); ++z ) { for ( int y = voi->From()[1]; (y < voi->To()[1]); ++y ) { // check which pixels in this row have a full sample count const size_t rowofs = templateGrid->GetOffsetFromIndex( voi->From()[0], y, z ); std::fill( count.begin(), count.end(), 0 ); for ( size_t img = 0; img < numberOfXforms; ++img ) { const byte* dataPtr = ThisConst->m_Data[img]+rowofs; for ( size_t x = 0; x < pixelsPerLineVOI; ++x ) { const byte dataThisPixel = dataPtr[x]; if ( dataThisPixel != paddingValue ) { ++count[x]; } } } size_t cparam = 3 * cpIndex; size_t currentParameter = 0; for ( size_t img = 0; img < numberOfXforms; ++img, cparam += parametersPerXform ) { SplineWarpXform::SmartPtr xform = This->GetXformByIndex(img); const UniformVolume* target = ThisConst->m_ImageVector[img]; const byte* targetDataPtr = static_cast( target->GetData()->GetDataPtr() ); for ( size_t dim = 0; dim < 3; ++dim ) { const size_t cdparam = cparam + dim; const size_t xfparam = 3 * cpIndex + dim; const Types::Coordinate pStep = ThisConst->m_ParamStepArray[cdparam] * threadParameters->m_Step; if ( pStep > 0 ) { const Types::Coordinate v0 = xform->GetParameter( xfparam ); for ( int delta = 0; delta < 2; ++delta, ++currentParameter ) { SumsAndProductsVectorType& dstSumOfProducts = This->m_ThreadSumOfProductsMatrix[threadDataIdx+currentParameter]; SumsAndProductsVectorType& dstSumsVector = This->m_ThreadSumsVector[threadDataIdx+currentParameter]; Types::Coordinate vTest = v0 + (2*delta-1) * pStep; xform->SetParameter( xfparam, vTest ); xform->GetTransformedGridRow( pixelsPerLineVOI, &(vectorList[0]), voi->From()[0], y, z ); byte* rowDataPtr = ThisConst->m_Data[img] + rowofs; for ( size_t x = 0; x < pixelsPerLineVOI; ++x, ++rowDataPtr ) { const byte baselineData = *rowDataPtr; if ( (count[x] == numberOfImages) || ((count[x] == numberOfImages-1) && (baselineData == paddingValue) ) ) // full count? { byte newData; if ( !target->ProbeData( newData, targetDataPtr, vectorList[x] ) ) newData = paddingValue; if ( newData != baselineData ) { if ( baselineData != paddingValue ) { dstSumsVector[img] -= baselineData; size_t midx = 0; for ( size_t img2 = imagesFrom; img2 < imagesTo; ++img2 ) { for ( size_t otherImg = imagesFrom; otherImg <= img2; ++otherImg, ++midx ) { if ( img2 == img ) { const byte otherData = ThisConst->m_Data[otherImg][rowofs+x]; dstSumOfProducts[midx] -= baselineData * otherData; } else { if ( otherImg == img ) { const byte otherData = ThisConst->m_Data[img2][rowofs+x]; dstSumOfProducts[midx] -= baselineData * otherData; } } } } } if ( newData != paddingValue ) { if ( count[x] == numberOfImages-1 ) { ++totalNumberOfSamples[currentParameter]; } dstSumsVector[img] += newData; size_t midx = 0; for ( size_t img2 = imagesFrom; img2 < imagesTo; ++img2 ) { for ( size_t otherImg = imagesFrom; otherImg <= img2; ++otherImg, ++midx ) { if ( img2 == img ) { if ( otherImg == img ) { dstSumOfProducts[midx] += newData * newData; } else { const byte otherData = ThisConst->m_Data[otherImg][rowofs+x]; dstSumOfProducts[midx] += newData * otherData; } } else { if ( otherImg == img ) { const byte otherData = ThisConst->m_Data[img2][rowofs+x]; dstSumOfProducts[midx] += newData * otherData; } } } } } else { if ( count[x] == numberOfImages ) { --totalNumberOfSamples[currentParameter]; } } } } } } xform->SetParameter( xfparam, v0 ); } else { currentParameter += 2; } } } } } Self::CovarianceMatrixType covarianceMatrix( numberOfImages ); // approximate gradient from upper and lower function evaluations size_t img = 0, currentParameter = 0; const Functional::ReturnType fBaseValue = threadParameters->m_MetricBaseValue; for ( size_t cparam = 3*cpIndex; cparam < paramVectorDim; cparam += parametersPerXform ) { for ( size_t dim = 0; dim < 3; ++dim, ++img, currentParameter += 2 ) { const Self::ReturnType fMinus = ThisConst->GetMetric( This->m_ThreadSumOfProductsMatrix[threadDataIdx+currentParameter], This->m_ThreadSumsVector[threadDataIdx+currentParameter], totalNumberOfSamples[currentParameter], covarianceMatrix ); const Self::ReturnType fPlus = ThisConst->GetMetric( This->m_ThreadSumOfProductsMatrix[threadDataIdx+currentParameter+1], This->m_ThreadSumsVector[threadDataIdx+currentParameter+1], totalNumberOfSamples[currentParameter], covarianceMatrix ); if ( (fPlus > fBaseValue) || (fMinus > fBaseValue) ) { threadParameters->m_Gradient[cparam+dim] = fPlus - fMinus; } else { threadParameters->m_Gradient[cparam+dim] = 0.0; } } } return CMTK_THREAD_RETURN_VALUE; } } // namespace cmtk cmtk-3.3.1/libs/Registration/cmtkSplineWarpMultiChannelIntensityCorrectionRegistrationFunctional.h000066400000000000000000000056321276303427400340720ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2010 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 2398 $ // // $LastChangedDate: 2010-10-05 14:54:37 -0700 (Tue, 05 Oct 2010) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkSplineWarpMultiChannelIntensityCorrectionRegistrationFunctional_h_included_ #define __cmtkSplineWarpMultiChannelIntensityCorrectionRegistrationFunctional_h_included_ #include #include #include #include namespace cmtk { /** \addtogroup Registration */ //@{ /** Class for spline warp multi-channel registration functional. */ template > class SplineWarpMultiChannelIntensityCorrectionRegistrationFunctional : /** Inherit from multi-channel registration functional base class. */ public SplineWarpMultiChannelRegistrationFunctional { public: /** This class. */ typedef SplineWarpMultiChannelIntensityCorrectionRegistrationFunctional Self; /** Smart pointer. */ typedef SmartPointer SmartPtr; /** This class. */ typedef SplineWarpMultiChannelRegistrationFunctional Superclass; /** Metric data class. */ typedef typename TMetricFunctional::MetricData MetricData; private: /** Continue metric computation and store reformatted floating channels for local recomputation. */ virtual void ContinueMetricStoreReformatted( MetricData& metricData, const size_t rindex, const Vector3D& fvector ); /** Continue metric computation and store reformatted floating channels for local recomputation. */ virtual void ContinueMetric( MetricData& metricData, const size_t rindex, const Vector3D& fvector ); }; //@} } // namespace cmtk #include "cmtkSplineWarpMultiChannelIntensityCorrectionRegistrationFunctional.txx" #endif // #ifndef __cmtkSplineWarpMultiChannelIntensityCorrectionRegistrationFunctional_h_included_ cmtk-3.3.1/libs/Registration/cmtkSplineWarpMultiChannelIntensityCorrectionRegistrationFunctional.txx000066400000000000000000000070411276303427400344620ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2013 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4882 $ // // $LastChangedDate: 2013-09-27 15:16:36 -0700 (Fri, 27 Sep 2013) $ // // $LastChangedBy: torstenrohlfing $ // */ #include namespace cmtk { /** \addtogroup Registration */ //@{ template void SplineWarpMultiChannelIntensityCorrectionRegistrationFunctional ::ContinueMetric( MetricData& metricData, const size_t rindex, const Vector3D& fvector ) { std::vector values( this->m_NumberOfChannels ); size_t idx = 0; for ( size_t ref = 0; ref < this->m_ReferenceChannels.size(); ++ref ) { if ( !this->m_ReferenceChannels[ref]->GetDataAt( values[idx++], rindex ) ) return; } const int planeSize = this->m_ReferenceDims[0] * this->m_ReferenceDims[1]; const int z = rindex / planeSize; const int y = (rindex % planeSize) / this->m_ReferenceDims[0]; const int x = rindex % this->m_ReferenceDims[0]; const Types::DataItem jacobian = static_cast( this->m_Transformation.GetJacobianDeterminant( x, y, z ) ); for ( size_t flt = 0; flt < this->m_FloatingChannels.size(); ++flt, ++idx ) { if ( !this->m_FloatingInterpolators[flt]->GetDataAt( fvector, values[idx] ) ) return; values[idx] *= jacobian; } metricData += values; } template void SplineWarpMultiChannelIntensityCorrectionRegistrationFunctional ::ContinueMetricStoreReformatted( MetricData& metricData, const size_t rindex, const Vector3D& fvector ) { std::vector values( this->m_NumberOfChannels ); size_t idx = 0; for ( size_t ref = 0; ref < this->m_ReferenceChannels.size(); ++ref ) { if ( !this->m_ReferenceChannels[ref]->GetDataAt( values[idx++], rindex ) ) return; } const int planeSize = this->m_ReferenceDims[0] * this->m_ReferenceDims[1]; const int z = rindex / planeSize; const int y = (rindex % planeSize) / this->m_ReferenceDims[0]; const int x = rindex % this->m_ReferenceDims[0]; const Types::DataItem jacobian = static_cast( this->m_Transformation.GetJacobianDeterminant( x, y, z ) ); for ( size_t flt = 0; flt < this->m_FloatingChannels.size(); ++flt, ++idx ) { if ( !this->m_FloatingInterpolators[flt]->GetDataAt( fvector, values[idx] ) ) { for ( size_t f = 0; f < this->m_FloatingChannels.size(); ++f ) this->m_ReformattedFloatingChannels[f][rindex] = std::numeric_limits::signaling_NaN(); return; } values[idx] *= jacobian; this->m_ReformattedFloatingChannels[flt][rindex] = static_cast( values[idx] ); } metricData += values; } } // namespace cmtk cmtk-3.3.1/libs/Registration/cmtkSplineWarpMultiChannelRegistrationFunctional.h000066400000000000000000000204301276303427400301240ustar00rootroot00000000000000/* // // Copyright 1997-2010 Torsten Rohlfing // // Copyright 2004-2012 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 3975 $ // // $LastChangedDate: 2012-03-06 15:04:42 -0800 (Tue, 06 Mar 2012) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkSplineWarpMultiChannelRegistrationFunctional_h_included_ #define __cmtkSplineWarpMultiChannelRegistrationFunctional_h_included_ #include #include #include #include #include #include #include #include namespace cmtk { /** \addtogroup Registration */ //@{ /** Forward declaration of class template. */ template class SplineWarpMultiChannelIntensityCorrectionRegistrationFunctional; /** Class for spline warp multi-channel registration functional. */ template > class SplineWarpMultiChannelRegistrationFunctional : /** Inherit from multi-channel registration functional base class. */ public TemplateMultiChannelRegistrationFunctional { public: /** This class. */ typedef SplineWarpMultiChannelRegistrationFunctional Self; /** Smart pointer. */ typedef SmartPointer SmartPtr; /** This class. */ typedef TemplateMultiChannelRegistrationFunctional Superclass; /** Metric data subclass */ typedef typename Superclass::MetricData MetricData; /** Default constructor. */ SplineWarpMultiChannelRegistrationFunctional(); /** Constructor from affine multi-channel functional. */ template SplineWarpMultiChannelRegistrationFunctional ( AffineMultiChannelRegistrationFunctional& affineFunctional ); /** Set initial affine transformation. */ virtual void SetInitialAffineTransformation( const AffineXform& initialAffine ) { this->m_InitialAffineTransformation = initialAffine; } /** Set flag for entropy vs. intensity thresholding. */ void SetAdaptiveFixEntropyThreshold( const bool flag ) { this->m_AdaptiveFixEntropyThreshold = flag; } /** Set adaptive fixing entropy threshold factor. */ void SetAdaptiveFixThreshFactor( const float factor ) { this->m_AdaptiveFixThreshFactor = factor; } /** Clear list of fixed coordinate dimensions. */ void ClearFixedCoordinateDimensions() { this->m_FixedCoordinateDimensions.clear(); } /** Add a coordinate list of fixed coordinate dimensions. */ void AddFixedCoordinateDimension( const int dim ) { this->m_FixedCoordinateDimensions.push_back( dim ); } /** Set Jacobian volume preservation constraint weight. */ void SetJacobianConstraintWeight( const float weight ) { this->m_JacobianConstraintWeight = weight; } /** Initialize transformation. */ virtual void InitTransformation( const Vector3D& domain, const Types::Coordinate gridSpacing, const bool exact ); /** Refine transformation control point grid by factor 2. */ virtual void RefineTransformation(); /** Reset channels, clear all images. */ virtual void ClearAllChannels() { this->ClearReformattedFloatingChannels(); Superclass::ClearAllChannels(); } /** Compute functional value. */ virtual typename Self::ReturnType Evaluate(); /** Compute functional value and gradient. */ virtual typename Self::ReturnType EvaluateWithGradient( CoordinateVector& v, CoordinateVector& g, const Types::Coordinate step = 1 ); protected: /** Update all transformation-related data after init, refine, or image change. */ virtual void NewReferenceChannelGeometry() { this->UpdateTransformationData(); } private: /** Initial affine transformation. */ AffineXform m_InitialAffineTransformation; /** Update all transformation-related data after init, refine, or image change. */ virtual void UpdateTransformationData(); /** Floating channels reformatted under current baseline transformation. */ std::vector< std::vector > m_ReformattedFloatingChannels; /** Allocate reformatted floating channel memory. */ virtual void AllocateReformattedFloatingChannels(); /** Clear and free reformatted floating channel memory. */ virtual void ClearReformattedFloatingChannels(); /** Evaluate metric after a local transformation change. */ typename Self::ReturnType EvaluateIncremental( const SplineWarpXform* warp, MetricData& metricData, const DataGrid::RegionType& region ); /** Continue metric computation and store reformatted floating channels for local recomputation. */ virtual void ContinueMetricStoreReformatted( MetricData& metricData, const size_t rindex, const Vector3D& fvector ); /** Locally undo metric computation. */ virtual void BacktraceMetric( MetricData& metricData, const DataGrid::RegionType& voi ); /** Parameter step scale vector. */ std::vector m_StepScaleVector; /** Table of volumes of influence per warp control point. */ std::vector m_VolumeOfInfluenceVector; /** Flag for entropy vs. intensity thresholding */ bool m_AdaptiveFixEntropyThreshold; /** Threshold for adaptive parameter fixing. */ float m_AdaptiveFixThreshFactor; /** List of coordinate dimensions (x=0, y=1, z=2) that are fixed during optimization. */ std::list m_FixedCoordinateDimensions; /** Weight for Jacobian volume preservation constraint. */ float m_JacobianConstraintWeight; /** Update fixed and active control points in transformation. */ virtual void UpdateTransformationFixedControlPoints(); /** Flag whether fixed control points need to be updated. */ bool m_UpdateTransformationFixedControlPointsRequired; /** Number of parallel threads. */ const size_t m_NumberOfThreads; /** Thread function for gradient computation. */ static void EvaluateThreadFunction( void* args, const size_t taskIdx, const size_t taskCnt, const size_t threadIdx, const size_t ); /** Mutex lock for shared metric data object. */ MutexLock m_MetricDataMutex; /** Separate transformations for threaded gradient computation. */ std::vector m_ThreadTransformations; /** Parameters for threaded gradient computation. */ class EvaluateGradientThreadParameters : /// Inherit from generic thread parameters. public ThreadParameters { public: /// Global parameter step scale factor. Types::Coordinate m_Step; /// Pointer to array with computed local gradient components. Types::Coordinate* m_Gradient; /** Current baseline parameter vector. */ const CoordinateVector* m_ParameterVector; /// Current metric value. typename Self::ReturnType m_MetricBaseValue; }; /** Thread function for gradient computation. */ static void EvaluateWithGradientThreadFunction( void* args, const size_t taskIdx, const size_t taskCnt, const size_t threadIdx, const size_t ); /** Make functional class with Jacobian intensity correction a friend. */ template friend class SplineWarpMultiChannelIntensityCorrectionRegistrationFunctional; }; //@} } // namespace cmtk #include "cmtkSplineWarpMultiChannelRegistrationFunctional.txx" #include "cmtkSplineWarpMultiChannelRegistrationFunctionalThreadFunctions.txx" #endif // #ifndef __cmtkSplineWarpMultiChannelRegistrationFunctional_h_included_ cmtk-3.3.1/libs/Registration/cmtkSplineWarpMultiChannelRegistrationFunctional.txx000066400000000000000000000463451276303427400305350ustar00rootroot00000000000000/* // // Copyright 1997-2010 Torsten Rohlfing // // Copyright 2004-2013 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4882 $ // // $LastChangedDate: 2013-09-27 15:16:36 -0700 (Fri, 27 Sep 2013) $ // // $LastChangedBy: torstenrohlfing $ // */ #include #include #include #include #ifdef HAVE_IEEEFP_H # include #endif #include namespace cmtk { /** \addtogroup Registration */ //@{ template SplineWarpMultiChannelRegistrationFunctional ::SplineWarpMultiChannelRegistrationFunctional() : m_AdaptiveFixEntropyThreshold( false ), m_AdaptiveFixThreshFactor( 0.5 ), m_JacobianConstraintWeight( 0.0 ), m_UpdateTransformationFixedControlPointsRequired( false ), m_NumberOfThreads( ThreadPool::GetGlobalThreadPool().GetNumberOfThreads() ) { } template template SplineWarpMultiChannelRegistrationFunctional ::SplineWarpMultiChannelRegistrationFunctional ( AffineMultiChannelRegistrationFunctional& affineFunctional ) : m_AdaptiveFixEntropyThreshold( false ), m_AdaptiveFixThreshFactor( 0.5 ), m_JacobianConstraintWeight( 0.0 ), m_UpdateTransformationFixedControlPointsRequired( false ), m_NumberOfThreads( ThreadPool::GetGlobalThreadPool().GetNumberOfThreads() ) { this->SetInitialAffineTransformation( affineFunctional.GetTransformation() ); this->AddReferenceChannels( affineFunctional.m_ReferenceChannels.begin(), affineFunctional.m_ReferenceChannels.end() ); this->AddFloatingChannels( affineFunctional.m_FloatingChannels.begin(), affineFunctional.m_FloatingChannels.end() ); } template void SplineWarpMultiChannelRegistrationFunctional ::InitTransformation( const Vector3D& domain, const Types::Coordinate gridSpacing, const bool exact ) { if ( this->m_ReferenceChannels.size() == 0 ) { StdErr << "ERROR: call to SplineWarpMultiChannelRegistrationFunctional::InitTransformation() before reference channel image was set.\n"; exit( 1 ); } this->m_Transformation.Init( domain, gridSpacing, &this->m_InitialAffineTransformation, exact ); this->m_ThreadTransformations.resize( this->m_NumberOfThreads, SplineWarpXform::SmartPtr::Null() ); for ( size_t thread = 0; thread < this->m_NumberOfThreads; ++thread ) { this->m_ThreadTransformations[thread] = SplineWarpXform::SmartPtr( new SplineWarpXform( domain, gridSpacing, &this->m_InitialAffineTransformation, exact ) ); } this->UpdateTransformationData(); } template void SplineWarpMultiChannelRegistrationFunctional ::RefineTransformation() { this->m_Transformation.Refine(); for ( size_t thread = 0; thread < this->m_ThreadTransformations.size(); ++thread ) { this->m_ThreadTransformations[thread]->Refine(); } this->UpdateTransformationData(); } template void SplineWarpMultiChannelRegistrationFunctional ::UpdateTransformationData() { this->m_Transformation.RegisterVolume( *(this->m_ReferenceChannels[0]) ); for ( size_t thread = 0; thread < this->m_ThreadTransformations.size(); ++thread ) { this->m_ThreadTransformations[thread]->RegisterVolume( *(this->m_ReferenceChannels[0]) ); } this->m_StepScaleVector.resize( this->m_Transformation.VariableParamVectorDim() ); for ( size_t idx = 0; idx < this->m_StepScaleVector.size(); ++idx ) { this->m_StepScaleVector[idx] = this->GetParamStep( idx ); } const size_t numberOfControlPoints = this->m_Transformation.VariableParamVectorDim() / 3; this->m_VolumeOfInfluenceVector.resize( numberOfControlPoints ); const UniformVolume::CoordinateRegionType referenceDomain( this->m_ReferenceChannels[0]->m_Offset, this->m_ReferenceChannels[0]->m_Size ); for ( size_t idx = 0; idx < numberOfControlPoints; ++idx ) { this->m_VolumeOfInfluenceVector[idx] = this->m_ReferenceChannels[0]->GetGridRange( this->m_Transformation.GetVolumeOfInfluence( idx * 3, referenceDomain ) ); } m_UpdateTransformationFixedControlPointsRequired = true; } template void SplineWarpMultiChannelRegistrationFunctional ::UpdateTransformationFixedControlPoints() { this->m_UpdateTransformationFixedControlPointsRequired = false; std::vector valueRange( this->m_NumberOfChannels, Types::DataItemRange( 0,0 ) ); { size_t channel = 0; for ( size_t ref = 0; ref < this->m_ReferenceChannels.size(); ++ref, ++channel ) { valueRange[channel] = this->m_ReferenceChannels[ref]->GetData()->GetRange(); } for ( size_t flt = 0; flt < this->m_FloatingChannels.size(); ++flt, ++channel ) { valueRange[channel] = this->m_FloatingChannels[flt]->GetData()->GetRange(); } } const size_t numberOfControlPoints = this->m_Transformation.VariableParamVectorDim() / 3; const UniformVolume::CoordinateRegionType referenceDomain( this->m_ReferenceChannels[0]->m_Offset, this->m_ReferenceChannels[0]->m_Size ); std::vector active( numberOfControlPoints ); std::fill( active.begin(), active.end(), true ); if ( this->m_AdaptiveFixThreshFactor > 0 ) { if ( this->m_AdaptiveFixEntropyThreshold ) { Histogram histogram( 128 ); std::vector channelEntropy( numberOfControlPoints * this->m_NumberOfChannels ); std::vector minEntropy( this->m_NumberOfChannels ); std::vector maxEntropy( this->m_NumberOfChannels ); size_t channelEntropyIdx = 0; for ( size_t cp = 0; cp < numberOfControlPoints; ++cp ) { // We cannot use the precomputed table of VOIs here because in "fast" mode, these VOIs are smaller than we want them here. const DataGrid::RegionType region = this->m_ReferenceChannels[0]->GetGridRange( this->m_Transformation.GetVolumeOfInfluence( 3 * cp, referenceDomain, false /*fast*/ ) ); for ( size_t channel = 0; channel < this->m_NumberOfChannels; ++channel ) { histogram.SetRange( valueRange[channel] ); size_t r = region.From()[0] + this->m_ReferenceDims[0] * ( region.From()[1] + this->m_ReferenceDims[1] * region.From()[2] ); const int endOfLine = ( region.From()[0] + ( this->m_ReferenceDims[0]-region.To()[0]) ); const int endOfPlane = this->m_ReferenceDims[0] * ( region.From()[1] + (this->m_ReferenceDims[1]-region.To()[1]) ); if ( channel < this->m_ReferenceChannels.size() ) { const TypedArray* refChannel = this->m_ReferenceChannels[channel]->GetData(); for ( int pZ = region.From()[2]; pZGet( refValue, r ) ) histogram.Increment( histogram.ValueToBin( refValue ) ); } } else { const float* fltChannel = &(this->m_ReformattedFloatingChannels[channel-this->m_ReferenceChannels.size()][0]); for ( int pZ = region.From()[2]; pZ( histogram.GetEntropy() ); } } size_t idx = 0; for ( size_t channel = 0; channel < this->m_NumberOfChannels; ++channel, ++idx ) { minEntropy[channel] = channelEntropy[idx]; maxEntropy[channel] = channelEntropy[idx]; } for ( size_t cp = 1; cp < numberOfControlPoints; ++cp ) { for ( size_t channel = 0; channel < this->m_NumberOfChannels; ++channel, ++idx ) { minEntropy[channel] = std::min( minEntropy[channel], channelEntropy[idx] ); maxEntropy[channel] = std::max( maxEntropy[channel], channelEntropy[idx] ); } } for ( size_t channel = 0; channel < this->m_NumberOfChannels; ++channel, ++idx ) { minEntropy[channel] += this->m_AdaptiveFixThreshFactor * (maxEntropy[channel] - minEntropy[channel]); } idx = 0; for ( size_t cp=0; cpm_NumberOfChannels; ++channel, ++idx ) { if ( channelEntropy[idx] > minEntropy[channel] ) { active[cp] = true; } } } } else // use intensity, not entropy threshold { // scale min values with threshold factor for ( size_t channel = 0; channel < this->m_NumberOfChannels; ++channel ) { valueRange[channel].m_LowerBound = valueRange[channel].m_LowerBound + valueRange[channel].Width() * this->m_AdaptiveFixThreshFactor; } for ( size_t cp = 0; cp < numberOfControlPoints; ++cp ) { // We cannot use the precomputed table of VOIs here because in "fast" mode, these VOIs are smaller than we want them here. const DataGrid::RegionType region = this->m_ReferenceChannels[0]->GetGridRange( this->m_Transformation.GetVolumeOfInfluence( 3 * cp, referenceDomain, false /*fast*/ ) ); active[cp] = false; for ( size_t channel = 0; channel < this->m_NumberOfChannels; ++channel ) { size_t r = region.From()[0] + this->m_ReferenceDims[0] * ( region.From()[1] + this->m_ReferenceDims[1] * region.From()[2] ); const int endOfLine = ( region.From()[0] + ( this->m_ReferenceDims[0]-region.To()[0]) ); const int endOfPlane = this->m_ReferenceDims[0] * ( region.From()[1] + (this->m_ReferenceDims[1]-region.To()[1]) ); if ( channel < this->m_ReferenceChannels.size() ) { const TypedArray* refChannel = this->m_ReferenceChannels[channel]->GetData(); for ( int pZ = region.From()[2]; pZGet( refValue, r ) && (refValue > valueRange[channel].m_LowerBound ) ) { /* found pixel over threshold; set flag and terminate loops */ active[cp] = true; channel = this->m_NumberOfChannels; pX = region.To()[0]; pY = region.To()[1]; pZ = region.To()[2]; } } } else { const float* fltChannel = &(this->m_ReformattedFloatingChannels[channel-this->m_ReferenceChannels.size()][0]); for ( int pZ = region.From()[2]; pZ valueRange[channel].m_LowerBound) ) { /* found pixel over threshold; set flag and terminate loops */ active[cp] = true; channel = this->m_NumberOfChannels; pX = region.To()[0]; pY = region.To()[1]; pZ = region.To()[2]; } } } } } } } size_t inactive = 0; for ( size_t cp = 0; cp < numberOfControlPoints; ++cp ) { size_t param = 3 * cp; if ( active[cp] ) { for ( size_t dim = 0; dim<3; ++dim, ++param ) { this->m_Transformation.SetParameterActive( param ); this->m_StepScaleVector[param] = this->GetParamStep( param ); } } else { for ( size_t dim = 0; dim<3; ++dim, ++param ) { this->m_Transformation.SetParameterInactive( param ); this->m_StepScaleVector[param] = 0; } inactive += 3; } } // now fix any fixed coordinate dimensions for ( std::list::const_iterator it = this->m_FixedCoordinateDimensions.begin(); it != this->m_FixedCoordinateDimensions.end(); ++it ) { size_t param = *it; for ( size_t cp = 0; cp < numberOfControlPoints; ++cp, param += 3 ) { this->m_Transformation.SetParameterInactive( param ); this->m_StepScaleVector[param] = 0; if ( active[cp] ) ++inactive; } } DebugOutput( 1 ).GetStream().printf( "Deactivated %d out of %d control points.\n", (int)inactive / 3, (int)this->ParamVectorDim() / 3 ); } template void SplineWarpMultiChannelRegistrationFunctional ::ContinueMetricStoreReformatted( MetricData& metricData, const size_t rindex, const Vector3D& fvector ) { std::vector values( this->m_NumberOfChannels ); size_t idx = 0; for ( size_t ref = 0; ref < this->m_ReferenceChannels.size(); ++ref ) { if ( !this->m_ReferenceChannels[ref]->GetDataAt( values[idx++], rindex ) ) return; } for ( size_t flt = 0; flt < this->m_FloatingChannels.size(); ++flt ) { if ( !this->m_FloatingInterpolators[flt]->GetDataAt( fvector, values[idx++] ) ) { for ( size_t f = 0; f < this->m_FloatingChannels.size(); ++f ) this->m_ReformattedFloatingChannels[f][rindex] = std::numeric_limits::signaling_NaN(); return; } } idx = this->m_ReferenceChannels.size(); for ( size_t flt = 0; flt < this->m_FloatingChannels.size(); ++flt, ++idx ) this->m_ReformattedFloatingChannels[flt][rindex] = static_cast( values[idx] ); metricData += values; } template void SplineWarpMultiChannelRegistrationFunctional ::BacktraceMetric( MetricData& metricData, const DataGrid::RegionType& region ) { std::vector values( this->m_NumberOfChannels ); for ( int pZ = region.From()[2]; pZ < region.To()[2]; ++pZ ) { for ( int pY = region.From()[1]; pY < region.To()[1]; ++pY ) { size_t rindex = region.From()[0] + this->m_ReferenceDims[0] * ( pY + this->m_ReferenceDims[1] ); for ( int pX = region.From()[0]; pX < region.To()[0]; ++pX, ++rindex ) { bool allChannelsValid = true; size_t idx = 0; for ( size_t ref = 0; (ref < this->m_ReferenceChannels.size()) && allChannelsValid; ++ref ) { if ( !this->m_ReferenceChannels[ref]->GetDataAt( values[idx++], rindex ) ) allChannelsValid = false; } for ( size_t flt = 0; (flt < this->m_FloatingChannels.size()) && allChannelsValid; ++flt, ++idx ) { values[idx] = this->m_ReformattedFloatingChannels[flt][rindex]; if ( !finite( values[idx] ) ) allChannelsValid = false; } if ( allChannelsValid ) { metricData -= values; } } } } } template typename SplineWarpMultiChannelRegistrationFunctional::ReturnType SplineWarpMultiChannelRegistrationFunctional ::Evaluate() { if ( this->m_ReformattedFloatingChannels.size() == 0 ) { this->AllocateReformattedFloatingChannels(); } this->m_MetricData.Init( this ); ThreadPool& threadPool = ThreadPool::GetGlobalThreadPool(); const size_t numberOfThreads = threadPool.GetNumberOfThreads(); const size_t numberOfTasks = 4 * numberOfThreads - 3; std::vector< ThreadParameters > threadParams( numberOfTasks ); for ( size_t taskIdx = 0; taskIdx < numberOfTasks; ++taskIdx ) { threadParams[taskIdx].thisObject = this; } threadPool.Run( EvaluateThreadFunction, threadParams ); typename Self::ReturnType costFunction = this->GetMetric( this->m_MetricData ); if ( this->m_JacobianConstraintWeight > 0 ) { costFunction -= this->m_JacobianConstraintWeight * this->m_Transformation.GetJacobianConstraint(); } return costFunction; } template typename SplineWarpMultiChannelRegistrationFunctional::ReturnType SplineWarpMultiChannelRegistrationFunctional ::EvaluateIncremental ( const SplineWarpXform* transformation, MetricData& metricData, const DataGrid::RegionType& region ) { const size_t pixelsPerLineRegion = region.To()[0] - region.From()[0]; std::vector pFloating( pixelsPerLineRegion ); const DataGrid::IndexType& dims = this->m_ReferenceDims; const int dimsX = dims[0], dimsY = dims[1]; for ( int pZ = region.From()[2]; pZ < region.To()[2]; ++pZ ) { for ( int pY = region.From()[1]; pY < region.To()[1]; ++pY ) { transformation->GetTransformedGridRow( pixelsPerLineRegion, &pFloating[0], region.From()[0], pY, pZ ); size_t r = region.From()[0] + dimsX * (pY + dimsY * pZ ); for ( int pX = region.From()[0]; pX < region.To()[0]; ++pX, ++r ) { // Continue metric computation. this->ContinueMetric( metricData, r, pFloating[pX-region.From()[0]] ); } } } return this->GetMetric( metricData ); } template typename SplineWarpMultiChannelRegistrationFunctional::ReturnType SplineWarpMultiChannelRegistrationFunctional ::EvaluateWithGradient ( CoordinateVector& v, CoordinateVector& g, const Types::Coordinate step ) { const typename Self::ReturnType current = this->EvaluateAt( v ); // need to call EvaluateAt() first to make sure all reformatted floating channels are up to date. if ( this->m_UpdateTransformationFixedControlPointsRequired ) this->UpdateTransformationFixedControlPoints(); ThreadPool& threadPool = ThreadPool::GetGlobalThreadPool(); const size_t numberOfThreads = threadPool.GetNumberOfThreads(); const size_t numberOfTasks = 4 * numberOfThreads - 3; std::vector< EvaluateGradientThreadParameters > threadParams( numberOfTasks ); for ( size_t taskIdx = 0; taskIdx < numberOfTasks; ++taskIdx ) { threadParams[taskIdx].thisObject = this; threadParams[taskIdx].m_Step = step; threadParams[taskIdx].m_ParameterVector = &v; threadParams[taskIdx].m_Gradient = g.Elements; threadParams[taskIdx].m_MetricBaseValue = current; } threadPool.Run( EvaluateWithGradientThreadFunction, threadParams ); return current; } template void SplineWarpMultiChannelRegistrationFunctional ::AllocateReformattedFloatingChannels() { this->m_ReformattedFloatingChannels.resize( this->GetNumberOfFloatingChannels() ); for ( size_t flt = 0; flt < this->m_ReformattedFloatingChannels.size(); ++flt ) { this->m_ReformattedFloatingChannels[flt].resize( this->m_ReferenceChannels[0]->GetNumberOfPixels() ); } } template void SplineWarpMultiChannelRegistrationFunctional ::ClearReformattedFloatingChannels() { this->m_ReformattedFloatingChannels.resize( 0 ); } } // namespace cmtk cmtk-3.3.1/libs/Registration/cmtkSplineWarpMultiChannelRegistrationFunctionalThreadFunctions.txx000066400000000000000000000112101276303427400335350ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2010 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 2453 $ // // $LastChangedDate: 2010-10-18 10:33:06 -0700 (Mon, 18 Oct 2010) $ // // $LastChangedBy: torstenrohlfing $ // */ namespace cmtk { /** \addtogroup Registration */ //@{ template void SplineWarpMultiChannelRegistrationFunctional ::EvaluateThreadFunction( void* args, const size_t taskIdx, const size_t taskCnt, const size_t, const size_t ) { ThreadParameters* params = static_cast*>( args ); Self* This = params->thisObject; const Self* constThis = This; typename Self::MetricData metricData; metricData.Init( This ); const DataGrid::IndexType& dims = constThis->m_ReferenceDims; const int dimsX = dims[0], dimsY = dims[1], dimsZ = dims[2]; std::vector pFloating( dimsX ); for ( int pZ = taskIdx; pZ < dimsZ; pZ += taskCnt ) { for ( int pY = 0; pY < dimsY; ++pY ) { constThis->m_Transformation.GetTransformedGridRow( dimsX, &pFloating[0], 0, pY, pZ ); size_t r = dimsX * (pY + dimsY * pZ ); for ( int pX = 0; pX < dimsX; ++pX, ++r ) { // Continue metric computation. This->ContinueMetricStoreReformatted( metricData, r, pFloating[pX] ); } } } This->m_MetricDataMutex.Lock(); This->m_MetricData += metricData; This->m_MetricDataMutex.Unlock(); } template void SplineWarpMultiChannelRegistrationFunctional ::EvaluateWithGradientThreadFunction( void* args, const size_t taskIdx, const size_t taskCnt, const size_t threadIdx, const size_t ) { EvaluateGradientThreadParameters* params = static_cast( args ); Self* This = params->thisObject; const Self* constThis = This; const size_t numberOfParameters = This->VariableParamVectorDim(); const size_t numberOfControlPoints = numberOfParameters / 3; SplineWarpXform::SmartPtr transformation = constThis->m_ThreadTransformations[threadIdx]; transformation->SetParamVector( *(params->m_ParameterVector) ); for ( size_t cp = taskIdx; cp < numberOfControlPoints; cp += taskCnt ) { typename Superclass::MetricData localMetricDataCP = constThis->m_MetricData; This->BacktraceMetric( localMetricDataCP, constThis->m_VolumeOfInfluenceVector[cp] ); size_t idx = 3 * cp; for ( int dim = 0; dim < 3; ++dim, ++idx ) { if ( constThis->m_StepScaleVector[idx] <= 0 ) { params->m_Gradient[idx] = 0; } else { const Types::Coordinate vOld = transformation->GetParameter( idx ); Types::Coordinate thisStep = params->m_Step * constThis->m_StepScaleVector[idx]; typename Superclass::MetricData localMetricData = localMetricDataCP; transformation->SetParameter( idx, vOld + thisStep ); double upper = This->EvaluateIncremental( transformation, localMetricData, constThis->m_VolumeOfInfluenceVector[cp] ); localMetricData = localMetricDataCP; transformation->SetParameter( idx, vOld - thisStep ); double lower = This->EvaluateIncremental( transformation, localMetricData, constThis->m_VolumeOfInfluenceVector[cp] ); transformation->SetParameter( idx, vOld ); if ( constThis->m_JacobianConstraintWeight > 0 ) { double lowerConstraint = 0, upperConstraint = 0; transformation->GetJacobianConstraintDerivative( lowerConstraint, upperConstraint, idx, constThis->m_VolumeOfInfluenceVector[cp], thisStep ); lower -= constThis->m_JacobianConstraintWeight * lowerConstraint; upper -= constThis->m_JacobianConstraintWeight * upperConstraint; } if ( finite( upper ) && finite(lower) && ((upper > params->m_MetricBaseValue ) || (lower > params->m_MetricBaseValue)) ) { params->m_Gradient[idx] = upper-lower; } else { params->m_Gradient[idx] = 0; } } } } } } // namespace cmtk cmtk-3.3.1/libs/Registration/cmtkSymmetricElasticFunctional.cxx000066400000000000000000000107441276303427400250040ustar00rootroot00000000000000/* // // Copyright 2016 Google, Inc. // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2010 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 5399 $ // // $LastChangedDate: 2016-01-14 19:00:40 -0800 (Thu, 14 Jan 2016) $ // // $LastChangedBy: torstenrohlfing $ // */ #include #include #include #include #include #include #include #include #include namespace cmtk { /** \addtogroup Registration */ //@{ template void SymmetricElasticFunctional_Template::SetWarpXform ( SplineWarpXform::SmartPtr& warpFwd, SplineWarpXform::SmartPtr& warpBwd ) { this->FwdFunctional.SetWarpXform( warpFwd ); this->FwdFunctional.SetInverseTransformation( warpBwd ); this->BwdFunctional.SetWarpXform( warpBwd ); this->BwdFunctional.SetInverseTransformation( warpFwd ); } template typename SymmetricElasticFunctional_Template::ReturnType SymmetricElasticFunctional_Template ::EvaluateWithGradient( CoordinateVector& v, CoordinateVector& g, const Types::Coordinate step ) { CoordinateVector vFwd( this->FwdFunctional.ParamVectorDim(), v.Elements, false /*freeElements*/ ); CoordinateVector gFwd( this->FwdFunctional.ParamVectorDim(), g.Elements, false /*freeElements*/ ); CoordinateVector vBwd( this->BwdFunctional.ParamVectorDim(), v.Elements+this->FwdFunctional.ParamVectorDim(), false /*freeElements*/ ); CoordinateVector gBwd( this->BwdFunctional.ParamVectorDim(), g.Elements+this->FwdFunctional.ParamVectorDim(), false /*freeElements*/ ); return this->FwdFunctional.EvaluateWithGradient( vFwd, gFwd, step ) + this->BwdFunctional.EvaluateWithGradient( vBwd, gBwd, step ); } SymmetricElasticFunctional* CreateSymmetricElasticFunctional( const int metric, UniformVolume::SmartPtr& refVolume, UniformVolume::SmartPtr& fltVolume ) { switch ( fltVolume->GetData()->GetDataClass() ) { case DATACLASS_UNKNOWN : case DATACLASS_GREY : switch ( metric ) { case 0: return new SymmetricElasticFunctional_Template< VoxelMatchingNormMutInf_Trilinear>( refVolume, fltVolume ); case 1: return new SymmetricElasticFunctional_Template( refVolume, fltVolume ); case 2: return new SymmetricElasticFunctional_Template( refVolume, fltVolume ); case 3: return NULL; // masked NMI retired case 4: return new SymmetricElasticFunctional_Template( refVolume, fltVolume ); case 5: return new SymmetricElasticFunctional_Template( refVolume, fltVolume ); default: return NULL; } case DATACLASS_LABEL: switch ( metric ) { case 0: return new SymmetricElasticFunctional_Template( refVolume, fltVolume ); case 1: return new SymmetricElasticFunctional_Template( refVolume, fltVolume ); case 2: return new SymmetricElasticFunctional_Template( refVolume, fltVolume ); case 3: return NULL; // masked NMI retired case 4: return new SymmetricElasticFunctional_Template( refVolume, fltVolume ); case 5: return new SymmetricElasticFunctional_Template( refVolume, fltVolume ); default: return NULL; } } return NULL; } } // namespace cmtk cmtk-3.3.1/libs/Registration/cmtkSymmetricElasticFunctional.h000066400000000000000000000166701276303427400244350ustar00rootroot00000000000000/* // // Copyright 2016 Google, Inc. // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2012 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 5399 $ // // $LastChangedDate: 2016-01-14 19:00:40 -0800 (Thu, 14 Jan 2016) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkSymmetricElasticFunctional_h_included_ #define __cmtkSymmetricElasticFunctional_h_included_ #include #include #include #include namespace cmtk { /** \addtogroup Registration */ //@{ /// Symmtric-consistent elastic registration functional. class SymmetricElasticFunctional : /** Inherit from generic functional. */ public Functional { public: /// This class. typedef SymmetricElasticFunctional Self; /// Smart pointer to this class. typedef SmartPointer SmartPtr; /// Superclass. typedef Functional Superclass; /// Set inverse consistency weight. virtual void SetInverseConsistencyWeight( const Self::ReturnType ) = 0; /// Set adaptive parameter fixing flag. virtual void SetAdaptiveFixParameters( const bool ) = 0; /// Set adaptive parameter fixing flag. virtual void SetAdaptiveFixThreshFactor( const Self::ReturnType ) = 0; /// Set Jacobian constraint weight. virtual void SetJacobianConstraintWeight( const Self::ReturnType ) = 0; /// Set Jacobian constraint weight. virtual void SetRigidityConstraintWeight( const Self::ReturnType ) = 0; /// Set smoothness constraint weight. virtual void SetGridEnergyWeight( const Self::ReturnType ) = 0; /// Set warp for forward and backward functional. virtual void SetWarpXform( SplineWarpXform::SmartPtr& warpFwd, SplineWarpXform::SmartPtr& warpBwd ) = 0; }; /// Template for symmtric-consistent elastic registration functional. template class SymmetricElasticFunctional_Template : /** Inherit from non-template base functional class. */ public SymmetricElasticFunctional { public: /// This class. typedef SymmetricElasticFunctional_Template Self; /// Smart pointer to this class. typedef SmartPointer SmartPtr; /// Superclass. typedef SymmetricElasticFunctional Superclass; /// The forward functional. VoxelMatchingElasticFunctional_Template FwdFunctional; /// The backward functional. VoxelMatchingElasticFunctional_Template BwdFunctional; /// Constructor. SymmetricElasticFunctional_Template( UniformVolume::SmartPtr& reference, UniformVolume::SmartPtr& floating ) : FwdFunctional( reference, floating ), BwdFunctional( floating, reference ) {} /// Set inverse consistency weight. virtual void SetInverseConsistencyWeight( const typename Self::ReturnType inverseConsistencyWeight ) { this->FwdFunctional.SetInverseConsistencyWeight( inverseConsistencyWeight ); this->BwdFunctional.SetInverseConsistencyWeight( inverseConsistencyWeight ); } /// Set adaptive parameter fixing flag. virtual void SetAdaptiveFixParameters( const bool adaptiveFixParameters ) { this->FwdFunctional.SetAdaptiveFixParameters( adaptiveFixParameters ); this->BwdFunctional.SetAdaptiveFixParameters( adaptiveFixParameters ); } /// Set adaptive parameter fixing threshold. virtual void SetAdaptiveFixThreshFactor( const typename Self::ReturnType threshFactor ) { this->FwdFunctional.SetAdaptiveFixThreshFactor( threshFactor ); this->BwdFunctional.SetAdaptiveFixThreshFactor( threshFactor ); } /// Set Jacobian constraint weight. virtual void SetJacobianConstraintWeight( const typename Self::ReturnType jacobianConstraintWeight ) { this->FwdFunctional.SetJacobianConstraintWeight( jacobianConstraintWeight ); this->BwdFunctional.SetJacobianConstraintWeight( jacobianConstraintWeight ); } /// Set rigidity constraint weight. virtual void SetRigidityConstraintWeight( const typename Self::ReturnType rigidityConstraintWeight ) { this->FwdFunctional.SetRigidityConstraintWeight( rigidityConstraintWeight ); this->BwdFunctional.SetRigidityConstraintWeight( rigidityConstraintWeight ); } /// Set smoothness constraint weight. virtual void SetGridEnergyWeight( const typename Self::ReturnType gridEnergyWeight ) { this->FwdFunctional.SetGridEnergyWeight( gridEnergyWeight ); this->BwdFunctional.SetGridEnergyWeight( gridEnergyWeight ); } /// Set warp for forward and backward functional. virtual void SetWarpXform( SplineWarpXform::SmartPtr& warpFwd, SplineWarpXform::SmartPtr& warpBwd ); /// Return parameter vector. virtual void GetParamVector ( CoordinateVector& v ) { CoordinateVector vFwd, vBwd; this->FwdFunctional.GetParamVector( vFwd ); this->BwdFunctional.GetParamVector( vBwd ); v.SetDim( vFwd.Dim + vBwd.Dim ); v.CopyToOffset( vFwd ); v.CopyToOffset( vBwd, vFwd.Dim ); } /// Evaluate functional value and gradient. virtual typename Self::ReturnType EvaluateWithGradient( CoordinateVector& v, CoordinateVector& g, const Types::Coordinate step = 1 ); /// Evaluate functional value. virtual typename Self::ReturnType EvaluateAt ( CoordinateVector& v ) { CoordinateVector vFwd( this->FwdFunctional.ParamVectorDim(), v.Elements, false /*freeElements*/ ); CoordinateVector vBwd( this->BwdFunctional.ParamVectorDim(), v.Elements+this->FwdFunctional.ParamVectorDim(), false /*freeElements*/ ); return this->FwdFunctional.EvaluateAt( vFwd ) + this->BwdFunctional.EvaluateAt( vBwd ); } virtual typename Self::ReturnType Evaluate () { return this->FwdFunctional.Evaluate() + this->BwdFunctional.Evaluate(); } /// Get parameter stepping in milimeters. virtual Types::Coordinate GetParamStep( const size_t idx, const Types::Coordinate mmStep = 1 ) const { if ( idx < this->FwdFunctional.ParamVectorDim() ) return this->FwdFunctional.GetParamStep( idx, mmStep ); else return this->BwdFunctional.GetParamStep( idx - this->FwdFunctional.ParamVectorDim(), mmStep ); } /// Return the transformation's parameter vector dimension. virtual size_t ParamVectorDim() const { return this->FwdFunctional.ParamVectorDim() + this->BwdFunctional.ParamVectorDim(); } /// Return the number of variable parameters of the transformation. virtual size_t VariableParamVectorDim() const { return this->FwdFunctional.VariableParamVectorDim() + this->BwdFunctional.VariableParamVectorDim(); } }; /// Constructor function. SymmetricElasticFunctional* CreateSymmetricElasticFunctional( const int metric, UniformVolume::SmartPtr& refVolume, UniformVolume::SmartPtr& fltVolume ); //@} } // namespace cmtk #endif // #ifndef __cmtkSymmetricElasticFunctional_h_included_ cmtk-3.3.1/libs/Registration/cmtkSymmetryPlaneFunctional.cxx000066400000000000000000000071001276303427400243240ustar00rootroot00000000000000/* // // Copyright 2016 Google, Inc. // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2012 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 5400 $ // // $LastChangedDate: 2016-01-14 19:42:40 -0800 (Thu, 14 Jan 2016) $ // // $LastChangedBy: torstenrohlfing $ // */ #include "cmtkSymmetryPlaneFunctional.h" #include namespace cmtk { /** \addtogroup Registration */ //@{ SymmetryPlaneFunctional::SymmetryPlaneFunctional ( UniformVolume::SmartPtr& volume ) : m_Volume( NULL ) { this->SetVolume( volume ); m_Metric = new VoxelMatchingNormMutInf<>( volume, volume ); } SymmetryPlaneFunctional::SymmetryPlaneFunctional ( UniformVolume::SmartPtr& volume, const Types::DataItemRange& valueRange ) : m_Volume( NULL ) { this->SetVolume( volume ); m_Metric = new VoxelMatchingNormMutInf<>( volume, volume, valueRange, valueRange ); } Types::Coordinate SymmetryPlaneFunctional::GetParamStep ( const size_t idx, const Types::Coordinate mmStep ) const { switch ( idx ) { // plane offset is a translation case 0: return mmStep; // the other two parameters are rotations case 1: case 2: return mmStep / sqrt( MathUtil::Square( 0.5 * m_Volume->m_Size[0] ) + MathUtil::Square( 0.5 * m_Volume->m_Size[1] ) + MathUtil::Square( 0.5 * m_Volume->m_Size[2] ) ) * 90/M_PI; } return mmStep; } SymmetryPlaneFunctional::ReturnType SymmetryPlaneFunctional::Evaluate() { const TransformedVolumeAxes gridHash( *m_Volume, this->m_ParametricPlane, m_Volume->Deltas().begin() ); const Vector3D *HashX = gridHash[0], *HashY = gridHash[1], *HashZ = gridHash[2]; Vector3D pFloating; m_Metric->Reset(); const DataGrid::IndexType& Dims = m_Volume->GetDims(); const Types::GridIndexType DimsX = Dims[0], DimsY = Dims[1], DimsZ = Dims[2]; Types::GridIndexType fltIdx[3]; Types::Coordinate fltFrac[3]; Vector3D planeStart, rowStart; Types::GridIndexType r = 0; for ( Types::GridIndexType pZ = 0; pZFindVoxelByIndex( pFloating, fltIdx, fltFrac ) ) { // Compute data index of the model voxel in the model volume. Types::GridIndexType offset = fltIdx[0] + DimsX * (fltIdx[1] + DimsY * fltIdx[2]); // Continue metric computation. m_Metric->Proceed( (Types::GridIndexType) r, offset, fltFrac ); } } } } return m_Metric->Get(); } } // namespace cmtk cmtk-3.3.1/libs/Registration/cmtkSymmetryPlaneFunctional.h000066400000000000000000000062701276303427400237600ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2010 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 2398 $ // // $LastChangedDate: 2010-10-05 14:54:37 -0700 (Tue, 05 Oct 2010) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkSymmetryPlaneFunctional_h_included_ #define __cmtkSymmetryPlaneFunctional_h_included_ #include #include #include #include #include namespace cmtk { /** \addtogroup Registration */ //@{ /** Functional for finding a symmetry plane in 3-D volumes. */ class SymmetryPlaneFunctional : /// Inherit functional interface. public Functional { protected: /// Volume image. UniformVolume::SmartPtr m_Volume; public: /// This class. typedef SymmetryPlaneFunctional Self; /// Smart pointer to this class. typedef SmartPointer SmartPtr; /// Superclass typedef Functional Superclass; /// Constructor. SymmetryPlaneFunctional( UniformVolume::SmartPtr& volume ); /// Constructor with value range limits. SymmetryPlaneFunctional( UniformVolume::SmartPtr& volume, const Types::DataItemRange& valueRange ); /// Destructor. virtual ~SymmetryPlaneFunctional() {} /// Set volume. void SetVolume( UniformVolume::SmartPtr& volume ) { m_Volume = volume; } virtual void GetParamVector ( CoordinateVector& v ) { this->m_ParametricPlane.GetParameters( v ); } /// Compute functional value. virtual Self::ReturnType Evaluate(); /// Compute functional value. virtual Self::ReturnType EvaluateAt( CoordinateVector& v ) { this->m_ParametricPlane.SetParameters( v ); return this->Evaluate(); } /// Return the symmetry plane's parameter vector dimension. virtual size_t ParamVectorDim() const { return 6; } /// Return the number of variable parameters of the transformation. virtual size_t VariableParamVectorDim() const { return 3; } /// Return the parameter stepping for 1 mm optimization steps. virtual Types::Coordinate GetParamStep( const size_t idx, const Types::Coordinate mmStep = 1 ) const; private: /// Image similarity measure. VoxelMatchingNormMutInf<>* m_Metric; /// The symmetry plane. ParametricPlane m_ParametricPlane; }; //@} } // namespace cmtk #endif // #ifndef __cmtkSymmetryPlaneFunctional_h_included_ cmtk-3.3.1/libs/Registration/cmtkTemplateMultiChannelRegistrationFunctional.h000066400000000000000000000066021276303427400276200ustar00rootroot00000000000000/* // // Copyright 1997-2010 Torsten Rohlfing // // Copyright 2004-2012 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 3975 $ // // $LastChangedDate: 2012-03-06 15:04:42 -0800 (Tue, 06 Mar 2012) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkTemplateMultiChannelRegistrationFunctional_h_included_ #define __cmtkTemplateMultiChannelRegistrationFunctional_h_included_ #include #include namespace cmtk { /** \addtogroup Registration */ //@{ /** Class for transformation-templated multi-channel registration functional. */ template class TemplateMultiChannelRegistrationFunctional : /* Inherit from multi-channel registration functional base class. */ public TMetricFunctional { public: /** This class. */ typedef TemplateMultiChannelRegistrationFunctional Self; /** Smart pointer. */ typedef SmartPointer SmartPtr; /** This class. */ typedef TMetricFunctional Superclass; /** The transformation type. */ typedef TXform TransformationType; /** Get transformation. */ TransformationType& GetTransformation() { return this->m_Transformation; } /** Get constant transformation. */ const TransformationType& GetTransformation() const { return this->m_Transformation; } /** Set number of degrees of freedom for transformation. */ void SetNumberDOFs( const int numberDOFs ) { this->m_Transformation.SetNumberDOFs( numberDOFs ); } /// Return parameter vector. virtual void GetParamVector ( CoordinateVector& v ) { this->m_Transformation.GetParamVector( v ); }; /// Return parameter vector. virtual void SetParamVector ( CoordinateVector& v ) { this->m_Transformation.SetParamVector( v ); }; /// Return parameter stepping. virtual Types::Coordinate GetParamStep( const size_t idx, const Types::Coordinate mmStep = 1 ) const { return this->m_Transformation.GetParamStep( idx, this->m_ReferenceSize, mmStep ); } /// Return the transformation's parameter vector dimension. virtual size_t ParamVectorDim() const { return this->m_Transformation.ParamVectorDim(); } /// Return the number of variable parameters of the transformation. virtual size_t VariableParamVectorDim() const { return this->m_Transformation.VariableParamVectorDim(); } protected: /** The templated coordinate transformation. */ TransformationType m_Transformation; }; //@} } // namespace cmtk #endif // #ifndef __cmtkTemplateMultiChannelRegistrationFunctional_h_included_ cmtk-3.3.1/libs/Registration/cmtkTypedArraySimilarity.cxx000066400000000000000000000263351276303427400236360ustar00rootroot00000000000000/* // // Copyright 2004-2013 SRI International // // Copyright 1997-2009 Torsten Rohlfing // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4935 $ // // $LastChangedDate: 2013-10-04 10:33:33 -0700 (Fri, 04 Oct 2013) $ // // $LastChangedBy: torstenrohlfing $ // */ #include "cmtkTypedArraySimilarity.h" #include #include #include namespace cmtk { /** \addtogroup Registration */ //@{ TypedArraySimilarity::ReturnType TypedArraySimilarity::GetMutualInformation ( const TypedArray* array0, const TypedArray* array1, TypedArraySimilarityMemory *const memory ) { if ( ! CheckArrayDimensions( array0, array1 ) ) return std::numeric_limits::signaling_NaN(); size_t dataSize = array0->GetDataSize(); JointHistogram::SmartPtr histogram; if ( memory ) { histogram = JointHistogram::SmartPtr( memory->CreateHistogram( array0, array1 ) ); } else { size_t numBins = std::max( std::min( static_cast( sqrt( (float)dataSize ) ), 128 ), 8 ); histogram = JointHistogram::SmartPtr( new JointHistogram( numBins, numBins ) ); histogram->SetRangeX( array0->GetRange() ); histogram->SetRangeY( array1->GetRange() ); } Types::DataItem value0, value1; for ( unsigned int idx = 0; idx < dataSize; ++idx ) { if ( array0->Get( value0, idx ) && array1->Get( value1, idx ) ) { histogram->Increment( histogram->ValueToBinX( value0 ), histogram->ValueToBinY( value1 ) ); } } return static_cast( histogram->GetMutualInformation( false ) ); } TypedArraySimilarity::ReturnType TypedArraySimilarity::GetCorrelationRatio ( const TypedArray* array0, const TypedArray* array1 ) { // check if both images have same number of pixels. if ( ! CheckArrayDimensions( array0, array1 ) ) return std::numeric_limits::signaling_NaN(); // determine reference image value range. const Types::DataItemRange range = array0->GetRange(); // get pixel count and determine histogram size. const unsigned int dataSize = array0->GetDataSize(); unsigned int numBins = std::max( std::min( static_cast( sqrt( (float)dataSize ) ), 128 ), 8 ); // make sure number of classes doesn't exceed number of distinct values for // discrete data types. if ( (array0->GetType() != TYPE_FLOAT) && (array0->GetType() != TYPE_DOUBLE) ) { numBins = std::min( numBins, static_cast(range.Width()+1) ); } // create histogram to count floating pixels in each reference class Histogram histogram( numBins ); // set value range for histogram to range of reference image. histogram.SetRange( range ); // initialize arrays that hold the sums of all floating values and their // squares, separated by histogram classes of the reference image. double* sumJ = Memory::ArrayC::Allocate( numBins ); memset( sumJ, 0, numBins * sizeof( sumJ[0] ) ); double* sumSquareJ = Memory::ArrayC::Allocate( numBins ); memset( sumSquareJ, 0, numBins * sizeof( sumSquareJ[0] ) ); // sort all image intensities into data structures. Types::DataItem value0, value1; for ( unsigned int idx = 0; idx < dataSize; ++idx ) { // for all valid voxel pairs if ( array0->Get( value0, idx ) && array1->Get( value1, idx ) ) { // what's the reference histogram bin? unsigned int bin = histogram.ValueToBin( value0 ); // count this sample histogram.Increment( bin ); // add floating value to sum of values for this class sumJ[bin] += value1; // add squared floating value to sum of squared values for this class sumSquareJ[bin] += MathUtil::Square( value1 ); } } double invSampleCount = 1.0 / histogram.SampleCount(); // initialize variable for the weighted sum of the sigma^2 values over all // reference intensity classes. double sumSigmaSquare = 0; // run over all bins, i.e., reference classes for ( unsigned int j = 0; j < numBins; ++j ) { // are there any values in the current class? if ( histogram[j] ) { // compute mean floating value for this reference class double mu = sumJ[j] / histogram[j]; // compute variance of floating values for this reference class double sigmaSq = ( mu*mu*histogram[j] - 2.0*mu*sumJ[j] + sumSquareJ[j] ) / histogram[j]; // update sum over all classes with weighted sigma^2 for this class. sumSigmaSquare += (invSampleCount * histogram[j]) * sigmaSq; } } // get variance of complete floating image for normalization Types::DataItem sigmaSqJ, muJ; array1->GetStatistics( muJ, sigmaSqJ ); Memory::ArrayC::Delete( sumJ ); Memory::ArrayC::Delete( sumSquareJ ); // return (supposedly) correlation ratio return 1.0 - (1.0 / sigmaSqJ ) * sumSigmaSquare; } TypedArraySimilarity::ReturnType TypedArraySimilarity::GetNormalizedMutualInformation ( const TypedArray* array0, const TypedArray* array1, TypedArraySimilarityMemory *const memory ) { if ( ! CheckArrayDimensions( array0, array1 ) ) return std::numeric_limits::signaling_NaN(); size_t dataSize = array0->GetDataSize(); JointHistogram::SmartPtr histogram; if ( memory ) histogram = JointHistogram::SmartPtr( memory->CreateHistogram( array0, array1 ) ); else { size_t numBins = std::max( std::min( static_cast( sqrt( (float)dataSize ) ), 128 ), 8 ); histogram = JointHistogram::SmartPtr( new JointHistogram( numBins, numBins ) ); histogram->SetRangeX( array0->GetRange() ); histogram->SetRangeY( array1->GetRange() ); } Types::DataItem value0, value1; for ( unsigned int idx = 0; idx < dataSize; ++idx ) { if ( array0->Get( value0, idx ) && array1->Get( value1, idx ) ) { histogram->Increment( histogram->ValueToBinX( value0 ), histogram->ValueToBinY( value1 ) ); } } return static_cast( histogram->GetMutualInformation( true ) ); } TypedArraySimilarity::ReturnType TypedArraySimilarity::GetMinusMeanSquaredDifference ( const TypedArray* array0, const TypedArray* array1 ) { if ( ! CheckArrayDimensions( array0, array1 ) ) return std::numeric_limits::signaling_NaN(); unsigned int countPixels = 0; Types::DataItem pixel0, pixel1; Types::DataItem sumOfSquares = 0; unsigned int numberOfPixels = array0->GetDataSize(); for ( unsigned int idx = 0; idx < numberOfPixels; ++idx ) { if ( array0->Get( pixel0, idx ) && array1->Get( pixel1, idx ) ) { sumOfSquares += MathUtil::Square( pixel0 - pixel1 ); ++countPixels; } } if ( !countPixels ) return std::numeric_limits::signaling_NaN(); else return static_cast( -(sumOfSquares / (float)countPixels) ); } TypedArraySimilarity::ReturnType TypedArraySimilarity::GetPeakSignalToNoiseRatio ( const TypedArray* data, const TypedArray* signal ) { return -10.0 * log( -GetMinusMeanSquaredDifference( data, signal ) / signal->GetRange().Width() ) / log( 10.0 ); } TypedArraySimilarity::ReturnType TypedArraySimilarity::GetCrossCorrelation ( const TypedArray* array0, const TypedArray* array1 ) { if ( ! CheckArrayDimensions( array0, array1 ) ) return std::numeric_limits::signaling_NaN(); const size_t numberOfPixels = array0->GetDataSize(); Types::DataItem sumOfProducts = 0, sumOfSquares0 = 0, sumOfSquares1 = 0; Types::DataItem mean0 = 0, mean1 = 0; size_t count = 0; for ( int idx = 0; idx < static_cast( numberOfPixels ); ++idx ) { Types::DataItem pixel0, pixel1; if ( array0->Get( pixel0, idx ) && array1->Get( pixel1, idx ) ) { mean0 += pixel0; mean1 += pixel1; ++count; } } if ( count ) { mean0 /= count; mean1 /= count; } for ( int idx = 0; idx < static_cast( numberOfPixels ); ++idx ) { Types::DataItem pixel0, pixel1; if ( array0->Get( pixel0, idx ) && array1->Get( pixel1, idx ) ) { sumOfProducts += (pixel0 - mean0) * (pixel1 - mean1); sumOfSquares0 += MathUtil::Square( pixel0 - mean0 ); sumOfSquares1 += MathUtil::Square( pixel1 - mean1 ); } } return sumOfProducts / ( sqrt( sumOfSquares0 ) * sqrt( sumOfSquares1 ) ); } TypedArray::SmartPtr TypedArraySimilarity::GetDifferenceArray ( const TypedArray* array0, const TypedArray* array1, Types::DataItem &scaleFactor ) { const size_t numberOfPixels = array0->GetDataSize(); TypedArray::SmartPtr differenceArray = TypedArray::Create( GetSignedDataType( array0->GetType() ), numberOfPixels ); Types::DataItem value0, value1; Types::DataItem ATA = 0.0, ATB = 0.0; for ( size_t i=0; iGet( value0, i ); ATA += (value0 * value0); array1->Get( value1, i ); ATB += (value0 * value1); } // invert to get scale convention correct ( array0 = s*array1 ) scaleFactor = ATA/ATB; Types::DataItem pixel0, pixel1; for ( size_t idx = 0; idx < numberOfPixels; ++idx ) { if ( array0->Get( pixel0, idx ) && array1->Get( pixel1, idx ) ) { differenceArray->Set( pixel0 - scaleFactor * pixel1, idx ); } } return differenceArray; } TypedArraySimilarity::ReturnType TypedArraySimilarity::GetDifferenceArrayEntropy ( const TypedArray* array0, const TypedArray* array1, Types::DataItem &scaleFactor ) { TypedArray::SmartPtr differenceArray( GetDifferenceArray( array0, array1, scaleFactor ) ); return differenceArray->GetEntropy(); } bool TypedArraySimilarity::CheckArrayDimensions ( const TypedArray* array0, const TypedArray* array1 ) { if ( !array0 || !array1 ) return false; return ( array0->GetDataSize() == array1->GetDataSize() ); } TypedArraySimilarity::ReturnType TypedArraySimilarity::GetOptimalScale ( const TypedArray* array0, const TypedArray* array1 ) { unsigned int dataSize = array0->GetDataSize(); Types::DataItem value0, value1; TypedArraySimilarity::ReturnType ATA = 0.0; TypedArraySimilarity::ReturnType ATb = 0.0; for (unsigned int i=0; iGet( value0, i ); ATA += (TypedArraySimilarity::ReturnType) (value0 * value0); array1->Get( value1, i ); ATb += (TypedArraySimilarity::ReturnType) (value0 * value1); } return ATb/ATA; } } // namespace cmtk cmtk-3.3.1/libs/Registration/cmtkTypedArraySimilarity.h000066400000000000000000000113341276303427400232540ustar00rootroot00000000000000/* // // Copyright 2004-2010 SRI International // // Copyright 1997-2009 Torsten Rohlfing // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 2398 $ // // $LastChangedDate: 2010-10-05 14:54:37 -0700 (Tue, 05 Oct 2010) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkTypedArraySimilarity_h_included_ #define __cmtkTypedArraySimilarity_h_included_ #include #include #include #include #include namespace cmtk { /** \addtogroup Registration */ //@{ /** Class with operators to compute various pixel similarity measures. */ class TypedArraySimilarity { public: /// Identifiers for all available metrics. typedef enum { /// Mutual information. MI = 0, /// Normalized mutual information. NMI = 1, /// Correlation ratio. CR = 2, /// Cross correlation. CC = 3, /// Mean squared difference. MSD = 4, /// Peak signal-to-noise ratio. PSNR = 5, /// Difference array entropy. DAE = 6 } ID; /// Return type for all similarity measures: match cmtk::Functional's typedef Functional::ReturnType ReturnType; /// Compute mutual information between two pixel arrays. static ReturnType GetMutualInformation ( const TypedArray* array0, const TypedArray* array1, TypedArraySimilarityMemory *const memory = NULL ); /// Compute mutual information between two sets of pixel arrays. static ReturnType GetMutualInformation( const std::vector& data0, const std::vector& data1, const bool normalized = false ); /// Compute norrmalized mutual information between two sets of pixel arrays. static ReturnType GetNormalizedMutualInformation( const std::vector& data0, const std::vector& data1 ) { return GetMutualInformation( data0, data1, true /*normalized*/ ); } /** Compute correlation ratio between two pixel arrays. * This function is implemented using a 1-D histogram. */ static ReturnType GetCorrelationRatio( const TypedArray* array0, const TypedArray* array1 ); /// Compute normalized mutual information between two pixel arrays. static ReturnType GetNormalizedMutualInformation( const TypedArray* array0, const TypedArray* array1, TypedArraySimilarityMemory *const memory = NULL ); /// Compute negated (i.e., sign-switched) mean squared pixel difference between two pixel arrays. static ReturnType GetMinusMeanSquaredDifference( const TypedArray* array0, const TypedArray* array1 ); /** Compute Peak-Signal-to-Noise-Ratio. *\param data Measured data. *\param signal Pure signal without noise. */ static ReturnType GetPeakSignalToNoiseRatio( const TypedArray* data, const TypedArray* signal ); /// Compute normalized cross correlation between two pixel arrays. static ReturnType GetCrossCorrelation( const TypedArray* array0, const TypedArray* array1 ); /** Compute scaled difference of two images. * The values of the second array are scaled with a comon factor so that the * entropy of the difference array is minimized. The resulting scale factor * is returned via a reference argument. */ static TypedArray::SmartPtr GetDifferenceArray( const TypedArray* array0, const TypedArray* array1, Types::DataItem &scaleFactor ); /// Compute entropy of difference of two images. static ReturnType GetDifferenceArrayEntropy( const TypedArray* array0, const TypedArray* array1, Types::DataItem &scaleFactor ); /// Check whether two pixel arrays have matching pixel dimensions. static bool CheckArrayDimensions( const TypedArray* array0, const TypedArray* array1 ); /** Compute the optimal scale factor between two images. * This implementation uses least squares fitting */ static ReturnType GetOptimalScale( const TypedArray* array0, const TypedArray* array1 ); }; } // namespace #endif // #ifndef __cmtkTypedArraySimilarity_h_included_ cmtk-3.3.1/libs/Registration/cmtkTypedArraySimilarityMemory.cxx000066400000000000000000000105721276303427400250230ustar00rootroot00000000000000/* // // Copyright 1997-2010 Torsten Rohlfing // // Copyright 2004-2010 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 2022 $ // // $LastChangedDate: 2010-07-21 15:26:03 -0700 (Wed, 21 Jul 2010) $ // // $LastChangedBy: torstenrohlfing $ // */ #include "cmtkTypedArraySimilarityMemory.h" namespace cmtk { /** \addtogroup Registration */ //@{ const Types::DataItemRange TypedArraySimilarityMemory::GetRangeX ( const TypedArray* array, const size_t numBins ) { if ( ! this->ValidX ) this->NumberBinsX = numBins; if ( ! this->ValidX || this->RepeatCheck ) { const Types::DataItemRange range = array->GetRange(); if ( ! this->ValidX ) { this->RangeX.m_LowerBound = range.m_LowerBound; this->RangeX.m_UpperBound = range.m_UpperBound; this->ValidX = true; } else if ( (range.m_LowerBound < this->RangeX.m_LowerBound) || (range.m_UpperBound > this->RangeX.m_UpperBound ) ) { Types::DataItem binDelta = (this->RangeX.m_UpperBound - this->RangeX.m_LowerBound) / (this->NumberBinsX - 1); if ( range.m_LowerBound < this->RangeX.m_LowerBound ) { const size_t addBins = 1 + static_cast( (this->RangeX.m_LowerBound - range.m_LowerBound) / binDelta); this->RangeX.m_LowerBound -= ( binDelta * addBins ); this->NumberBinsY += addBins; } if ( range.m_UpperBound > this->RangeX.m_UpperBound ) { const size_t addBins = 1 + static_cast( (range.m_UpperBound - this->RangeX.m_UpperBound) / binDelta); this->RangeX.m_UpperBound += ( binDelta * addBins ); this->NumberBinsY += addBins; } } } return this->RangeX; } const Types::DataItemRange TypedArraySimilarityMemory::GetRangeY ( const TypedArray* array, const size_t numBins ) { if ( ! this->ValidY ) this->NumberBinsY = numBins; if ( ! this->ValidY || this->RepeatCheck ) { const Types::DataItemRange range = array->GetRange(); if ( ! this->ValidY ) { this->RangeY.m_LowerBound = range.m_LowerBound; this->RangeY.m_UpperBound = range.m_UpperBound; this->ValidY = true; } else if ( (range.m_LowerBound < this->RangeY.m_LowerBound) || (range.m_UpperBound > this->RangeY.m_UpperBound ) ) { Types::DataItem binDelta = (this->RangeY.m_UpperBound - this->RangeY.m_LowerBound) / (this->NumberBinsY - 1); if ( range.m_LowerBound < this->RangeY.m_LowerBound ) { const size_t addBins = 1 + static_cast( (this->RangeY.m_LowerBound - range.m_LowerBound) / binDelta); this->RangeY.m_LowerBound -= ( binDelta * addBins ); this->NumberBinsY += addBins; } if ( range.m_UpperBound > this->RangeY.m_UpperBound ) { const size_t addBins = 1 + static_cast( (range.m_UpperBound - this->RangeY.m_UpperBound) / binDelta); this->RangeY.m_UpperBound += ( binDelta * addBins ); this->NumberBinsY += addBins; } } } return this->RangeY; } JointHistogram::SmartPtr TypedArraySimilarityMemory::CreateHistogram ( const TypedArray* array0, const TypedArray* array1 ) { const unsigned int dataSize = array0->GetDataSize(); const size_t numBins = std::max( std::min( static_cast( sqrt( (float)dataSize ) ), this->MaxNumBins ), this->MinNumBins ); Types::DataItemRange rangeX = this->GetRangeX( array0, numBins ); Types::DataItemRange rangeY = this->GetRangeY( array1, numBins ); JointHistogram::SmartPtr histogram( new JointHistogram( this->NumberBinsX, this->NumberBinsY ) ); histogram->SetRangeX( rangeX ); histogram->SetRangeY( rangeY ); return histogram; } } // namespace cmtk-3.3.1/libs/Registration/cmtkTypedArraySimilarityMemory.h000066400000000000000000000102451276303427400244450ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2011, 2013 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4873 $ // // $LastChangedDate: 2013-09-24 12:53:12 -0700 (Tue, 24 Sep 2013) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkTypedArraySimilarityMemory_h_included_ #define __cmtkTypedArraySimilarityMemory_h_included_ #include #include namespace cmtk { /** \addtogroup Registration */ //@{ /** Memory for typed array similarity computation. * This class provides for optional persistent memory between similarity * computations. This ensures, for example, identical value ranges in * histogram-based similarity measures. */ class TypedArraySimilarityMemory { public: /** Initialize a similarity instance with memory. * By instantiating a class object, the otherwise static member functions * can be given a memory that coordinates their behaviour between calls. For * example, we can make sure that all evaluations of Mutual Information use * the same histogram resolution. *\param repeatCheck If this flag is set, the object will repeat the range * check for every call to GetRangeX or GetRangeY. If the current data * range exceeds the one stored in this object, the latter will be adapted * accordingly. */ TypedArraySimilarityMemory( const bool repeatCheck = true ) : ValidX( false ), RangeX( 0, 0 ), NumberBinsX( 0 ), ValidY( false ), RangeY( 0, 0 ), NumberBinsY( 0 ), MinNumBins( 8 ), MaxNumBins( 128 ) { RepeatCheck = repeatCheck; } /** Get range of X distribution. * If this object is not yet initialized, the given array is queried for * its value range, and this object is initialized accordingly. */ const Types::DataItemRange GetRangeX( const TypedArray* array, const size_t numBins ); /** Get range of Y distribution. * If this object is not yet initialized, the given array is queried for * its value range, and this object is initialized accordingly. */ const Types::DataItemRange GetRangeY( const TypedArray* array, const size_t numBins ); /// Set minimum number of histogram bins. void SetMinNumBins( const size_t minNumBins ) { MinNumBins = minNumBins; } /// Set maximum number of histogram bins. void SetMaxNumBins( const size_t maxNumBins ) { MaxNumBins = maxNumBins; } /// Create histogram based on memorized settings. JointHistogram::SmartPtr CreateHistogram( const TypedArray* array0, const TypedArray* array1 ); private: /// Repeat range check with each call to GetRangeX and GetRangeY. bool RepeatCheck; /// Flag whether memory for X distribution is already initialized. bool ValidX; /// Remembered range of X values. Types::DataItemRange RangeX; /// Remembered number of bins for the X distribution. size_t NumberBinsX; /// Flag whether memory for X distribution is already initialized. bool ValidY; /// Remembered range of Y values. Types::DataItemRange RangeY; /// Remembered number of bins for the Y distribution. size_t NumberBinsY; /// Minimum number of histogram bins. size_t MinNumBins; /// Maximum number of histogram bins. size_t MaxNumBins; /// Allow similarity computation class access. friend class TypedArraySimilarity; }; //@} } // namespace cmtk #endif // #ifndef __cmtkTypedArraySimilarityMemory_h_included_ cmtk-3.3.1/libs/Registration/cmtkTypedArraySimilarityRMI.cxx000066400000000000000000000072561276303427400242070ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2010, 2013 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4844 $ // // $LastChangedDate: 2013-09-13 14:57:26 -0700 (Fri, 13 Sep 2013) $ // // $LastChangedBy: torstenrohlfing $ // */ #include "cmtkTypedArraySimilarity.h" namespace cmtk { /** \addtogroup Registration */ //@{ TypedArraySimilarity::ReturnType TypedArraySimilarity::GetMutualInformation ( const std::vector& data0, const std::vector& data1, const bool normalized ) { const size_t N = data0[0]->GetDataSize(); const size_t dim0 = data0.size(); const size_t dim1 = data1.size(); const size_t dim = dim0 + dim1; std::vector pts( N*dim ); Types::DataItem tmp; // CREATE NEIGHBORHOOD VECTORS for ( size_t nidx = 0; nidx < N; ++nidx ) { for ( size_t lidx = 0; lidx < dim0; ++lidx ) { data0[lidx]->Get(tmp,nidx); pts[lidx * N + nidx] = tmp; } for ( size_t lidx = 0; lidx < dim1; ++lidx ) { data1[lidx]->Get(tmp,nidx); pts[(dim0 + lidx) * N + nidx] = tmp; } } // SUBTRACT MEAN std::vector mean(dim,0.0); for (size_t i=0; i cM( dim, dim ); double sum; size_t iN, jN; for (size_t i=0; i mcM0( dim0, dim0 ); // image0's bloc for (size_t i=0; i mcM1( dim1, dim1 ); // image0's bloc for (size_t i=0; i. // // $Revision: 3288 $ // // $LastChangedDate: 2011-07-26 16:27:51 -0700 (Tue, 26 Jul 2011) $ // // $LastChangedBy: torstenrohlfing $ // */ #include #ifdef CMTK_USE_SMP # include #endif #include #include #include #include #include #include namespace cmtk { /** \addtogroup Registration */ //@{ VoxelMatchingAffineFunctional* VoxelMatchingAffineFunctional ::Create ( const int metric, UniformVolume::SmartPtr& refVolume, UniformVolume::SmartPtr& fltVolume, AffineXform::SmartPtr& affineXform ) { switch ( fltVolume->GetData()->GetDataClass() ) { case DATACLASS_UNKNOWN : case DATACLASS_GREY : switch ( metric ) { case 0: return new VoxelMatchingAffineFunctionalTemplate< VoxelMatchingNormMutInf_Trilinear >( refVolume, fltVolume, affineXform ); case 1: return new VoxelMatchingAffineFunctionalTemplate( refVolume, fltVolume, affineXform ); case 2: return new VoxelMatchingAffineFunctionalTemplate( refVolume, fltVolume, affineXform ); case 3: return NULL; // masked nmi retired case 4: return new VoxelMatchingAffineFunctionalTemplate( refVolume, fltVolume, affineXform ); case 5: return new VoxelMatchingAffineFunctionalTemplate( refVolume, fltVolume, affineXform ); default: break; } break; case DATACLASS_LABEL : switch ( metric ) { case 0: return new VoxelMatchingAffineFunctionalTemplate( refVolume, fltVolume, affineXform ); case 1: return new VoxelMatchingAffineFunctionalTemplate( refVolume, fltVolume, affineXform ); case 2: return new VoxelMatchingAffineFunctionalTemplate( refVolume, fltVolume, affineXform ); case 3: return NULL; // masked nmi retired case 4: return new VoxelMatchingAffineFunctionalTemplate( refVolume, fltVolume, affineXform ); case 5: return new VoxelMatchingAffineFunctionalTemplate( refVolume, fltVolume, affineXform ); default: break; } break; } return NULL; } } // namespace cmtk cmtk-3.3.1/libs/Registration/cmtkVoxelMatchingAffineFunctional.h000066400000000000000000000252471276303427400250350ustar00rootroot00000000000000/* // Copyright 2016 Google, Inc. // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2011 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 5398 $ // // $LastChangedDate: 2016-01-14 18:29:18 -0800 (Thu, 14 Jan 2016) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkVoxelMatchingAffineFunctional_h_included_ #define __cmtkVoxelMatchingAffineFunctional_h_included_ #include #include #include #include #include #include #include #include #include #include #include namespace cmtk { /** \addtogroup Registration */ //@{ /** Base-class for affine registration functionals. */ class VoxelMatchingAffineFunctional : /// Inherit from voxel matching functional. public VoxelMatchingFunctional { public: /// This class type. typedef VoxelMatchingAffineFunctional Self; /// Superclass. typedef VoxelMatchingFunctional Superclass; /// Smart pointer to this class. typedef SmartPointer SmartPtr; /// Return parameter vector. virtual void GetParamVector ( CoordinateVector& v ) { this->m_AffineXform->GetParamVector( v ); } /// Return parameter stepping. virtual Types::Coordinate GetParamStep( const size_t idx, const Types::Coordinate mmStep = 1 ) const { return this->m_AffineXform->GetParamStep( idx, Vector3D( FloatingSize ), mmStep ); } /// Return the transformation's parameter vector dimension. virtual size_t ParamVectorDim() const { return this->m_AffineXform->ParamVectorDim(); } /// Return the number of variable parameters of the transformation. virtual size_t VariableParamVectorDim() const { return this->m_AffineXform->VariableParamVectorDim(); } protected: /// Current coordinate transformation. AffineXform::SmartPtr m_AffineXform; /// Utility object for volume clipping. VolumeClipping Clipper; /** Perform clipping/cropping in z-direction. * This function computes the intersection of reference and floating data in * z-direction. It determines the range of indices of those planes in the * reference that intersect the floating. This is the range over which to * for-loop during metric computation. *\param clipper A volume clipping object with clipping boundaries and grid * orientation set. *\param origin Starting point of the reference volume. *\param start Upon return, this reference is set to the index of first plane * in the reference that intersects the floating. *\param end Upon return, this reference is set to one plus the index of the * last plane in the reference that intersects the floating. *\return 1 if there is an intersection of reference and floating, 0 if there * isn't. The range of indices returned in "start" and "end" is only * guaranteed to be valid if 1 is the return value. */ Types::GridIndexType ClipZ ( const VolumeClipping& clipper, const Vector3D& origin, DataGrid::IndexType::ValueType& start, DataGrid::IndexType::ValueType &end ) const { // perform clipping Types::Coordinate fromFactor, toFactor; if (! clipper.ClipZ( fromFactor, toFactor, origin ) ) return 0; // there is an intersection: Look up the corresponding grid indices start = static_cast( (ReferenceDims[2]-1)*fromFactor ); end = 1+std::min( (Types::GridIndexType)(ReferenceDims[2]-1), (Types::GridIndexType)(1 + ((ReferenceDims[2]-1)*toFactor) ) ); // finally, apply cropping boundaries of the reference volume start = std::max( start, this->m_ReferenceCropRegion.From()[2] ); end = std::min( end, this->m_ReferenceCropRegion.To()[2] ); // return 1 iff index range is non-empty. return (start < end ); } /** Perform clipping/cropping in x-direction. * This function computes the intersection of reference and floating data in * x-direction. It determines the range of indices of those voxels in the * current reference row that intersect the floating image. This is the range * over which to for-loop during metric computation. * * Compared to ClipZ and ClipY, this step has to operate very exact as there * is no further level that would reduce remaining invalid voxels. Therefore, * clipper.ClipX() is called with an extended initial range of indices and an * explicitly open upper bound. * * This is necessary to discriminate inside-boundary from on-boundary voxels. * For the right, upper and back boundary, on-boundary voxels are already * outside the allowed range as the upper boundaries of the volume are open * in terms of interpolation. *\param clipper A volume clipping object with clipping boundaries and grid * orientation set. *\param origin Starting point of the current row in the reference volume. *\param start Upon return, this reference is set to the index of first voxel * in the reference that intersects the floating image. *\param end Upon return, this reference is set to one plus the index of the * last voxel in the reference that intersects the floating image. *\return 1 if there is an intersection of the current reference row and * the floating, 0 if there isn't. The range of indices returned in "start" * and "end" is only guaranteed to be valid if 1 is the return value. */ Types::GridIndexType ClipX ( const VolumeClipping& clipper, const Vector3D& origin, DataGrid::IndexType::ValueType& start, DataGrid::IndexType::ValueType &end ) const { // perform clipping Types::Coordinate fromFactor, toFactor; if ( ! clipper.ClipX( fromFactor, toFactor, origin, 0, 2, false, true ) ) return 0; fromFactor = std::min( 1.0, fromFactor ); // there is an intersection: Look up the corresponding grid indices start = std::max( 0, (Types::GridIndexType)((ReferenceDims[0]-1)*fromFactor)-1 ); while ( ( start*ReferenceGrid->m_Delta[0] < fromFactor*ReferenceSize[0]) && ( start < ReferenceDims[0] ) ) ++start; if ( (toFactor > 1.0) || (start == ReferenceDims[0]) ) { end = ReferenceDims[0]; } else { end = std::min( ReferenceDims[0]-2, (Types::GridIndexType)(1 + (ReferenceDims[0]-1)*toFactor)); while ( end*ReferenceGrid->m_Delta[0] > toFactor*ReferenceSize[0] ) // 'if' not sufficient! --end; ++end; // otherwise end=1+min(...) and ...[0][end-1] above!! } // finally, apply cropping boundaries of the reference volume start = std::max( start, this->m_ReferenceCropRegion.From()[0] ); end = std::min( end, this->m_ReferenceCropRegion.To()[0] ); // return 1 iff index range is non-empty. return (start < end ); } /** Perform clipping/cropping in y-direction. * This function computes the intersection of reference and floating data in * y-direction. It determines the range of indices of those rows in the * current reference plane that intersect the floating image. This is the * range over which to for-loop during metric computation. *\param clipper A volume clipping object with clipping boundaries and grid * orientation set. *\param origin Starting point of the current plane in the reference volume. *\param start Upon return, this reference is set to the index of first row * in the reference that intersects the floating image. *\param end Upon return, this reference is set to one plus the index of the * last row in the reference that intersects the floating image. *\return 1 if there is an intersection of the current reference plane and * the floating, 0 if there isn't. The range of indices returned in "start" * and "end" is only guaranteed to be valid if 1 is the return value. */ Types::GridIndexType ClipY ( const VolumeClipping& clipper, const Vector3D& origin, DataGrid::IndexType::ValueType& start, DataGrid::IndexType::ValueType &end ) const { // perform clipping Types::Coordinate fromFactor, toFactor; if ( !clipper.ClipY( fromFactor, toFactor, origin ) ) return 0; // there is an intersection: Look up the corresponding grid indices start = static_cast( (ReferenceDims[1]-1)*fromFactor ); if ( toFactor > 1.0 ) { end = ReferenceDims[1]; } else { end = 1+std::min( ReferenceDims[1]-1, (Types::GridIndexType)(1+(ReferenceDims[1]-1)*toFactor ) ); } // finally, apply cropping boundaries of the reference volume start = std::max( start, this->m_ReferenceCropRegion.From()[1] ); end = std::min( end, this->m_ReferenceCropRegion.To()[1] ); // return 1 iff index range is non-empty. return (start < end ); } public: /// Constructor. VoxelMatchingAffineFunctional( UniformVolume::SmartPtr refVolume, UniformVolume::SmartPtr modVolume, AffineXform::SmartPtr& affineXform ) : VoxelMatchingFunctional( refVolume, modVolume ), m_AffineXform( affineXform ) {} /// Destructor. virtual ~VoxelMatchingAffineFunctional() {} /** Constructor function for affine voxel registration functionals. * This function takes the index of a metric in the list of available voxel * similarity measures plus all required objects. It the creates an appropriate * instance of VoxelMatchingAffineFunctional with the correct metric class as template * parameter. */ static VoxelMatchingAffineFunctional* Create( const int metric, UniformVolume::SmartPtr& refVolume, UniformVolume::SmartPtr& modVolume, AffineXform::SmartPtr& affineXform ); }; //@} } // namespace cmtk #endif // #ifndef __cmtkVoxelMatchingAffineFunctional_h_included_ cmtk-3.3.1/libs/Registration/cmtkVoxelMatchingAffineFunctionalTemplate.h000066400000000000000000000250761276303427400265310ustar00rootroot00000000000000/* // Copyright 2016 Google, Inc. // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2011 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 5400 $ // // $LastChangedDate: 2016-01-14 19:42:40 -0800 (Thu, 14 Jan 2016) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkVoxelMatchingAffineFunctionalTemplate_h_included_ #define __cmtkVoxelMatchingAffineFunctionalTemplate_h_included_ #include #include #include #include #include namespace cmtk { /** \addtogroup Registration */ //@{ /** Functional that evaluates a voxel-based similarity measure. * This class defines the type of functional that is optimized during * voxel-based registration. It holds references to reference and floating data * and computes similarity as well as its gradient w.r.t. a given * transformation. * * The metric to be optimized is given by a template parameter, therefore * allowing inlined code to be generated for efficient evaluation. */ template class VoxelMatchingAffineFunctionalTemplate : /// Inherit from affine voxel matching functional public VoxelMatchingAffineFunctional, /// Inherit from metric template functional. public VoxelMatchingFunctional_Template { public: /// This class type. typedef VoxelMatchingAffineFunctionalTemplate Self; /// Smart pointer to this class. typedef SmartPointer SmartPtr; /// Superclass. typedef VoxelMatchingAffineFunctional Superclass; /// Return type. typedef Functional::ReturnType ReturnType; /** Constructor. * Init pointers to volume and transformation objects and initialize * internal data structures. *\param reference The reference (i.e. static) volume. *\param floating The floating (i.e. transformed) volume. *\param affineXform A transformation template. This object determines the type of transformation to be optimized. Its initial value is not relevant. */ VoxelMatchingAffineFunctionalTemplate( UniformVolume::SmartPtr& reference, UniformVolume::SmartPtr& floating, AffineXform::SmartPtr& affineXform ) : VoxelMatchingAffineFunctional( reference, floating, affineXform ), VoxelMatchingFunctional_Template( reference, floating ), m_NumberOfThreads( ThreadPool::GetGlobalThreadPool().GetNumberOfThreads() ) { this->m_ThreadMetric.resize( m_NumberOfThreads, dynamic_cast( *(this->Metric) ) ); } /// Destructor. virtual ~VoxelMatchingAffineFunctionalTemplate() {} /// Evaluate with new parameter vector. virtual typename Self::ReturnType EvaluateAt ( CoordinateVector& v ) { this->m_AffineXform->SetParamVector( v ); return this->Evaluate(); } /** Compute functional value with volume clipping. * This function iterates over all voxels of the reference image that - after * applying the current coordinate transformation - are located inside the * mode image. This set of voxels is determined on-the-fly by an extension of * Liang and Barsky's "Parameterized Line-Clipping" technique. * * From the resulting sequence of reference/floating voxel pairs, the * selected voxel-based similarity measure (metric) is computed. *\return The computed similarity measure as returned by the "Metric" * subobject. *\see VolumeClipping */ virtual typename Self::ReturnType Evaluate() { const TransformedVolumeAxes axesHash( *this->ReferenceGrid, this->m_AffineXform, this->FloatingGrid->Deltas().begin(), this->FloatingGrid->m_Offset.begin() ); const Vector3D *axesHashX = axesHash[0], *axesHashY = axesHash[1], *axesHashZ = axesHash[2]; this->Metric->Reset(); const DataGrid::IndexType& Dims = this->ReferenceGrid->GetDims(); const Types::GridIndexType DimsX = Dims[0], DimsY = Dims[1], DimsZ = Dims[2]; this->Clipper.SetDeltaX( axesHashX[DimsX-1] - axesHashX[0] ); this->Clipper.SetDeltaY( axesHashY[DimsY-1] - axesHashY[0] ); this->Clipper.SetDeltaZ( axesHashZ[DimsZ-1] - axesHashZ[0] ); this->Clipper.SetClippingBoundaries( this->m_FloatingCropRegionFractional ); DataGrid::IndexType::ValueType startZ, endZ; if ( this->ClipZ( this->Clipper, axesHashZ[0], startZ, endZ ) ) { startZ = std::max( startZ, this->m_ReferenceCropRegion.From()[2] ); endZ = std::min( endZ, this->m_ReferenceCropRegion.To()[2] + 1 ); const int numberOfTasks = std::min( 4 * this->m_NumberOfThreads - 3, endZ - startZ + 1 ); this->m_EvaluateTaskInfo.resize( numberOfTasks ); for ( int taskIdx = 0; taskIdx < numberOfTasks; ++taskIdx ) { this->m_EvaluateTaskInfo[taskIdx].thisObject = this; this->m_EvaluateTaskInfo[taskIdx].AxesHash = &axesHash; this->m_EvaluateTaskInfo[taskIdx].StartZ = startZ; this->m_EvaluateTaskInfo[taskIdx].EndZ = endZ; } ThreadPool::GetGlobalThreadPool().Run( EvaluateThread, this->m_EvaluateTaskInfo ); } return this->Metric->Get(); } /** Number of threads that this object was created for. * This is the actual maximum number of threads running at any time, but not * necessarily the number of parallel tasks to be completed. * All duplicated data structures are generated with the multiplicity given * by this value. It is determined from Threads when the object is first * instanced. It cannot be changed afterwards. */ size_t m_NumberOfThreads; /// Metric objects for the separate threads. std::vector m_ThreadMetric; /// Mutex lock for access to global Metric field. MutexLock m_MetricMutex; /** Thread parameter block for incremental gradient computation. * This structure holds all thread-specific information. A pointer to an * instance of this structure is given to EvaluateGradientThread() for * each thread created. */ typedef struct { /// Pointer to the functional object that created the thread. Self *thisObject; /// Axes hash. const TransformedVolumeAxes* AxesHash; /// First plane of clipped reference volume. DataGrid::IndexType::ValueType StartZ; /// Last plane of clipped reference volume. DataGrid::IndexType::ValueType EndZ; } EvaluateTaskInfo; /// Info blocks for parallel threads evaluating functional gradient. std::vector m_EvaluateTaskInfo; /** Compute functional gradient as a thread. * This function (i.e., each thread) iterates over all parameters of the * current warp transformation. Among all active (i.e., not disabled) * parameters, it selects the ones that have an index with modulus * equal to the threads index when divided by the total number of threads. * For these parameters, the thread computes the partial derivative of the * functional by finite-difference approximation. */ static void EvaluateThread( void *const args, const size_t taskIdx, const size_t taskCnt, const size_t threadIdx, const size_t ) { typename Self::EvaluateTaskInfo *info = static_cast( args ); Self *me = info->thisObject; const VM* Metric = me->Metric; VM& threadMetric = me->m_ThreadMetric[threadIdx]; threadMetric.Reset(); const Vector3D *hashX = (*info->AxesHash)[0], *hashY = (*info->AxesHash)[1], *hashZ = (*info->AxesHash)[2]; Vector3D pFloating; const DataGrid::IndexType& Dims = me->ReferenceGrid->GetDims(); const Types::GridIndexType DimsX = Dims[0], DimsY = Dims[1]; Types::GridIndexType fltIdx[3]; Types::Coordinate fltFrac[3]; const Types::GridIndexType FltDimsX = me->FloatingDims[0], FltDimsY = me->FloatingDims[1]; Vector3D rowStart; Vector3D planeStart; Types::GridIndexType offset; DataGrid::IndexType::ValueType pX, pY, pZ; // Loop over all remaining planes for ( pZ = info->StartZ + taskIdx; pZ < info->EndZ; pZ += taskCnt ) { // Offset of current reference voxel Types::GridIndexType r = pZ * DimsX * DimsY; planeStart = hashZ[pZ]; DataGrid::IndexType::ValueType startY, endY; if ( me->ClipY( me->Clipper, planeStart, startY, endY ) ) { startY = std::max( startY, me->m_ReferenceCropRegion.From()[1] ); endY = std::min( endY, me->m_ReferenceCropRegion.To()[1] + 1 ); r += startY * DimsX; // Loop over all remaining rows for ( pY = startY; pYClipX( me->Clipper, rowStart, startX, endX ) ) { startX = std::max( startX, me->m_ReferenceCropRegion.From()[0] ); endX = std::min( endX, me->m_ReferenceCropRegion.To()[0] + 1 ); r += startX; // Loop over all remaining voxels in current row for ( pX = startX; pXFloatingGrid->FindVoxelByIndex( pFloating, fltIdx, fltFrac ) ) { // Compute data index of the floating voxel in the floating // volume. offset = fltIdx[0]+FltDimsX*(fltIdx[1]+FltDimsY*fltIdx[2]); // Continue metric computation. threadMetric.Increment( Metric->GetSampleX( r ), Metric->GetSampleY( offset, fltFrac ) ); } } r += (DimsX-endX); } else { r += DimsX; } } r += (DimsY-endY) * DimsX; } else { r += DimsY * DimsX; } } me->m_MetricMutex.Lock(); me->Metric->AddMetric( threadMetric ); me->m_MetricMutex.Unlock(); } }; //@} } // namespace cmtk #endif // #ifndef __cmtkVoxelMatchingAffineFunctionalTemplate_h_included_ cmtk-3.3.1/libs/Registration/cmtkVoxelMatchingCorrRatio.cxx000066400000000000000000000057621276303427400241010ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2010 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 2022 $ // // $LastChangedDate: 2010-07-21 15:26:03 -0700 (Wed, 21 Jul 2010) $ // // $LastChangedBy: torstenrohlfing $ // */ #include "cmtkVoxelMatchingCorrRatio.h" namespace cmtk { /** \addtogroup Registration */ //@{ template typename VoxelMatchingCorrRatio::ReturnType VoxelMatchingCorrRatio::Get () const { double invSampleCount = 1.0 / HistogramI.SampleCount(); // initialize variable for the weighted sum of the sigma^2 values over all // reference intensity classes. double sumSigmaSquare = 0; // run over all bins, i.e., reference classes for ( unsigned int j = 0; j < NumBinsX; ++j ) { // are there any values in the current class? if ( HistogramI[j] ) { // compute mean floating value for this reference class double mu = SumJ[j] / HistogramI[j]; // compute variance of floating values for this reference class double sigmaSq = ( mu*mu*HistogramI[j] - 2.0*mu*SumJ[j] + SumJ2[j] ) / HistogramI[j]; // update sum over all classes with weighted sigma^2 for this class. sumSigmaSquare += (invSampleCount * HistogramI[j]) * sigmaSq; } } // compute (supposedly) correlation ratio typename Self::ReturnType cr = static_cast( 1.0 - (1.0 / SigmaSqJ ) * sumSigmaSquare ); sumSigmaSquare = 0; for ( unsigned int i = 0; i < NumBinsY; ++i ) { if ( HistogramJ[i] ) { double mu = SumI[i] / HistogramJ[i]; double sigmaSq = ( mu*mu*HistogramJ[i] - 2.0*mu*SumI[i] + SumI2[i] ) / HistogramJ[i]; // update sum over all classes with weighted sigma^2 for this class. sumSigmaSquare += (invSampleCount * HistogramJ[i]) * sigmaSq; } } // add reverse correlation ratio cr += static_cast(1.0 - (1.0 / SigmaSqI ) * sumSigmaSquare); return cr; } /// Explicit instantiation. template class VoxelMatchingCorrRatio; /// Explicit instantiation. template class VoxelMatchingCorrRatio; } // namespace cmtk cmtk-3.3.1/libs/Registration/cmtkVoxelMatchingCorrRatio.h000066400000000000000000000155251276303427400235240ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2010 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 2398 $ // // $LastChangedDate: 2010-10-05 14:54:37 -0700 (Tue, 05 Oct 2010) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkVoxelMatchingCorrRatio_h_included_ #define __cmtkVoxelMatchingCorrRatio_h_included_ #include #include #include #include namespace cmtk { /** \addtogroup Registration */ //@{ /** Voxel metric "correlation ratio". *\deprecated For future code, use cmtk::ImagePairSimilarityMetricCR instead. */ template< Interpolators::InterpolationEnum I = Interpolators::LINEAR > class VoxelMatchingCorrRatio : public VoxelMatchingMetric { public: /// This type. typedef VoxelMatchingCorrRatio Self; /// Smart pointer. typedef SmartPointer SmartPtr; /** Constructor. * The inherited constructor is called to initialize the given datasets. * Afterwards, the original (untransformed) probability distribution * functions of model and reference are calculated. */ VoxelMatchingCorrRatio ( const UniformVolume* refVolume, const UniformVolume* fltVolume, const unsigned int numBins = CMTK_HISTOGRAM_AUTOBINS ) : VoxelMatchingMetric( refVolume, fltVolume ) { NumBinsX = NumBinsY = numBins; if ( NumBinsX == CMTK_HISTOGRAM_AUTOBINS ) NumBinsX = std::max( std::min( refVolume->GetNumberOfPixels(), 128 ), 8 ); HistogramI.Resize( NumBinsX ); if ( NumBinsY == CMTK_HISTOGRAM_AUTOBINS ) NumBinsY = std::max( std::min( fltVolume->GetNumberOfPixels(), 128 ), 8 ); HistogramJ.Resize( NumBinsY ); HistogramI.SetRange( refVolume->GetData()->GetRange() ); SumJ.resize( NumBinsX ); SumJ2.resize( NumBinsX ); fltVolume->GetData()->GetStatistics( MuJ, SigmaSqJ ); HistogramJ.SetRange( fltVolume->GetData()->GetRange() ); SumI.resize( NumBinsY ); SumI2.resize( NumBinsY ); refVolume->GetData()->GetStatistics( MuI, SigmaSqI ); } /** Reset computation. * Initialize arrays that hold the sums of all floating values and their * squares, separated by histogram classes of the reference image. */ void Reset() { HistogramI.Reset(); HistogramJ.Reset(); std::fill( SumI.begin(), SumI.end(), 0 ); std::fill( SumJ.begin(), SumJ.end(), 0 ); std::fill( SumI2.begin(), SumI2.end(), 0 ); std::fill( SumJ2.begin(), SumJ2.end(), 0 ); } /** Continue incremental calculation. */ /** Add a pair of values to the metric. */ template void Increment( const T a, const T b ) { // what's the reference histogram bin? size_t bin = HistogramI.ValueToBin( a ); // count this sample HistogramI.Increment( bin ); // add floating value to sum of values for this class SumJ[bin] += b; // add squared floating value to sum of squared values for this class SumJ2[bin] += b * b; // same in reverse bin = HistogramJ.ValueToBin( b ); HistogramJ.Increment( bin ); SumI[bin] += a; SumI2[bin] += a * a; } /** Remove a pair of values from the metric. */ template void Decrement( const T a, const T b ) { // what's the reference histogram bin? size_t bin = HistogramI.ValueToBin( a ); // count this sample HistogramI.Decrement( bin ); // add floating value to sum of values for this class SumJ[bin] -= b; // add squared floating value to sum of squared values for this class SumJ2[bin] -= b * b; // same in reverse bin = HistogramJ.ValueToBin( b ); HistogramJ.Decrement( bin ); SumI[bin] -= a; SumI2[bin] -= a * a; } /** */ void AddMetric ( const Self& other ) { HistogramI.AddHistogram( other.HistogramI ); for ( size_t bin = 0; bin < NumBinsX; ++bin ) { SumJ[bin] += other.SumJ[bin]; SumJ2[bin] += other.SumJ2[bin]; } HistogramJ.AddHistogram( other.HistogramJ ); for ( size_t bin = 0; bin < NumBinsY; ++bin ) { SumI[bin] += other.SumI[bin]; SumI2[bin] += other.SumI2[bin]; } } /** */ void RemoveMetric ( const Self& other ) { HistogramI.RemoveHistogram( other.HistogramI ); for ( size_t bin = 0; bin < NumBinsX; ++bin ) { SumJ[bin] -= other.SumJ[bin]; SumJ2[bin] -= other.SumJ2[bin]; } HistogramJ.RemoveHistogram( other.HistogramJ ); for ( size_t bin = 0; bin < NumBinsY; ++bin ) { SumI[bin] -= other.SumI[bin]; SumI2[bin] -= other.SumI2[bin]; } } /// Return correlation ratio. typename Self::ReturnType Get () const; private: /// Number of bins for the X-distribution. size_t NumBinsX; /// Array with sums of all Y-values by X-bins. std::vector SumJ; /// Array with sums of squares of all Y-values by X-bins. std::vector SumJ2; /// Histogram with counts of all X-values. Histogram HistogramI; // Variance of complete floating image for normalization. Types::DataItem SigmaSqJ; // Mean of complete floating image for normalization. Types::DataItem MuJ; /// Number of bins for the Y-distribution. size_t NumBinsY; /// Array with sums of all X-values by Y-bins. std::vector SumI; /// Array with sums of squares of all X-values by Y-bins. std::vector SumI2; /// Histogram with counts of all X-values. Histogram HistogramJ; // Variance of complete floating image for normalization. Types::DataItem SigmaSqI; // Mean of complete floating image for normalization. Types::DataItem MuI; }; /// Correlation ratio with trilinear interpolation. typedef VoxelMatchingCorrRatio VoxelMatchingCorrRatio_Trilinear; /// Correlation ratio with nearest-neighbor interpolation. typedef VoxelMatchingCorrRatio VoxelMatchingCorrRatio_NearestNeighbor; //@} } // namespace cmtk #endif // #ifndef __cmtkVoxelMatchingCorrRatio_h_included_ cmtk-3.3.1/libs/Registration/cmtkVoxelMatchingCrossCorrelation.cxx000066400000000000000000000036061276303427400254630ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2010, 2013 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4874 $ // // $LastChangedDate: 2013-09-24 12:54:03 -0700 (Tue, 24 Sep 2013) $ // // $LastChangedBy: torstenrohlfing $ // */ #include "cmtkVoxelMatchingCrossCorrelation.h" namespace cmtk { /** \addtogroup Registration */ //@{ VoxelMatchingCrossCorrelation ::VoxelMatchingCrossCorrelation( const UniformVolume* refVolume, const UniformVolume* fltVolume ) : VoxelMatchingMetricShort( refVolume, fltVolume ), SumX( 0.0 ), SumY( 0.0 ), SumXY( 0.0 ), SumSqX( 0.0 ), SumSqY( 0.0 ), Samples( 0 ) {} VoxelMatchingCrossCorrelation::ReturnType VoxelMatchingCrossCorrelation ::Get() const { const double muX = SumX / Samples; const double muY = SumY / Samples; const double p = SumXY - muY * SumX - muX * SumY + Samples * muX * muY; const double qX = SumSqX - 2 * muX * SumX + Samples * muX * muX; const double qY = SumSqY - 2 * muY * SumY + Samples * muY * muY; return static_cast( p / sqrt( qX * qY ) ); } } // namespace cmtk cmtk-3.3.1/libs/Registration/cmtkVoxelMatchingCrossCorrelation.h000066400000000000000000000101011276303427400250740ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2010, 2013 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4882 $ // // $LastChangedDate: 2013-09-27 15:16:36 -0700 (Fri, 27 Sep 2013) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkVoxelMatchingCrossCorrelation_h_included_ #define __cmtkVoxelMatchingCrossCorrelation_h_included_ #include #include #include #include #include #include namespace cmtk { /** \addtogroup Registration */ //@{ #ifdef _MSC_VER #pragma warning (disable:4521) #endif /** Normalized Cross Correlation Metric. *\deprecated For future code, use cmtk::ImagePairSimilarityMetricNCC instead. */ class VoxelMatchingCrossCorrelation : /// Inherit generic voxel metric with internal short data. public VoxelMatchingMetricShort { public: /// This type. typedef VoxelMatchingCrossCorrelation Self; /// Smart pointer. typedef SmartPointer SmartPtr; /** Default constructor. */ VoxelMatchingCrossCorrelation() : SumX( 0.0 ), SumY( 0.0 ), SumXY( 0.0 ), SumSqX( 0.0 ), SumSqY( 0.0 ), Samples( 0 ) {} /** Constructor. * For reference and floating volume, InitDataset is called. *\param refVolume The reference (fixed) volume. *\param fltVolume The floating (transformed) volume. */ VoxelMatchingCrossCorrelation ( const UniformVolume* refVolume, const UniformVolume* fltVolume ); /** Add a pair of values to the metric. */ template void Increment( const T a, const T b ) { if ( (a != DataX.padding()) && (b != DataY.padding()) ) { ++Samples; SumX += a; SumY += b; SumSqX += a * a; SumSqY += b * b; SumXY += a * b; } } /** Remove a pair of values from the metric. */ template void Decrement( const T a, const T b ) { if ( (a != DataX.padding()) && (b != DataY.padding()) ) { --Samples; SumX -= a; SumY -= b; SumSqX -= a * a; SumSqY -= b * b; SumXY -= a * b; } } /// Start with a new computation. void Reset () { SumX = SumY = SumSqX = SumSqY = SumXY = 0; Samples = 0; } /// Compute cross correlation. Self::ReturnType Get() const; void AddMetric ( const Self& other ) { SumX += other.SumX; SumY += other.SumY; SumXY += other.SumXY; SumSqX += other.SumSqX; SumSqY += other.SumSqY; Samples += other.Samples; } void RemoveMetric ( const Self& other ) { assert( Samples >= other.Samples ); SumX -= other.SumX; SumY -= other.SumY; SumXY -= other.SumXY; SumSqX -= other.SumSqX; SumSqY -= other.SumSqY; Samples -= other.Samples; } private: /// Sum over all samples in X distribution. double SumX; /// Sum over all samples in Y distribution. double SumY; /// Sum over products of corresponding samples in X and Y distribution. double SumXY; /// Sum over all squared samples in X distribution. double SumSqX; /// Sum over all squared samples in Y distribution. double SumSqY; /// Number of samples. size_t Samples; }; //@} } // namespace cmtk #endif // #ifndef __cmtkVoxelMatchingCrossCorrelation_h_included_ cmtk-3.3.1/libs/Registration/cmtkVoxelMatchingElasticFunctional.cxx000066400000000000000000000357261276303427400256070ustar00rootroot00000000000000/* // // Copyright 2016 Google, Inc. // // Copyright 1997-2010 Torsten Rohlfing // // Copyright 2004-2013 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 5399 $ // // $LastChangedDate: 2016-01-14 19:00:40 -0800 (Thu, 14 Jan 2016) $ // // $LastChangedBy: torstenrohlfing $ // */ #include "cmtkVoxelMatchingElasticFunctional.h" #include #include #include #include #include #include #include #include #include #ifdef _OPENMP # include #endif namespace cmtk { /** \addtogroup Registration */ //@{ VoxelMatchingElasticFunctional::VoxelMatchingElasticFunctional ( UniformVolume::SmartPtr& reference, UniformVolume::SmartPtr& floating ) : VoxelMatchingFunctional( reference, floating ), m_ActiveCoordinates( NULL ), m_JacobianConstraintWeight( 0.0 ), m_RigidityConstraintWeight( 0.0 ), m_GridEnergyWeight( 0.0 ), m_Regularize( false ), WarpNeedsFixUpdate( false ) { Dim = 0; this->m_ReferenceDomain = UniformVolume::CoordinateRegionType( UniformVolume::CoordinateVectorType( 0.0 ), reference->m_Size ); this->m_AdaptiveFixParameters = false; this->m_AdaptiveFixThreshFactor = 0.5; VectorCache = Memory::ArrayC::Allocate( ReferenceDims[0] ); VolumeOfInfluence = NULL; } VoxelMatchingElasticFunctional::~VoxelMatchingElasticFunctional() { Memory::ArrayC::Delete( VectorCache ); } template void VoxelMatchingElasticFunctional_WarpTemplate::WeightedDerivative ( double& lower, double& upper, W& warp, const int param, const Types::Coordinate step ) const { if ( this->m_JacobianConstraintWeight > 0 ) { double lowerConstraint = 0, upperConstraint = 0; warp.GetJacobianConstraintDerivative( lowerConstraint, upperConstraint, param, VolumeOfInfluence[param], step ); lower -= this->m_JacobianConstraintWeight * lowerConstraint; upper -= this->m_JacobianConstraintWeight * upperConstraint; } if ( this->m_RigidityConstraintWeight > 0 ) { double lowerConstraint = 0, upperConstraint = 0; if ( this->m_RigidityConstraintMap ) { warp.GetRigidityConstraintDerivative( lowerConstraint, upperConstraint, param, VolumeOfInfluence[param], step, this->m_RigidityConstraintMap ); } else { warp.GetRigidityConstraintDerivative( lowerConstraint, upperConstraint, param, VolumeOfInfluence[param], step ); } lower -= this->m_RigidityConstraintWeight * lowerConstraint; upper -= this->m_RigidityConstraintWeight * upperConstraint; } if ( this->m_GridEnergyWeight > 0 ) { double lowerEnergy = 0, upperEnergy = 0; warp.GetGridEnergyDerivative( lowerEnergy, upperEnergy, param, step ); lower -= this->m_GridEnergyWeight * lowerEnergy; upper -= this->m_GridEnergyWeight * upperEnergy; } // Catch infinite values that result from a folding grid. Effectively // prevent this by setting the gradient term to 0. if ( !finite(upper) || !finite(lower) ) { lower = upper = 0; } else { if ( this->m_LandmarkPairs ) { double lowerMSD, upperMSD; warp.GetDerivativeLandmarksMSD( lowerMSD, upperMSD, *(this->m_LandmarkPairs), param, step ); lower -= this->m_LandmarkErrorWeight * lowerMSD; upper -= this->m_LandmarkErrorWeight * upperMSD; } if ( InverseTransformation ) { double lowerIC, upperIC; warp.GetDerivativeInverseConsistencyError( lowerIC, upperIC, this->InverseTransformation, this->ReferenceGrid, &(this->VolumeOfInfluence[param]), param, step ); lower -= InverseConsistencyWeight * lowerIC; upper -= InverseConsistencyWeight * upperIC; } } } template void VoxelMatchingElasticFunctional_WarpTemplate::SetWarpXform ( typename W::SmartPtr& warp ) { Warp = W::SmartPtr::DynamicCastFrom( warp ); if ( Warp ) { Warp->RegisterVolume( *(ReferenceGrid) ); if ( Dim != Warp->VariableParamVectorDim() ) { if ( VolumeOfInfluence ) Memory::ArrayC::Delete( VolumeOfInfluence ); Dim = Warp->VariableParamVectorDim(); this->StepScaleVector.resize( Dim ); this->VolumeOfInfluence = Memory::ArrayC::Allocate( Dim ); } DataGrid::RegionType *VOIptr = this->VolumeOfInfluence; for ( size_t dim=0; dimGetParamStep( dim ); *VOIptr = this->GetReferenceGridRange( Warp->GetVolumeOfInfluence( dim, this->m_ReferenceDomain ) ); } WarpNeedsFixUpdate = true; } } template void VoxelMatchingElasticFunctional_Template::UpdateWarpFixedParameters() { int numCtrlPoints = this->Dim / 3; std::vector mapRef( numCtrlPoints ); std::vector mapMod( numCtrlPoints ); int inactive = 0; const typename VM::Exchange unsetY = this->Metric->DataY.padding(); if ( this->ReferenceDataClass == DATACLASS_LABEL ) { if ( this->m_ActiveCoordinates ) this->Warp->SetParametersActive( this->m_ActiveCoordinates ); else this->Warp->SetParametersActive(); #pragma omp parallel for reduction(+:inactive) for ( int ctrl = 0; ctrl < numCtrlPoints; ++ctrl ) { /// We cannot use the precomputed table of VOIs here because in "fast" mode, these VOIs are smaller than we want them here. const DataGrid::RegionType voi = this->GetReferenceGridRange( this->Warp->GetVolumeOfInfluence( 3 * ctrl, this->m_ReferenceDomain, false /* disable fast mode */ ) ); Types::GridIndexType r = voi.From()[0] + this->DimsX * ( voi.From()[1] + this->DimsY * voi.From()[2] ); bool active = false; for ( Types::GridIndexType pZ = voi.From()[2]; (pZ < voi.To()[2]) && !active; ++pZ ) { for ( Types::GridIndexType pY = voi.From()[1]; (pY < voi.To()[1]) && !active; ++pY ) { for ( Types::GridIndexType pX = voi.From()[0]; (pX < voi.To()[0]); ++pX, ++r ) { if ( ( this->Metric->GetSampleX( r ) != 0 ) || ( ( this->WarpedVolume[r] != unsetY ) && ( this->WarpedVolume[r] != 0 ) ) ) { active = true; break; } } r += ( voi.From()[0] + ( this->DimsX-voi.To()[0] ) ); } r += this->DimsX * ( voi.From()[1] + ( this->DimsY-voi.To()[1] ) ); } if ( !active ) { inactive += 3; int dim = 3 * ctrl; for ( int idx=0; idx<3; ++idx, ++dim ) { this->Warp->SetParameterInactive( dim ); } } } } else { #ifdef _OPENMP if ( this->m_ThreadConsistencyHistograms.size() < static_cast( omp_get_max_threads() ) ) { this->m_ThreadConsistencyHistograms.resize( omp_get_max_threads() ); const Types::GridIndexType numSamplesX = this->Metric->DataX.NumberOfSamples; const Types::DataItemRange rangeX = this->Metric->DataX.GetValueRange(); const size_t numBinsX = JointHistogramBase::CalcNumBins( numSamplesX, rangeX ); const Types::GridIndexType numSamplesY = this->Metric->DataY.NumberOfSamples; const Types::DataItemRange rangeY = this->Metric->DataY.GetValueRange(); const size_t numBinsY = JointHistogramBase::CalcNumBins( numSamplesY, rangeY ); for ( size_t thread = 0; thread < static_cast( omp_get_max_threads() ); ++thread ) { if ( ! this->m_ThreadConsistencyHistograms[thread] ) { this->m_ThreadConsistencyHistograms[thread] = JointHistogram::SmartPtr( new JointHistogram() ); this->m_ThreadConsistencyHistograms[thread]->Resize( numBinsX, numBinsY ); this->m_ThreadConsistencyHistograms[thread]->SetRangeX( rangeX ); this->m_ThreadConsistencyHistograms[thread]->SetRangeY( rangeY ); } } } #else const Types::GridIndexType numSamplesX = this->Metric->DataX.NumberOfSamples; const Types::DataItemRange rangeX = this->Metric->DataX.GetValueRange(); const size_t numBinsX = JointHistogramBase::CalcNumBins( numSamplesX, rangeX ); const Types::GridIndexType numSamplesY = this->Metric->DataY.NumberOfSamples; const Types::DataItemRange rangeY = this->Metric->DataY.GetValueRange(); const size_t numBinsY = JointHistogramBase::CalcNumBins( numSamplesY, rangeY ); this->m_ConsistencyHistogram = JointHistogram::SmartPtr( new JointHistogram() ); this->m_ConsistencyHistogram->Resize( numBinsX, numBinsY ); this->m_ConsistencyHistogram->SetRangeX( rangeX ); this->m_ConsistencyHistogram->SetRangeY( rangeY ); #endif #pragma omp parallel for for ( int ctrl = 0; ctrl < numCtrlPoints; ++ctrl ) { #ifdef _OPENMP JointHistogram& threadHistogram = *(this->m_ThreadConsistencyHistograms[ omp_get_thread_num() ]); #else JointHistogram& threadHistogram = *(this->m_ConsistencyHistogram); #endif threadHistogram.Reset(); // We cannot use the precomputed table of VOIs here because in "fast" mode, these VOIs are smaller than we want them here. const DataGrid::RegionType voi = this->GetReferenceGridRange( this->Warp->GetVolumeOfInfluence( 3 * ctrl, this->m_ReferenceDomain, false /* disable fast mode */ ) ); Types::GridIndexType r = voi.From()[0] + this->DimsX * ( voi.From()[1] + this->DimsY * voi.From()[2] ); const Types::GridIndexType endOfLine = ( voi.From()[0] + ( this->DimsX-voi.To()[0]) ); const Types::GridIndexType endOfPlane = this->DimsX * ( voi.From()[1] + (this->DimsY-voi.To()[1]) ); for ( Types::GridIndexType pZ = voi.From()[2]; pZWarpedVolume[r] != unsetY ) { threadHistogram.Increment( threadHistogram.ValueToBinX( this->Metric->GetSampleX( r ) ), threadHistogram.ValueToBinY( this->WarpedVolume[r] ) ); } } r += endOfLine; } r += endOfPlane; } threadHistogram.GetMarginalEntropies( mapRef[ctrl], mapMod[ctrl] ); } double refMin = HUGE_VAL, refMax = -HUGE_VAL; double modMin = HUGE_VAL, modMax = -HUGE_VAL; for ( int ctrl=0; ctrl refMax ) refMax = mapRef[ctrl]; if ( mapMod[ctrl] < modMin ) modMin = mapMod[ctrl]; if ( mapMod[ctrl] > modMax ) modMax = mapMod[ctrl]; } const double refThresh = refMin + this->m_AdaptiveFixThreshFactor * (refMax - refMin); const double modThresh = modMin + this->m_AdaptiveFixThreshFactor * (modMax - modMin); if ( this->m_ActiveCoordinates ) this->Warp->SetParametersActive( this->m_ActiveCoordinates ); else this->Warp->SetParametersActive(); for ( int ctrl=0; ctrlWarp->SetParameterInactive( dim ); } inactive += 3; } } } for ( size_t idx = 0; idx < this->Dim; ++idx ) { if ( this->Warp->GetParameterActive( idx ) ) { this->StepScaleVector[idx] = this->GetParamStep( idx ); } else { this->StepScaleVector[idx] = 0; } } DebugOutput( 1 ).GetStream().printf( "Deactivated %d out of %d parameters.\n", inactive, (int)this->Dim ); this->WarpNeedsFixUpdate = false; } VoxelMatchingElasticFunctional* CreateElasticFunctional ( const int metric, UniformVolume::SmartPtr& refVolume, UniformVolume::SmartPtr& fltVolume ) { switch ( fltVolume->GetData()->GetDataClass() ) { case DATACLASS_UNKNOWN : case DATACLASS_GREY : switch ( metric ) { case 0: return new VoxelMatchingElasticFunctional_Template< VoxelMatchingNormMutInf_Trilinear>( refVolume, fltVolume ); case 1: return new VoxelMatchingElasticFunctional_Template( refVolume, fltVolume ); case 2: return new VoxelMatchingElasticFunctional_Template( refVolume, fltVolume ); case 3: return NULL; // masked nmi retired case 4: return new VoxelMatchingElasticFunctional_Template( refVolume, fltVolume ); case 5: return new VoxelMatchingElasticFunctional_Template( refVolume, fltVolume ); default: return NULL; } case DATACLASS_LABEL: switch ( metric ) { case 0: return new VoxelMatchingElasticFunctional_Template( refVolume, fltVolume ); case 1: return new VoxelMatchingElasticFunctional_Template( refVolume, fltVolume ); case 2: return new VoxelMatchingElasticFunctional_Template( refVolume, fltVolume ); case 3: return NULL; // masked nmi retired case 4: return new VoxelMatchingElasticFunctional_Template( refVolume, fltVolume ); case 5: return new VoxelMatchingElasticFunctional_Template( refVolume, fltVolume ); default: return NULL; } } return NULL; } template class VoxelMatchingElasticFunctional_WarpTemplate; template class VoxelMatchingElasticFunctional_Template; template class VoxelMatchingElasticFunctional_Template; template class VoxelMatchingElasticFunctional_Template; template class VoxelMatchingElasticFunctional_Template; template class VoxelMatchingElasticFunctional_Template; template class VoxelMatchingElasticFunctional_Template; template class VoxelMatchingElasticFunctional_Template; template class VoxelMatchingElasticFunctional_Template; } // namespace cmtk cmtk-3.3.1/libs/Registration/cmtkVoxelMatchingElasticFunctional.h000066400000000000000000000717251276303427400252330ustar00rootroot00000000000000/* // // Copyright 2016 Google, Inc. // // Copyright 1997-2010 Torsten Rohlfing // // Copyright 2004-2013 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 5400 $ // // $LastChangedDate: 2016-01-14 19:42:40 -0800 (Thu, 14 Jan 2016) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkVoxelMatchingElasticFunctional_h_included_ #define __cmtkVoxelMatchingElasticFunctional_h_included_ #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef HAVE_IEEEFP_H # include #endif #ifdef HAVE_VALUES_H # include #endif namespace cmtk { /** \addtogroup Registration */ //@{ /** Common base class for all elastic registration functionals. * This class holds all members that are not related to the effective metric * and therefore need not be present in the derived template class. */ class VoxelMatchingElasticFunctional : /// Inherit basic voxel matching functions. public VoxelMatchingFunctional { public: /// This class. typedef VoxelMatchingElasticFunctional Self; /// Smart pointer to this class. typedef SmartPointer SmartPtr; /// Superclass. typedef VoxelMatchingFunctional Superclass; /** Set Warp transformation. * This virtual function will be overridden by the derived classes that add * the actual warp transformation as a template parameters. It serves as a * common access point to update the warp transformation after construction * of the functional. */ virtual void SetWarpXform( SplineWarpXform::SmartPtr& warp ) = 0; /// Set flag and value for forcing pixels outside the floating image. virtual void SetForceOutside( const bool flag = true, const Types::DataItem value = 0 ) = 0; /// Destructor. virtual ~VoxelMatchingElasticFunctional (); protected: /// Constructor. VoxelMatchingElasticFunctional( UniformVolume::SmartPtr& reference, UniformVolume::SmartPtr& floating ); /** Set active and passive warp parameters adaptively. * If this flag is set, the functional will adaptively determine active and * passive parameters of the warp transformation prior to gradient * computation. */ cmtkGetSetMacroDefault(bool,AdaptiveFixParameters,true); /** Set threshold factor for selecting passive warp parameters adaptively. * If the flag AdaptiveFixParameters is set, this value determines the * threshold by which active vs. passive parameters are selected. All * control points are set to passive for which the local region entropy is * below this factor times sum of min and max region entropy. The default * value is 0.5. */ cmtkGetSetMacro(double,AdaptiveFixThreshFactor); /** Active coordinate directions. */ cmtkGetSetMacroString(ActiveCoordinates); /** Weight of the Jacobian constraint relative to voxel similarity measure. * If this is zero, only the voxel-based similarity will be computed. */ cmtkGetSetMacroDefault(double,JacobianConstraintWeight,0); /** Weight of the rigidity constraint relative to voxel similarity measure. */ cmtkGetSetMacroDefault(double,RigidityConstraintWeight,0); /** Map of rigidity weights constraint relative to voxel similarity measure. */ cmtkGetSetMacro(DataGrid::SmartPtr,RigidityConstraintMap); /** Weight of the grid energy relative to voxel similarity measure. * If this is zero, only the voxel-based similarity will be computed. If * equal to one, only the grid energy will be computed. */ cmtkGetSetMacroDefault(double,GridEnergyWeight,0); /** Regularize the deformation. */ cmtkGetSetMacroDefault(bool,Regularize,false); /** Warp's fixed parameters need to be updated. * This flag is set when the warp transformation is set or modified. It * signals that the active and passive parameters of the transformation * will have to be updated before the next gradient computation. */ bool WarpNeedsFixUpdate; /// Histogram used for consistency computation. JointHistogram::SmartPtr m_ConsistencyHistogram; /// Dimension of warp parameter vector size_t Dim; /** Parameter scaling vector. * This array holds the scaling factors for all warp parameters as returned * by the transformation class. These factors can be used to equalized all * parameter modifications during gradient computation etc. */ std::vector StepScaleVector; /** Volume of influence table. * This array holds the precomputed volumes of influence for all * transformation parameters. Six successive numbers per parameter define the * voxel range with respect to the reference colume grid that is affected by * the respective parameter. */ DataGrid::RegionType *VolumeOfInfluence; /// Reference volume coordinate domain. UniformVolume::CoordinateRegionType m_ReferenceDomain; /// Storage for simultaneously retrieving multiple deformed vectors. Vector3D *VectorCache; }; /** Template class for elastic registration functional. * This class incorporates all deformation-specific parts of the registration * functional for voxel-based non-rigid registration. The deformation type * itself is defined by the template-parameter W. *\note * The way multithreading is implemented by this class is as follows: A * fixed number of threads (currently two) is created. Each thread is assigned * a unique number from 0 through numberOfThreads-1. It then iterates over all * parameters of the current warp transformation object, counting the variable * (i.e., non-fixed) parameters. Out of these, it computes the partial * derivative of the image similarity measure for those parameters for which * the modulus of the active parameter index divided by the number of threads * equals its own thread id. *\par * There are several advantages to this approach over others. First, if one * were to have one thread compute the upper and one the lower neighbor in * parameter space for each parameter, that would require 2 times the number * of parameters many threads, resulting in severy computation overhead. * Instead, with the approach implemented here, we only need the given number * of threads which will then work in parallel as much as possible. */ template class VoxelMatchingElasticFunctional_WarpTemplate : /// Inherit from non-template base class. public VoxelMatchingElasticFunctional { public: /// This class. typedef VoxelMatchingElasticFunctional_WarpTemplate Self; /// Smart pointer to this class. typedef SmartPointer SmartPtr; /// Superclass. typedef VoxelMatchingElasticFunctional Superclass; /// Pointer to the local warp transformation. typename W::SmartPtr Warp; protected: /// Optional inverse transformation for inverse-consistent deformation. typename W::SmartPtr InverseTransformation; /// Weight for inverse consistency constraint. double InverseConsistencyWeight; public: /// Set inverse transformation. void SetInverseTransformation( typename W::SmartPtr& inverseTransformation ) { this->InverseTransformation = W::SmartPtr::DynamicCastFrom( inverseTransformation ); } /// Set inverse consistency weight void SetInverseConsistencyWeight( const double inverseConsistencyWeight ) { this->InverseConsistencyWeight = inverseConsistencyWeight; } protected: /// Return weighted combination of voxel similarity and grid energy. typename Self::ReturnType WeightedTotal( const typename Self::ReturnType metric, const W* warp ) const { double result = metric; if ( this->m_JacobianConstraintWeight > 0 ) { result -= this->m_JacobianConstraintWeight * warp->GetJacobianConstraint(); } if ( this->m_RigidityConstraintWeight > 0 ) { if ( this->m_RigidityConstraintMap ) { result -= this->m_RigidityConstraintWeight * warp->GetRigidityConstraint( this->m_RigidityConstraintMap ); } else { result -= this->m_RigidityConstraintWeight * warp->GetRigidityConstraint(); } } if ( this->m_GridEnergyWeight > 0 ) { result -= this->m_GridEnergyWeight * warp->GetGridEnergy(); } if ( !finite( result ) ) return -FLT_MAX; if ( this->m_LandmarkPairs ) { result -= this->m_LandmarkErrorWeight * warp->GetLandmarksMSD( *(this->m_LandmarkPairs) ); } if ( InverseTransformation ) { result -= this->InverseConsistencyWeight * warp->GetInverseConsistencyError( this->InverseTransformation, this->ReferenceGrid ); } return static_cast( result ); } /// Return weighted combination of similarity and grid energy derivatives. void WeightedDerivative( double& lower, double& upper, W& warp, const int param, const Types::Coordinate step ) const; public: /// Get parameter stepping in milimeters. virtual Types::Coordinate GetParamStep( const size_t idx, const Types::Coordinate mmStep = 1 ) const { return Warp->GetParamStep( idx, Vector3D( this->FloatingSize ), mmStep ); } /// Return the transformation's parameter vector dimension. virtual size_t ParamVectorDim() const { return Warp->ParamVectorDim(); } /// Return the number of variable parameters of the transformation. virtual size_t VariableParamVectorDim() const { return Warp->VariableParamVectorDim(); } /** Set warp transformation. * In the multi-threaded implementation, Warp[0] will be linked directly to * the given warp, while for all other threads a copy of the original object * is created by a call to WarpXform::Clone(). */ virtual void SetWarpXform ( typename W::SmartPtr& warp ); /// Return parameter vector. virtual void GetParamVector ( CoordinateVector& v ) { Warp->GetParamVector( v ); } protected: /// Constructor. VoxelMatchingElasticFunctional_WarpTemplate( UniformVolume::SmartPtr& reference, UniformVolume::SmartPtr& floating ) : VoxelMatchingElasticFunctional( reference, floating ), Warp( NULL ), InverseTransformation( NULL ), InverseConsistencyWeight( 0.0 ) {} /// Dummy virtual destructor. virtual ~VoxelMatchingElasticFunctional_WarpTemplate() {} }; /** Functional that evaluates a voxel-based similarity measure. * This class defines the type of functional that is optimized during * voxel-based registration. It holds references to reference and floating data * and computes similarity as well as its gradient w.r.t. a given * transformation. * * The metric to be optimized is given by a template parameter, therefore * allowing inlined code to be generated for efficient evaluation. */ template class VoxelMatchingElasticFunctional_Template : public VoxelMatchingFunctional_Template, public VoxelMatchingElasticFunctional_WarpTemplate { public: /// This class. typedef VoxelMatchingElasticFunctional_Template Self; /// Superclass. typedef VoxelMatchingElasticFunctional_WarpTemplate Superclass; /** Constructor. * Init pointers to volume and transformation objects and initialize * internal data structures. *\param reference The reference (i.e. static) volume. *\param floating The floating (i.e. transformed) volume. */ VoxelMatchingElasticFunctional_Template( UniformVolume::SmartPtr& reference, UniformVolume::SmartPtr& floating ) : VoxelMatchingFunctional_Template( reference, floating ), VoxelMatchingElasticFunctional_WarpTemplate( reference, floating ), m_ForceOutsideFlag( false ), m_ForceOutsideValueRescaled( 0 ) { IncrementalMetric = typename VM::SmartPtr( new VM( *(this->Metric) ) ); WarpedVolume = NULL; DimsX = this->ReferenceGrid->GetDims()[0]; DimsY = this->ReferenceGrid->GetDims()[1]; DimsZ = this->ReferenceGrid->GetDims()[2]; FltDimsX = this->FloatingGrid->GetDims()[0]; FltDimsY = this->FloatingGrid->GetDims()[1]; ThreadPool& threadPool = ThreadPool::GetGlobalThreadPool(); this->m_NumberOfThreads = threadPool.GetNumberOfThreads(); this->m_NumberOfTasks = 4 * this->m_NumberOfThreads - 3; ThreadWarp.resize( this->m_NumberOfThreads ); this->InfoTaskGradient.resize( this->m_NumberOfTasks ); this->InfoTaskComplete.resize( this->m_NumberOfTasks ); this->TaskMetric.resize( this->m_NumberOfThreads ); for ( size_t task = 0; task < this->m_NumberOfThreads; ++task ) this->TaskMetric[task] = new VM( *(this->Metric) ); this->ThreadVectorCache = Memory::ArrayC::Allocate( this->m_NumberOfThreads ); for ( size_t thread = 0; thread < this->m_NumberOfThreads; ++thread ) this->ThreadVectorCache[thread] = Memory::ArrayC::Allocate( this->ReferenceDims[0] ); } /// Virtual destructor. virtual ~VoxelMatchingElasticFunctional_Template() { for ( size_t thread = 0; thread < this->m_NumberOfThreads; ++thread ) if ( ThreadVectorCache[thread] ) Memory::ArrayC::Delete( this->ThreadVectorCache[thread] ); Memory::ArrayC::Delete( this->ThreadVectorCache ); for ( size_t task = 0; task < this->m_NumberOfThreads; ++task ) delete this->TaskMetric[task]; if ( WarpedVolume ) Memory::ArrayC::Delete( WarpedVolume ); } /// Set flag and value for forcing values outside the floating image. virtual void SetForceOutside ( const bool flag = true, const Types::DataItem value = 0 ) { this->m_ForceOutsideFlag = flag; this->m_ForceOutsideValueRescaled = this->Metric->DataY.ValueToIndex( value ); } /** Set warp transformation. * In the multi-threaded implementation, Warp[0] will be linked directly to * the given warp, while for all other threads a copy of the original object * is created by a call to WarpXform::Clone(). */ virtual void SetWarpXform ( SplineWarpXform::SmartPtr& warp ) { this->Superclass::SetWarpXform( warp ); for ( size_t thread = 0; thread < this->m_NumberOfThreads; ++thread ) { if ( this->Warp ) { if ( thread ) { ThreadWarp[thread] = SplineWarpXform::SmartPtr( this->Warp->Clone() ); ThreadWarp[thread]->RegisterVolume( *(this->ReferenceGrid) ); } else { ThreadWarp[thread] = this->Warp; } } else { ThreadWarp[thread] = SplineWarpXform::SmartPtr::Null(); } } } /** Evaluate functional after change of a single parameter. *\param warp The current deformation. *\param localMetric The local working metric. *\param voi Volume-of-Influence for the parameter under consideration. *\param vectorCache Pre-allocated storage for holding transformed vectors. *\return The metric after recomputation over the given volume-of-influence. */ typename Self::ReturnType EvaluateIncremental( const SplineWarpXform& warp, VM *const localMetric, const DataGrid::RegionType& voi, Vector3D *const vectorCache ) { Vector3D *pVec; Types::GridIndexType pX, pY, pZ, offset, r; Types::GridIndexType fltIdx[3]; Types::Coordinate fltFrac[3]; Types::GridIndexType endLineIncrement = ( voi.From()[0] + ( this->DimsX - voi.To()[0]) ); Types::GridIndexType endPlaneIncrement = this->DimsX * ( voi.From()[1] + (this->DimsY - voi.To()[1]) ); const typename VM::Exchange unsetY = this->Metric->DataY.padding(); *localMetric = *this->Metric; r = voi.From()[0] + this->DimsX * ( voi.From()[1] + this->DimsY * voi.From()[2] ); for ( pZ = voi.From()[2]; pZMetric->GetSampleX( r ); if ( this->WarpedVolume[r] != unsetY ) localMetric->Decrement( sampleX, this->WarpedVolume[r] ); // Tell us whether the current location is still within the floating volume and get the respective voxel. *pVec *= this->FloatingInverseDelta; if ( this->FloatingGrid->FindVoxelByIndex( *pVec, fltIdx, fltFrac ) ) { // Compute data index of the floating voxel in the floating volume. offset = fltIdx[0] + this->FltDimsX * ( fltIdx[1] + this->FltDimsY * fltIdx[2] ); // Continue metric computation. localMetric->Increment( sampleX, this->Metric->GetSampleY(offset, fltFrac ) ); } else { if ( this->m_ForceOutsideFlag ) { localMetric->Increment( sampleX, this->m_ForceOutsideValueRescaled ); } } } r += endLineIncrement; } r += endPlaneIncrement; } return localMetric->Get(); } /** Using OpenMP, update set of active and passive parameters. * This function computes local entropies in the neighborhood of all control * points of the Warp transformation. Those control points for which both * reference and floating image have less than half the maximum entropy in * this neighborhood as compared to the rest of the image are set passive. * The passive parameters are not considered for gradient computation and * therefore save significant computation time. */ virtual void UpdateWarpFixedParameters(); /// Compute functional value and gradient. virtual typename Self::ReturnType EvaluateWithGradient( CoordinateVector& v, CoordinateVector& g, const typename Self::ParameterType step = 1 ) { const typename Self::ReturnType current = this->EvaluateAt( v ); if ( this->m_AdaptiveFixParameters && this->WarpNeedsFixUpdate ) { this->UpdateWarpFixedParameters(); } // Make sure we don't create more threads than we have parameters. // Actually, we shouldn't create more than the number of ACTIVE parameters. // May add this at some point. Anyway, unless we have A LOT of processors, // we shouldn't really ever have more threads than active parameters :)) const size_t numberOfTasks = std::min( this->m_NumberOfTasks, this->Dim ); for ( size_t taskIdx = 0; taskIdx < numberOfTasks; ++taskIdx ) { InfoTaskGradient[taskIdx].thisObject = this; InfoTaskGradient[taskIdx].Step = step; InfoTaskGradient[taskIdx].Gradient = g.Elements; InfoTaskGradient[taskIdx].BaseValue = current; InfoTaskGradient[taskIdx].Parameters = &v; } ThreadPool& threadPool = ThreadPool::GetGlobalThreadPool(); threadPool.Run( EvaluateGradientThread, InfoTaskGradient, numberOfTasks ); return current; } /// Evaluate functional. virtual typename Self::ReturnType EvaluateAt ( CoordinateVector& v ) { ThreadWarp[0]->SetParamVector( v ); return this->Evaluate(); } virtual typename Self::ReturnType Evaluate () { this->Metric->Reset(); if ( ! this->WarpedVolume ) this->WarpedVolume = Memory::ArrayC::Allocate( this->DimsX * this->DimsY * this->DimsZ ); const size_t numberOfTasks = std::min( this->m_NumberOfTasks, this->DimsY * this->DimsZ ); for ( size_t taskIdx = 0; taskIdx < numberOfTasks; ++taskIdx ) { InfoTaskComplete[taskIdx].thisObject = this; } for ( size_t taskIdx = 0; taskIdx < this->m_NumberOfThreads; ++taskIdx ) { this->TaskMetric[taskIdx]->Reset(); } ThreadPool::GetGlobalThreadPool().Run( EvaluateCompleteThread, this->InfoTaskComplete, numberOfTasks ); for ( size_t taskIdx = 0; taskIdx < this->m_NumberOfThreads; ++taskIdx ) { this->Metric->AddMetric( *(this->TaskMetric[taskIdx]) ); } return this->WeightedTotal( this->Metric->Get(), ThreadWarp[0] ); } private: /** Metric object for threadwise computation. * The objects in this array are the per-thread equivalent of the * VoxelMatchingElasticFunctional::IncrementalMetric object. */ std::vector TaskMetric; #ifdef _OPENMP /// Consistency histogram objects for threadwise computation. std::vector::SmartPtr> m_ThreadConsistencyHistograms; #endif // #ifdef _OPENMP /** Thread parameter block for incremental gradient computation. * This structure holds all thread-specific information. A pointer to an * instance of this structure is given to EvaluateGradientThread() for * each thread created. */ class EvaluateGradientTaskInfo { public: /** Pointer to the functional object that created the thread. */ Self *thisObject; /// Current parameter vector. CoordinateVector *Parameters; /// Current global coordinate stepping. typename Self::ParameterType Step; /// Pointer to gradient vector that is the target for computation results. Types::Coordinate *Gradient; /// Base functional value used for comparing new values to. double BaseValue; }; /// Info blocks for parallel threads evaluating functional gradient. std::vector InfoTaskGradient; /** Compute functional gradient as a thread. * This function (i.e., each thread) iterates over all parameters of the * current warp transformation. Among all active (i.e., not disabled) * parameters, it selects the ones that have an index with modulus * equal to the threads index when divided by the total number of threads. * For these parameters, the thread computes the partial derivative of the * functional by finite-difference approximation. */ static void EvaluateGradientThread( void* arg, const size_t taskIdx, const size_t taskCnt, const size_t threadIdx, const size_t ) { typename Self::EvaluateGradientTaskInfo *info = static_cast( arg ); Self *me = info->thisObject; SplineWarpXform& myWarp = *(me->ThreadWarp[threadIdx]); myWarp.SetParamVector( *info->Parameters ); VM* threadMetric = me->TaskMetric[threadIdx]; Vector3D *vectorCache = me->ThreadVectorCache[threadIdx]; Types::Coordinate *p = myWarp.m_Parameters; Types::Coordinate pOld; double upper, lower; const DataGrid::RegionType *voi = me->VolumeOfInfluence + taskIdx; for ( size_t dim = taskIdx; dim < me->Dim; dim+=taskCnt, voi+=taskCnt ) { if ( me->StepScaleVector[dim] <= 0 ) { info->Gradient[dim] = 0; } else { const typename Self::ParameterType thisStep = info->Step * me->StepScaleVector[dim]; pOld = p[dim]; p[dim] += thisStep; upper = me->EvaluateIncremental( myWarp, threadMetric, *voi, vectorCache ); p[dim] = pOld - thisStep; lower = me->EvaluateIncremental( myWarp, threadMetric, *voi, vectorCache ); p[dim] = pOld; me->WeightedDerivative( lower, upper, myWarp, dim, thisStep ); if ( (upper > info->BaseValue ) || (lower > info->BaseValue) ) { // strictly mathematically speaking, we should divide here by step*StepScaleVector[dim], but StepScaleVector[idx] is either zero or a constant independent of idx info->Gradient[dim] = upper - lower; } else { info->Gradient[dim] = 0; } } } } /** Thread parameter block for complete functional evaluation. * This structure holds all thread-specific information. A pointer to an * instance of this structure is given to EvaluateGradientThread() for * each thread created. */ class EvaluateCompleteTaskInfo { public: /** Pointer to the functional object that created the thread. */ Self *thisObject; }; /** Info blocks for parallel threads evaluating complete functional. */ std::vector InfoTaskComplete; /// Multi-threaded implementation of complete metric evaluation. static void EvaluateCompleteThread ( void *arg, const size_t taskIdx, const size_t taskCnt, const size_t threadIdx, const size_t ) { typename Self::EvaluateCompleteTaskInfo *info = static_cast( arg ); Self *me = info->thisObject; const SplineWarpXform& warp = *(me->ThreadWarp[0]); VM* threadMetric = me->TaskMetric[threadIdx]; Vector3D *vectorCache = me->ThreadVectorCache[threadIdx]; typename VM::Exchange* warpedVolume = me->WarpedVolume; const typename VM::Exchange unsetY = me->Metric->DataY.padding(); Vector3D *pVec; Types::GridIndexType pX, pY, pZ; Types::GridIndexType fltIdx[3]; Types::Coordinate fltFrac[3]; Types::GridIndexType rowCount = ( me->DimsY * me->DimsZ ); Types::GridIndexType rowFrom = ( rowCount / taskCnt ) * taskIdx; Types::GridIndexType rowTo = ( taskIdx == (taskCnt-1) ) ? rowCount : ( rowCount / taskCnt ) * ( taskIdx + 1 ); Types::GridIndexType rowsToDo = rowTo - rowFrom; Types::GridIndexType pYfrom = rowFrom % me->DimsY; Types::GridIndexType pZfrom = rowFrom / me->DimsY; Types::GridIndexType r = rowFrom * me->DimsX; for ( pZ = pZfrom; (pZ < me->DimsZ) && rowsToDo; ++pZ ) { for ( pY = pYfrom; (pY < me->DimsY) && rowsToDo; pYfrom = 0, ++pY, --rowsToDo ) { warp.GetTransformedGridRow( me->DimsX, vectorCache, 0, pY, pZ ); pVec = vectorCache; for ( pX = 0; pXDimsX; ++pX, ++r, ++pVec ) { // Tell us whether the current location is still within the floating volume and get the respective voxel. *pVec *= me->FloatingInverseDelta; if ( me->FloatingGrid->FindVoxelByIndex( *pVec, fltIdx, fltFrac ) ) { // Compute data index of the floating voxel in the floating // volume. const size_t offset = fltIdx[0] + me->FltDimsX * ( fltIdx[1] + me->FltDimsY*fltIdx[2] ); // Continue metric computation. warpedVolume[r] = me->Metric->GetSampleY(offset, fltFrac ); threadMetric->Increment( me->Metric->GetSampleX(r), warpedVolume[r] ); } else { if ( me->m_ForceOutsideFlag ) { warpedVolume[r] = me->m_ForceOutsideValueRescaled; threadMetric->Increment( me->Metric->GetSampleX(r), warpedVolume[r] ); } else { warpedVolume[r] = unsetY; } } } } } } protected: /** Ground transformed volume. */ typename VM::Exchange *WarpedVolume; /// Flag for forcing pixel values outside the floating image. bool m_ForceOutsideFlag; /// Rescaled byte value for forcing pixel values outside the floating image. typename VM::Exchange m_ForceOutsideValueRescaled; /** Metric object for incremental computation. * Before computing the incremental metric after change of one parameter, * the global metric is copied to this object. It is then used for in-place * application of all necessary changes, leaving the original metric intact. *\see #EvaluateIncremental */ SmartPointer IncrementalMetric; /// Shortcut variables for x, y, z dimension of the reference image. DataGrid::IndexType::ValueType DimsX, DimsY, DimsZ; /// Shorcut variables for x and y dimensions of the floating image. DataGrid::IndexType::ValueType FltDimsX, FltDimsY; /// Array of warp transformation objects for the parallel threads. std::vector ThreadWarp; /// Array of storage for simultaneously retrieving multiple deformed vectors. Vector3D **ThreadVectorCache; /** Number of actual parallel threads used for computations. * All duplicated data structures are generated with the multiplicity given * by this value. It is determined from Threads when the object is first * instanced. It cannot be changed afterwards. */ size_t m_NumberOfThreads; /// Number of parallel tasks. size_t m_NumberOfTasks; }; /** Create functional from matching template. * This constructor function returns a pointer to a newly created elastic * matching functional. The functional is created from the template * corresponding to the given parameters. *\return A pointer to the newly created functional of NULL if creation failed. *\param metric Index of the voxel similarity measure to be used. *\param refVolume Reference volume data. *\param fltVolume Floating volume data. * template. */ VoxelMatchingElasticFunctional* CreateElasticFunctional( const int metric, UniformVolume::SmartPtr& refVolume, UniformVolume::SmartPtr& fltVolume ); } // namespace #endif // __cmtkVoxelMatchingElasticFunctional_h_included_ cmtk-3.3.1/libs/Registration/cmtkVoxelMatchingFunctional.cxx000066400000000000000000000057731276303427400243010ustar00rootroot00000000000000/* // // Copyright 2016 Google, Inc. // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2012 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 5400 $ // // $LastChangedDate: 2016-01-14 19:42:40 -0800 (Thu, 14 Jan 2016) $ // // $LastChangedBy: torstenrohlfing $ // */ #include #include #include namespace cmtk { /** \addtogroup Registration */ //@{ void VoxelMatchingFunctional::InitFloating( UniformVolume::SmartPtr& floating ) { FloatingGrid = floating; this->FloatingDims = this->FloatingGrid->GetDims(); this->FloatingSize = this->FloatingGrid->m_Size; this->m_FloatingCropRegionCoordinates = FloatingGrid->GetHighResCropRegion(); for ( int dim = 0; dim < 3; ++dim ) { this->FloatingInverseDelta[dim] = 1.0 / FloatingGrid->m_Delta[dim]; this->m_FloatingCropRegionFractional.From()[dim] = this->m_FloatingCropRegionCoordinates.From()[dim] * FloatingInverseDelta[dim]; this->m_FloatingCropRegionFractional.To()[dim] = this->m_FloatingCropRegionCoordinates.To()[dim] * FloatingInverseDelta[dim]; } FloatingDataClass = floating->GetData()->GetDataClass(); } void VoxelMatchingFunctional::InitReference( UniformVolume::SmartPtr& reference ) { ReferenceGrid = reference; this->ReferenceDims = this->ReferenceGrid->GetDims(); this->ReferenceSize = this->ReferenceGrid->m_Size; this->m_ReferenceCropRegion = ReferenceGrid->CropRegion(); for ( int dim = 0; dim < 3; ++dim ) this->ReferenceInvDelta[dim] = 1.0 / ReferenceGrid->m_Delta[dim]; ReferenceDataClass = reference->GetData()->GetDataClass(); } const DataGrid::RegionType VoxelMatchingFunctional::GetReferenceGridRange ( const UniformVolume::CoordinateRegionType& region ) const { DataGrid::IndexType from, to; for ( int i = 0; i < 3; ++i ) { from[i] = std::max( this->m_ReferenceCropRegion.From()[i], static_cast( region.From()[i] * this->ReferenceInvDelta[i] ) ); to[i] = 1+std::min( this->m_ReferenceCropRegion.To()[i]-1, 1+static_cast( region.To()[i] * this->ReferenceInvDelta[i] ) ); } return DataGrid::RegionType( from, to ); } } // namespace cmtk cmtk-3.3.1/libs/Registration/cmtkVoxelMatchingFunctional.h000066400000000000000000000151451276303427400237200ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2012 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4093 $ // // $LastChangedDate: 2012-03-27 13:05:25 -0700 (Tue, 27 Mar 2012) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkVoxelMatchingFunctional_h_included_ #define __cmtkVoxelMatchingFunctional_h_included_ #include #include #include #include #include #include #include #include #include #if defined(CMTK_USE_SMP) # include #endif #include namespace cmtk { /** \addtogroup Registration */ //@{ /** Base class for voxel matching functionals. * This class is used as the common base class for more specific functional * classes. It contains all data structure and functions that do not depend on * any template parameters introduced later in the inheritance hierarchy. It * should therefore help avoiding unnecessary code duplication. */ class VoxelMatchingFunctional : public Functional, private CannotBeCopied { public: /// This class. typedef VoxelMatchingFunctional Self; /// Superclass. typedef Functional Superclass; protected: /// Pointer to the reference grid. UniformVolume::SmartPtr ReferenceGrid; /// Pointer to the floating grid. UniformVolume::SmartPtr FloatingGrid; /// Data class of reference image. DataClass ReferenceDataClass; /// Data class of floating image. DataClass FloatingDataClass; /// Crop region in the reference volume. DataGrid::RegionType m_ReferenceCropRegion; /// Optional list of matched landmarks. cmtkGetSetMacro(LandmarkPairList::SmartPtr,LandmarkPairs); /// Weight for the landmark registration error relative to image similarity. cmtkGetSetMacro(Self::ReturnType,LandmarkErrorWeight); public: /** Constructor. * Init pointers to volume and transformation objects and initialize * internal data structures. *\param reference The reference (i.e. static) volume. *\param floating The floating (i.e. transformed) volume. */ VoxelMatchingFunctional( UniformVolume::SmartPtr& reference, UniformVolume::SmartPtr& floating ) { this->InitFloating( floating ); this->InitReference( reference ); this->m_LandmarkErrorWeight = 0; } /** Destructor. */ virtual ~VoxelMatchingFunctional() {} protected: /// Grid dimensions of the floating volume. DataGrid::IndexType FloatingDims; /// Extents of the floating volume in real-world coordinates. UniformVolume::CoordinateVectorType FloatingSize; /// Inverse pixel sizes of the floating volume. Vector3D FloatingInverseDelta; /// Coordinates of the floating image's cropping region. UniformVolume::CoordinateRegionType m_FloatingCropRegionCoordinates; /// Fractional index coordinates of the floating image's cropping region. UniformVolume::CoordinateRegionType m_FloatingCropRegionFractional; /// Grid dimensions of the reference volume. DataGrid::IndexType ReferenceDims; /// Extents of the reference volume in real-world coordinates. UniformVolume::CoordinateVectorType ReferenceSize; /// Inverse pixel deltas of the reference volume. UniformVolume::CoordinateVectorType ReferenceInvDelta; /** Find rectilinear area in original reference grid. *\return The smallest box of reference grid voxels that contains the given coordinate range. */ const DataGrid::RegionType GetReferenceGridRange( const UniformVolume::CoordinateRegionType& region ) const; private: /// Initialize internal data structures for floating image. void InitFloating( UniformVolume::SmartPtr& floating ); /// Initialize internal data structures for reference image. void InitReference( UniformVolume::SmartPtr& reference ); }; /** Functional that evaluates a voxel-based similarity measure. * This class defines the type of functional that is optimized during * voxel-based registration. It holds references to reference and floating data * and computes similarity as well as its gradient w.r.t. a given * transformation. * * The metric to be optimized is given by a template parameter, therefore * allowing inlined code to be generated for efficient evaluation. * * This class, however, is still universal with respect to the registration * transformation. Derived classes can therefore efficiently implement all * transformation-dependent operations. */ template class VoxelMatchingFunctional_Template { protected: /// Pointer to the voxel-metric. SmartPointer Metric; public: /** Constructor. * Init pointers to volume and transformation objects and initialize * internal data structures. *\param reference The reference (i.e. static) volume. *\param floating The floating (i.e. transformed) volume. */ VoxelMatchingFunctional_Template ( UniformVolume::SmartPtr& reference, UniformVolume::SmartPtr& floating ) { Metric = SmartPointer( new VM( reference, floating ) ); } /** Destructor. * Delete metric object. */ virtual ~VoxelMatchingFunctional_Template () {} #if defined(CMTK_USE_SMP) protected: /** Mutex lock. * This mutex is used by the multi-threaded complete functional evaluation. * At the end of its computation, each thread adds its contribution in the * form of a local 2-D histogram to the global histogram. This one step is * protected by the mutex lock, as it requires exclusive write access to the * global metric object. It makes sense, however, to have the threads do * this final step as some of them may finish before others, which gives * them the required exclusive time. */ MutexLock MetricMutex; #endif }; //@} } // namespace cmtk #endif // __cmtkVoxelMatchingFunctional_h_included_ cmtk-3.3.1/libs/Registration/cmtkVoxelMatchingMeanSquaredDifference.cxx000066400000000000000000000026451276303427400263520ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2010, 2013 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4873 $ // // $LastChangedDate: 2013-09-24 12:53:12 -0700 (Tue, 24 Sep 2013) $ // // $LastChangedBy: torstenrohlfing $ // */ #include "cmtkVoxelMatchingMeanSquaredDifference.h" namespace cmtk { /** \addtogroup Registration */ //@{ VoxelMatchingMeanSquaredDifference::VoxelMatchingMeanSquaredDifference ( const UniformVolume* refVolume, const UniformVolume* fltVolume ) : VoxelMatchingMetricShort( refVolume, fltVolume ), Sum( 0.0 ), Samples( 0 ) {} } // namespace cmtk cmtk-3.3.1/libs/Registration/cmtkVoxelMatchingMeanSquaredDifference.h000066400000000000000000000064761276303427400260050ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2011 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 2752 $ // // $LastChangedDate: 2011-01-17 11:33:31 -0800 (Mon, 17 Jan 2011) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkVoxelMatchingMeanSquaredDifference_h_included_ #define __cmtkVoxelMatchingMeanSquaredDifference_h_included_ #include #include #include #include #include #include namespace cmtk { /** \addtogroup Registration */ //@{ /** Mean squared difference metric. *\deprecated For future code, use cmtk::ImagePairSimilarityMetricMSD instead. */ class VoxelMatchingMeanSquaredDifference : /// Inherit generic voxel metric with internal short data. public VoxelMatchingMetricShort { public: /// This type. typedef VoxelMatchingMeanSquaredDifference Self; /// Smart pointer. typedef SmartPointer SmartPtr; /** Constructor. * For reference and model volume, InitDataset is called. *\param refVolume The reference (fixed) volume. *\param fltVolume The floating (moving) volume. */ VoxelMatchingMeanSquaredDifference( const UniformVolume* refVolume, const UniformVolume* fltVolume ); /** Add a pair of values to the metric. */ template void Increment( const T a, const T b ) { if ( (a == this->DataX.padding()) || (b == this->DataY.padding()) ) return; ++Samples; Sum -= MathUtil::Square( a - b ); } /** Remove a pair of values from the metric. */ template void Decrement( const T a, const T b ) { if ( (a == this->DataX.padding()) || (b == this->DataY.padding()) ) return; --Samples; Sum += MathUtil::Square( a - b ); } /// Reset internal variables for next computation. void Reset () { Sum = 0; Samples = 0; } /// Get similarity measure value. Self::ReturnType Get() const { return static_cast( Sum / Samples ); } void AddMetric ( const Self& other ) { Sum += other.Sum; Samples += other.Samples; } void RemoveMetric ( const Self& other ) { Sum -= other.Sum; assert( Sum <= 0 ); Samples -= other.Samples; assert( Samples >= 0 ); } private: /// Sum of all value pair differences. double Sum; /// Counter for number of sample pairs. int Samples; }; //@} } // namespace cmtk #endif // #ifndef __cmtkVoxelMatchingMeanSquaredDifference_h_included_ cmtk-3.3.1/libs/Registration/cmtkVoxelMatchingMetric.cxx000066400000000000000000000037301276303427400234110ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2010 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 2398 $ // // $LastChangedDate: 2010-10-05 14:54:37 -0700 (Tue, 05 Oct 2010) $ // // $LastChangedBy: torstenrohlfing $ // */ #include "cmtkVoxelMatchingMetric.h" #include namespace cmtk { /** \addtogroup Registration */ //@{ template VoxelMatchingMetric::VoxelMatchingMetric ( const UniformVolume* refVolume, const UniformVolume* fltVolume, const bool initData ) { this->DataX.PrecomputeIncrements( refVolume ); this->DataY.PrecomputeIncrements( fltVolume ); if ( initData ) { this->DataX.Init( refVolume ); this->DataY.Init( fltVolume ); } } // instantiate necessary templates. template class VoxelMatchingMetric; template class VoxelMatchingMetric; template class VoxelMatchingMetric; template class VoxelMatchingMetric; } // namespace cmtk cmtk-3.3.1/libs/Registration/cmtkVoxelMatchingMetric.h000066400000000000000000000071551276303427400230430ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2011 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 2752 $ // // $LastChangedDate: 2011-01-17 11:33:31 -0800 (Mon, 17 Jan 2011) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkVoxelMatchingMetric_h_included_ #define __cmtkVoxelMatchingMetric_h_included_ #include #include #include #include #include #include #include #include namespace cmtk { /** \addtogroup Registration */ //@{ /** Base class for voxel metrics with pre-converted image data. */ template class VoxelMatchingMetric : /// Inherit from type-template class. public VoxelMatchingMetric_Type { public: /// This type. typedef VoxelMatchingMetric Self; /// Smart pointer. typedef SmartPointer SmartPtr; /// Return type: same as cmtk::Functional. typedef Functional::ReturnType ReturnType; /** Constructor. * For reference and model volume, InitDataset is called. *\param refVolume The reference (fixed) volume. *\param fltVolume The model (transformed) volume. *\param initData If this flag is set (default), then the internal * representation of the pixel data for both volumes is also created. * Derived classes may want to prevent this if they define their own * specific initialization (e.g., igsVoxelMatchingJointHistogram). */ VoxelMatchingMetric( const UniformVolume* refVolume, const UniformVolume* fltVolume, const bool initData = true ); /** Default constructor. */ VoxelMatchingMetric() {}; /// Get a value from the X distribution (reference image). T GetSampleX ( const size_t index ) const { return this->DataX.Data[index]; } /// Get a value from the Y distribution (floating image). T GetSampleY ( const size_t index ) const { return this->DataY.Data[index]; } /// Interpolate a value from the Y distribution (floating image). T GetSampleY ( const size_t baseIndex, const Types::Coordinate* frac ) const; }; /// Convenience typedef. typedef VoxelMatchingMetric VoxelMatchingMetricShort; /// Convenience typedef. typedef VoxelMatchingMetric VoxelMatchingMetricByte; /// Convenience typedef. typedef VoxelMatchingMetric VoxelMatchingMetricShort_NN; /// Convenience typedef. typedef VoxelMatchingMetric VoxelMatchingMetricByte_NN; //@} } // namespace cmtk #include "cmtkVoxelMatchingMetric.txx" #endif // #ifndef __cmtkVoxelMatchingMetric_h_included_ cmtk-3.3.1/libs/Registration/cmtkVoxelMatchingMetric.txx000066400000000000000000000037131276303427400234330ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // Copyright 2004-2009 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 11 $ // // $LastChangedDate: 2009-05-30 11:30:08 -0700 (Sat, 30 May 2009) $ // // $LastChangedBy: torstenrohlfing $ // */ namespace cmtk { /** \addtogroup Registration */ //@{ template T inline VoxelMatchingMetric::GetSampleY ( const size_t baseIndex, const Types::Coordinate* frac ) const { const Types::Coordinate offsX = 1.0 - frac[0]; const Types::Coordinate offsY = 1.0 - frac[1]; const Types::Coordinate offsZ = 1.0 - frac[2]; assert( (baseIndex+this->DataY.nextIJK) < this->DataY.NumberOfSamples ); const T *node = this->DataY.Data + baseIndex; return static_cast ( offsZ*(offsY*(offsX*node[0] + frac[0]*node[1]) + frac[1]*(offsX*node[this->DataY.nextJ] + frac[0]*node[this->DataY.nextIJ]) )+ frac[2]*(offsY*(offsX*node[this->DataY.nextK] + frac[0]*node[this->DataY.nextIK]) + frac[1]*(offsX*node[this->DataY.nextJK] + frac[0]*node[this->DataY.nextIJK]) ) ); } } // namespace cmtk cmtk-3.3.1/libs/Registration/cmtkVoxelMatchingMetric_Type.cxx000066400000000000000000000134271276303427400244160ustar00rootroot00000000000000/* // // Copyright 1997-2012 Torsten Rohlfing // // Copyright 2004-2010 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4243 $ // // $LastChangedDate: 2012-04-21 20:59:34 -0700 (Sat, 21 Apr 2012) $ // // $LastChangedBy: torsten_at_home $ // */ #include "cmtkVoxelMatchingMetric_Type.h" #include namespace cmtk { /** \addtogroup Registration */ //@{ template void VoxelMatchingMetric_Type::ImageData::Init ( const UniformVolume* volume ) { const TypedArray *srcArray = volume->GetData(); DataArray = TypedArray::SmartPtr( srcArray->Convert( DT ) ); Data = static_cast( DataArray->GetDataPtr() ); NumberOfSamples = this->DataArray->GetDataSize(); this->m_ValueRange = DataArray->GetRange(); BinOffset = this->m_ValueRange.m_LowerBound; BinWidth = 1; if ( srcArray->GetPaddingFlag() ) { Padding = DataTypeTraits::Convert( srcArray->GetPaddingValue() ); } else { Padding = DataTypeTraits::ChoosePaddingValue(); } } template size_t VoxelMatchingMetric_Type::ImageData::Init ( const UniformVolume* volume, const size_t defNumBins, const Types::DataItemRange& bounds ) { const TypedArray* data = volume->GetData(); this->AllocDataArray( data ); Types::DataItem value = 0, minValue = FLT_MAX, maxValue = -FLT_MAX; const DataGrid::IndexType cropFrom = volume->CropRegion().From(); const DataGrid::IndexType cropTo = volume->CropRegion().To(); const DataGrid::IndexType increments = volume->GetCropRegionIncrements(); int offset = increments[0]; for ( int z = cropFrom[2]; z < cropTo[2]; ++z, offset += increments[2] ) { for ( int y = cropFrom[1]; y < cropTo[1]; ++y, offset += increments[1] ) { for ( int x = cropFrom[0]; x < cropTo[0]; ++x, ++offset ) { if ( data->Get( value, offset ) ) { if ( value > maxValue ) maxValue = value; if ( value < minValue ) minValue = value; } } } } minValue = std::max( minValue, bounds.m_LowerBound ); maxValue = std::min( maxValue, bounds.m_UpperBound ); unsigned int defaultValue = 0; unsigned int numBins = defNumBins; if ( numBins != CMTK_HISTOGRAM_AUTOBINS ) { BinOffset = minValue; BinWidth = ( maxValue - minValue ) / (numBins-1); double factor = 1.0 / BinWidth; for ( size_t idx = 0; idx < NumberOfSamples; ++idx ) { if ( data->Get( value, idx ) ) { value = std::max( std::min( value, maxValue ), minValue ); Data[idx] = static_cast( floor(factor * (value-BinOffset)) ); } else { // point to extra bins at the end of each row/column for NULL data. Data[idx] = defaultValue; } } } else { switch ( data->GetDataClass() ) { case DATACLASS_LABEL: { numBins = 1 + static_cast(maxValue-minValue); if ( numBins > 254 ) { fprintf( stderr, "Fatal error: Cannot handle more than 254 different labels.\n" ); exit( 1 ); } BinOffset = 0; BinWidth = 1; for ( size_t idx = 0; idx < NumberOfSamples; ++idx ) { if ( data->Get( value, idx ) ) Data[idx] = static_cast( value - minValue ); else // point to extra bins at the end of each row/column for NULL data. Data[idx] = defaultValue; } } break; default: // Handle everything else as grey-level data. case DATACLASS_GREY: { numBins = JointHistogramBase::CalcNumBins( volume ); BinOffset = minValue; BinWidth = ( maxValue - minValue ) / (numBins-1); double factor = 1.0 / BinWidth; for ( size_t idx = 0; idx < NumberOfSamples; ++idx ) { if ( data->Get( value, idx ) ) { value = std::max( std::min( value, maxValue ), minValue ); Data[idx] = static_cast( floor(factor*(value-BinOffset)) ); } else { // point to extra bins at the end of each row/column for NULL data. Data[idx] = defaultValue; } } } break; } } this->m_ValueRange = Types::DataItemRange( 0, static_cast( numBins - 1 ) ); return (Padding = numBins); } template void VoxelMatchingMetric_Type::ImageData::AllocDataArray ( const TypedArray* templateArray ) { NumberOfSamples = templateArray->GetDataSize(); DataArray = TypedArray::SmartPtr( TypedArray::Create( DT, NumberOfSamples ) ); Data = static_cast( DataArray->GetDataPtr() ); } template void VoxelMatchingMetric_Type::ImageData::PrecomputeIncrements ( const UniformVolume* volume ) { this->ImageDims = volume->GetDims(); // pre-compute relative offsets for tri-linear model interpolation nextJ = volume->GetDims()[0]; nextK = nextJ * volume->GetDims()[1]; nextIJ = nextJ + 1; nextIK = nextK + 1; nextJK = nextK + nextJ; nextIJK = nextJK + 1; } // instantiate necessary templates. template class VoxelMatchingMetric_Type; template class VoxelMatchingMetric_Type; } // namespace cmtk cmtk-3.3.1/libs/Registration/cmtkVoxelMatchingMetric_Type.h000066400000000000000000000141201276303427400240320ustar00rootroot00000000000000/* // // Copyright 1997-2012 Torsten Rohlfing // // Copyright 2004-2011, 2013 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4877 $ // // $LastChangedDate: 2013-09-24 13:25:18 -0700 (Tue, 24 Sep 2013) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkVoxelMatchingMetric_Type_h_included_ #define __cmtkVoxelMatchingMetric_Type_h_included_ #include #include #include #include #include #include #include namespace cmtk { /** \addtogroup Registration */ //@{ /** Base class for voxel metrics with pre-converted image data. */ template class VoxelMatchingMetric_Type { public: /// This class. typedef VoxelMatchingMetric_Type Self; /// This is the type used internally for storing pre-processed image data. typedef T Exchange; /// Structure for handling the two images compared. class ImageData { private: /// Padding data used in this instance. T Padding; public: /// This function returns the constant actually used for padding data. T padding() const { return this->Padding; } /// Precomputed reference voxel data bin indices. T* Data; /// Reference-counting wrapper for Data. TypedArray::SmartPtr DataArray; /// Bin offset. Types::DataItem BinOffset; /// Bin width. Types::DataItem BinWidth; /// Value range. Types::DataItemRange m_ValueRange; /// Number of pixels per dimension in the original image. DataGrid::IndexType ImageDims; /// Total number of pixels. size_t NumberOfSamples; /// Get value range of distribution as stored herein. const Types::DataItemRange GetValueRange() const { return this->m_ValueRange; } /// Convert value to bin. byte ValueToIndex( const Types::DataItem value ) { return static_cast( (std::min( std::max( value, this->m_ValueRange.m_LowerBound ), this->m_ValueRange.m_UpperBound )- this->BinOffset) / BinWidth ); } /// Default constructor. ImageData() : Padding( DataTypeTraits::ChoosePaddingValue() ), Data( NULL ), DataArray( NULL ), BinOffset( 0 ), BinWidth( 0 ), m_ValueRange( 0, 0 ), NumberOfSamples( 0 ) { nextJ = nextK = nextIJ = nextJK = nextIK = nextIJK = 0; } /// Allocate internal data array and create wrapper for reference counting. void AllocDataArray( const TypedArray* templateArray ); /** Initialize internal storage for one volume. * The volume's data is converted into an array of byte values that * directly represent the bin to sort the respective sample into. In * addition, the number of bins is determined and the bins array allocated. *\param volume The original volume data. *\param defNumBins The desired number of bins. If this parameter is * zero, a suitable number is automatically determined. *\param bounds User-specified bounds for data values. All values * outside this range will be set to the upper or lower limit and sorted into the * first or last histogram bin, respectively. *\return The number of bins required to hold the data. Note that there * will be an extra bin allocated to hold non-existing data values. */ size_t Init( const UniformVolume* volume, const size_t defNumBins, const Types::DataItemRange& bounds = Types::DataItemRange( -HUGE_VAL, HUGE_VAL ) ); /** Initialize internal storage for one (reference of model) volume. * The volume's data is converted into an array of byte values that * directly represent the bin to sort the respective sample into. In * addition, the number of bins is determined and the bins array allocated. * This function can distinguish between different kinds of data * (grey-level, binary, and labels) and handle these accordingly. *\param volume The original volume data. */ void Init( const UniformVolume* volume ); /// Precompute grid increments. void PrecomputeIncrements( const UniformVolume* volume ); /// Offset of next voxel row. unsigned int nextJ; /// Offset for next row and column. unsigned int nextIJ; /// Offset for next plane. unsigned int nextK; /// Offset for next plane and column. unsigned int nextIK; /// Offset for next plane and row. unsigned int nextJK; /// Offset for next plane, row, and column. unsigned int nextIJK; }; /// Data of the X distribution. ImageData DataX; /// Data of the Y distribution. ImageData DataY; /// Set data for the X distribution (reference image). void SetDataX( const UniformVolume* volume ) { DataX.PrecomputeIncrements( volume ); } /// Set data for the Y distribution (floating image). void SetDataY( const UniformVolume* volume ) { DataY.PrecomputeIncrements( volume ); } /// Set data for the X and Y distribution (both images). void SetDataXY( const UniformVolume* volumeX, const UniformVolume* volumeY ) { DataX.PrecomputeIncrements( volumeX ); DataY.PrecomputeIncrements( volumeY ); } }; //@} } // namespace cmtk #endif // #ifndef __cmtkVoxelMatchingMetric_Type_h_included_ cmtk-3.3.1/libs/Registration/cmtkVoxelMatchingMutInf.h000066400000000000000000000063761276303427400230260ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2011 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 2752 $ // // $LastChangedDate: 2011-01-17 11:33:31 -0800 (Mon, 17 Jan 2011) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkVoxelMatchingMutInf_h_included_ #define __cmtkVoxelMatchingMutInf_h_included_ #include #include #include #include #include #include namespace cmtk { /** \addtogroup Registration */ //@{ /** Voxel metric "mutual information". *\deprecated For future code, use cmtk::ImagePairSimilarityMetricNCC instead. */ template class VoxelMatchingMutInf : /// Inherit basic functionality from 2D histogram. public RegistrationJointHistogram { public: /// This type. typedef VoxelMatchingMutInf Self; /// Smart pointer. typedef SmartPointer SmartPtr; /** Constructor. * For reference and model volume, InitDataset is called. *\param refVolume The reference (fixed) volume. *\param fltVolume The model (transformed) volume. *\param numRefBins The desired number of bins to classify the * reference data. If this parameter is zero (default), a suitable value * is automatically determined. *\param numFltBins The desired number of bins to classify the * model data. If this parameter is zero (default), a suitable value * is automatically determined. */ VoxelMatchingMutInf ( const UniformVolume* refVolume, const UniformVolume* fltVolume, const unsigned int numRefBins = CMTK_HISTOGRAM_AUTOBINS, const unsigned int numFltBins = CMTK_HISTOGRAM_AUTOBINS ) : RegistrationJointHistogram( refVolume, fltVolume, numRefBins, numFltBins ) {}; /// Return mutual information. typename Self::ReturnType Get () const { double HX, HY; this->GetMarginalEntropies(HX,HY); const double HXY = this->GetJointEntropy(); return static_cast( HX + HY - HXY ); } }; /// Mutual information with trilinear interpolation. typedef VoxelMatchingMutInf VoxelMatchingMutInf_Trilinear; /// Mutual information with nearest-neighbor interpolation. typedef VoxelMatchingMutInf VoxelMatchingMutInf_NearestNeighbor; //@} } // namespace cmtk #endif // #ifndef _VoxelMatchingMUTINF_H_ cmtk-3.3.1/libs/Registration/cmtkVoxelMatchingNormMutInf.h000066400000000000000000000100271276303427400236460ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2011 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 2752 $ // // $LastChangedDate: 2011-01-17 11:33:31 -0800 (Mon, 17 Jan 2011) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkVoxelMatchingNormMutInf_h_included_ #define __cmtkVoxelMatchingNormMutInf_h_included_ #include #include #include #include namespace cmtk { /** \addtogroup Registration */ //@{ /** Voxel metric "normalized mutual information". *\deprecated For future code, use cmtk::ImagePairSimilarityMetricNMI instead. */ template class VoxelMatchingNormMutInf : /// Inherit basic functionality from 2D histogram. public RegistrationJointHistogram { public: /// This type. typedef VoxelMatchingNormMutInf Self; /// Smart pointer. typedef SmartPointer SmartPtr; /// Parent class. typedef RegistrationJointHistogram Superclass; /** Constructor. * For reference and floating volume, InitDataset is called. *\param refVolume The reference (fixed) volume. *\param fltVolume The floating (transformed) volume. *\param numRefBins The desired number of bins to classify the * reference data. If this parameter is zero (default), a suitable value * is automatically determined. *\param numFltBins The desired number of bins to classify the * floating image data. If this parameter is zero (default), a suitable value * is automatically determined. */ VoxelMatchingNormMutInf ( const UniformVolume* refVolume, const UniformVolume* fltVolume, const unsigned int numRefBins = CMTK_HISTOGRAM_AUTOBINS, const unsigned int numFltBins = CMTK_HISTOGRAM_AUTOBINS ) : RegistrationJointHistogram( refVolume, fltVolume, numRefBins, numFltBins ) {}; /** Constructor with explicit value range limits. * For reference and floating volume, InitDataset is called. *\param refVolume The reference (fixed) volume. *\param fltVolume The floating (transformed) volume. *\param rangeRef Range of reference image values. *\param rangeFlt Range of floating image values. */ VoxelMatchingNormMutInf ( const UniformVolume* refVolume, const UniformVolume* fltVolume, const Types::DataItemRange& rangeRef, const Types::DataItemRange& rangeFlt ) : RegistrationJointHistogram( refVolume, fltVolume, CMTK_HISTOGRAM_AUTOBINS, CMTK_HISTOGRAM_AUTOBINS, rangeRef, rangeFlt ) {}; /// Return normalized mutual information. typename Self::ReturnType Get () const { double HX, HY; this->GetMarginalEntropies(HX,HY); const double HXY = this->GetJointEntropy(); return static_cast( (HX + HY) / HXY ); } }; /// Normalized mutual information with trilinear interpolation. typedef VoxelMatchingNormMutInf VoxelMatchingNormMutInf_Trilinear; /// Normalized mutual information with nearest-neighbor interpolation. typedef VoxelMatchingNormMutInf VoxelMatchingNormMutInf_NearestNeighbor; //@} } // namespace cmtk #endif // #ifndef __cmtkVoxelMatchingNormMutInf_h_included_ cmtk-3.3.1/libs/Registration/cmtkVoxelRegistration.cxx000066400000000000000000000123141276303427400231630ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2011, 2013 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4882 $ // // $LastChangedDate: 2013-09-27 15:16:36 -0700 (Fri, 27 Sep 2013) $ // // $LastChangedBy: torstenrohlfing $ // */ #include #include #include #include #include #include #include #ifdef HAVE_SYS_UTSNAME_H # include #endif namespace cmtk { /** \addtogroup Registration */ //@{ VoxelRegistration::VoxelRegistration () : m_Metric( 0 ), m_DeltaFThreshold( 0.0 ), m_PreprocessorRef( "Reference", "ref" ), m_PreprocessorFlt( "Floating", "flt" ), m_InitialTransformation( NULL ), m_InitialXformIsInverse( false ), m_Xform( NULL ), m_Optimizer( NULL ) { this->m_Callback = RegistrationCallback::SmartPtr( new RegistrationCallback() ); this->m_Protocol = NULL; this->m_Exploration = -1; this->m_Accuracy = -1; this->m_Sampling = -1; this->CoarsestResolution = -1; this->m_UseOriginalData = true; this->m_Algorithm = 0; UseMaxNorm = true; OptimizerStepFactor = 0.5; this-> SwitchVolumes = false; this->TimeStartRegistration = this->TimeStartLevel = this->WalltimeStartRegistration = this->WalltimeStartLevel = this->ThreadTimeStartRegistration = this->ThreadTimeStartLevel = 0.0; } VoxelRegistration::~VoxelRegistration () { free( this->m_Protocol ); } CallbackResult VoxelRegistration::InitRegistration () { if ( this->m_Sampling <= 0 ) this->m_Sampling = std::max( this->m_Volume_1->GetMaxDelta(), this->m_Volume_2->GetMaxDelta() ); if ( this->m_Exploration <= 0 ) this->m_Exploration = 8.0 * this->m_Sampling; if ( this->m_Accuracy <= 0 ) this->m_Accuracy = this->m_Sampling / 128; TimeStartLevel = TimeStartRegistration = cmtk::Timers::GetTimeProcess(); WalltimeStartLevel = WalltimeStartRegistration = cmtk::Timers::GetWalltime(); ThreadTimeStartLevel = ThreadTimeStartRegistration = cmtk::Timers::GetTimeThread(); return CALLBACK_OK; } CallbackResult VoxelRegistration::Register () { CallbackResult irq = this->InitRegistration(); if ( irq != CALLBACK_OK ) { this->DoneRegistration(); return irq; } this->m_Optimizer->SetDeltaFThreshold( this->m_DeltaFThreshold ); Types::Coordinate currentExploration = this->m_Exploration; CoordinateVector::SmartPtr v( new CoordinateVector() ); int NumResolutionLevels = FunctionalStack.size(); Progress::Begin( 0, NumResolutionLevels, 1, "Multi-level Registration" ); int index = 1; while ( ! FunctionalStack.empty() && ( irq == CALLBACK_OK ) ) { Functional::SmartPtr nextFunctional = FunctionalStack.top(); FunctionalStack.pop(); this->m_Optimizer->SetFunctional( nextFunctional ); int doneResolution = 0; while ( ! doneResolution && ( irq == CALLBACK_OK ) ) { this->EnterResolution( v, nextFunctional, index, NumResolutionLevels ); if ( irq == CALLBACK_OK ) { Types::Coordinate effectiveAccuracy = (index == NumResolutionLevels) ? std::max( this->m_Accuracy, currentExploration/1024 ) : this->m_Accuracy; irq = this->m_Optimizer->Optimize( *v, currentExploration, effectiveAccuracy ); this->m_Xform->SetParamVector( *v ); } doneResolution = this->DoneResolution( v, nextFunctional, index, NumResolutionLevels ); } this->m_Optimizer->SetFunctional( Functional::SmartPtr::Null() ); currentExploration *= 0.5; Progress::SetProgress( index ); ++index; } Progress::Done(); this->OutputResult( v, irq ); this->DoneRegistration( v ); return irq; } void VoxelRegistration::DoneRegistration( const CoordinateVector* v ) { if ( v ) this->m_Xform->SetParamVector( *v ); } void VoxelRegistration::EnterResolution ( CoordinateVector::SmartPtr& v, Functional::SmartPtr& f, const int idx, const int total ) { if ( this->m_Callback ) { char comment[128]; snprintf( comment, sizeof( comment ), "Entering resolution level %d out of %d.", idx, total ); this->m_Callback->Comment( comment ); } TimeStartLevel = cmtk::Timers::GetTimeProcess(); WalltimeStartLevel = cmtk::Timers::GetWalltime(); ThreadTimeStartLevel = cmtk::Timers::GetTimeThread(); f->GetParamVector( *v ); } } // namespace cmtk cmtk-3.3.1/libs/Registration/cmtkVoxelRegistration.h000066400000000000000000000273041276303427400226150ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2013 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4833 $ // // $LastChangedDate: 2013-09-12 13:34:33 -0700 (Thu, 12 Sep 2013) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkVoxelRegistration_h_included_ #define __cmtkVoxelRegistration_h_included_ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace cmtk { /** \addtogroup Registration */ //@{ /** Generic multiresolution voxel-registration class. * By implementing member functions to retrieve parameters and report results * in derived classes, registration can be integrated into various * environments. */ class VoxelRegistration { protected: /// Metric to use. cmtkGetSetMacro(int,Metric); /// Optimization algorithm to use. cmtkGetSetMacro(int,Algorithm); /// Exploration, i.e. initial step size. cmtkGetSetMacro(double,Exploration); /// Accuracy, i.e. final step size. cmtkGetSetMacro(double,Accuracy); /** Coarsest resolution to resample image data to. * If this value is unset, ie. less than or equal to zero, then the coarsest * resolution is automatically computed from the initial step size * (Exploration). */ double CoarsestResolution; /// Flag whether the last resolution level uses the original images. cmtkGetSetMacro(bool,UseOriginalData); /// Factor between optimization step sizes. double OptimizerStepFactor; /// Use maximum norm instead of Euclid where applicable. bool UseMaxNorm; /// Threshold for terminating optimization based on changes of the target function. Optimizer::ReturnType m_DeltaFThreshold; /// Sampling, i.e. last non-original resolution. cmtkGetSetMacro(Types::Coordinate,Sampling); /// Name of protocol file. cmtkGetSetMacroString(Protocol); /// First data volume. cmtkGetSetMacro(UniformVolume::SmartPtr,Volume_1); /// Second data volume. cmtkGetSetMacro(UniformVolume::SmartPtr,Volume_2); /** Reference data volume. * This is a pointer to the actual reference volume, which is either Volume_1 or Volume_2 above, * depending on whether registration was instructed to switch the two or not. */ cmtkGetSetMacro(UniformVolume::SmartPtr,ReferenceVolume); /** Floating data volume. * This is a pointer to the actual floating volume, which is either Volume_2 or Volume_1 above, * depending on whether registration was instructed to switch the two or not. */ cmtkGetSetMacro(UniformVolume::SmartPtr,FloatingVolume); /// Local class for preprocessing image data, e.g., by histogram operations, thresholding, and cropping. class ImagePreprocessor { public: /// Data class string ("grey", "labels", or "binary") const char* m_DataClassString; /// Data class ID. DataClass m_DataClass; /// Flag for pixel padding. bool m_PaddingFlag; /// Padding value. Types::DataItem m_PaddingValue; /// Lower threshold flag. bool m_LowerThresholdActive; /// Lower threshold value. Types::DataItem m_LowerThresholdValue; /// Upper threshold flag. bool m_UpperThresholdActive; /// Upper threshold value. Types::DataItem m_UpperThresholdValue; /// Flag for image histogram pruning. bool m_UsePruneHistogramBins; /// Prune histogram for image: number of target bins. unsigned int m_PruneHistogramBins; /// Flag for histogram equalization. bool m_HistogramEqualization; /// Flag for application of Sobel edge detection filter. bool m_SobelFilter; /// Crop region in index coordinates. const char* m_CropIndex; /// Crop region in world coordinates. const char* m_CropWorld; /// Flag for auto cropping. bool m_AutoCropFlag; /// Auto cropping level. Types::DataItem m_AutoCropLevel; /// Constructor. ImagePreprocessor( const std::string& name /*!< There are two preprocessors, for reference and floating image: this parameter names a parameter group for this instance.*/, const std::string& key /*!< This parameter gives a string key that is appended to each command line option so that reference and floating preprocessors do not collide.*/ ); /// Attach this preprocessor to a command line parse. void AttachToCommandLine( CommandLine& cl /*!< The command line object to add our options to.*/ ); /// Get pre-processed image from original image. UniformVolume::SmartPtr GetProcessedImage( const UniformVolume* original ); /// Write settings of this object to class stream for archiving. void WriteSettings( ClassStreamOutput& stream ) const; private: /// Store the name that identifies this instance ("Reference" or "Floating") std::string m_Name; /// Store the key that identifies this instance ("ref" or "flt") std::string m_Key; }; /// Image preprocessor for reference image. ImagePreprocessor m_PreprocessorRef; /// Image preprocessor for floating image. ImagePreprocessor m_PreprocessorFlt; /// Flag whether model and reference are exchanged. bool SwitchVolumes; /// Pointer to callback object. cmtkGetSetMacro(RegistrationCallback::SmartPtr,Callback); /// Initial transformation. cmtkGetSetMacro(AffineXform::SmartPtr,InitialTransformation); /// FLag whether initial transformation is an inverse. cmtkGetSetMacro(bool,InitialXformIsInverse); /// Current / final transformation. Xform::SmartPtr m_Xform; /// Stack of functional objects for the resolution steps. std::stack FunctionalStack; /// Pointer to optimizer object. Optimizer::SmartPtr m_Optimizer; /**\name Member functions to be overwritten. */ //@{ /** Initialize registration. * This function is called by Register before any other operations. It can * be overloaded to open status dialog windows, etc. Derived implementations * should call their base class' InitRegistration first. *\return */ virtual CallbackResult InitRegistration (); /** Output registration result. * This function is called after finishing registration. It can overloaded * to report the resulting transformation, export it to an encapsulating * application, etc... */ virtual void OutputResult ( const CoordinateVector* /*!< The vector of resulting transformation parameters. */, const CallbackResult = CALLBACK_OK /*!< The interrupt status - this allows the output function to determine whether computation finished or was interrupted. */ ) {} /** Finalize registration. * This function is called after registration has been terminated. It can * be used to destroy progress dialog windows, free memory etc. Its last * operation should be a call to the respective parent class' implementation. */ virtual void DoneRegistration ( const CoordinateVector* v = NULL); /** Enter resolution level. * This function is called before entering each resolution level. It can * be used to update status displays etc. *\param v Current parameter vector. *\param f Functional for next level. *\param idx Index of the current resolution level. 0 is first (coarsest), * subsequent (finer) resolutions have increasing numbers. *\param total Total number of resolution levels. */ virtual void EnterResolution( CoordinateVector::SmartPtr& v, Functional::SmartPtr& f, const int idx, const int total ); /** Finish resolution level. * This function is called after every resolution level. It should do any * necessary cleanups resulting from the previous call to EnterRegistration. *\return If the current level is finished, 1 is returned. Otherwise, ie. * if the derived class requests another run of the same level, 0 may be * returned. This is used for example by the affine registration in order * to make repeated runs of the same level with different numbers of degrees * of freedom. Be careful not to create any inifinite loops. */ virtual int DoneResolution( CoordinateVector::SmartPtr&, Functional::SmartPtr&, const int, const int ) { return 1; } //@} public: /// Exception class. class ConstructorFailed {}; /** Default constructor. */ VoxelRegistration (); /** Destructor. */ virtual ~VoxelRegistration (); /** Do registration. * This function must be called to start the multiresolution optimization * using the specified parameters. *\return 1 if registration was terminated by a user interrupt, 0 if * registration was finished. */ virtual CallbackResult Register (); /** Return total elapsed process time. */ double GetTotalElapsedTime() const { return cmtk::Timers::GetTimeProcess() - TimeStartRegistration; } /** Return elapsed process time during current level. */ double GetLevelElapsedTime() const { return cmtk::Timers::GetTimeProcess() - TimeStartLevel; } /** Return total elapsed walltime. */ double GetTotalElapsedWalltime() const { return cmtk::Timers::GetWalltime() - WalltimeStartRegistration; } /** Return elapsed walltime during current level. */ double GetLevelElapsedWalltime() const { return cmtk::Timers::GetWalltime() - WalltimeStartLevel; } /** Return total elapsed thread time. */ double GetThreadTotalElapsedTime() const { return cmtk::Timers::GetTimeThread() - ThreadTimeStartRegistration; } /** Return elapsed thread time during current level. */ double GetThreadLevelElapsedTime() const { return cmtk::Timers::GetTimeThread() - ThreadTimeStartLevel; } private: /** Time of registration start. * This is used as the reference for absolute computation time calculation. */ double TimeStartRegistration; /** Time of entering the current resolution level. * This is used as the reference for per-level computation time calculation. */ double TimeStartLevel; /** Reference walltime of registration start. * This is used as the reference for absolute computation time calculation. */ double WalltimeStartRegistration; /** Reference walltime of entering the current resolution level. * This is used as the reference for per-level computation time calculation. */ double WalltimeStartLevel; /** Time of registration start. * This is used as the reference for absolute computation time calculation. */ double ThreadTimeStartRegistration; /** Time of entering the current resolution level. * This is used as the reference for per-level computation time calculation. */ double ThreadTimeStartLevel; }; //@} } // namespace cmtk #endif // #ifndef __cmtkVoxelRegistration_h_included_ cmtk-3.3.1/libs/Registration/cmtkVoxelRegistrationImagePreprocessor.cxx000066400000000000000000000172371276303427400265460ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2013 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4873 $ // // $LastChangedDate: 2013-09-24 12:53:12 -0700 (Tue, 24 Sep 2013) $ // // $LastChangedBy: torstenrohlfing $ // */ #include #include #include #include namespace cmtk { VoxelRegistration::ImagePreprocessor::ImagePreprocessor( const std::string& name, const std::string& key ) : m_DataClassString( NULL ), m_DataClass( DATACLASS_GREY ), m_PaddingFlag( false ), m_PaddingValue( 0 ), m_LowerThresholdActive( false ), m_LowerThresholdValue( -CMTK_ITEM_MAX ), m_UpperThresholdActive( false ), m_UpperThresholdValue( CMTK_ITEM_MAX ), m_UsePruneHistogramBins( false ), m_PruneHistogramBins( 0 ), m_HistogramEqualization( false ), m_SobelFilter( false ), m_CropIndex( NULL ), m_CropWorld( NULL ), m_AutoCropFlag( false ), m_AutoCropLevel( 0 ), m_Name( name ), m_Key( key ) { } void VoxelRegistration::ImagePreprocessor::AttachToCommandLine ( CommandLine& cl ) { cl.BeginGroup( this->m_Name, this->m_Name + " Image Preprocessing" )->SetProperties( CommandLine::PROPS_NOXML ); cl.AddOption( CommandLine::Key( std::string( "class-" ) + this->m_Key ), &this->m_DataClassString, "Data class: grey (default) or label" ); cl.AddOption( CommandLine::Key( std::string( "pad-" ) + this->m_Key ), &this->m_PaddingValue, "Padding value", &this->m_PaddingFlag ); cl.AddOption( CommandLine::Key( std::string( "thresh-min-" ) + this->m_Key ), &this->m_LowerThresholdValue, "Minimum value truncation threshold", &this->m_LowerThresholdActive ); cl.AddOption( CommandLine::Key( std::string( "thresh-max-" ) + this->m_Key ), &this->m_UpperThresholdValue, "Maximum value truncation threshold", &this->m_UpperThresholdActive ); cl.AddOption( CommandLine::Key( std::string( "prune-histogram-" ) + this->m_Key ), &this->m_PruneHistogramBins, "Number of bins for histogram-based pruning", &this->m_UsePruneHistogramBins ); cl.AddSwitch( CommandLine::Key( std::string( "histogram-equalization-" ) + this->m_Key ), &this->m_HistogramEqualization, true, "Apply histogram equalization" ); cl.AddSwitch( CommandLine::Key( std::string( "sobel-filter-" ) + this->m_Key ), &this->m_SobelFilter, true, "Apply Sobel edge detection filter" ); cl.AddOption( CommandLine::Key( std::string( "crop-index-" ) + this->m_Key ), &this->m_CropIndex, "Cropping region in pixel index coordinates [parsed as %d,%d,%d,%d,%d,%d for i0,j0,k0,i1,j1,k1]" ); cl.AddOption( CommandLine::Key( std::string( "crop-world-" ) + this->m_Key ), &this->m_CropWorld, "Cropping region in world coordinates [parsed as %f,%f,%f,%f,%f,%f for x0,y0,z0,x1,y1,z1]" ); cl.EndGroup(); } UniformVolume::SmartPtr VoxelRegistration::ImagePreprocessor::GetProcessedImage( const UniformVolume* original ) { UniformVolume::SmartPtr volume( original->Clone() ); TypedArray::SmartPtr data = volume->GetData(); if ( this->m_DataClassString ) { this->m_DataClass = StringToDataClass( this->m_DataClassString ); data->SetDataClass( this->m_DataClass ); } if ( this->m_PaddingFlag ) { data->SetPaddingValue( this->m_PaddingValue ); } if ( this->m_LowerThresholdActive || this->m_UpperThresholdActive ) { data->Threshold( Types::DataItemRange( this->m_LowerThresholdValue, this->m_UpperThresholdValue ) ); } if ( this->m_PruneHistogramBins ) { data->PruneHistogram( true /*pruneHi*/, false /*pruneLo*/, this->m_PruneHistogramBins ); } if ( this->m_HistogramEqualization ) { data->ApplyFunctionObject( TypedArrayFunctionHistogramEqualization( *data ) ); } if ( this->m_SobelFilter ) { volume->SetData( DataGridFilter( volume ).GetDataSobelFiltered() ); } if ( this->m_CropIndex ) { int cropFrom[3], cropTo[3]; if ( 6 != sscanf( this->m_CropIndex, "%6d,%6d,%6d,%6d,%6d,%6d", cropFrom, cropFrom+1, cropFrom+2, cropTo, cropTo+1, cropTo+2 ) ) { StdErr << "Option index coordinate cropping expects six integer parameters but got '" << this->m_CropIndex << "'\n"; exit( 1 ); } for ( int dim=0; dim<3; ++dim ) { if ( cropTo[dim] < 0 ) { cropTo[dim] = volume->GetDims()[dim] + cropTo[dim] + 1; } } volume->CropRegion() = DataGrid::RegionType( DataGrid::IndexType::FromPointer( cropFrom ), DataGrid::IndexType::FromPointer( cropTo ) ); } if ( this->m_CropWorld ) { float crop[6]; if ( 6 != sscanf( this->m_CropWorld, "%15f,%15f,%15f,%15f,%15f,%15f", crop, crop+1, crop+2, crop+3, crop+4, crop+5 ) ) { StdErr << "Option world coordinate cropping expects six floating-point parameters but got '" << this->m_CropWorld << "'\n"; exit( 1 ); } Types::Coordinate realCropFrom[3], realCropTo[3]; for ( int dim=0; dim<3; ++dim ) { realCropFrom[dim] = crop[dim]; if ( crop[3+dim] < 0 ) { realCropTo[dim] = volume->m_Size[dim] + crop[3+dim]; } else { realCropTo[dim] = crop[3+dim]; } } volume->SetHighResCropRegion( UniformVolume::CoordinateRegionType( UniformVolume::CoordinateRegionType::IndexType::FromPointer( realCropFrom ), UniformVolume::CoordinateRegionType::IndexType::FromPointer( realCropTo ) ) ); } if ( this->m_AutoCropFlag ) { volume->AutoCrop( this->m_AutoCropLevel, true /*recrop*/ ); } return volume; } void VoxelRegistration::ImagePreprocessor::WriteSettings ( ClassStreamOutput& stream ) const { stream.Begin( std::string( "preprocessing_" ) + this->m_Key ); switch ( this->m_DataClass ) { case DATACLASS_GREY: stream.WriteString( "dataclass", "GreyLevel" ); break; case DATACLASS_LABEL: stream.WriteString( "dataclass", "LabelField" ); break; default: stream.WriteString( "dataclass", "Unknown" ); break; } if ( this->m_PaddingFlag ) stream.WriteDouble( "padding_value", this->m_PaddingValue ); if ( this->m_LowerThresholdActive ) stream.WriteDouble( "thresh_lower", this->m_LowerThresholdValue ); if ( this->m_UpperThresholdActive ) stream.WriteDouble( "thresh_upper", this->m_UpperThresholdValue ); if ( this->m_PruneHistogramBins ) stream.WriteInt( "prune_histogram_bins", this->m_PruneHistogramBins ); if ( this->m_HistogramEqualization ) stream.WriteBool( "histogram_equalization", true ); if ( this->m_SobelFilter ) stream.WriteBool( "sobel_filter", true ); if ( this->m_CropIndex ) stream.WriteString( "crop_index", this->m_CropIndex ); if ( this->m_CropWorld ) stream.WriteString( "crop_world", this->m_CropWorld ); if ( this->m_AutoCropFlag ) stream.WriteDouble( "auto_crop_level", this->m_AutoCropLevel ); stream.End(); } } // namespace cmtk cmtk-3.3.1/libs/Registration/doxygen.h000066400000000000000000000023331276303427400177160ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2011 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 2734 $ // // $LastChangedDate: 2011-01-14 10:23:45 -0800 (Fri, 14 Jan 2011) $ // // $LastChangedBy: torstenrohlfing $ // */ /** \defgroup Registration cmtkRegistration Library * This library provides classes related to computing and processing registrations * between images. */ cmtk-3.3.1/libs/Segmentation/000077500000000000000000000000001276303427400160525ustar00rootroot00000000000000cmtk-3.3.1/libs/Segmentation/CMakeLists.txt000066400000000000000000000055561276303427400206250ustar00rootroot00000000000000## ## Copyright 1997-2009 Torsten Rohlfing ## ## Copyright 2004-2013 SRI International ## ## This file is part of the Computational Morphometry Toolkit. ## ## http://www.nitrc.org/projects/cmtk/ ## ## The Computational Morphometry Toolkit is free software: you can ## redistribute it and/or modify it under the terms of the GNU General Public ## License as published by the Free Software Foundation, either version 3 of ## the License, or (at your option) any later version. ## ## The Computational Morphometry Toolkit is distributed in the hope that it ## will be useful, but WITHOUT ANY WARRANTY; without even the implied ## warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License along ## with the Computational Morphometry Toolkit. If not, see ## . ## ## $Revision: 4672 $ ## ## $LastChangedDate: 2013-01-10 15:36:19 -0800 (Thu, 10 Jan 2013) $ ## ## $LastChangedBy: torstenrohlfing $ ## # Sources of non-templated classes. SET(cmtkSegmentation_SRCS cmtkAtlasSegmentation.cxx cmtkEntropyMinimizationIntensityCorrectionFunctionalBase.cxx cmtkEntropyMinimizationIntensityCorrectionFunctional.cxx cmtkLabelCombinationLocalBinaryShapeBasedAveraging.cxx cmtkLabelCombinationLocalShapeBasedAveraging.cxx cmtkLabelCombinationLocalVoting.cxx cmtkLabelCombinationLocalWeighting.cxx cmtkLabelCombinationMultiClassSTAPLE.cxx cmtkLabelCombinationSTAPLE.cxx cmtkLabelCombinationVoting.cxx cmtkLabelCombinationShapeBasedAveraging.cxx cmtkLabelCombinationShapeBasedAveragingInterpolation.cxx cmtkLeastSquaresPolynomialIntensityBiasField.cxx cmtkOverlapMeasures.cxx cmtkSimpleLevelset.cxx cmtkSimpleLevelsetCommandLineBase.cxx ) SET(cmtkSegmentation_LIBS cmtkSegmentation cmtkRegistration cmtkBase cmtkSystem cmtkNumerics) IF(CMTK_USE_FFTW_FOUND) LIST(APPEND cmtkSegmentation_SRCS cmtkDetectPhantomMagphanEMR051.cxx cmtkSphereDetectionBipolarMatchedFilterFFT.cxx cmtkSphereDetectionNormalizedBipolarMatchedFilterFFT.cxx) LIST(APPEND cmtkSegmentation_LIBS ${CMTK_FFTW_LIBRARIES}) ENDIF(CMTK_USE_FFTW_FOUND) ADD_LIBRARY(cmtkSegmentation ${cmtkSegmentation_SRCS}) TARGET_LINK_LIBRARIES(${cmtkSegmentation_LIBS}) IF(CMTK_LIBRARY_PROPERTIES) SET_TARGET_PROPERTIES(cmtkSegmentation PROPERTIES ${CMTK_LIBRARY_PROPERTIES}) ENDIF(CMTK_LIBRARY_PROPERTIES) INSTALL(TARGETS cmtkSegmentation RUNTIME DESTINATION ${CMTK_INSTALL_BIN_DIR} COMPONENT runtime LIBRARY DESTINATION ${CMTK_INSTALL_LIB_DIR} COMPONENT runtime ARCHIVE DESTINATION ${CMTK_INSTALL_LIB_DIR} COMPONENT libraries) FILE(GLOB files_h "${CMAKE_CURRENT_SOURCE_DIR}/*.h") FILE(GLOB files_txx "${CMAKE_CURRENT_SOURCE_DIR}/*.txx") INSTALL(FILES ${files_h} ${files_txx} DESTINATION ${CMTK_INSTALL_INCLUDE_DIR}/Segmentation COMPONENT headers) cmtk-3.3.1/libs/Segmentation/cmtkAtlasSegmentation.cxx000066400000000000000000000101201276303427400230710ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2013 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 5129 $ // // $LastChangedDate: 2014-01-09 13:48:15 -0800 (Thu, 09 Jan 2014) $ // // $LastChangedBy: torstenrohlfing $ // */ #include "cmtkAtlasSegmentation.h" #include #include #include #include #include #include cmtk::AtlasSegmentation::AtlasSegmentation ( UniformVolume::SmartPtr& targetImage, UniformVolume::SmartPtr& atlasImage, UniformVolume::SmartPtr& atlasLabels ) : m_Fast( false ), m_TargetImage( targetImage ), m_AtlasImage( atlasImage ), m_AtlasLabels( atlasLabels ), m_LabelMap( NULL ) { } void cmtk::AtlasSegmentation ::RegisterAffine() { AffineRegistration ar; ar.SetVolume_1( this->m_TargetImage ); ar.SetVolume_2( this->m_AtlasImage ); // run 6 DOFs first, then 9 at each level. ar.AddNumberDOFs( 6 ); ar.AddNumberDOFs( 9 ); ar.SetInitialAlignCenters( true ); ar.SetExploration( 4 * this->m_TargetImage->GetMaxDelta() ); ar.SetAccuracy( .1 * this->m_TargetImage->GetMaxDelta() ); ar.SetSampling( 2 * this->m_TargetImage->GetMaxDelta() ); ar.SetUseOriginalData( !this->m_Fast ); (DebugOutput( 1 ) << "Affine registration...").flush(); ar.Register(); DebugOutput( 1 ) << " done.\n"; try { this->m_AffineXform = ar.GetTransformation(); } catch ( const AffineXform::MatrixType::SingularMatrixException& ) { StdErr << "ERROR: singular matrix in AffineRegistration::GetTransformation()\n"; throw ExitException( 1 ); } } void cmtk::AtlasSegmentation ::RegisterSpline() { ElasticRegistration er; er.SetVolume_1( this->m_TargetImage ); er.SetVolume_2( this->m_AtlasImage ); er.SetInitialTransformation( this->GetAffineXform() ); er.SetUseOriginalData( !this->m_Fast ); er.SetFastMode( this->m_Fast ); const Types::Coordinate minSize = std::min( std::min( this->m_TargetImage->m_Size[0], this->m_TargetImage->m_Size[1] ), this->m_TargetImage->m_Size[2] ); er.SetGridSpacing( minSize / 2 ); er.SetRefineGrid( std::max( 0, static_cast( (log( minSize / this->m_TargetImage->GetMaxDelta() ) / log(2.0)) - 3 ) ) ); er.SetDelayRefineGrid( !this->m_Fast ); er.SetGridEnergyWeight( 1e-1f ); er.SetAdaptiveFixParameters( true ); er.SetAlgorithm( 3 ); er.SetExploration( minSize / 8 ); er.SetAccuracy( .1 * this->m_TargetImage->GetMinDelta() ); er.SetSampling( 2 * this->m_TargetImage->GetMaxDelta() ); (DebugOutput( 1 ) << "Nonrigid registration...").flush(); er.Register(); DebugOutput( 1 ) << " done.\n"; this->m_WarpXform = er.GetTransformation(); } void cmtk::AtlasSegmentation ::ReformatLabels() { ReformatVolume reformat; reformat.SetInterpolation( Interpolators::PARTIALVOLUME ); reformat.SetPaddingValue( 0 ); reformat.SetUsePaddingValue( true ); reformat.SetReferenceVolume( this->m_TargetImage ); reformat.SetFloatingVolume( this->m_AtlasLabels ); WarpXform::SmartPtr warpXform = this->GetWarpXform(); reformat.SetWarpXform( warpXform ); this->m_LabelMap = UniformVolume::SmartPtr( reformat.PlainReformat() ); } cmtk-3.3.1/libs/Segmentation/cmtkAtlasSegmentation.h000066400000000000000000000062051276303427400225270ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2012 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4134 $ // // $LastChangedDate: 2012-04-06 08:59:05 -0700 (Fri, 06 Apr 2012) $ // // $LastChangedBy: torstenrohlfing $ // */ #include #include #include #include namespace cmtk { /** \addtogroup Segmentation */ //@{ /** Class for integrated atlas-based segmentation. * This class encapsulates the following stages of atlas-based image segmentation: 1) linear image-to-atlas registration, 2) non-linear * image-to-atlas registration, 3) label map reformatting. */ class AtlasSegmentation { public: /// Constructor: compute registrations. AtlasSegmentation( UniformVolume::SmartPtr& targetImage, UniformVolume::SmartPtr& atlasImage, UniformVolume::SmartPtr& atlasLabels ); /// Get affine transformation. AffineXform::SmartPtr& GetAffineXform() { if ( ! this->m_AffineXform ) this->RegisterAffine(); return this->m_AffineXform; } /// Get nonrigid transformation. WarpXform::SmartPtr GetWarpXform() { if ( ! this->m_WarpXform ) this->RegisterSpline(); return this->m_WarpXform; } /// Get nonrigid spline transformation. SplineWarpXform::SmartPtr GetSplineWarpXform() { return SplineWarpXform::SmartPtr::DynamicCastFrom( this->GetWarpXform() ); } /// Get reformatted label map. UniformVolume::SmartPtr& GetLabelMap() { if ( ! this->m_LabelMap ) this->ReformatLabels(); return this->m_LabelMap; } /// Set fast flag. void SetFast( const bool fast ) { this->m_Fast = fast; } private: /// Flag for "fast" computation. bool m_Fast; /// Target image. UniformVolume::SmartPtr m_TargetImage; /// Atlas image. UniformVolume::SmartPtr m_AtlasImage; /// Atlas labels. UniformVolume::SmartPtr m_AtlasLabels; /// Affine registration transformation. AffineXform::SmartPtr m_AffineXform; /// Compute affine registration. void RegisterAffine(); /// Nonrigid, B-spline transformation. WarpXform::SmartPtr m_WarpXform; /// Compute spline registration. void RegisterSpline(); /// Output label map. UniformVolume::SmartPtr m_LabelMap; /// Compute label map. void ReformatLabels(); }; //@} } // namespace cmtk cmtk-3.3.1/libs/Segmentation/cmtkDetectPhantomMagphanEMR051.cxx000066400000000000000000001024041276303427400243030ustar00rootroot00000000000000/* // // Copyright 2012-2014 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 5355 $ // // $LastChangedDate: 2014-05-09 16:55:27 -0700 (Fri, 09 May 2014) $ // // $LastChangedBy: torstenrohlfing $ // */ #include "cmtkDetectPhantomMagphanEMR051.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include cmtk::DetectPhantomMagphanEMR051::DetectPhantomMagphanEMR051( UniformVolume::SmartConstPtr& phantomImage, Self::Parameters& parameters ) : m_Parameters( parameters ), m_PhantomImage( phantomImage ), m_ExcludeMask( phantomImage->CloneGrid() ), m_IncludeMask( phantomImage->CloneGrid() ) { this->m_ExcludeMask->CreateDataArray( TYPE_BYTE, true /*setToZero*/ ); this->m_ExcludeMask->GetData()->SetDataClass( DATACLASS_LABEL ); this->m_IncludeMask->CreateDataArray( TYPE_BYTE ); this->m_IncludeMask->GetData()->SetDataClass( DATACLASS_LABEL ); this->m_Landmarks.resize( MagphanEMR051::NumberOfSpheres ); // create sphere detection filter based on bipolar FFT matched filtering SphereDetectionNormalizedBipolarMatchedFilterFFT sphereDetector( *(this->m_PhantomImage) ); // Find 1x 60mm SNR sphere TypedArray::SmartPtr filterResponse( sphereDetector.GetFilteredImageData( MagphanEMR051::SphereRadius( 0 ), this->m_Parameters.m_BipolarFilterMargin ) ); this->m_Landmarks[0] = this->RefineSphereLocation( this->FindSphere( *filterResponse ), MagphanEMR051::SphereRadius( 0 ), 1 /*label*/ ); // Nothing goes without the SNR sphere! if ( ! this->m_Landmarks[0].m_Valid ) { StdErr << "ERROR: cannot find SNR sphere.\n"; throw ExitException( 1 ); } // assume that SNR sphere defines center of phantom (may not be true if phantom is broken) Self::SpaceVectorType phantomCenter = this->m_Landmarks[0].m_Location; // The first pass at the CNR spheres is only temporary, so store exclusion mask (SNR sphere) TypedArray::SmartPtr saveExcludeMaskData = this->m_ExcludeMask->GetData()->Clone(); // Find 4x 30mm CNR spheres in the ANY order - sort them out by comparing signal intensity std::multimap cnrSpheres; Self::SpaceVectorType cnrCenter( 0.0 ); for ( size_t i = 3; i < 7; ++i ) { (DebugOutput( 5 ) << MagphanEMR051::SphereName( i ) << " \r").flush(); filterResponse = sphereDetector.GetFilteredImageData( MagphanEMR051::SphereRadius( i ), this->m_Parameters.m_BipolarFilterMargin ); const Types::Coordinate distanceFromCenter = (MagphanEMR051::SphereCenter( i ) - MagphanEMR051::SphereCenter( 0 )).RootSumOfSquares(); // at what distance from phantom center do we expect to find this sphere? const Self::SpaceVectorType location = this->RefineSphereLocation( this->FindSphereAtDistance( *filterResponse, this->m_Landmarks[0].m_Location, distanceFromCenter , 20 /*search band width*/ ), MagphanEMR051::SphereRadius( i ), i+1 /*label*/ ); cnrCenter += location; Types::DataItem mean, stdev; this->GetSphereMeanStdDeviation( mean, stdev, location, MagphanEMR051::SphereRadius( i ), this->m_Parameters.m_ErodeCNR, 2 /*biasFieldDegree*/ ); cnrSpheres.insert( std::pair( -mean, location ) ); } cnrCenter /= cnrSpheres.size(); // the CNR spheres are only temporaty, so bring back previous exclusion mask (SNR sphere) this->m_ExcludeMask->SetData( saveExcludeMaskData ); // find the two 15mm spheres near estimated position for ( size_t i = 1; i < 3; ++i ) { (DebugOutput( 5 ) << MagphanEMR051::SphereName( i ) << " \r").flush(); filterResponse = sphereDetector.GetFilteredImageData( MagphanEMR051::SphereRadius( i ), this->m_Parameters.m_BipolarFilterMargin ); const Types::Coordinate distanceFromCenter = (MagphanEMR051::SphereCenter( i ) - MagphanEMR051::SphereCenter( 0 )).RootSumOfSquares(); // at what distance from phantom center do we expect to find this sphere? try { this->m_Landmarks[i] = Self::LandmarkType( this->FindSphereAtDistance( *filterResponse, this->m_Landmarks[0].m_Location, distanceFromCenter , 10 /*search band width*/ ) ); this->m_Landmarks[i] = Self::LandmarkType( this->RefineSphereLocation( this->m_Landmarks[i].m_Location, MagphanEMR051::SphereRadius( i ), 1+i /*label*/ ) ); } catch (...) { if ( !this->m_Parameters.m_TolerateTruncation ) throw; } } // Check whether phantom center and the 15mm spheres actually define an orthogonal coordinate system if ( this->m_Landmarks[1].m_Valid && this->m_Landmarks[2].m_Valid ) { // Cosine of angle between SNR sphere center and the two 15mm spheres const Types::Coordinate angleCosineSNR = fabs( (this->m_Landmarks[0].m_Location - this->m_Landmarks[1].m_Location).GetAngleCosine( this->m_Landmarks[0].m_Location - this->m_Landmarks[2].m_Location ) ); // Cosine of angle between CNR 4-sphere centroid and the two 15mm spheres const Types::Coordinate angleCosineCNR = fabs( (cnrCenter - this->m_Landmarks[1].m_Location).GetAngleCosine( cnrCenter - this->m_Landmarks[2].m_Location ) ); // Use the phantom center estimate with the smallest angle cosine, i.e., the one generating the closest to an orthogonal coordinate system Types::Coordinate minAngleCosine; if ( (angleCosineSNR <= angleCosineCNR) && !this->m_Parameters.m_ForceFallbackCentroidCNR ) { minAngleCosine = angleCosineSNR; phantomCenter = this->m_Landmarks[0].m_Location; } else { // Set flag to mark that we could not use the SNR sphere. this->m_StatusFlags.m_FallbackCentroidCNR = true; this->m_StatusFlags.m_DistanceSNRtoCNR = (cnrCenter - this->m_Landmarks[0].m_Location).RootSumOfSquares(); minAngleCosine = angleCosineCNR; phantomCenter = cnrCenter; } if ( (minAngleCosine > 0.087) || // more than about 5 degrees deviation from right angle - neither phantom center estimate gave an orthogonal system, this means, one or both of the 15mm spheres weren't found properly ( this->m_Parameters.m_StandardOrientation && CrossProduct( this->m_Landmarks[0].m_Location - this->m_Landmarks[1].m_Location, this->m_Landmarks[0].m_Location - this->m_Landmarks[2].m_Location )[1] < 0 ) ) // wrong handedness (upside down phantom) for "standard orientation" { this->m_Landmarks[1].m_Valid = this->m_Landmarks[2].m_Valid = false; for ( size_t px = 0; px < this->m_ExcludeMask->GetNumberOfPixels(); ++px ) { if ( this->m_ExcludeMask->GetDataAt( px ) > 1 ) this->m_ExcludeMask->SetDataAt( 0.0, px ); } } } else { // Set flag to mark that we could not use the 15mm spheres this->m_StatusFlags.m_FallbackOrientationCNR = true; } // now use the SNR and the two 15mm spheres to define first intermediate coordinate system, assuming they were suiccessfully detected. LandmarkPairList landmarkList; landmarkList.push_back( LandmarkPair( "PhantomCenter", MagphanEMR051::SphereCenter( 0 ), phantomCenter ) ); for ( size_t i = 1; i < 3; ++i ) { if ( this->m_Landmarks[i].m_Valid ) { landmarkList.push_back( LandmarkPair( MagphanEMR051::SphereName( i ), MagphanEMR051::SphereCenter( i ), this->m_Landmarks[i].m_Location ) ); } } AffineXform::SmartPtr intermediateXform; // Check if all three initial landmarks were found successfully - if not, we need to fall back to CNR spheres for establishing initial gross orientation if ( landmarkList.size() >= 3 ) { // create initial rigid transformation to find approximate 10mm landmark sphere locations try { intermediateXform = FitRigidToLandmarks( landmarkList ).GetRigidXform(); } catch ( const AffineXform::MatrixType::SingularMatrixException& ) { StdErr << "ERROR: singular matrix encountered while fitting intermediate rigid transformation\n"; throw ExitException( 1 ); } // Find 4x 30mm CNR spheres in the right order. for ( size_t i = 3; i < 7; ++i ) { (DebugOutput( 5 ) << MagphanEMR051::SphereName( i ) << " \r").flush(); filterResponse = sphereDetector.GetFilteredImageData( MagphanEMR051::SphereRadius( i ), this->m_Parameters.m_BipolarFilterMargin ); const Self::SpaceVectorType candidate = intermediateXform->Apply( MagphanEMR051::SphereCenter( i ) ); this->m_Landmarks[i] = this->RefineSphereLocation( this->FindSphereAtDistance( *filterResponse, candidate, 0 /*search distance*/, 15 /*search band width*/ ), MagphanEMR051::SphereRadius( i ), 1+i /*label*/ ); } } else { // brightest sphere is landmark #3 this->m_Landmarks[3] = cnrSpheres.begin()->second; // make a temporary landmark list for the initial CNR spheres and phantom center only LandmarkPairList temporaryLandmarkList = landmarkList; temporaryLandmarkList.push_back( LandmarkPair( MagphanEMR051::SphereName( 3 ), MagphanEMR051::SphereCenter( 3 ), this->m_Landmarks[3].m_Location ) ); // find sphere closest to #3 - this is #6 for ( std::multimap::const_iterator it = cnrSpheres.begin(); it != cnrSpheres.end(); ++it ) { if ( it != cnrSpheres.begin() ) { if ( (this->m_Landmarks[3].m_Location - it->second).RootSumOfSquares() < 60 ) // Other two CNR spheres are at least about 120mm away, so 60mm should be a safe threshold { this->m_Landmarks[6] = it->second; temporaryLandmarkList.push_back( LandmarkPair( MagphanEMR051::SphereName( 6 ), MagphanEMR051::SphereCenter( 6 ), this->m_Landmarks[6].m_Location ) ); } } } // create intermediate transform based on spheres so far try { intermediateXform = FitRigidToLandmarks( temporaryLandmarkList ).GetRigidXform(); } catch ( const AffineXform::MatrixType::SingularMatrixException& ) { StdErr << "ERROR: singular matrix encountered while fitting intermediate rigid transformation\n"; throw ExitException( 1 ); } // Re-localize all CNR spheres, this time in the right place for ( size_t i = 3; i < 7; ++i ) { (DebugOutput( 5 ) << MagphanEMR051::SphereName( i ) << " \r").flush(); filterResponse = sphereDetector.GetFilteredImageData( MagphanEMR051::SphereRadius( i ), this->m_Parameters.m_BipolarFilterMargin ); const Self::SpaceVectorType candidate = intermediateXform->Apply( MagphanEMR051::SphereCenter( i ) ); this->m_Landmarks[i] = this->RefineSphereLocation( this->FindSphereAtDistance( *filterResponse, candidate, 0 /*search distance*/, 15 /*search band width*/ ), MagphanEMR051::SphereRadius( i ), 1+i /*label*/ ); landmarkList.push_back( LandmarkPair( MagphanEMR051::SphereName( i ), MagphanEMR051::SphereCenter( i ), this->m_Landmarks[i].m_Location ) ); } // refine initial xform using all four CNR spheres intermediateXform = FitRigidToLandmarks( landmarkList ).GetRigidXform(); } // Find 10mm spheres in order near projected locations for ( size_t i = 7; i < MagphanEMR051::NumberOfSpheres; ++i ) { (DebugOutput( 5 ) << MagphanEMR051::SphereName( i ) << " \r").flush(); filterResponse = sphereDetector.GetFilteredImageData( MagphanEMR051::SphereRadius( i ), this->m_Parameters.m_BipolarFilterMargin ); this->m_Landmarks[i] = Self::LandmarkType( intermediateXform->Apply( MagphanEMR051::SphereCenter( i ) ) ); try { this->m_Landmarks[i] = Self::LandmarkType( this->FindSphereAtDistance( *filterResponse, this->m_Landmarks[i].m_Location, 0 /*search distance*/, 5 /*search band width*/ ) ); this->m_Landmarks[i] = Self::LandmarkType( this->RefineSphereLocation( this->m_Landmarks[i].m_Location, MagphanEMR051::SphereRadius( i ), 1+i /*label*/ ) ); } catch (...) { this->m_Landmarks[i].m_Valid = false; if ( !this->m_Parameters.m_TolerateTruncation ) throw; } if ( this->m_Landmarks[i].m_Valid ) { landmarkList.push_back( LandmarkPair( MagphanEMR051::SphereName( i ), MagphanEMR051::SphereCenter( i ), this->m_Landmarks[i].m_Location ) ); // optional: refine initial xform using current landmark list if ( this->m_Parameters.m_RefineXformEachLandmark ) { intermediateXform = FitRigidToLandmarks( landmarkList ).GetRigidXform(); } } } // remove unreliable SNR sphere or CNR centroid before making final fit landmarkList.pop_front(); // create linear, not necessarily rigid, transformation based on all detected landmarks. this->m_PhantomToImageTransformationAffine = FitAffineToLandmarks( landmarkList ).GetAffineXform(); this->m_PhantomToImageTransformationAffine->ChangeCenter( MagphanEMR051::SphereCenter( 0 ) ); // SNR sphere center as center of rotation this->m_PhantomToImageTransformationRigid = FitRigidToLandmarks( landmarkList ).GetRigidXform(); this->m_PhantomToImageTransformationRigid->ChangeCenter( MagphanEMR051::SphereCenter( 0 ) ); // SNR sphere center as center of rotation if ( this->m_Parameters.m_RefineOutliers ) { this->RefineOutlierLandmarks( *filterResponse ); landmarkList.clear(); for ( size_t i = 1; i < 3; ++i ) { if ( this->m_Landmarks[i].m_Valid ) landmarkList.push_back( LandmarkPair( MagphanEMR051::SphereName( i ), MagphanEMR051::SphereCenter( i ), this->m_Landmarks[i].m_Location ) ); } for ( size_t i = 7; i < MagphanEMR051::NumberOfSpheres; ++i ) { if ( this->m_Landmarks[i].m_Valid ) { landmarkList.push_back( LandmarkPair( MagphanEMR051::SphereName( i ), MagphanEMR051::SphereCenter( i ), this->m_Landmarks[i].m_Location ) ); } } this->m_PhantomToImageTransformationAffine = FitAffineToLandmarks( landmarkList ).GetAffineXform(); this->m_PhantomToImageTransformationAffine->ChangeCenter( MagphanEMR051::SphereCenter( 0 ) ); // SNR sphere center as center of rotation this->m_PhantomToImageTransformationRigid = FitRigidToLandmarks( landmarkList ).GetRigidXform(); this->m_PhantomToImageTransformationRigid->ChangeCenter( MagphanEMR051::SphereCenter( 0 ) ); // SNR sphere center as center of rotation } if ( this->m_Parameters.m_ExcludeOutliers ) { this->ExcludeOutlierLandmarks(); this->m_PhantomToImageTransformationAffine = FitAffineToLandmarks( landmarkList ).GetAffineXform(); this->m_PhantomToImageTransformationAffine->ChangeCenter( MagphanEMR051::SphereCenter( 0 ) ); // SNR sphere center as center of rotation this->m_PhantomToImageTransformationRigid = FitRigidToLandmarks( landmarkList ).GetRigidXform(); this->m_PhantomToImageTransformationRigid->ChangeCenter( MagphanEMR051::SphereCenter( 0 ) ); // SNR sphere center as center of rotation } this->ComputeLandmarkFitResiduals( *(this->m_PhantomToImageTransformationAffine) ); Types::Coordinate averageFittingError = 0; Types::Coordinate maximumFittingError = 0; size_t maxErrorLabel = 0; size_t validSpheres = 0; for ( size_t i = 7; i < MagphanEMR051::NumberOfSpheres; ++i ) { if ( this->m_Landmarks[i].m_Valid ) { averageFittingError += this->m_LandmarkFitResiduals[i]; if ( this->m_LandmarkFitResiduals[i] > maximumFittingError ) { maximumFittingError = this->m_LandmarkFitResiduals[i]; maxErrorLabel = i+1; } ++validSpheres; } } averageFittingError /= validSpheres; DebugOutput( 2 ) << "INFO: landmark fitting error average = " << averageFittingError << " maximum = " << maximumFittingError << " maxErrName = " << MagphanEMR051::SphereName( maxErrorLabel-1 ) << " maxErrLabel = " << maxErrorLabel << "\n"; } void cmtk::DetectPhantomMagphanEMR051::RefineOutlierLandmarks( const TypedArray& filterResponse ) { // compute residuals with current transformation if ( this->ComputeLandmarkFitResiduals( *(this->m_PhantomToImageTransformationAffine) ) > this->m_Parameters.m_LandmarkFitResidualThreshold ) { // try to refine outliers, which probably were not properly located. for ( size_t i = 7; i < MagphanEMR051::NumberOfSpheres; ++i ) // we only care about the 10mm spheres. { if ( !this->m_Landmarks[i].m_Valid || (this->m_LandmarkFitResiduals[i] > this->m_Parameters.m_LandmarkFitResidualThreshold) ) { this->m_Landmarks[i].m_Valid = false; const Self::SpaceVectorType predicted = this->m_PhantomToImageTransformationAffine->Apply( MagphanEMR051::SphereCenter( i ) ); // if we predict a landmark outside the image field of view, then the phantom was not imaged completely and we need to bail if ( this->m_PhantomImage->IsInside( predicted ) ) { // Find actual sphere somewhere near the predicted location try { this->m_Landmarks[i] = this->FindSphereAtDistance( filterResponse, predicted, 0, 0.5 * this->m_Parameters.m_LandmarkFitResidualThreshold ); // Refine detection based on local center-of-mass computation const Self::SpaceVectorType refined = this->RefineSphereLocation( this->m_Landmarks[i].m_Location, MagphanEMR051::SphereRadius( i ), 1+i /*label*/ ); // if the refined landmark is outside the image field of view, then the phantom was not imaged completely and we need to bail if ( ! this->m_PhantomImage->IsInside( refined ) ) throw Self::OutsideFieldOfView( i, refined ); // some spheres are darker than background - only accept refinements that improve residual fit error if ( (refined - predicted).RootSumOfSquares() <= (this->m_Landmarks[i].m_Location - predicted).RootSumOfSquares() ) this->m_Landmarks[i] = refined; } catch (...) { if ( ! this->m_Parameters.m_TolerateTruncation ) throw; } } } } } } void cmtk::DetectPhantomMagphanEMR051::ExcludeOutlierLandmarks() { // compute residuals with current transformation if ( this->ComputeLandmarkFitResiduals( *(this->m_PhantomToImageTransformationAffine) ) > this->m_Parameters.m_LandmarkFitResidualThreshold ) { LandmarkPairList landmarkList; // add two 15mm spheres landmarkList.push_back( LandmarkPair( MagphanEMR051::SphereName( 1 ), MagphanEMR051::SphereCenter( 1 ), this->m_Landmarks[1].m_Location ) ); landmarkList.push_back( LandmarkPair( MagphanEMR051::SphereName( 2 ), MagphanEMR051::SphereCenter( 2 ), this->m_Landmarks[2].m_Location ) ); for ( size_t i = 7; i < MagphanEMR051::NumberOfSpheres; ++i ) { if ( this->m_Landmarks[i].m_Valid && (this->m_LandmarkFitResiduals[i] < this->m_Parameters.m_LandmarkFitResidualThreshold) ) { landmarkList.push_back( LandmarkPair( MagphanEMR051::SphereName( i ), MagphanEMR051::SphereCenter( i ), this->m_Landmarks[i].m_Location ) ); } } this->m_PhantomToImageTransformationAffine = FitAffineToLandmarks( landmarkList ).GetAffineXform(); this->m_PhantomToImageTransformationAffine->ChangeCenter( MagphanEMR051::SphereCenter( 0 ) ); // SNR sphere center as center of rotation this->m_PhantomToImageTransformationRigid = FitRigidToLandmarks( landmarkList ).GetRigidXform(); this->m_PhantomToImageTransformationRigid->ChangeCenter( MagphanEMR051::SphereCenter( 0 ) ); // SNR sphere center as center of rotation } } cmtk::Types::Coordinate cmtk::DetectPhantomMagphanEMR051::ComputeLandmarkFitResiduals( const AffineXform& xform ) { Types::Coordinate maxResidual = 0; this->m_LandmarkFitResiduals.resize( MagphanEMR051::NumberOfSpheres ); for ( size_t i = 0; i < MagphanEMR051::NumberOfSpheres; ++i ) { if ( this->m_Landmarks[i].m_Valid ) { this->m_LandmarkFitResiduals[i] = (this->m_Landmarks[i].m_Location - xform.Apply( MagphanEMR051::SphereCenter( i ) ) ).RootSumOfSquares(); if ( i > 6 ) { maxResidual = std::max( this->m_LandmarkFitResiduals[i], maxResidual ); } } } return maxResidual; } cmtk::UniformVolume::SmartPtr cmtk::DetectPhantomMagphanEMR051::GetDetectedSpheresLabelMap() { // draw final sphere mask UniformVolumePainter maskPainter( this->m_ExcludeMask, UniformVolumePainter::COORDINATES_ABSOLUTE ); this->m_ExcludeMask->GetData()->Fill( 0 ); for ( size_t i = 0; i < MagphanEMR051::NumberOfSpheres; ++i ) { if ( this->m_Landmarks[i].m_Valid ) { maskPainter.DrawSphere( this->m_Landmarks[i].m_Location, MagphanEMR051::SphereRadius( i ), 1+i ); } } return this->m_ExcludeMask; } cmtk::DetectPhantomMagphanEMR051::SpaceVectorType cmtk::DetectPhantomMagphanEMR051::FindSphere( const TypedArray& filterResponse ) { size_t maxIndex = 0; Types::DataItem maxValue = filterResponse.ValueAt( 0 ); for ( size_t px = 0; px < filterResponse.GetDataSize(); ++px ) { if ( this->m_ExcludeMask->GetDataAt( px ) == 0 ) { const Types::DataItem value = filterResponse.ValueAt( px ); if ( value > maxValue ) { maxValue = value; maxIndex = px; } } } return this->m_PhantomImage->GetGridLocation( maxIndex ); } cmtk::DetectPhantomMagphanEMR051::SpaceVectorType cmtk::DetectPhantomMagphanEMR051::FindSphereAtDistance ( const TypedArray& filterResponse, const Self::SpaceVectorType& bandCenter, const Types::Coordinate bandRadius, const Types::Coordinate bandWidth ) { UniformVolumePainter maskPainter( this->m_IncludeMask, UniformVolumePainter::COORDINATES_ABSOLUTE ); this->m_IncludeMask->GetData()->Fill( 0.0 ); maskPainter.DrawSphere( bandCenter, bandRadius+bandWidth, 1 ); if ( bandRadius > bandWidth ) { maskPainter.DrawSphere( bandCenter, bandRadius-bandWidth, 0 ); } int maxIndex = -1; Types::DataItem maxValue = 0; for ( size_t px = 0; px < filterResponse.GetDataSize(); ++px ) { if ( (this->m_ExcludeMask->GetDataAt( px ) == 0) && (this->m_IncludeMask->GetDataAt( px ) != 0) ) { const Types::DataItem value = filterResponse.ValueAt( px ); if ( (maxIndex < 0) || (value > maxValue) ) { maxValue = value; maxIndex = px; } } } if ( maxIndex < 0 ) { throw( Self::NoSphereInSearchRegion() ); } return this->m_PhantomImage->GetGridLocation( maxIndex ); } cmtk::DetectPhantomMagphanEMR051::SpaceVectorType cmtk::DetectPhantomMagphanEMR051::RefineSphereLocation( const Self::SpaceVectorType& estimate, const Types::Coordinate sphereRadius, const int label ) { const int margin = this->m_Parameters.m_RefineMarginPixels; DataGrid::IndexType centerPixelIndex; this->m_PhantomImage->GetClosestGridPointIndex( estimate, centerPixelIndex ); const int nSphereRadius[3] = { margin + static_cast( sphereRadius / this->m_PhantomImage->m_Delta[0] ), margin + static_cast( sphereRadius / this->m_PhantomImage->m_Delta[1] ), margin + static_cast( sphereRadius / this->m_PhantomImage->m_Delta[2] ) }; const DataGrid::RegionType region( centerPixelIndex - DataGrid::IndexType::FromPointer( nSphereRadius ), centerPixelIndex + DataGrid::IndexType::FromPointer( nSphereRadius ) + DataGrid::IndexType( 1 ) ); // Check whether region is entirely within image FOV if ( ! (region.From() >= DataGrid::IndexType( 0 ) && region.To() <= this->m_PhantomImage->m_Dims) ) { throw( Self::OutsideFieldOfView( label-1, estimate ) ); } UniformVolume::SmartPtr regionVolume = this->m_PhantomImage->GetCroppedVolume( region ); UniformVolume::SmartPtr regionMask = regionVolume->CloneGrid(); regionMask->CreateDataArray( TYPE_BYTE ); regionMask->GetData()->Fill( 0.0 ); UniformVolumePainter regionMaskPainter( regionMask, UniformVolumePainter::COORDINATES_ABSOLUTE ); regionMaskPainter.DrawSphere( regionVolume->GetCenterCropRegion(), sphereRadius , 1 ); const size_t nPixels = regionVolume->GetNumberOfPixels(); std::vector regionMaskVector( nPixels ); for ( size_t i = 0; i < nPixels; ++i ) { regionMaskVector[i] = ( regionMask->GetDataAt( i ) != 0 ); } if ( this->m_Parameters.m_CorrectSphereBiasField ) { regionMask->SetData( DataGridMorphologicalOperators( regionMask ).GetEroded( 1 /*erode by 1 pixel*/ ) ); std::vector regionMaskVectorErode( nPixels ); for ( size_t i = 0; i < nPixels; ++i ) { regionMaskVectorErode[i] = ( regionMask->GetDataAt( i ) != 0 ); } try { regionVolume->SetData( LeastSquaresPolynomialIntensityBiasField( *regionVolume, regionMaskVectorErode, 1 /* polynomial degree */ ).GetCorrectedData() ); } catch ( PolynomialHelper::DegreeUnsupported& ex ) { StdErr << "ERROR: polynomial degree not supported in LeastSquaresPolynomialIntensityBiasField() constructor.\n"; StdErr << ex.what() << "\n"; throw ExitException( 1 ); } catch ( const LeastSquaresPolynomialIntensityBiasField::EmptyMaskException& ) { // nothing to do; regionVolume still has its data } } // threshold to mask out background in bias-corrected region const Types::DataItem threshold = HistogramOtsuThreshold< Histogram >( *(regionVolume->GetData()->GetHistogram( 1024 )) ).Get(); for ( size_t i = 0; i < nPixels; ++i ) { if ( !regionMaskVector[i] || (regionVolume->GetDataAt( i ) < threshold) ) regionVolume->SetDataAt( 0.0, i ); } const Self::SpaceVectorType refined = estimate + regionVolume->GetCenterOfMass() - regionVolume->GetCenterCropRegion(); // update exclusion mask UniformVolumePainter maskPainter( this->m_ExcludeMask, UniformVolumePainter::COORDINATES_ABSOLUTE ); maskPainter.DrawSphere( refined, sphereRadius+this->m_Parameters.m_SphereExcludeSafetyMargin, label ); if ( ! (MathUtil::IsFinite( refined[0] ) && MathUtil::IsFinite( refined[1] ) && MathUtil::IsFinite( refined[2] ) ) ) { throw( Self::OutsideFieldOfView( label-1, estimate ) ); } return refined; } cmtk::LandmarkList cmtk::DetectPhantomMagphanEMR051::GetExpectedLandmarks( const bool includeUnreliable ) const { cmtk::LandmarkList list; if ( includeUnreliable ) { for ( size_t i = 0; i < 7; ++i ) { list.push_back( Landmark( MagphanEMR051::SphereName( i ), this->m_PhantomToImageTransformationRigid->Apply( MagphanEMR051::SphereCenter( i ) ) ) ); } } else { // Always include 15mm spheres list.push_back( Landmark( MagphanEMR051::SphereName( 0 ), this->m_PhantomToImageTransformationRigid->Apply( MagphanEMR051::SphereCenter( 0 ) ) ) ); list.push_back( Landmark( MagphanEMR051::SphereName( 1 ), this->m_PhantomToImageTransformationRigid->Apply( MagphanEMR051::SphereCenter( 1 ) ) ) ); } for ( size_t i = 7; i < MagphanEMR051::NumberOfSpheres; ++i ) { list.push_back( Landmark( MagphanEMR051::SphereName( i ), this->m_PhantomToImageTransformationRigid->Apply( MagphanEMR051::SphereCenter( i ) ) ) ); } return list; } cmtk::LandmarkList cmtk::DetectPhantomMagphanEMR051::GetDetectedLandmarks( const bool includeOutliers ) const { cmtk::LandmarkList list; for ( size_t i = 0; i < MagphanEMR051::NumberOfSpheres; ++i ) { if ( includeOutliers || (this->m_LandmarkFitResiduals[i] < this->m_Parameters.m_LandmarkFitResidualThreshold) ) list.push_back( Landmark( MagphanEMR051::SphereName( i ), this->m_Landmarks[i].m_Location ) ); } return list; } cmtk::DetectedPhantomMagphanEMR051::SmartPtr cmtk::DetectPhantomMagphanEMR051::GetDetectedPhantom() { DetectedPhantomMagphanEMR051* detected = new DetectedPhantomMagphanEMR051( *(this->m_PhantomToImageTransformationAffine) ); detected->m_EstimatedNonLinear = Self::SpaceVectorType( 0.0 ); size_t countSpheresNonLinear = 0; const AffineXform phantomToPhysical( this->m_PhantomImage->GetImageToPhysicalMatrix() ); for ( size_t i = 0; i < MagphanEMR051::NumberOfSpheres; ++i ) { if ( this->m_Landmarks[i].m_Valid ) { detected->AddLandmarkPair( MagphanEMR051::SphereName( i ), phantomToPhysical.Apply( this->m_PhantomToImageTransformationRigid->Apply( MagphanEMR051::SphereCenter( i ) ) ), phantomToPhysical.Apply( this->m_Landmarks[i].m_Location ), this->m_LandmarkFitResiduals[i], (i>=7) /*only the 10mm spheres #7 and above are considered precise enough for registration*/ ); if ( i >= 7 ) { const Self::SpaceVectorType residual = phantomToPhysical.Apply( this->m_PhantomToImageTransformationAffine->Apply( MagphanEMR051::SphereCenter( i ) ) ) - phantomToPhysical.Apply( this->m_Landmarks[i].m_Location ); detected->m_EstimatedNonLinear += residual.Abs(); ++countSpheresNonLinear; } } } detected->m_EstimatedNonLinear /= countSpheresNonLinear; detected->m_StatusFlags = this->m_StatusFlags; // get SNR estimate Types::DataItem mean, stdev; this->GetSphereMeanStdDeviation( mean, stdev, this->m_Landmarks[0].m_Location, MagphanEMR051::SphereRadius( 0 ), this->m_Parameters.m_ErodeSNR, 2 /*biasFieldDegree*/ ); detected->m_EstimatedSNR = mean / stdev; // get four CNR estimates for ( size_t i = 3; i < 7; ++i ) { // we compute CNR per CNR sphere using formula from http://www.mr-tip.com/serv1.php?type=db1&dbs=Contrast%20to%20Noise%20Ratio (plus "fabs") this->GetSphereMeanStdDeviation( mean, stdev, this->m_Landmarks[i].m_Location, MagphanEMR051::SphereRadius( i ), this->m_Parameters.m_ErodeCNR, 2 /*biasFieldDegree*/ ); detected->m_EstimatedCNR[i-3] = fabs( detected->m_EstimatedSNR - mean / stdev ); } // get the maximum fraction by which a 10mm sphere is dimmed (relative to 90th percentile of intensities over all spheres) Types::DataItem minIntensity = std::numeric_limits::infinity(); std::vector allIntensities; for ( size_t i = 7; i < MagphanEMR051::NumberOfSpheres; ++i ) { if ( this->m_Landmarks[i].m_Valid ) { this->GetSphereMeanStdDeviation( mean, stdev, this->m_Landmarks[i].m_Location, MagphanEMR051::SphereRadius( i ), this->m_Parameters.m_Erode10mm, 0 /*biasFieldDegree*/ ); minIntensity = std::min( minIntensity, mean ); allIntensities.push_back( mean ); } } if ( ! allIntensities.empty() ) { std::sort( allIntensities.begin(), allIntensities.end() ); detected->m_MaxDimming = minIntensity / allIntensities[ static_cast( allIntensities.size() * 0.9 ) ]; } return DetectedPhantomMagphanEMR051::SmartPtr( detected ); } void cmtk::DetectPhantomMagphanEMR051::GetSphereMeanStdDeviation( Types::DataItem& mean, Types::DataItem& stdev, const Self::SpaceVectorType& center, const Types::Coordinate radius, const Types::Coordinate erodeBy, const int biasFieldDegree ) { UniformVolume::SmartPtr maskVolume( this->m_PhantomImage->CloneGrid() ); maskVolume->CreateDataArray( TYPE_BYTE ); maskVolume->GetData()->Fill( 0 ); UniformVolumePainter maskPainter( maskVolume, UniformVolumePainter::COORDINATES_ABSOLUTE ); maskPainter.DrawSphere( center, radius, 1 ); if ( erodeBy ) { maskVolume->SetData( UniformVolumeMorphologicalOperators( maskVolume ).GetErodedByDistance( erodeBy ) ); } // crop both mask and phantom to sphere bounding box UniformVolume::SmartPtr dataVolume = this->m_PhantomImage->GetCroppedVolume( maskVolume->AutoCrop( 0.5 ) ); maskVolume = maskVolume->GetCroppedVolume(); // make bool vector of foreground pixels const size_t nPixels = maskVolume->GetNumberOfPixels(); std::vector regionMaskVector( nPixels ); for ( size_t i = 0; i < nPixels; ++i ) { regionMaskVector[i] = ( maskVolume->GetDataAt( i ) != 0 ); } TypedArray::SmartConstPtr dataArray = dataVolume->GetData(); // if bias correction is requested by caller, replace data with corrected data if ( biasFieldDegree ) { try { dataArray = LeastSquaresPolynomialIntensityBiasField( *dataVolume, regionMaskVector, biasFieldDegree ).GetCorrectedData(); } catch ( PolynomialHelper::DegreeUnsupported& ex ) { StdErr << "ERROR: polynomial degree not supported in LeastSquaresPolynomialIntensityBiasField() constructor.\n"; StdErr << ex.what() << "\n"; throw ExitException( 1 ); } catch ( const LeastSquaresPolynomialIntensityBiasField::EmptyMaskException& ) { // dataArray should still be the original data, but doesn't hurt to assign again, just in case. dataArray = dataVolume->GetData(); } } // compute summary statistics ValueSequence vs; for ( size_t i = 0; i < nPixels; ++i ) { if ( regionMaskVector[i] ) vs.Proceed( dataArray->ValueAt( i ) ); } mean = vs.GetAverage(); stdev = sqrt( vs.GetVariance() ); } cmtk-3.3.1/libs/Segmentation/cmtkDetectPhantomMagphanEMR051.h000066400000000000000000000254571276303427400237440ustar00rootroot00000000000000/* // // Copyright 2012-2014 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 5355 $ // // $LastChangedDate: 2014-05-09 16:55:27 -0700 (Fri, 09 May 2014) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkDetectPhantomMagphanEMR051_h_included_ #define __cmtkDetectPhantomMagphanEMR051_h_included_ #include #include #include #include #include namespace cmtk { /** \addtogroup Segmentation */ //@{ /** Class for detecting landmark locations of the Magphan EMR051 structural imaging phantom * (a.k.a The ADNI Phantom). */ class DetectPhantomMagphanEMR051 { public: /// This class. typedef DetectPhantomMagphanEMR051 Self; /// Smart pointer to this class. typedef SmartPointer SmartPtr; /// Smart pointer to const for this class. typedef SmartConstPointer SmartConstPtr; /// Class for parameters that control the phantom detection class Parameters { public: /// Default constructor. Parameters() : m_CorrectSphereBiasField( true ), m_TolerateTruncation( false ), m_StandardOrientation( true ), m_ForceFallbackCentroidCNR( false ), m_BipolarFilterMargin( 1 ), m_RefineMarginPixels( 1 ), m_SphereExcludeSafetyMargin( 10.0 ), m_ErodeSNR( 10.0 ), m_ErodeCNR( 5.0 ), m_Erode10mm( 1.0 ), m_RefineXformEachLandmark( false ), m_RefineOutliers( false ), m_ExcludeOutliers( false ), m_LandmarkFitResidualThreshold( 5.0 ) {} /// Flag for correction of (linear) bias field for each sphere. bool m_CorrectSphereBiasField; /** Flag for tolerant operation - if set, we are lenient when spheres are slightly truncated. * This should be considered a last resort, and both phantom scans and results should be carefully inspected. */ bool m_TolerateTruncation; /** Flag for standard orientation. * Setting this assumes that the phantom was scanned with the correct side up, rather than upside down. * This will make detection of defective phantoms more robust, but it will also prevent detection of * phantoms scanned upside down. */ bool m_StandardOrientation; /// Flag for forcing the fallback to use CNR centroid instead of SNR sphere center (e.g., when SNR sphere is broken off) bool m_ForceFallbackCentroidCNR; /// Margin (in pixels) for the bipolar sphere detection matched filter. int m_BipolarFilterMargin; /// Margin in pixels for center-of-mass-based refinement. int m_RefineMarginPixels; /// Safety margin (in mm) around detected spheres - no other sphere centers are permitted within this margin. Types::Coordinate m_SphereExcludeSafetyMargin; /// Erode SNR sphere by this many pixels for SNR computation Types::Coordinate m_ErodeSNR; /// Erode CNR spheres by this many pixels for SNR computation Types::Coordinate m_ErodeCNR; /// Erode 10mm spheres by this many pixels for brightness computation Types::Coordinate m_Erode10mm; /// Flag for optional refinement of transformation after each new landmark has been added. bool m_RefineXformEachLandmark; /// Flag for optional refinement of outlier landmarks bool m_RefineOutliers; /// Flag for optional exclusion of outlier landmarks from the final, fitted transformation bool m_ExcludeOutliers; /// Threshold for detecting outliers based on landmark fitting residuals. Types::Coordinate m_LandmarkFitResidualThreshold; private: /// Static default parameters. static Parameters Default; /// Let outside class have access. friend class DetectPhantomMagphanEMR051; }; /// Spatial coordinate vector. typedef UniformVolume::SpaceVectorType SpaceVectorType; /// Exception thrown if a landmark sphere cannot be localized in the search region. class NoSphereInSearchRegion : public Exception {}; /// Exception thrown if the field of view is insufficient. class OutsideFieldOfView : public Exception { public: // Constructor takes index and predicted location of offending landmark. OutsideFieldOfView( const size_t idx, const UniformVolume::CoordinateVectorType& v ) : m_Idx( idx ), m_Location( v ) {} // Offending landmark index. size_t m_Idx; // Offending predicted location. UniformVolume::CoordinateVectorType m_Location; }; /// Constructor: detect all landmark spheres. DetectPhantomMagphanEMR051( UniformVolume::SmartConstPtr& phantomImage, Self::Parameters& parameters = Self::Parameters::Default ); /// Get comprehensive description of phantom as detected in image. DetectedPhantomMagphanEMR051::SmartPtr GetDetectedPhantom(); /// Get rigid phantom-to-image transformation. AffineXform::SmartConstPtr GetPhantomToImageTransformationRigid() const { return this->m_PhantomToImageTransformationRigid; } /// Get affine phantom-to-image transformation. AffineXform::SmartConstPtr GetPhantomToImageTransformationAffine() const { return this->m_PhantomToImageTransformationAffine; } /// Get the image-space label map of detected spheres. UniformVolume::SmartPtr GetDetectedSpheresLabelMap(); /// Get expected landmark locations given rigid phantom-to-image transformation. LandmarkList GetExpectedLandmarks( const bool includeUnreliable = false /*!< If true, include unreliable landmarks, i.e., SNR and CNR spheres. */ ) const; /// Get actual, detected landmark locations. LandmarkList GetDetectedLandmarks( const bool includeOutliers = false /*!< If true, include landmarks detected as outliers based on linear affine transformation fitting residual */ ) const; private: /// Parameters that control the phantom detection Self::Parameters m_Parameters; /// Status flags that cover a variety of internal conditions. DetectedPhantomMagphanEMR051::StatusFlags m_StatusFlags; /// Image of the phantom. UniformVolume::SmartConstPtr m_PhantomImage; /** Evolving exclusion mask. * When a sphere is detected, its volume is marked as off-limits herein so other spheres are not incorrectly detected in the same place. */ UniformVolume::SmartPtr m_ExcludeMask; /** Temporary inclusion mask. * When we detect a sphere in a specific pre-determined area, this mask contains as non-zero the candidate pixels. */ UniformVolume::SmartPtr m_IncludeMask; /// Class for landmarks and validity flags class LandmarkType { public: /// Default constructor. LandmarkType() : m_Location( 0 ), m_Valid( false ) {} /// Constructor. LandmarkType( const Self::SpaceVectorType& location, const bool valid = true ) : m_Location( location ), m_Valid( valid ) {} /// Location of the landmark Self::SpaceVectorType m_Location; /// Is this landmark valid? bool m_Valid; }; /// The detected sphere centroid landmarks in image space. std::vector m_Landmarks; /// Fitted rigid transformation from phantom to image coordinates. AffineXform::SmartPtr m_PhantomToImageTransformationRigid; /// Fitted affine transformation from phantom to image coordinates. AffineXform::SmartPtr m_PhantomToImageTransformationAffine; /// Residuals of landmark locations after linear transformation fit. std::vector m_LandmarkFitResiduals; /** Find a sphere of given radius. *\return Location of the sphere center - this is the point where the filter response is maximal. */ Self::SpaceVectorType FindSphere( const TypedArray& filterResponse /*!< Response of the matched filter used for sphere finding. */ ); /** Find a sphere in a band of given radius. * If the given search region is already excluded from searching based on previously identified spheres, then the * NoSphereInSearchRegion exception is thrown. *\return Location of the sphere center. This is the location of maximum filter response in the search band, minus exclusion. */ Self::SpaceVectorType FindSphereAtDistance( const TypedArray& filterResponse /*!< Response of the matched filter used for sphere finding. */, const Self::SpaceVectorType& bandCenter /*!< Center of the band to search in. */, const Types::Coordinate bandRadius /*!< Radius of the band to search in. If this is zero, the search region is a sphere around bandCenter.*/, const Types::Coordinate bandWidth /*!< Width of the band to search in.*/ ); /// Refine sphere position based on intensity-weighted center of mass. Self::SpaceVectorType RefineSphereLocation( const Self::SpaceVectorType& estimate, const Types::Coordinate radius, const int label ); /** Compute landmark fitting residuals under given linear transformation *\return The maximum residual over all landmarks. This can be compared with a threshold to * determine whether refinement of outliers is necessary. */ Types::Coordinate ComputeLandmarkFitResiduals( const AffineXform& xform /*!< Linear transformation fitted to the landmarks.*/ ); /// Try to refine outlier (by current fitted linear transformation) landmarks. void RefineOutlierLandmarks( const TypedArray& filterResponse /*!< Existing filter response map. */ ); /// Exclude outlier landmarks and re-fit linear transformation. void ExcludeOutlierLandmarks(); /// Get the mean and standard deviation of intensities within a sphere of given location and radius. void GetSphereMeanStdDeviation( Types::DataItem& mean /*!< Reference to return mean intensity */, Types::DataItem& stdev /*!< Reference to return intensity standard deviation */, const Self::SpaceVectorType& center /*!< Center coordinate of the sphere. */, const Types::Coordinate radius /*!< Radius of the sphere */, const Types::Coordinate erodeBy /*!< Distance to erode the sphere by before computing mean and standard deviation. */, const int biasFieldDegree /*!< Polynomial degree of the estimated bias field before computing mean and standard deviation (0 = no bias field correction) */ ); }; //@} } // namespace cmtk #endif // #ifndef __cmtkDetectPhantomMagphanEMR051_h_included_ cmtk-3.3.1/libs/Segmentation/cmtkEntropyMinimizationIntensityCorrectionFunctional.cxx000066400000000000000000000112151276303427400314670ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2011, 2013 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4873 $ // // $LastChangedDate: 2013-09-24 12:53:12 -0700 (Tue, 24 Sep 2013) $ // // $LastChangedBy: torstenrohlfing $ // */ #include "cmtkEntropyMinimizationIntensityCorrectionFunctional.h" #include namespace cmtk { /** \addtogroup Segmentation */ //@{ template EntropyMinimizationIntensityCorrectionFunctionalBase::SmartPtr CreateEntropyMinimizationIntensityCorrectionFunctional ( const unsigned int polynomialDegreeAdd ) { typedef EntropyMinimizationIntensityCorrectionFunctionalBase::SmartPtr FunctionalPointer; FunctionalPointer functional; switch ( polynomialDegreeAdd ) { case 0 : functional = FunctionalPointer( new EntropyMinimizationIntensityCorrectionFunctional<0,NDegreeMul> ); break; case 1 : functional = FunctionalPointer( new EntropyMinimizationIntensityCorrectionFunctional<1,NDegreeMul> ); break; case 2 : functional = FunctionalPointer( new EntropyMinimizationIntensityCorrectionFunctional<2,NDegreeMul> ); break; case 3 : functional = FunctionalPointer( new EntropyMinimizationIntensityCorrectionFunctional<3,NDegreeMul> ); break; case 4 : functional = FunctionalPointer( new EntropyMinimizationIntensityCorrectionFunctional<4,NDegreeMul> ); break; default: StdErr.printf( "ERROR: combination of polynomial degrees %u (add) and %u (mul) not supported.\n", polynomialDegreeAdd, NDegreeMul ); exit( 1 ); } return functional; } EntropyMinimizationIntensityCorrectionFunctionalBase::SmartPtr CreateEntropyMinimizationIntensityCorrectionFunctional ( const unsigned int polynomialDegreeAdd, const unsigned int polynomialDegreeMul ) { typedef EntropyMinimizationIntensityCorrectionFunctionalBase::SmartPtr FunctionalPointer; FunctionalPointer functional; switch ( polynomialDegreeMul ) { case 0 : functional = CreateEntropyMinimizationIntensityCorrectionFunctional<0>( polynomialDegreeAdd ); break; case 1 : functional = CreateEntropyMinimizationIntensityCorrectionFunctional<1>( polynomialDegreeAdd ); break; case 2 : functional = CreateEntropyMinimizationIntensityCorrectionFunctional<2>( polynomialDegreeAdd ); break; case 3 : functional = CreateEntropyMinimizationIntensityCorrectionFunctional<3>( polynomialDegreeAdd ); break; case 4 : functional = CreateEntropyMinimizationIntensityCorrectionFunctional<4>( polynomialDegreeAdd ); break; default: StdErr.printf( "ERROR: polynomial degree %u (mul) not supported.\n", polynomialDegreeMul ); exit( 1 ); } return functional; } EntropyMinimizationIntensityCorrectionFunctionalBase::SmartPtr CreateEntropyMinimizationIntensityCorrectionFunctional ( const unsigned int polynomialDegreeAdd, const unsigned int polynomialDegreeMul, EntropyMinimizationIntensityCorrectionFunctionalBase::SmartPtr oldFunctional ) { EntropyMinimizationIntensityCorrectionFunctionalBase::SmartPtr newFunctional = CreateEntropyMinimizationIntensityCorrectionFunctional( polynomialDegreeAdd, polynomialDegreeMul ); if ( oldFunctional ) { CoordinateVector vOld; oldFunctional->GetParamVector( vOld ); CoordinateVector vNew( newFunctional->ParamVectorDim() ); vNew.SetAll( 0.0 ); for ( size_t degreeAdd = 0; degreeAdd < oldFunctional->GetNumberOfMonomialsAdd(); ++degreeAdd ) { vNew[degreeAdd] = vOld[degreeAdd]; } for ( size_t degreeMul = 0; degreeMul < oldFunctional->GetNumberOfMonomialsMul(); ++degreeMul ) { vNew[newFunctional->GetNumberOfMonomialsAdd() + degreeMul] = vOld[oldFunctional->GetNumberOfMonomialsAdd() + degreeMul]; } } return newFunctional; } } // namespace cmtk cmtk-3.3.1/libs/Segmentation/cmtkEntropyMinimizationIntensityCorrectionFunctional.h000066400000000000000000000234401276303427400311170ustar00rootroot00000000000000/* // // Copyright 1997-2012 Torsten Rohlfing // // Copyright 2004-2012, 2014 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 5137 $ // // $LastChangedDate: 2014-01-10 11:38:43 -0800 (Fri, 10 Jan 2014) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkEntropyMinimizationIntensityCorrectionFunctional_h_included_ #define __cmtkEntropyMinimizationIntensityCorrectionFunctional_h_included_ #include #include #include #include #include #include #include namespace cmtk { /** \addtogroup Segmentation */ //@{ /** Functional to correct MR intensity bias by miniming image entropy. */ template class EntropyMinimizationIntensityCorrectionFunctional : /// Inherit from base class. public EntropyMinimizationIntensityCorrectionFunctionalBase { public: /// This class type. typedef EntropyMinimizationIntensityCorrectionFunctional Self; /// Pointer to this class. typedef SmartPointer SmartPtr; /// Superclass type. typedef EntropyMinimizationIntensityCorrectionFunctionalBase Superclass; /// Additive correction polynomial. typedef Polynomial PolynomialTypeAdd; /// Multiplicative correction polynomial. typedef Polynomial PolynomialTypeMul; /// Constructor. EntropyMinimizationIntensityCorrectionFunctional() { this->m_ParameterVector.SetDim( this->ParamVectorDim() ); this->m_ParameterVector.SetAll( 0.0 ); ThreadPool& threadPool = ThreadPool::GetGlobalThreadPool(); this->m_NumberOfThreads = threadPool.GetNumberOfThreads(); this->m_MonomialsPerThread = std::max( (int)PolynomialTypeAdd::NumberOfMonomials, (int)PolynomialTypeMul::NumberOfMonomials ); this->m_MonomialsVec = Memory::ArrayC::Allocate( this->m_NumberOfThreads * this->m_MonomialsPerThread ); } /// Virtual destructor. virtual ~EntropyMinimizationIntensityCorrectionFunctional() { Memory::ArrayC::Delete( this->m_MonomialsVec ); } /// Total number of parameters is number of additive plus number of multiplicative parameters, each minus because we ignore the constant (zero-order) monomial. static const size_t m_NumberOfParameters = PolynomialTypeAdd::NumberOfMonomials - 1 + PolynomialTypeMul::NumberOfMonomials - 1; /// Return parameter vector length. virtual size_t ParamVectorDim() const { return Self::m_NumberOfParameters; } /// Return parameter stepping. #pragma GCC diagnostic ignored "-Wtype-limits" virtual Types::Coordinate GetParamStep( const size_t idx, const Types::Coordinate mmStep = 1 ) const { if ( idx < PolynomialTypeAdd::NumberOfMonomials-1 ) return (this->m_InputImageRange / 256) * this->m_StepSizeAdd[1+idx] * mmStep; else return (this->m_InputImageRange / 256) * this->m_StepSizeMul[1+idx-PolynomialTypeAdd::NumberOfMonomials+1] * mmStep; } /// Get number of additive monomials. virtual size_t GetNumberOfMonomialsAdd() const { return PolynomialTypeAdd::NumberOfMonomials - 1; } /// Get number of multiplicative monomials. virtual size_t GetNumberOfMonomialsMul() const { return PolynomialTypeMul::NumberOfMonomials - 1; } /// Copy parameters to the two correction polynomials. virtual void SetParamVector( CoordinateVector& v ) { this->m_ParameterVector = v; size_t ofs = 0; for ( int i = 1; i < PolynomialTypeAdd::NumberOfMonomials; ++i, ++ofs ) this->m_CoefficientsAdd[i] = v[ofs] * this->m_MulCorrectionAdd[i]; for ( int i = 1; i < PolynomialTypeMul::NumberOfMonomials; ++i, ++ofs ) this->m_CoefficientsMul[i] = v[ofs] * this->m_MulCorrectionMul[i]; } /// Extract parameter vector from the two correction polynomials. virtual void GetParamVector( CoordinateVector& v ) { v = this->m_ParameterVector; } /** Fast implementation of gradient evaluation. * This function only updates either the additive of the multiplicative bias * field, depending on which gradient component is being determined. */ virtual typename Self::ReturnType EvaluateWithGradient( CoordinateVector& v, CoordinateVector& g, const Types::Coordinate step ); protected: /// Keep a copy of the current parameter vector. CoordinateVector m_ParameterVector; /// Additive correction term coefficients. Types::Coordinate m_StepSizeAdd[1+PolynomialTypeAdd::NumberOfMonomials]; /// Multiplicative correction term coefficients. Types::Coordinate m_StepSizeMul[1+PolynomialTypeMul::NumberOfMonomials]; /// Additive correction term coefficients. Types::Coordinate m_CoefficientsAdd[1+PolynomialTypeAdd::NumberOfMonomials]; /// Multiplicative correction term coefficients. Types::Coordinate m_CoefficientsMul[1+PolynomialTypeMul::NumberOfMonomials]; /// Additive correction constants for additive polynomials. Types::Coordinate m_AddCorrectionAdd[1+PolynomialTypeAdd::NumberOfMonomials]; /// Multiplicative correction constants for additive polynomials. Types::Coordinate m_MulCorrectionAdd[1+PolynomialTypeAdd::NumberOfMonomials]; /// Additive correction constants for multiplicative polynomials. Types::Coordinate m_AddCorrectionMul[1+PolynomialTypeMul::NumberOfMonomials]; /// Multiplicative correction constants for additive polynomials. Types::Coordinate m_MulCorrectionMul[1+PolynomialTypeMul::NumberOfMonomials]; private: /** Number of parallel threads. * Because the constructor allocated temporary storage for monomial * computation, we need to know in advance how many threads there will * be, and make sure we never exceed this number later when calling the * thread functions. */ size_t m_NumberOfThreads; /// Maximum number of monomials per thread (maximum of additive and multiplicative polynomial number of monomials). size_t m_MonomialsPerThread; /// Temporary storage for evaluating monomials. Types::Coordinate* m_MonomialsVec; /// Update polynomial correctionfactors from input image. virtual void UpdateCorrectionFactors(); /// Jointly update both bias images. virtual void UpdateBiasFields( const bool foregroundOnly = true ); /// Thread function: jointly update both bias images. static void UpdateBiasFieldsThreadFunc( void* args, const size_t taskIdx, const size_t taskCnt, const size_t threadIdx, const size_t ); /// Thread function: jointly update both bias images. static void UpdateBiasFieldsAllThreadFunc( void* args, const size_t taskIdx, const size_t taskCnt, const size_t threadIdx, const size_t ); /// Update additive bias image. virtual void UpdateBiasFieldAdd( const bool foregroundOnly = true ); /// Thread function: update foreground additive bias images. static void UpdateBiasFieldAddThreadFunc( void* args, const size_t taskIdx, const size_t taskCnt, const size_t threadIdx, const size_t ); /// Thread function: update complete additive bias images. static void UpdateBiasFieldAddAllThreadFunc( void* args, const size_t taskIdx, const size_t taskCnt, const size_t threadIdx, const size_t ); /// Update multiplicative bias image. virtual void UpdateBiasFieldMul( const bool foregroundOnly = true ); /// Thread function: update foreground multiplicative bias images. static void UpdateBiasFieldMulThreadFunc( void* args, const size_t taskIdx, const size_t taskCnt, const size_t threadIdx, const size_t ); /// Thread function: update complete multiplicative bias images. static void UpdateBiasFieldMulAllThreadFunc( void* args, const size_t taskIdx, const size_t taskCnt, const size_t threadIdx, const size_t ); }; /// Create functional templated over polynomial degrees. template EntropyMinimizationIntensityCorrectionFunctionalBase::SmartPtr CreateEntropyMinimizationIntensityCorrectionFunctional ( const unsigned int polynomialDegreeAdd ); /// Create functional templated over polynomial degrees. EntropyMinimizationIntensityCorrectionFunctionalBase::SmartPtr CreateEntropyMinimizationIntensityCorrectionFunctional ( const unsigned int polynomialDegreeAdd, const unsigned int polynomialDegreeMul ); /** Create functional templated over polynomial degrees with initialization from old functional. * This function creates a new functional and copies the polynomial coefficients from an existing * functional of equal or lower polynomial degrees into the correct locations of the new functional's * parameter vector. This is for incremental computation. */ EntropyMinimizationIntensityCorrectionFunctionalBase::SmartPtr CreateEntropyMinimizationIntensityCorrectionFunctional ( const unsigned int polynomialDegreeAdd, const unsigned int polynomialDegreeMul, EntropyMinimizationIntensityCorrectionFunctionalBase::SmartPtr oldFunctional ); //@} } // namespace cmtk #include "cmtkEntropyMinimizationIntensityCorrectionFunctional.txx" #endif // #ifndef __cmtkEntropyMinimizationIntensityCorrectionFunctional_h_included_ cmtk-3.3.1/libs/Segmentation/cmtkEntropyMinimizationIntensityCorrectionFunctional.txx000066400000000000000000000533731276303427400315230ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2011, 2013-2014 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 5137 $ // // $LastChangedDate: 2014-01-10 11:38:43 -0800 (Fri, 10 Jan 2014) $ // // $LastChangedBy: torstenrohlfing $ // */ #include #include #pragma GCC diagnostic ignored "-Wtype-limits" template void cmtk::EntropyMinimizationIntensityCorrectionFunctional ::UpdateCorrectionFactors() { const DataGrid::IndexType& dims = this->m_InputImage->GetDims(); // All equation numbers refer to paper by Likar et al., IEEE-TMI 20(12):1398--1410, 2001. for ( unsigned int i = 1; i < PolynomialTypeAdd::NumberOfMonomials; ++i ) { this->m_AddCorrectionAdd[i] = 0; this->m_MulCorrectionAdd[i] = 0; } for ( unsigned int i = 1; i < PolynomialTypeMul::NumberOfMonomials; ++i ) { this->m_AddCorrectionMul[i] = 0; this->m_MulCorrectionMul[i] = 0; } double totalImageEnergy = 0.0; size_t foregroundNumberOfPixels = 0; // first, compute additive correction factors according to // Eqs. (A8) and (A12). size_t ofs = 0; for ( int z = 0; z < dims[2]; ++z ) { const Types::Coordinate Z = 2.0*(z-dims[2]/2) / dims[2]; for ( int y = 0; y < dims[1]; ++y ) { const Types::Coordinate Y = 2.0*(y-dims[1]/2) / dims[1]; for ( int x = 0; x < dims[0]; ++x, ++ofs ) { const Types::Coordinate X = 2.0*(x-dims[0]/2) / dims[0]; if ( this->m_ForegroundMask[ofs] ) { ++foregroundNumberOfPixels; Types::DataItem value; if ( this->m_InputImage->GetDataAt( value, x, y, z ) ) totalImageEnergy += value; else value = 0.0; // Eq. (A8) PolynomialTypeAdd::EvaluateAllMonomials( this->m_MonomialsVec, X, Y, Z ); for ( unsigned int i = 1; i < PolynomialTypeAdd::NumberOfMonomials; ++i ) { this->m_AddCorrectionAdd[i] += this->m_MonomialsVec[i]; } // Eq. (A12) PolynomialTypeMul::EvaluateAllMonomials( this->m_MonomialsVec, X, Y, Z ); for ( unsigned int i = 1; i < PolynomialTypeMul::NumberOfMonomials; ++i ) { this->m_AddCorrectionMul[i] += value * this->m_MonomialsVec[i]; } } } } } // Normalization according to (A8) if ( foregroundNumberOfPixels ) { for ( unsigned int i = 1; i < PolynomialTypeAdd::NumberOfMonomials; ++i ) { this->m_AddCorrectionAdd[i] /= foregroundNumberOfPixels; } } // Normalization according to (A12) if ( totalImageEnergy != 0 ) { for ( unsigned int i = 1; i < PolynomialTypeMul::NumberOfMonomials; ++i ) { this->m_AddCorrectionMul[i] /= totalImageEnergy; } } // Now, compute multiplicative correction factors according to // Eqs. (A14) and (A16). ofs = 0; for ( int z = 0; z < dims[2]; ++z ) { const Types::Coordinate Z = 2.0*(z-dims[2]/2) / dims[2]; for ( int y = 0; y < dims[1]; ++y ) { const Types::Coordinate Y = 2.0*(y-dims[1]/2) / dims[1]; for ( int x = 0; x < dims[0]; ++x, ++ofs ) { const Types::Coordinate X = 2.0*(x-dims[0]/2) / dims[0]; if ( this->m_ForegroundMask[ofs] ) { Types::DataItem value; if ( !this->m_InputImage->GetDataAt( value, x, y, z ) ) value = 0.0; // Eq. (A8) PolynomialTypeAdd::EvaluateAllMonomials( this->m_MonomialsVec, X, Y, Z ); for ( unsigned int i = 1; i < PolynomialTypeAdd::NumberOfMonomials; ++i ) { this->m_MulCorrectionAdd[i] += fabs( this->m_MonomialsVec[i] - this->m_AddCorrectionAdd[i] ); } // Eq. (A12) PolynomialTypeMul::EvaluateAllMonomials( this->m_MonomialsVec, X, Y, Z ); for ( unsigned int i = 1; i < PolynomialTypeMul::NumberOfMonomials; ++i ) { this->m_MulCorrectionMul[i] += value * fabs( this->m_MonomialsVec[i] - this->m_AddCorrectionMul[i] ); } } } } } // Normalization according to (A14) for ( unsigned int i = 1; i < PolynomialTypeAdd::NumberOfMonomials; ++i ) { // invert for speedup of application this->m_MulCorrectionAdd[i] = foregroundNumberOfPixels / this->m_MulCorrectionAdd[i]; this->m_StepSizeAdd[i] = 0.0; } // Normalization according to (A16) for ( unsigned int i = 1; i < PolynomialTypeMul::NumberOfMonomials; ++i ) { // invert for speedup of application this->m_MulCorrectionMul[i] = foregroundNumberOfPixels / this->m_MulCorrectionMul[i]; this->m_StepSizeMul[i] = 0.0; } // Finally, compute step scale factors according to Eq. (11). ofs = 0; for ( int z = 0; z < dims[2]; ++z ) { const Types::Coordinate Z = 2.0*(z-dims[2]/2) / dims[2]; for ( int y = 0; y < dims[1]; ++y ) { const Types::Coordinate Y = 2.0*(y-dims[1]/2) / dims[1]; for ( int x = 0; x < dims[0]; ++x, ++ofs ) { const Types::Coordinate X = 2.0*(x-dims[0]/2) / dims[0]; if ( this->m_ForegroundMask[ofs] ) { Types::DataItem value; if ( !this->m_InputImage->GetDataAt( value, x, y, z ) ) value = 0.0; // Eq. (A8) PolynomialTypeAdd::EvaluateAllMonomials( this->m_MonomialsVec, X, Y, Z ); for ( unsigned int i = 1; i < PolynomialTypeAdd::NumberOfMonomials; ++i ) { this->m_StepSizeAdd[i] += fabs( this->m_MulCorrectionAdd[i] * ( this->m_MonomialsVec[i] - this->m_AddCorrectionAdd[i] ) ); } // Eq. (A12) PolynomialTypeMul::EvaluateAllMonomials( this->m_MonomialsVec, X, Y, Z ); for ( unsigned int i = 1; i < PolynomialTypeMul::NumberOfMonomials; ++i ) { this->m_StepSizeMul[i] += fabs( value * this->m_MulCorrectionMul[i] * ( this->m_MonomialsVec[i] - this->m_AddCorrectionMul[i] ) ); } } } } } // Normalization according to (11) for ( unsigned int i = 1; i < PolynomialTypeAdd::NumberOfMonomials; ++i ) { // invert for speedup of application this->m_StepSizeAdd[i] = foregroundNumberOfPixels / this->m_StepSizeAdd[i]; } // Normalization according to (11) for ( unsigned int i = 1; i < PolynomialTypeMul::NumberOfMonomials; ++i ) { // invert for speedup of application this->m_StepSizeMul[i] = foregroundNumberOfPixels / this->m_StepSizeMul[i]; } } #pragma GCC diagnostic ignored "-Wtype-limits" template typename cmtk::EntropyMinimizationIntensityCorrectionFunctional::ReturnType cmtk::EntropyMinimizationIntensityCorrectionFunctional ::EvaluateWithGradient ( CoordinateVector& v, CoordinateVector& g, const Types::Coordinate step ) { const typename Self::ReturnType baseValue = this->EvaluateAt( v ); for ( size_t dim = 0; dim < this->VariableParamVectorDim(); ++dim ) { const Types::Coordinate stepScale = this->GetParamStep( dim, step ); if ( stepScale <= 0 ) { g[dim] = 0; } else { const Types::Coordinate v0 = v[dim]; v[dim] += stepScale; this->SetParamVector( v ); if ( dim < PolynomialTypeAdd::NumberOfMonomials-1 ) this->UpdateBiasFieldAdd(); else this->UpdateBiasFieldMul(); this->UpdateOutputImage(); const typename Self::ReturnType upper = this->Evaluate(); v[dim] = v0 - stepScale; this->SetParamVector( v ); if ( dim < PolynomialTypeAdd::NumberOfMonomials-1 ) this->UpdateBiasFieldAdd(); else this->UpdateBiasFieldMul(); this->UpdateOutputImage(); const typename Self::ReturnType lower = this->Evaluate(); v[dim] = v0; if ( (upper > baseValue) || (lower > baseValue) ) { g[dim] = upper-lower; } else { g[dim] = 0; } } } return baseValue; } template void cmtk::EntropyMinimizationIntensityCorrectionFunctional ::UpdateBiasFields( bool foregroundOnly ) { ThreadPool& threadPool = ThreadPool::GetGlobalThreadPool(); const size_t numberOfTasks = 4 * threadPool.GetNumberOfThreads() - 3; std::vector< ThreadParameters > taskParameters( numberOfTasks ); for ( size_t task = 0; task < numberOfTasks; ++task ) { taskParameters[task].thisObject = this; } if ( foregroundOnly ) threadPool.Run( UpdateBiasFieldsThreadFunc, taskParameters ); else threadPool.Run( UpdateBiasFieldsAllThreadFunc, taskParameters ); } template void cmtk::EntropyMinimizationIntensityCorrectionFunctional ::UpdateBiasFieldsThreadFunc( void *args, const size_t taskIdx, const size_t taskCnt, const size_t threadIdx, const size_t ) { ThreadParameters* threadParameters = static_cast*>( args ); Self* This = threadParameters->thisObject; const Self* ThisConst = threadParameters->thisObject; const DataGrid::IndexType& dims = ThisConst->m_InputImage->GetDims(); const UniformVolume* inputImage = ThisConst->m_InputImage; float* biasFieldPtrAdd = This->m_BiasFieldAdd->GetDataPtrTemplate(); float* biasFieldPtrMul = This->m_BiasFieldMul->GetDataPtrTemplate(); Types::Coordinate* monomialsVec = This->m_MonomialsVec + (threadIdx * ThisConst->m_MonomialsPerThread); const int zFrom = taskIdx * (dims[2] / taskCnt); const int zTo = std::max( (taskIdx+1) * (dims[2] / taskCnt), dims[2] ); Types::DataItem value; size_t ofs = zFrom * dims[0] * dims[1]; for ( int z = zFrom; z < zTo; ++z ) { const Types::Coordinate Z = 2.0*(z-dims[2]/2) / dims[2]; for ( int y = 0; y < dims[1]; ++y ) { const Types::Coordinate Y = 2.0*(y-dims[1]/2) / dims[1]; for ( int x = 0; x < dims[0]; ++x, ++ofs ) { const Types::Coordinate X = 2.0*(x-dims[0]/2) / dims[0]; Types::Coordinate normMul = 1.0; Types::Coordinate normAdd = 0.0; if ( This->m_ForegroundMask[ofs] ) { if ( inputImage->GetDataAt( value, ofs ) ) { // Normalization according to Eq (13) PolynomialTypeMul::EvaluateAllMonomials( monomialsVec, X, Y, Z ); for ( unsigned int i = 1; i < PolynomialTypeMul::NumberOfMonomials; ++i ) { normMul += ThisConst->m_CoefficientsMul[i] * ( monomialsVec[i] - ThisConst->m_AddCorrectionMul[i] ); } PolynomialTypeAdd::EvaluateAllMonomials( monomialsVec, X, Y, Z ); for ( unsigned int i = 1; i < PolynomialTypeAdd::NumberOfMonomials; ++i ) { normAdd += ThisConst->m_CoefficientsAdd[i] * ( monomialsVec[i] - ThisConst->m_AddCorrectionAdd[i] ); } } } biasFieldPtrAdd[ofs] = static_cast( normAdd ); biasFieldPtrMul[ofs] = static_cast( normMul ); } } } } template void cmtk::EntropyMinimizationIntensityCorrectionFunctional ::UpdateBiasFieldsAllThreadFunc( void *args, const size_t taskIdx, const size_t taskCnt, const size_t threadIdx, const size_t ) { ThreadParameters* threadParameters = static_cast*>( args ); Self* This = threadParameters->thisObject; const Self* ThisConst = threadParameters->thisObject; const DataGrid::IndexType& dims = ThisConst->m_InputImage->GetDims(); const UniformVolume* inputImage = ThisConst->m_InputImage; float* biasFieldPtrAdd = This->m_BiasFieldAdd->GetDataPtrTemplate(); float* biasFieldPtrMul = This->m_BiasFieldMul->GetDataPtrTemplate(); Types::Coordinate* monomialsVec = This->m_MonomialsVec + (threadIdx * ThisConst->m_MonomialsPerThread); const int zFrom = taskIdx * (dims[2] / taskCnt); const int zTo = std::max( (taskIdx+1) * (dims[2] / taskCnt), dims[2] ); Types::DataItem value; size_t ofs = zFrom * dims[0] * dims[1]; for ( int z = zFrom; z < zTo; ++z ) { const Types::Coordinate Z = 2.0*(z-dims[2]/2) / dims[2]; for ( int y = 0; y < dims[1]; ++y ) { const Types::Coordinate Y = 2.0*(y-dims[1]/2) / dims[1]; for ( int x = 0; x < dims[0]; ++x, ++ofs ) { const Types::Coordinate X = 2.0*(x-dims[0]/2) / dims[0]; Types::Coordinate normMul = 1.0; Types::Coordinate normAdd = 0.0; if ( inputImage->GetDataAt( value, ofs ) ) { // Normalization according to Eq (13) PolynomialTypeMul::EvaluateAllMonomials( monomialsVec, X, Y, Z ); for ( unsigned int i = 1; i < PolynomialTypeMul::NumberOfMonomials; ++i ) { normMul += ThisConst->m_CoefficientsMul[i] * ( monomialsVec[i] - ThisConst->m_AddCorrectionMul[i] ); } PolynomialTypeAdd::EvaluateAllMonomials( monomialsVec, X, Y, Z ); for ( unsigned int i = 1; i < PolynomialTypeAdd::NumberOfMonomials; ++i ) { normAdd += ThisConst->m_CoefficientsAdd[i] * ( monomialsVec[i] - ThisConst->m_AddCorrectionAdd[i] ); } } biasFieldPtrAdd[ofs] = static_cast( normAdd ); biasFieldPtrMul[ofs] = static_cast( normMul ); } } } } template void cmtk::EntropyMinimizationIntensityCorrectionFunctional ::UpdateBiasFieldAdd( const bool foregroundOnly ) { ThreadPool& threadPool = ThreadPool::GetGlobalThreadPool(); const size_t numberOfTasks = 4 * threadPool.GetNumberOfThreads() - 3; std::vector< ThreadParameters > taskParameters( numberOfTasks ); for ( size_t task = 0; task < numberOfTasks; ++task ) { taskParameters[task].thisObject = this; } if ( foregroundOnly ) threadPool.Run( UpdateBiasFieldAddThreadFunc, taskParameters ); else threadPool.Run( UpdateBiasFieldAddAllThreadFunc, taskParameters ); } template void cmtk::EntropyMinimizationIntensityCorrectionFunctional ::UpdateBiasFieldAddThreadFunc( void *args, const size_t taskIdx, const size_t taskCnt, const size_t threadIdx, const size_t ) { ThreadParameters* threadParameters = static_cast*>( args ); Self* This = threadParameters->thisObject; const Self* ThisConst = threadParameters->thisObject; const DataGrid::IndexType& dims = ThisConst->m_InputImage->GetDims(); const UniformVolume* inputImage = ThisConst->m_InputImage; float* biasFieldPtrAdd = This->m_BiasFieldAdd->GetDataPtrTemplate(); Types::Coordinate* monomialsVec = This->m_MonomialsVec + (threadIdx * ThisConst->m_MonomialsPerThread); const int zFrom = taskIdx * (dims[2] / taskCnt); const int zTo = std::max( (taskIdx+1) * (dims[2] / taskCnt), dims[2] ); size_t ofs = zFrom * dims[0] * dims[1]; for ( int z = zFrom; z < zTo; ++z ) { const Types::Coordinate Z = 2.0*(z-dims[2]/2) / dims[2]; for ( int y = 0; y < dims[1]; ++y ) { const Types::Coordinate Y = 2.0*(y-dims[1]/2) / dims[1]; for ( int x = 0; x < dims[0]; ++x, ++ofs ) { const Types::Coordinate X = 2.0*(x-dims[0]/2) / dims[0]; Types::Coordinate normAdd = 0.0; if ( This->m_ForegroundMask[ofs] ) { Types::DataItem value; if ( inputImage->GetDataAt( value, ofs ) ) { // Normalization according to Eq (13) PolynomialTypeAdd::EvaluateAllMonomials( monomialsVec, X, Y, Z ); for ( unsigned int i = 1; i < PolynomialTypeAdd::NumberOfMonomials; ++i ) { normAdd += ThisConst->m_CoefficientsAdd[i] * ( monomialsVec[i] - ThisConst->m_AddCorrectionAdd[i] ); } } } biasFieldPtrAdd[ofs] = static_cast( normAdd ); } } } } template void cmtk::EntropyMinimizationIntensityCorrectionFunctional ::UpdateBiasFieldAddAllThreadFunc( void *args, const size_t taskIdx, const size_t taskCnt, const size_t threadIdx, const size_t ) { ThreadParameters* threadParameters = static_cast*>( args ); Self* This = threadParameters->thisObject; const Self* ThisConst = threadParameters->thisObject; const DataGrid::IndexType& dims = ThisConst->m_InputImage->GetDims(); const UniformVolume* inputImage = ThisConst->m_InputImage; float* biasFieldPtrAdd = This->m_BiasFieldAdd->GetDataPtrTemplate(); Types::Coordinate* monomialsVec = This->m_MonomialsVec + (threadIdx * ThisConst->m_MonomialsPerThread); const int zFrom = taskIdx * (dims[2] / taskCnt); const int zTo = std::max( (taskIdx+1) * (dims[2] / taskCnt), dims[2] ); size_t ofs = zFrom * dims[0] * dims[1]; for ( int z = zFrom; z < zTo; ++z ) { const Types::Coordinate Z = 2.0*(z-dims[2]/2) / dims[2]; for ( int y = 0; y < dims[1]; ++y ) { const Types::Coordinate Y = 2.0*(y-dims[1]/2) / dims[1]; for ( int x = 0; x < dims[0]; ++x, ++ofs ) { const Types::Coordinate X = 2.0*(x-dims[0]/2) / dims[0]; Types::Coordinate normAdd = 0.0; Types::DataItem value; if ( inputImage->GetDataAt( value, ofs ) ) { // Normalization according to Eq (13) PolynomialTypeAdd::EvaluateAllMonomials( monomialsVec, X, Y, Z ); for ( unsigned int i = 1; i < PolynomialTypeAdd::NumberOfMonomials; ++i ) { normAdd += ThisConst->m_CoefficientsAdd[i] * ( monomialsVec[i] - ThisConst->m_AddCorrectionAdd[i] ); } } biasFieldPtrAdd[ofs] = static_cast( normAdd ); } } } } template void cmtk::EntropyMinimizationIntensityCorrectionFunctional ::UpdateBiasFieldMul( const bool foregroundOnly ) { ThreadPool& threadPool = ThreadPool::GetGlobalThreadPool(); const size_t numberOfTasks = 4 * threadPool.GetNumberOfThreads() - 3; std::vector< ThreadParameters > taskParameters( numberOfTasks ); for ( size_t task = 0; task < numberOfTasks; ++task ) { taskParameters[task].thisObject = this; } if ( foregroundOnly ) threadPool.Run( UpdateBiasFieldMulThreadFunc, taskParameters ); else threadPool.Run( UpdateBiasFieldMulAllThreadFunc, taskParameters ); } template void cmtk::EntropyMinimizationIntensityCorrectionFunctional ::UpdateBiasFieldMulThreadFunc( void *args, const size_t taskIdx, const size_t taskCnt, const size_t threadIdx, const size_t ) { ThreadParameters* threadParameters = static_cast*>( args ); Self* This = threadParameters->thisObject; const Self* ThisConst = threadParameters->thisObject; const DataGrid::IndexType& dims = ThisConst->m_InputImage->GetDims(); const UniformVolume* inputImage = ThisConst->m_InputImage; float* biasFieldPtrMul = This->m_BiasFieldMul->GetDataPtrTemplate(); Types::Coordinate* monomialsVec = This->m_MonomialsVec + (threadIdx * ThisConst->m_MonomialsPerThread); const int zFrom = taskIdx * (dims[2] / taskCnt); const int zTo = std::max( (taskIdx+1) * (dims[2] / taskCnt), dims[2] ); size_t ofs = zFrom * dims[0] * dims[1]; for ( int z = zFrom; z < zTo; ++z ) { const Types::Coordinate Z = 2.0*(z-dims[2]/2) / dims[2]; for ( int y = 0; y < dims[1]; ++y ) { const Types::Coordinate Y = 2.0*(y-dims[1]/2) / dims[1]; for ( int x = 0; x < dims[0]; ++x, ++ofs ) { const Types::Coordinate X = 2.0*(x-dims[0]/2) / dims[0]; Types::Coordinate normMul = 1.0; if ( This->m_ForegroundMask[ofs] ) { Types::DataItem value; if ( inputImage->GetDataAt( value, ofs ) ) { // Normalization according to Eq (13) PolynomialTypeMul::EvaluateAllMonomials( monomialsVec, X, Y, Z ); for ( unsigned int i = 1; i < PolynomialTypeMul::NumberOfMonomials; ++i ) { normMul += ThisConst->m_CoefficientsMul[i] * ( monomialsVec[i] - ThisConst->m_AddCorrectionMul[i] ); } } } biasFieldPtrMul[ofs] = static_cast( normMul ); } } } } template void cmtk::EntropyMinimizationIntensityCorrectionFunctional ::UpdateBiasFieldMulAllThreadFunc( void *args, const size_t taskIdx, const size_t taskCnt, const size_t threadIdx, const size_t ) { ThreadParameters* threadParameters = static_cast*>( args ); Self* This = threadParameters->thisObject; const Self* ThisConst = threadParameters->thisObject; const DataGrid::IndexType& dims = ThisConst->m_InputImage->GetDims(); const UniformVolume* inputImage = ThisConst->m_InputImage; float* biasFieldPtrMul = This->m_BiasFieldMul->GetDataPtrTemplate(); Types::Coordinate* monomialsVec = This->m_MonomialsVec + (threadIdx * ThisConst->m_MonomialsPerThread); const int zFrom = taskIdx * (dims[2] / taskCnt); const int zTo = std::max( (taskIdx+1) * (dims[2] / taskCnt), dims[2] ); size_t ofs = zFrom * dims[0] * dims[1]; for ( int z = zFrom; z < zTo; ++z ) { const Types::Coordinate Z = 2.0*(z-dims[2]/2) / dims[2]; for ( int y = 0; y < dims[1]; ++y ) { const Types::Coordinate Y = 2.0*(y-dims[1]/2) / dims[1]; for ( int x = 0; x < dims[0]; ++x, ++ofs ) { const Types::Coordinate X = 2.0*(x-dims[0]/2) / dims[0]; Types::Coordinate normMul = 1.0; Types::DataItem value; if ( inputImage->GetDataAt( value, ofs ) ) { // Normalization according to Eq (13) PolynomialTypeMul::EvaluateAllMonomials( monomialsVec, X, Y, Z ); for ( unsigned int i = 1; i < PolynomialTypeMul::NumberOfMonomials; ++i ) { normMul += ThisConst->m_CoefficientsMul[i] * ( monomialsVec[i] - ThisConst->m_AddCorrectionMul[i] ); } } biasFieldPtrMul[ofs] = static_cast( normMul ); } } } } cmtk-3.3.1/libs/Segmentation/cmtkEntropyMinimizationIntensityCorrectionFunctionalBase.cxx000066400000000000000000000145171276303427400322720ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2010 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 2438 $ // // $LastChangedDate: 2010-10-11 15:36:16 -0700 (Mon, 11 Oct 2010) $ // // $LastChangedBy: torstenrohlfing $ // */ #include #include #include #include #ifdef CMTK_BUILD_DEMO # include #endif // #ifdef CMTK_BUILD_DEMO namespace cmtk { /** \addtogroup Segmentation */ //@{ void EntropyMinimizationIntensityCorrectionFunctionalBase ::SetInputImage( UniformVolume::SmartConstPtr& inputImage ) { this->m_InputImage = inputImage; this->m_NumberOfPixels = this->m_InputImage->GetNumberOfPixels(); const Types::DataItemRange range = this->m_InputImage->GetData()->GetRange(); this->m_InputImageRange = range.Width(); if ( this->m_UseLogIntensities ) { this->m_EntropyHistogram = HistogramType::SmartPtr( new LogHistogramType( this->m_NumberOfHistogramBins ) ); } else { this->m_EntropyHistogram = HistogramType::SmartPtr( new HistogramType( this->m_NumberOfHistogramBins ) ); } // extend value range to accomodate corrected intensities without overflows this->m_EntropyHistogram->SetRange( Types::DataItemRange( range.m_LowerBound - this->m_InputImageRange, range.m_UpperBound + this->m_InputImageRange ) ); if ( this->m_ForegroundMask.size() ) this->UpdateCorrectionFactors(); this->m_BiasFieldAdd = FloatArray::Create( this->m_NumberOfPixels ); this->m_BiasFieldAdd->Fill( 0.0 ); this->m_BiasFieldMul = FloatArray::Create( this->m_NumberOfPixels ); this->m_BiasFieldAdd->Fill( 1.0 ); this->m_OutputImage = UniformVolume::SmartPtr( this->m_InputImage->CloneGrid() ); this->m_OutputImage->CreateDataArray( TYPE_FLOAT ); } void EntropyMinimizationIntensityCorrectionFunctionalBase ::SetForegroundMask( const UniformVolume& foregroundMask ) { const size_t maskPixels = foregroundMask.GetNumberOfPixels(); if ( maskPixels != this->m_NumberOfPixels ) { throw Exception( "Number of mask pixels does not match number of input image pixels." ); } this->m_ForegroundMask.resize( maskPixels ); if ( (this->m_SamplingDensity > 0) && (this->m_SamplingDensity < 1) ) { for ( size_t i = 0; i < maskPixels; ++i ) { this->m_ForegroundMask[i] = (foregroundMask.GetDataAt( i ) > 0) && (MathUtil::UniformRandom() <= this->m_SamplingDensity); } } else { for ( size_t i = 0; i < maskPixels; ++i ) { this->m_ForegroundMask[i] = (foregroundMask.GetDataAt( i ) > 0); } } if ( this->m_InputImage ) this->UpdateCorrectionFactors(); } UniformVolume::SmartPtr& EntropyMinimizationIntensityCorrectionFunctionalBase ::GetOutputImage( CoordinateVector& v, const bool foregroundOnly ) { this->SetParamVector( v ); this->UpdateBiasFields( foregroundOnly ); this->UpdateOutputImage( foregroundOnly ); return this->m_OutputImage; } EntropyMinimizationIntensityCorrectionFunctionalBase::ReturnType EntropyMinimizationIntensityCorrectionFunctionalBase ::EvaluateAt( CoordinateVector& v ) { this->SetParamVector( v ); this->UpdateBiasFields(); this->UpdateOutputImage(); return this->Evaluate(); } #ifdef CMTK_BUILD_DEMO void EntropyMinimizationIntensityCorrectionFunctionalBase ::SnapshotAt( CoordinateVector& v ) { this->SetParamVector( v ); this->UpdateBiasFields(); static int it = 0; this->UpdateOutputImage( false /*foregroundOnly*/ ); UniformVolume::SmartConstPtr slice = this->m_OutputImage->ExtractSlice( AXIS_Z, this->m_OutputImage->m_Dims[2] / 2 ); char path[PATH_MAX]; snprintf( path, PATH_MAX, "mrbias-%03d.nii", it++ ); VolumeIO::Write( *slice, path ); } #endif void EntropyMinimizationIntensityCorrectionFunctionalBase ::UpdateOutputImage( const bool foregroundOnly ) { ThreadPool& threadPool = ThreadPool::GetGlobalThreadPool(); const size_t numberOfTasks = 4 * threadPool.GetNumberOfThreads() - 3; std::vector taskParameters( numberOfTasks ); for ( size_t task = 0; task < numberOfTasks; ++task ) { taskParameters[task].thisObject = this; taskParameters[task].m_ForegroundOnly = foregroundOnly; } threadPool.Run( UpdateOutputImageThreadFunc, taskParameters ); } void EntropyMinimizationIntensityCorrectionFunctionalBase ::UpdateOutputImageThreadFunc( void *args, const size_t taskIdx, const size_t taskCnt, const size_t, const size_t ) { UpdateOutputImageThreadParameters* threadParameters = static_cast( args ); Self* This = threadParameters->thisObject; const Self* ThisConst = threadParameters->thisObject; const UniformVolume* inputImage = ThisConst->m_InputImage; TypedArray::SmartPtr outputData = This->m_OutputImage->GetData(); const size_t numberOfPixels = inputImage->GetNumberOfPixels(); const float* biasFieldPtrAdd = ThisConst->m_BiasFieldAdd->GetDataPtrTemplate(); const float* biasFieldPtrMul = ThisConst->m_BiasFieldMul->GetDataPtrTemplate(); Types::DataItem value; for ( size_t ofs = taskIdx; ofs < numberOfPixels; ofs += taskCnt ) { if ( !threadParameters->m_ForegroundOnly || ThisConst->m_ForegroundMask[ofs] ) { if ( inputImage->GetDataAt( value, ofs ) ) { outputData->Set( value * biasFieldPtrMul[ofs] + biasFieldPtrAdd[ofs], ofs ); } else { outputData->SetPaddingAt( ofs ); } } else { outputData->SetPaddingAt( ofs ); } } } } // namespace cmtk cmtk-3.3.1/libs/Segmentation/cmtkEntropyMinimizationIntensityCorrectionFunctionalBase.h000066400000000000000000000202511276303427400317070ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2011, 2013 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4882 $ // // $LastChangedDate: 2013-09-27 15:16:36 -0700 (Fri, 27 Sep 2013) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkEntropyMinimizationIntensityCorrectionFunctionalBase_h_included_ #define __cmtkEntropyMinimizationIntensityCorrectionFunctionalBase_h_included_ #include #include #include #include #include #include #include #include #include #include namespace cmtk { /** \addtogroup Segmentation */ //@{ /// Base class for entropy-minimzation MR bias correction functional. class EntropyMinimizationIntensityCorrectionFunctionalBase : public Functional { public: /// This class type. typedef EntropyMinimizationIntensityCorrectionFunctionalBase Self; /// Pointer to this class. typedef SmartPointer SmartPtr; /// Superclass type. typedef Functional Superclass; /// Constructor. EntropyMinimizationIntensityCorrectionFunctionalBase() : m_InputImageRange( 0.0 ), m_NumberOfPixels( 0 ), m_SamplingDensity( 1.0 ), m_NumberOfHistogramBins( 256 ), m_UseLogIntensities( false ) {} /// Virtual destructor. virtual ~EntropyMinimizationIntensityCorrectionFunctionalBase() {} /// Get number of additive monomials. virtual size_t GetNumberOfMonomialsAdd() const = 0; /// Get number of multiplicative monomials. virtual size_t GetNumberOfMonomialsMul() const = 0; /// Set input image. virtual void SetInputImage( UniformVolume::SmartConstPtr& inputImage ); /// Set foreground mask. virtual void SetForegroundMask( const UniformVolume& foregroundMask ); /// Set sampling density. virtual void SetSamplingDensity( const float samplingDensity ) { this->m_SamplingDensity = samplingDensity; } /// Set number of histogram bins. virtual void SetNumberOfHistogramBins( const size_t numberOfHistogramBins ) { this->m_NumberOfHistogramBins = numberOfHistogramBins; } /** Set flag for use of log intensities for entropy estimation. * Using log intensities compensates for the entropy increase otherwise caused by * spreading distributions of values in brightened areas. This can help make * bias field estimation more robust, potentially without any masking. */ void SetUseLogIntensities( const bool flag ) { this->m_UseLogIntensities = flag; } /// Get corrected output image. virtual UniformVolume::SmartPtr& GetOutputImage( const bool update = false ) { if ( update ) this->UpdateOutputImage( false /*foregroundOnly*/); return this->m_OutputImage; } /// Update and return corrected output image. virtual UniformVolume::SmartPtr& GetOutputImage( CoordinateVector& v, const bool foregroundOnly = false ); /// Get additive bias field. virtual UniformVolume::SmartPtr GetBiasFieldAdd( const bool updateCompleteImage = false ) { if ( updateCompleteImage ) this->UpdateBiasFieldAdd( false /*foregroundOnly*/ ); UniformVolume::SmartPtr biasField( this->m_OutputImage->CloneGrid() ); biasField->SetData( this->m_BiasFieldAdd ); return biasField; } /// Set additive bias field. virtual void SetBiasFieldAdd( const UniformVolume& biasFieldAdd ) { biasFieldAdd.GetData()->BlockCopy( *(this->m_BiasFieldAdd), 0, 0, this->m_BiasFieldAdd->GetDataSize() ); } /// Get multiplicative bias field. virtual UniformVolume::SmartPtr GetBiasFieldMul( const bool updateCompleteImage = false ) { if ( updateCompleteImage ) this->UpdateBiasFieldMul( false /*foregroundOnly*/ ); UniformVolume::SmartPtr biasField( this->m_OutputImage->CloneGrid() ); biasField->SetData( this->m_BiasFieldMul ); return biasField; } /// Set multiplicative bias field. virtual void SetBiasFieldMul( const UniformVolume& biasFieldMul ) { biasFieldMul.GetData()->BlockCopy( *(this->m_BiasFieldMul), 0, 0, this->m_BiasFieldMul->GetDataSize() ); } /// Evaluate functional. virtual Self::ReturnType Evaluate() { return static_cast( -this->m_OutputImage->GetData()->GetEntropy( *this->m_EntropyHistogram ) ); } /// Evaluate functional for given parameter vector. virtual Self::ReturnType EvaluateAt( CoordinateVector& v ); #ifdef CMTK_BUILD_DEMO /// Evaluate functional for given parameter vector. virtual void SnapshotAt( CoordinateVector& v ); #endif protected: /// Original input image. UniformVolume::SmartConstPtr m_InputImage; /// Input intensity image range. Types::DataItem m_InputImageRange; /// Evolving corrected output image. UniformVolume::SmartPtr m_OutputImage; /// Type for histogram. typedef Histogram HistogramType; /// Type for histogram using log-intensities. typedef LogHistogram LogHistogramType; /// Histogram for entropy evaluation. HistogramType::SmartPtr m_EntropyHistogram; /// Binary foreground mask. std::vector m_ForegroundMask; /// Update polynomial correction factors from input image. virtual void UpdateCorrectionFactors() = 0; /** Update output image estimate based on current bias field parameters. *\param foregroundOnly If this flag is set and an image foreground mask is set, then only image pixels are updated for which the mask is nonzero. */ virtual void UpdateOutputImage( const bool foregroundOnly = true ); /// Additive bias field. FloatArray::SmartPtr m_BiasFieldAdd; /// Multiplicative bias field. FloatArray::SmartPtr m_BiasFieldMul; /** Jointly update both bias images. *\param foregroundOnly If this flag is set and an image foreground mask is set, then only image pixels are updated for which the mask is nonzero. */ virtual void UpdateBiasFields( const bool foregroundOnly = true ) = 0; /** Update additive bias image. *\param foregroundOnly If this flag is set and an image foreground mask is set, then only image pixels are updated for which the mask is nonzero. */ virtual void UpdateBiasFieldAdd( const bool foregroundOnly = true ) = 0; /** Update additive bias image. *\param foregroundOnly If this flag is set and an image foreground mask is set, then only image pixels are updated for which the mask is nonzero. */ virtual void UpdateBiasFieldMul( const bool foregroundOnly = true ) = 0; /// Number of input image pixels. size_t m_NumberOfPixels; protected: /** Sampling density. * This defines the fraction of foreground pixels that are considered in * the computation. */ float m_SamplingDensity; /// Number of histogram bins for entropy estimation. size_t m_NumberOfHistogramBins; /// Flag for using log-intensities for entropy estimation. bool m_UseLogIntensities; private: /// Class for output image update thread parameters. class UpdateOutputImageThreadParameters : public ThreadParameters { public: /// Flag as given to UpdateOutputImage(). bool m_ForegroundOnly; }; /// Thread function: update output image. static void UpdateOutputImageThreadFunc( void* args, const size_t taskIdx, const size_t taskCnt, const size_t, const size_t ); }; //@} } // namespace cmtk #endif // #ifndef __cmtkEntropyMinimizationIntensityCorrectionFunctionalBase_h_included_ cmtk-3.3.1/libs/Segmentation/cmtkLabelCombinationLocalBinaryShapeBasedAveraging.cxx000066400000000000000000000162521276303427400305510ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2012, 2014 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 5268 $ // // $LastChangedDate: 2014-03-28 11:41:16 -0700 (Fri, 28 Mar 2014) $ // // $LastChangedBy: torstenrohlfing $ // */ #include "cmtkLabelCombinationLocalBinaryShapeBasedAveraging.h" #include #include #include #include #include #include #ifdef _OPENMP # include #endif void cmtk::LabelCombinationLocalBinaryShapeBasedAveraging::AddAtlas( const UniformVolume::SmartConstPtr image, const UniformVolume::SmartConstPtr atlas ) { Superclass::AddAtlasImage( image ); this->m_AtlasDMaps.push_back( UniformDistanceMap( *atlas, UniformDistanceMap::SIGNED | UniformDistanceMap::SQUARED ).Get() ); } cmtk::TypedArray::SmartPtr cmtk::LabelCombinationLocalBinaryShapeBasedAveraging::GetResult() const { const UniformVolume& targetImage = *(this->m_TargetImage); cmtk::TypedArray::SmartPtr result( TypedArray::Create( TYPE_SHORT, targetImage.GetNumberOfPixels() ) ); result->SetDataClass( DATACLASS_LABEL ); const TargetRegionType region = targetImage.CropRegion(); #ifdef _OPENMP #pragma omp parallel for for ( int slice = region.From()[2]; slice < region.To()[2]; ++slice ) { TargetRegionType threadRegion = region; threadRegion.From()[2] = slice; threadRegion.To()[2] = slice+1; this->ComputeResultForRegion( threadRegion, *result ); } #else // _OPENMP this->ComputeResultForRegion( region, *result ); #endif // _OPENMP return result; } void cmtk::LabelCombinationLocalBinaryShapeBasedAveraging::ComputeResultForRegion( const Self::TargetRegionType& region, TypedArray& result ) const { const UniformVolume& targetImage = *(this->m_TargetImage); const Self::TargetRegionType wholeImageRegion = targetImage.CropRegion(); const size_t nAtlases = this->m_AtlasImages.size(); std::vector valid( nAtlases ); std::vector labels( nAtlases ); std::vector weights( nAtlases ); std::vector bestPatchOffset( nAtlases ); std::vector distances( nAtlases ); for ( RegionIndexIterator it( region ); it != it.end(); ++it ) { const size_t i = targetImage.GetOffsetFromIndex( it.Index() ); for ( size_t n = 0; n < nAtlases; ++n ) { Types::DataItem value; if ( (valid[n] = this->m_AtlasDMaps[n]->GetData()->Get( value, i ) ) ) labels[n] = static_cast( (value <= 0) ? 1 : 0 ); } // detect local outliers in the distance maps, ie., grossly misregistered atlases if ( this->m_DetectLocalOutliers ) { // create vector of distance values size_t nn = 0; for ( size_t n = 0; n < nAtlases; ++n ) { if ( valid[n] ) distances[nn++] = this->m_AtlasDMaps[n]->GetDataAt( i ); } // sort distance std::sort( distances.begin(), distances.begin()+nn ); // determine 1st and 3rd quartile values const double Q1 = distances[static_cast( 0.25 * nn )]; const double Q3 = distances[static_cast( 0.75 * nn )]; // compute thresholds from quartiles and inter-quartile range const double lThresh = Q1 - 1.5 * (Q3-Q1); const double uThresh = Q3 + 1.5 * (Q3-Q1); // mark as invalid those atlases with values outside the "inlier" range for ( size_t n = 0; n < nAtlases; ++n ) { if ( valid[n] ) { const double d = this->m_AtlasDMaps[n]->GetDataAt( i ); if ( (d < lThresh) || (d > uThresh) ) valid[n] = false; } } } // find first non-padding atlas label size_t firstValid = 0; while ( (firstValid < nAtlases) && !valid[firstValid] ) ++firstValid; // if all input atlases are undefined (padding) for this pixel, set output to padding and skip to next pixel. if ( firstValid == nAtlases ) { result.SetPaddingAt( i ); continue; } // check if all (valid) input atlas labels are the same bool allTheSame = true; for ( size_t n = 1; n < nAtlases; ++n ) { if ( valid[n] ) { if ( labels[n] != labels[firstValid] ) { allTheSame = false; break; } } } // no need for weighted combination if all labels are the same. if ( allTheSame ) { result.Set( labels[firstValid] ? 1 : 0, i ); } else { std::fill( weights.begin(), weights.end(), -1 ); std::fill( bestPatchOffset.begin(), bestPatchOffset.end(), 0 ); const TargetRegionType patchSearchRegion( Max( (-1)*wholeImageRegion.From(), this->m_SearchRegion.From() ), Min( wholeImageRegion.To() - it.Index(), this->m_SearchRegion.To() ) ); for ( RegionIndexIterator searchIt( patchSearchRegion ); searchIt != searchIt.end(); ++searchIt ) { const TargetRegionType patchRegion( Max( wholeImageRegion.From(), it.Index() + searchIt.Index() - this->m_PatchRadius ), Min( wholeImageRegion.To(), it.Index() + searchIt.Index() + this->m_PatchRadiusPlusOne ) ); TypedArray::SmartConstPtr targetDataPatch( targetImage.GetRegionData( patchRegion ) ); for ( size_t n = 0; n < nAtlases; ++n ) { if ( valid[n] ) { TypedArray::SmartConstPtr atlasDataPatch( this->m_AtlasImages[n]->GetRegionData( patchRegion ) ); const Types::DataItem w = TypedArraySimilarity::GetCrossCorrelation( targetDataPatch, atlasDataPatch ); if ( w > weights[n] ) { weights[n] = w; bestPatchOffset[n] = targetImage.GetOffsetFromIndex( searchIt.Index() ); } } } } // Compute weights for the atlases from local image patch similarity. Types::DataItem minWeight = FLT_MAX; Types::DataItem maxWeight = FLT_MIN; for ( size_t n = 0; n < nAtlases; ++n ) { if ( valid[n] ) { minWeight = std::min( minWeight, weights[n] ); maxWeight = std::max( maxWeight, weights[n] ); } } maxWeight -= minWeight; // turn "max" into "range" double totalDistance = 0; for ( size_t n = 0; n < nAtlases; ++n ) { if ( valid[n] ) { totalDistance += (weights[n]-minWeight)/maxWeight * this->m_AtlasDMaps[n]->GetDataAt( i + bestPatchOffset[n] ); } } result.Set( (totalDistance <= 0) ? 1 : 0, i ); } } } cmtk-3.3.1/libs/Segmentation/cmtkLabelCombinationLocalBinaryShapeBasedAveraging.h000066400000000000000000000065251276303427400302000ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2013 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4873 $ // // $LastChangedDate: 2013-09-24 12:53:12 -0700 (Tue, 24 Sep 2013) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkLabelCombinationLocalBinaryShapeBasedAveraging_h_included_ #define __cmtkLabelCombinationLocalBinaryShapeBasedAveraging_h_included_ #include #include namespace cmtk { /** \addtogroup Segmentation */ //@{ /** Combination of binary segmentations by locally-weighted shape-based averaging. *\attention All labels maps are treated as binary maps, i.e., all labels not equal to zero are considered equal. */ class LabelCombinationLocalBinaryShapeBasedAveraging : public LabelCombinationLocalWeighting { public: /// This class. typedef LabelCombinationLocalBinaryShapeBasedAveraging Self; /// Smart pointer to this class. typedef SmartPointer SmartPtr; /// Smart pointer to const to this class. typedef SmartConstPointer SmartConstPtr; /// Parent class. typedef LabelCombinationLocalWeighting Superclass; /// Constructor: compute label combination. LabelCombinationLocalBinaryShapeBasedAveraging( const UniformVolume::SmartConstPtr targetImage ) : Superclass( targetImage ), m_DetectLocalOutliers( false ) {} /// Set flag to detect local outliers at each pixel in the co-registered distance maps. void SetDetectLocalOutliers( const bool detectOutliers = true ) { this->m_DetectLocalOutliers = detectOutliers; } /// Add an atlas (pair of reformatted, target-matched intensity image and label map). void AddAtlas( const UniformVolume::SmartConstPtr image, const UniformVolume::SmartConstPtr atlas ); /// Get resulting combined segmentation. virtual TypedArray::SmartPtr GetResult() const; private: /// Compute result for a region. void ComputeResultForRegion( const Self::TargetRegionType& region, TypedArray& result ) const; /// Signed distance maps for the atlas label maps. std::vector m_AtlasDMaps; /// Flag for outlier detection. bool m_DetectLocalOutliers; protected: /** Delete atlas with given index. * Call inherited member, then delete distance map. */ virtual void DeleteAtlas( const size_t i ) { this->Superclass::DeleteAtlas( i ); this->m_AtlasDMaps.erase( this->m_AtlasDMaps.begin() + i ); } }; } // namespace cmtk #endif // #ifndef __cmtkLabelCombinationLocalBinaryShapeBasedAveraging_h_included_ cmtk-3.3.1/libs/Segmentation/cmtkLabelCombinationLocalShapeBasedAveraging.cxx000066400000000000000000000170161276303427400274030ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2012, 2014 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 5270 $ // // $LastChangedDate: 2014-03-28 11:50:48 -0700 (Fri, 28 Mar 2014) $ // // $LastChangedBy: torstenrohlfing $ // */ #include "cmtkLabelCombinationLocalShapeBasedAveraging.h" #include #include #include #include #include #include #include #ifdef _OPENMP # include #endif cmtk::TypedArray::SmartPtr cmtk::LabelCombinationLocalShapeBasedAveraging::GetResult() const { const UniformVolume& targetImage = *(this->m_TargetImage); const size_t nPixels = targetImage.GetNumberOfPixels(); cmtk::TypedArray::SmartPtr result( TypedArray::Create( TYPE_SHORT, nPixels ) ); result->SetDataClass( DATACLASS_LABEL ); std::vector resultDistance( nPixels, 1.0 ); const TargetRegionType region = targetImage.CropRegion(); // signed distance maps for the atlas label maps. const size_t nAtlases = this->m_AtlasImages.size(); std::vector atlasDMaps( nAtlases ); const int maxLabelValue = (this->m_MaxLabelValue>0) ? this->m_MaxLabelValue : this->ComputeMaximumLabelValue(); for ( int label = 0; label <= maxLabelValue; ++label ) { if ( this->ComputeLabelNumberOfPixels( label ) > 0 ) // skip unused label values { DebugOutput( 2 ) << "Processing label " << label << "\n"; DebugOutput( 5 ) << " Creating distance maps\n"; for ( size_t n = 0; n < nAtlases; ++n ) { atlasDMaps[n] = ( UniformDistanceMap( *(this->m_AtlasLabels[n]), DistanceMap::SIGNED | DistanceMap::SQUARED | DistanceMap::VALUE_EXACT, label ).Get() ); } DebugOutput( 5 ) << " Combining distance maps\n"; #ifdef _OPENMP #pragma omp parallel for for ( int slice = region.From()[2]; slice < region.To()[2]; ++slice ) { TargetRegionType threadRegion = region; threadRegion.From()[2] = slice; threadRegion.To()[2] = slice+1; this->ComputeResultForRegion( *result, resultDistance, label, threadRegion, atlasDMaps ); } #else // _OPENMP this->ComputeResultForRegion( *result, resultDistance, label, region, atlasDMaps ); #endif // _OPENMP } } return result; } void cmtk::LabelCombinationLocalShapeBasedAveraging::ComputeResultForRegion ( TypedArray& result, std::vector& resultDistance, const int label, const Self::TargetRegionType& region, std::vector dmaps ) const { const UniformVolume& targetImage = *(this->m_TargetImage); const Self::TargetRegionType wholeImageRegion = targetImage.CropRegion(); const size_t nAtlases = this->m_AtlasImages.size(); std::vector valid( nAtlases ); std::vector labels( nAtlases ); std::vector weights( nAtlases ); std::vector bestPatchOffset( nAtlases ); std::vector distances( nAtlases ); for ( RegionIndexIterator it( region ); it != it.end(); ++it ) { const size_t i = targetImage.GetOffsetFromIndex( it.Index() ); for ( size_t n = 0; n < nAtlases; ++n ) { Types::DataItem value; if ( (valid[n] = dmaps[n]->GetData()->Get( value, i ) ) ) labels[n] = static_cast( (value <= 0) ? label : -1 ); } // detect local outliers in the distance maps, ie., grossly misregistered atlases if ( this->m_DetectLocalOutliers ) { // create vector of distance values size_t nn = 0; for ( size_t n = 0; n < nAtlases; ++n ) { if ( valid[n] ) distances[nn++] = dmaps[n]->GetDataAt( i ); } // sort distance std::sort( distances.begin(), distances.begin()+nn ); // determine 1st and 3rd quartile values const double Q1 = distances[static_cast( 0.25 * nn )]; const double Q3 = distances[static_cast( 0.75 * nn )]; // compute thresholds from quartiles and inter-quartile range const double lThresh = Q1 - 1.5 * (Q3-Q1); const double uThresh = Q3 + 1.5 * (Q3-Q1); // mark as invalid those atlases with values outside the "inlier" range for ( size_t n = 0; n < nAtlases; ++n ) { if ( valid[n] ) { const double d = dmaps[n]->GetDataAt( i ); if ( (d < lThresh) || (d > uThresh) ) valid[n] = false; } } } // find first non-padding atlas label size_t firstValid = 0; while ( (firstValid < nAtlases) && !valid[firstValid] ) ++firstValid; // if all input atlases are undefined (padding) for this pixel, set output to padding and skip to next pixel. if ( firstValid == nAtlases ) { continue; } std::fill( weights.begin(), weights.end(), -1 ); std::fill( bestPatchOffset.begin(), bestPatchOffset.end(), 0 ); const TargetRegionType patchSearchRegion( Max( (-1)*wholeImageRegion.From(), this->m_SearchRegion.From() ), Min( wholeImageRegion.To() - it.Index(), this->m_SearchRegion.To() ) ); for ( RegionIndexIterator searchIt( patchSearchRegion ); searchIt != searchIt.end(); ++searchIt ) { const TargetRegionType patchRegion( Max( wholeImageRegion.From(), it.Index() + searchIt.Index() - this->m_PatchRadius ), Min( wholeImageRegion.To(), it.Index() + searchIt.Index() + this->m_PatchRadiusPlusOne ) ); TypedArray::SmartConstPtr targetDataPatch( targetImage.GetRegionData( patchRegion ) ); for ( size_t n = 0; n < nAtlases; ++n ) { if ( valid[n] ) { TypedArray::SmartConstPtr atlasDataPatch( this->m_AtlasImages[n]->GetRegionData( patchRegion ) ); const Types::DataItem w = TypedArraySimilarity::GetCrossCorrelation( targetDataPatch, atlasDataPatch ); if ( w > weights[n] ) { weights[n] = w; bestPatchOffset[n] = targetImage.GetOffsetFromIndex( searchIt.Index() ); } } } } // Compute weights for the atlases from local image patch similarity. Types::DataItem minWeight = FLT_MAX; Types::DataItem maxWeight = FLT_MIN; for ( size_t n = 0; n < nAtlases; ++n ) { if ( valid[n] ) { minWeight = std::min( minWeight, weights[n] ); maxWeight = std::max( maxWeight, weights[n] ); } } maxWeight -= minWeight; // turn "max" into "range" double totalDistance = 0; for ( size_t n = 0; n < nAtlases; ++n ) { if ( valid[n] ) { totalDistance += (weights[n]-minWeight)/maxWeight * dmaps[n]->GetDataAt( i + bestPatchOffset[n] ); } } if ( totalDistance < resultDistance[i] ) { result.Set( label, i ); resultDistance[i] = static_cast( totalDistance ); } } } cmtk-3.3.1/libs/Segmentation/cmtkLabelCombinationLocalShapeBasedAveraging.h000066400000000000000000000062011276303427400270220ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2013 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4873 $ // // $LastChangedDate: 2013-09-24 12:53:12 -0700 (Tue, 24 Sep 2013) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkLabelCombinationLocalShapeBasedAveraging_h_included_ #define __cmtkLabelCombinationLocalShapeBasedAveraging_h_included_ #include #include namespace cmtk { /** \addtogroup Segmentation */ //@{ /** Segmentation combination by locally-weighted shape-based averaging. */ class LabelCombinationLocalShapeBasedAveraging : public LabelCombinationLocalVoting { public: /// This class. typedef LabelCombinationLocalShapeBasedAveraging Self; /// Smart pointer to this class. typedef SmartPointer SmartPtr; /// Smart pointer to const to this class. typedef SmartConstPointer SmartConstPtr; /// Parent class. typedef LabelCombinationLocalVoting Superclass; /// Constructor: compute label combination. LabelCombinationLocalShapeBasedAveraging( const UniformVolume::SmartConstPtr targetImage ) : Superclass( targetImage ), m_MaxLabelValue( 0 ), m_DetectLocalOutliers( false ) {} /// Set flag to detect local outliers at each pixel in the co-registered distance maps. void SetDetectLocalOutliers( const bool detectOutliers = true ) { this->m_DetectLocalOutliers = detectOutliers; } /// Get resulting combined segmentation. virtual TypedArray::SmartPtr GetResult() const; private: /** Maximum label value. * If this is zero, then the class will determine the largest value that is present in the input label maps. */ int m_MaxLabelValue; /// Flag for outlier detection. bool m_DetectLocalOutliers; /// Compute result for a region. void ComputeResultForRegion( TypedArray& result /*!< Evolving result label map */, std::vector& resultDistance /*!< Evolving result distance map */, const int label /*!< Current label */, const Self::TargetRegionType& region /*!< Image region to work on */, std::vector dmaps /*!< Vector of distance maps per atlas for the current label */ ) const; }; } // namespace cmtk #endif // #ifndef __cmtkLabelCombinationLocalShapeBasedAveraging_h_included_ cmtk-3.3.1/libs/Segmentation/cmtkLabelCombinationLocalVoting.cxx000066400000000000000000000145271276303427400250320ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2012, 2014 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 5268 $ // // $LastChangedDate: 2014-03-28 11:41:16 -0700 (Fri, 28 Mar 2014) $ // // $LastChangedBy: torstenrohlfing $ // */ #include "cmtkLabelCombinationLocalVoting.h" #include #include #include #include #include #include #ifdef _OPENMP # include #endif void cmtk::LabelCombinationLocalVoting::AddAtlas ( const UniformVolume::SmartConstPtr image, const UniformVolume::SmartConstPtr atlas ) { Superclass::AddAtlasImage( image ); if ( !this->m_TargetImage->GridMatches( *atlas ) ) { StdErr << "Atlas label image grid does not match target image.\n"; throw ExitException( 1 ); } this->m_AtlasLabels.push_back( atlas ); } int cmtk::LabelCombinationLocalVoting::ComputeMaximumLabelValue() const { int maxLabel = 0; for ( size_t n = 0; n < this->m_AtlasLabels.size(); ++n ) { maxLabel = std::max( maxLabel, static_cast( this->m_AtlasLabels[n]->GetData()->GetRange().m_UpperBound ) ); } return maxLabel; } size_t cmtk::LabelCombinationLocalVoting::ComputeLabelNumberOfPixels( const int label ) const { size_t nPixelsLabel = 0; for ( size_t n = 0; n < this->m_AtlasLabels.size(); ++n ) { const size_t nPixels = this->m_AtlasLabels[n]->GetNumberOfPixels(); for ( size_t px = 0; px < nPixels; ++px ) { if ( label == static_cast( this->m_AtlasLabels[n]->GetDataAt( px, -1 ) ) ) ++nPixelsLabel; } } return nPixelsLabel; } cmtk::TypedArray::SmartPtr cmtk::LabelCombinationLocalVoting::GetResult() const { const UniformVolume& targetImage = *(this->m_TargetImage); cmtk::TypedArray::SmartPtr result( TypedArray::Create( TYPE_SHORT, targetImage.GetNumberOfPixels() ) ); result->SetDataClass( DATACLASS_LABEL ); const size_t nAtlases = this->m_AtlasImages.size(); this->m_GlobalAtlasWeights.resize( nAtlases ); if ( this->m_UseGlobalAtlasWeights ) { for ( size_t n = 0; n < nAtlases; ++n ) { this->m_GlobalAtlasWeights[n] = 1.0 / TypedArraySimilarity::GetCrossCorrelation( targetImage.GetData(), this->m_AtlasImages[n]->GetData() ); } } else { std::fill( this->m_GlobalAtlasWeights.begin(), this->m_GlobalAtlasWeights.end(), 1.0 ); } const TargetRegionType region = targetImage.CropRegion(); #ifdef _OPENMP #pragma omp parallel for for ( int slice = region.From()[2]; slice < region.To()[2]; ++slice ) { TargetRegionType threadRegion = region; threadRegion.From()[2] = slice; threadRegion.To()[2] = slice+1; this->ComputeResultForRegion( threadRegion, *result ); } #else // _OPENMP this->ComputeResultForRegion( region, *result ); #endif // _OPENMP return result; } void cmtk::LabelCombinationLocalVoting::ComputeResultForRegion( const Self::TargetRegionType& region, TypedArray& result ) const { const UniformVolume& targetImage = *(this->m_TargetImage); const Self::TargetRegionType wholeImageRegion = targetImage.CropRegion(); const size_t nAtlases = this->m_AtlasImages.size(); std::vector valid( nAtlases ); std::vector labels( nAtlases ); for ( RegionIndexIterator it( region ); it != it.end(); ++it ) { const size_t i = targetImage.GetOffsetFromIndex( it.Index() ); for ( size_t n = 0; n < nAtlases; ++n ) { Types::DataItem value; if ( (valid[n] = this->m_AtlasLabels[n]->GetData()->Get( value, i ) ) ) labels[n] = static_cast( value ); } // find first non-padding atlas label size_t firstValid = 0; while ( (firstValid < nAtlases) && !valid[firstValid] ) ++firstValid; // if all input atlases are undefined (padding) for this pixel, set output to padding and skip to next pixel. if ( firstValid == nAtlases ) { result.SetPaddingAt( i ); continue; } // check if all (valid) input atlas labels are the same bool allTheSame = true; for ( size_t n = 1; n < nAtlases; ++n ) { if ( valid[n] ) { if ( labels[n] != labels[firstValid] ) { allTheSame = false; break; } } } // no need for weighted combination if all labels are the same. if ( allTheSame ) { result.Set( labels[firstValid], i ); } else { // Compute weights for the atlases from local image patch similarity. const TargetRegionType patchRegion( Max( wholeImageRegion.From(), it.Index() - this->m_PatchRadius ), Min( wholeImageRegion.To(), it.Index() + this->m_PatchRadiusPlusOne ) ); TypedArray::SmartConstPtr targetDataPatch( targetImage.GetRegionData( patchRegion ) ); std::map labelToTotalWeight; for ( size_t n = 0; n < nAtlases; ++n ) { if ( valid[n] ) { TypedArray::SmartConstPtr atlasDataPatch( this->m_AtlasImages[n]->GetRegionData( patchRegion ) ); labelToTotalWeight[labels[n]] += TypedArraySimilarity::GetCrossCorrelation( targetDataPatch, atlasDataPatch ) * this->m_GlobalAtlasWeights[n]; } } short maxLabel = 0; Types::DataItem maxWeight = 0; for ( std::map::const_iterator mapIt = labelToTotalWeight.begin(); mapIt != labelToTotalWeight.end(); ++mapIt ) { if ( mapIt->second > maxWeight ) { maxLabel = mapIt->first; maxWeight = mapIt->second; } } result.Set( maxLabel, i ); } } } cmtk-3.3.1/libs/Segmentation/cmtkLabelCombinationLocalVoting.h000066400000000000000000000074441276303427400244570ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2013 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4873 $ // // $LastChangedDate: 2013-09-24 12:53:12 -0700 (Tue, 24 Sep 2013) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkLabelCombinationLocalVoting_h_included_ #define __cmtkLabelCombinationLocalVoting_h_included_ #include #include #include #include #include #include namespace cmtk { /** \addtogroup Segmentation */ //@{ /** Segmentation combination by locally-weighted label voting. *\attention All labels must be representable as unsigned, short integers between 0 and 65535. */ class LabelCombinationLocalVoting : public LabelCombinationLocalWeighting { public: /// This class. typedef LabelCombinationLocalVoting Self; /// Smart pointer to this class. typedef SmartPointer SmartPtr; /// Smart pointer to const to this class. typedef SmartConstPointer SmartConstPtr; /// Parent class. typedef LabelCombinationLocalWeighting Superclass; /// Constructor: compute label combination. LabelCombinationLocalVoting( const UniformVolume::SmartConstPtr targetImage ) : Superclass( targetImage ), m_UseGlobalAtlasWeights( false ) {} /// Add an atlas (pair of reformatted, target-matched intensity image and label map). void AddAtlas( const UniformVolume::SmartConstPtr image, const UniformVolume::SmartConstPtr atlas ); /// Get resulting combined segmentation. virtual TypedArray::SmartPtr GetResult() const; /// Set flag for using global atlas weights for normalization. void SetUseGlobalAtlasWeights( const bool flag ) { this->m_UseGlobalAtlasWeights = flag; } protected: /// Vector of target-matched atlas label maps. std::vector m_AtlasLabels; /// Compute maximum label value in the input label maps. int ComputeMaximumLabelValue() const; /// Compute number of pixels with given label value in all input label maps. size_t ComputeLabelNumberOfPixels( const int label ) const; /** Delete atlas with given index. * Call inherited member, then delete distance map. */ virtual void DeleteAtlas( const size_t i ) { this->Superclass::DeleteAtlas( i ); this->m_AtlasLabels.erase( this->m_AtlasLabels.begin() + i ); } private: /// Flag for using global atlas weights for normalization. bool m_UseGlobalAtlasWeights; /** Vector of global atlas weights. * If global weights are used, these are the inverses of each atlas intensity image's correlation with the * target image. Otherwise, these are all 1. */ mutable std::vector m_GlobalAtlasWeights; /// Compute result for a region. void ComputeResultForRegion( const Self::TargetRegionType& region, TypedArray& result ) const; }; } // namespace cmtk #endif // #ifndef __cmtkLabelCombinationLocalVoting_h_included_ cmtk-3.3.1/libs/Segmentation/cmtkLabelCombinationLocalWeighting.cxx000066400000000000000000000054731276303427400255110ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2011 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 3814 $ // // $LastChangedDate: 2012-02-03 09:46:58 -0800 (Fri, 03 Feb 2012) $ // // $LastChangedBy: torstenrohlfing $ // */ #include "cmtkLabelCombinationLocalWeighting.h" #include #include #include #include #include #include #include #ifdef _OPENMP # include #endif void cmtk::LabelCombinationLocalWeighting::AddAtlasImage ( const UniformVolume::SmartConstPtr image ) { if ( !this->m_TargetImage->GridMatches( *image ) ) { StdErr << "Atlas intensity image grid does not match target image.\n"; throw ExitException( 1 ); } this->m_AtlasImages.push_back( image ); } void cmtk::LabelCombinationLocalWeighting::ExcludeGlobalOutliers() { std::vector ncc( this->m_AtlasImages.size() ); for ( size_t i = 0; i < this->m_AtlasImages.size(); ++i ) { ncc[i] = TypedArraySimilarity::GetCrossCorrelation( this->m_TargetImage->GetData(), this->m_AtlasImages[i]->GetData() ); } std::vector nccSort = ncc; std::sort( nccSort.begin(), nccSort.end() ); // determine 1st and 3rd quartile values const Types::DataItem Q1 = nccSort[static_cast( 0.25 * nccSort.size() )]; const Types::DataItem Q3 = nccSort[static_cast( 0.75 * nccSort.size() )]; // compute threshold from 1st quartile and inter-quartile range const Types::DataItem lThresh = Q1 - 1.5 * (Q3-Q1); size_t iAtlas = 0; for ( size_t i = 0; i < this->m_AtlasImages.size(); ++i ) { if ( ncc[i] < lThresh ) { DebugOutput( 2 ) << "INFO: atlas #" << i << " excluded as outlier based on low NCC with target image (" << ncc[i] << ", thresh=" << lThresh << ")\n"; this->DeleteAtlas( iAtlas ); } else { ++iAtlas; } } } cmtk-3.3.1/libs/Segmentation/cmtkLabelCombinationLocalWeighting.h000066400000000000000000000100001276303427400251140ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2012 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4427 $ // // $LastChangedDate: 2012-06-12 11:23:22 -0700 (Tue, 12 Jun 2012) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkLabelCombinationLocalWeighting_h_included_ #define __cmtkLabelCombinationLocalWeighting_h_included_ #include #include #include #include #include namespace cmtk { /** \addtogroup Segmentation */ //@{ /** Base class for segmentation combination using local weighting. */ class LabelCombinationLocalWeighting { public: /// This class. typedef LabelCombinationLocalWeighting Self; /// Smart pointer to this class. typedef SmartPointer SmartPtr; /// Smart pointer to const to this class. typedef SmartConstPointer SmartConstPtr; /// Constructor: compute label combination. LabelCombinationLocalWeighting( const UniformVolume::SmartConstPtr targetImage ) : m_TargetImage( targetImage ), m_PatchRadius( 1 ), m_PatchRadiusPlusOne( 2 ), m_SearchRegion( UniformVolume::IndexType( 0 ), UniformVolume::IndexType( 1 ) ) {} /// Add an atlas image (reformatted, target-matched intensity image). void AddAtlasImage( const UniformVolume::SmartConstPtr image ); /** Exclude global outliers. * Detect atlases with abnormally low correlation between reformatted atlas and target image, * then delete these atlases. Outliers are defined as NCC below Q1-1.5*(Q3-Q1), where Q1 is * the 25th percentile of NCC between atlas and target over all atlases, Q3 is the 75th * percentile. */ void ExcludeGlobalOutliers(); /// Set patch radius. void SetPatchRadius( const int radius ) { this->m_PatchRadius = UniformVolume::IndexType( radius ); this->m_PatchRadiusPlusOne = UniformVolume::IndexType( radius+1 ); } /// Set patch radius. void SetSearchRadius( const int radius ) { this->m_SearchRegion.From() = UniformVolume::IndexType( -radius ); this->m_SearchRegion.To() = UniformVolume::IndexType( radius+1 ); } /// Get resulting combined segmentation. virtual TypedArray::SmartPtr GetResult() const = 0; protected: /// Target image region type. typedef UniformVolume::RegionType TargetRegionType; /// The target image. UniformVolume::SmartConstPtr m_TargetImage; /// Vector of target-matched atlas images. std::vector m_AtlasImages; /// Image patch radius in pixels (x,y,z). UniformVolume::IndexType m_PatchRadius; /// Image patch radius in pixels (x,y,z) plus one added to each dimension. UniformVolume::IndexType m_PatchRadiusPlusOne; /// Patch search region in pixels (x,y,z). UniformVolume::RegionType m_SearchRegion; /** Delete atlas with given index. * Derived classes may need to overload this to make sure additional atlas components (e.g., * distance map, label map) are also properly deleted. */ virtual void DeleteAtlas( const size_t i ) { this->m_AtlasImages.erase( this->m_AtlasImages.begin() + i ); } }; } // namespace cmtk #endif // #ifndef __cmtkLabelCombinationLocalWeighting_h_included_ cmtk-3.3.1/libs/Segmentation/cmtkLabelCombinationMultiClassSTAPLE.cxx000066400000000000000000000210101276303427400255630ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2011, 2013 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4926 $ // // $LastChangedDate: 2013-10-04 10:12:38 -0700 (Fri, 04 Oct 2013) $ // // $LastChangedBy: torstenrohlfing $ // */ #include #include #include #include #include #include namespace cmtk { /** \addtogroup Segmentation */ //@{ LabelCombinationMultiClassSTAPLE ::LabelCombinationMultiClassSTAPLE ( const std::vector& data, const int maxIterations, const bool disputedOnly ) { const size_t numberOfInputs = data.size(); const size_t numberOfPixels = data[ 0 ]->GetDataSize(); // First, determine which pixels are "active" and will be considered by the algorithm. // If "disputedOnly" is true, these will be the pixels for which at least one input map disagrees with the others. std::vector activePixels( numberOfPixels ); if ( disputedOnly ) { size_t nDisputed = 0; for ( size_t n = 0; n < numberOfPixels; ++n ) { bool disputed = false; Types::DataItem refVal = -1; for ( size_t k = 0; (k < numberOfInputs) && !disputed; ++k ) { Types::DataItem lVal; if ( data[k]->Get( lVal, n ) ) { if ( refVal == -1 ) { refVal = lVal; } else { if ( lVal != refVal ) { disputed = true; ++nDisputed; } } } } activePixels[n] = disputed; } DebugOutput( 5 ) << "MultiClassSTAPLE running for " << nDisputed << " disputed out of " << numberOfPixels << " total pixels.\n"; } else { std::fill( activePixels.begin(), activePixels.end(), true ); DebugOutput( 5 ) << "MultiClassSTAPLE running for all pixels.\n"; } // Next figure out how many classes thare are in the first place. int numberOfClasses = 1; for ( size_t k = 0; k < numberOfInputs; ++k ) { const Types::DataItemRange range = data[k]->GetRange(); numberOfClasses = std::max( numberOfClasses, 1+static_cast( range.m_UpperBound ) ); } // allocate priors vector this->m_Priors.resize( numberOfClasses ); // init priors size_t totalMass = 0; std::fill( this->m_Priors.begin(), this->m_Priors.end(), static_cast( 0.0 ) ); for ( size_t k = 0; k < numberOfInputs; ++k ) { Types::DataItem lVal; for ( size_t n = 0; n < numberOfPixels; ++n ) { if ( activePixels[n] ) { if ( data[k]->Get( lVal, n ) ) { this->m_Priors[static_cast(lVal)]++; ++totalMass; } } } } if ( totalMass ) { for ( int l = 0; l < numberOfClasses; ++l ) this->m_Priors[l] /= totalMass; } // initialize result using simple voting. { LabelCombinationVoting voting( data ); this->m_Result = voting.GetResult(); } // use local scope to free voting object storage right away // allocate current and updated confusion matrix arrays this->m_Confusion.resize( numberOfInputs ); this->m_ConfusionNew.resize( numberOfInputs ); for ( size_t k = 0; k < numberOfInputs; ++k ) { this->m_Confusion[k].Resize( 1+numberOfClasses, numberOfClasses ); this->m_ConfusionNew[k].Resize( 1+numberOfClasses, numberOfClasses ); } // initialize confusion matrices from voting result for ( size_t k = 0; k < numberOfInputs; ++k ) { this->m_Confusion[k].SetAll( 0.0 ); for ( size_t n = 0; n < numberOfPixels; ++n ) { if ( activePixels[n] ) { Types::DataItem lValue, vValue; if ( data[k]->Get( lValue, n ) ) { if ( this->m_Result->Get( vValue, n ) && (vValue >= 0) ) ++(this->m_Confusion[k][static_cast(lValue)][static_cast(vValue)]); } } } } // normalize matrix rows to unit probability sum for ( size_t k = 0; k < numberOfInputs; ++k ) { for ( int inLabel = 0; inLabel <= numberOfClasses; ++inLabel ) { // compute sum over all output labels for given input label double sum = 0; for ( int outLabel = 0; outLabel < numberOfClasses; ++outLabel ) { sum += this->m_Confusion[k][inLabel][outLabel]; } // make sure that this input label did in fact show up in the input!! if ( sum > 0 ) { // normalize for ( int outLabel = 0; outLabel < numberOfClasses; ++outLabel ) { this->m_Confusion[k][inLabel][outLabel] /= sum; } } } } // allocate array for pixel class weights std::vector W( numberOfClasses ); Progress::Begin( 0, maxIterations, 1, "Multi-label STAPLE" ); // main EM loop for ( int it = 0; it < maxIterations; ++it ) { Progress::SetProgress( it ); // reset updated confusion matrices. for ( size_t k = 0; k < numberOfInputs; ++k ) { this->m_ConfusionNew[k].SetAll( 0.0 ); } for ( size_t n = 0; n < numberOfPixels; ++n ) { if ( activePixels[n] ) { // the following is the E step for ( int ci = 0; ci < numberOfClasses; ++ci ) W[ci] = this->m_Priors[ci]; for ( size_t k = 0; k < numberOfInputs; ++k ) { Types::DataItem lValue; if ( data[k]->Get( lValue, n ) ) { for ( int ci = 0; ci < numberOfClasses; ++ci ) { W[ci] *= this->m_Confusion[k][static_cast(lValue)][ci]; } } } // the following is the M step double sumW = W[0]; for ( int ci = 1; ci < numberOfClasses; ++ci ) sumW += W[ci]; if ( sumW ) { for ( int ci = 0; ci < numberOfClasses; ++ci ) W[ci] /= sumW; } for ( size_t k = 0; k < numberOfInputs; ++k ) { Types::DataItem lValue; if ( data[k]->Get( lValue, n ) ) { for ( int ci = 0; ci < numberOfClasses; ++ci ) { this->m_ConfusionNew[k][static_cast(lValue)][ci] += W[ci]; } } } } } // Normalize matrix elements of each of the updated confusion matrices // with sum over all expert decisions. for ( size_t k = 0; k < numberOfInputs; ++k ) { // compute sum over all output classifications for ( int ci = 0; ci < numberOfClasses; ++ci ) { double sumW = this->m_ConfusionNew[k][0][ci]; for ( int j = 1; j <= numberOfClasses; ++j ) sumW += this->m_ConfusionNew[k][j][ci]; // normalize with for each class ci if ( sumW ) { for ( int j = 0; j <= numberOfClasses; ++j ) this->m_ConfusionNew[k][j][ci] /= sumW; } } } // now we're applying the update to the confusion matrices and compute the // maximum parameter change in the process. for ( size_t k = 0; k < numberOfInputs; ++k ) for ( int j = 0; j <= numberOfClasses; ++j ) for ( int ci = 0; ci < numberOfClasses; ++ci ) { this->m_Confusion[k][j][ci] = this->m_ConfusionNew[k][j][ci]; } } // main EM loop // assemble output (this time, all voxels, disputed and non-disputed, to get the complete image) for ( size_t n = 0; n < numberOfPixels; ++n ) { // basically, we'll repeat the E step from above for ( int ci = 0; ci < numberOfClasses; ++ci ) W[ci] = this->m_Priors[ci]; for ( size_t k = 0; k < numberOfInputs; ++k ) { Types::DataItem lValue; if ( data[k]->Get( lValue, n ) ) { for ( int ci = 0; ci < numberOfClasses; ++ci ) { W[ci] *= this->m_Confusion[k][static_cast(lValue)][ci]; } } } // now determine the label with the maximum W int winningLabel = -1; double winningLabelW = 0; for ( int ci = 0; ci < numberOfClasses; ++ci ) { if ( W[ci] > winningLabelW ) { winningLabelW = W[ci]; winningLabel = ci; } else if ( ! (W[ci] < winningLabelW ) ) { winningLabel = -1; } } this->m_Result->Set( winningLabel, n ); } Progress::Done(); } } // namespace cmtk cmtk-3.3.1/libs/Segmentation/cmtkLabelCombinationMultiClassSTAPLE.h000066400000000000000000000052371276303427400252250ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2011 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 3471 $ // // $LastChangedDate: 2011-10-18 12:53:01 -0700 (Tue, 18 Oct 2011) $ // // $LastChangedBy: torsten_at_home $ // */ #ifndef __cmtkLabelCombinationMultiClassSTAPLE_h_included_ #define __cmtkLabelCombinationMultiClassSTAPLE_h_included_ #include #include #include #include #include namespace cmtk { /** \addtogroup Segmentation */ //@{ /** Multi-class STAPLE label combination. * This class implements combination of multiple-label images using the * multi-class STAPLE algorithm. */ class LabelCombinationMultiClassSTAPLE { public: /// This class. typedef LabelCombinationMultiClassSTAPLE Self; /// Real value type for internal computations. typedef double RealValueType; /// Confusion matrix type. typedef Matrix2D ConfusionMatrixType; /// Constructor: compute label combination. LabelCombinationMultiClassSTAPLE( const std::vector& data /*!< Array of typed arrays with input data.*/, const int maxIterations /*!< Maximum number of STAPLE iterations.*/, const bool disputedOnly /*!< If set, restrict computation to "disputed" voxels. */ ); /// Get result. TypedArray::SmartPtr& GetResult() { return this->m_Result; } private: /// Resulting data array. TypedArray::SmartPtr m_Result; /// Array of prior probabilities per class. std::vector m_Priors; /// Array of confusion matrices. std::vector m_Confusion; /// Array of updated confusion matrices. std::vector m_ConfusionNew; }; }; // namespace cmtk #endif // #ifndef __cmtkLabelCombinationMultiClassSTAPLE_h_included_ cmtk-3.3.1/libs/Segmentation/cmtkLabelCombinationSTAPLE.cxx000066400000000000000000000101001276303427400235600ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2011, 2014 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 5268 $ // // $LastChangedDate: 2014-03-28 11:41:16 -0700 (Fri, 28 Mar 2014) $ // // $LastChangedBy: torstenrohlfing $ // */ #include "cmtkLabelCombinationSTAPLE.h" #ifdef CMTK_USE_GCD # include # include #endif namespace cmtk { /** \addtogroup Segmentation */ //@{ LabelCombinationSTAPLE::LabelCombinationSTAPLE( const std::vector& data, const int maxIterations, const ScalarDataType resultType ) { const size_t numberOfInputs = data.size(); const size_t numberOfPixels = data[ 0 ]->GetDataSize(); this->m_Result = TypedArray::SmartPtr( TypedArray::Create( resultType, numberOfPixels ) ); this->m_Result->SetDataClass( DATACLASS_LABEL ); // compute initial estimate as the average of all inputs; // this is also the first E-step with all p/q equal to 0.5 double totalSum = 0; // The following is currently broken due to Apple bug: // http://forums.macrumors.com/showthread.php?t=952857 // http://lists.apple.com/archives/perfoptimization-dev/2009/Sep/msg00043.html //#ifdef CMTK_USE_GCD // const cmtk::Threads::Stride stride( numberOfPixels ); // float* threadSum = new float[stride.NBlocks()]; // dispatch_apply( stride.NBlocks(), dispatch_get_global_queue(0, 0), ^(size_t b) // { for ( size_t numberOfPixels = stride.From( b ); numberOfPixels < stride.To( b ); ++numberOfPixels ) //#else #pragma omp parallel for reduction(+:totalSum) for ( int n = 0; n < static_cast( numberOfPixels ); ++n ) //#endif { Types::DataItem w = 0; for ( size_t i = 0; i < numberOfInputs; ++i ) { Types::DataItem value; if ( data[i]->Get( value, n ) ) { w += value; totalSum += value; } } this->m_Result->Set( w / numberOfInputs, n ); } //#ifdef CMTK_USE_GCD // }); //#endif // global prior probability const double globalPrior = totalSum / (numberOfInputs * numberOfPixels ); // expert parameters this->m_VecP.resize( numberOfInputs ); this->m_VecQ.resize( numberOfInputs ); // iterate for ( int it = 0; it < maxIterations; ++it ) { // M-step for ( size_t i = 0; i < numberOfInputs; ++i ) { this->m_VecP[i] = this->m_VecQ[i] = 0; } double sumW = 0; for ( size_t n = 0; n < numberOfPixels; ++n ) { Types::DataItem w; this->m_Result->Get( w, n ); sumW += w; for ( size_t i = 0; i < numberOfInputs; ++i ) { Types::DataItem value; data[i]->Get( value, n ); this->m_VecP[i] += w * value; this->m_VecQ[i] += (1.0 - w) * (1.0 - value); } } for ( size_t i = 0; i < numberOfInputs; ++i ) { this->m_VecP[i] /= sumW; this->m_VecQ[i] /= (numberOfPixels - sumW); } // E-step #pragma omp parallel for for ( int n = 0; n < static_cast( numberOfPixels ); ++n ) { double alpha = globalPrior; double beta = (1.0-globalPrior); Types::DataItem w = 0; for ( size_t i = 0; i < numberOfInputs; ++i ) { if ( data[i]->Get( w, n ) ) { alpha *= (1.0-w-m_VecP[i]); beta *= (w-m_VecQ[i]); } } this->m_Result->Set( alpha / (alpha+beta), n ); } } } } // namespace cmtk cmtk-3.3.1/libs/Segmentation/cmtkLabelCombinationSTAPLE.h000066400000000000000000000047701276303427400232250ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2011 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 3138 $ // // $LastChangedDate: 2011-04-12 13:53:37 -0700 (Tue, 12 Apr 2011) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkLabelCombinationSTAPLE_h_included_ #define __cmtkLabelCombinationSTAPLE_h_included_ #include #include #include #include namespace cmtk { /** \addtogroup Segmentation */ //@{ /** Binary STAPLE label combination. * This class implements combination of binary images using the * binary STAPLE algorithm. If multi-class images are provided as inputs, * all values other than zero are interpreted as binary "1" for the * purpose of the algorithm. */ class LabelCombinationSTAPLE { public: /// Constructor: compute label combination. LabelCombinationSTAPLE( const std::vector& data /*!< Array of typed arrays with input data.*/, const int maxIterations /*!< Maximum number of STAPLE iterations. */, const ScalarDataType resultType = TYPE_DOUBLE /*!< Primitive data type for results.*/ ); /// Get result. TypedArray::SmartPtr& GetResult() { return this->m_Result; } /// Get one p value. double GetPValue( const size_t i ) const { return this->m_VecP[i]; } /// Get one q value. double GetQValue( const size_t i ) const { return this->m_VecQ[i]; } private: /// Resulting data array. TypedArray::SmartPtr m_Result; /// p-Values. std::vector m_VecP; /// q-Values. std::vector m_VecQ; }; } // namespace cmtk #endif // #ifndef __cmtkLabelCombinationSTAPLE_h_included_ cmtk-3.3.1/libs/Segmentation/cmtkLabelCombinationShapeBasedAveraging.cxx000066400000000000000000000153321276303427400264270ustar00rootroot00000000000000/* // // Copyright 1997-2012 Torsten Rohlfing // // Copyright 2004-2012, 2014 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 5268 $ // // $LastChangedDate: 2014-03-28 11:41:16 -0700 (Fri, 28 Mar 2014) $ // // $LastChangedBy: torstenrohlfing $ // */ #include "cmtkLabelCombinationShapeBasedAveraging.h" #include #include #include #include #include #include #include namespace cmtk { /** \addtogroup Segmentation */ //@{ LabelCombinationShapeBasedAveraging::LabelCombinationShapeBasedAveraging( const std::vector& labelImages, const Self::LabelIndexType numberOfLabels ) : m_NumberOfLabels( numberOfLabels ), m_LabelImages( labelImages ) { if ( ! this->m_NumberOfLabels ) { this->m_NumberOfLabels = 1; for ( size_t k = 0; k < this->m_LabelImages.size(); ++k ) { const Types::DataItemRange range = this->m_LabelImages[k]->GetData()->GetRange(); this->m_NumberOfLabels = std::max( this->m_NumberOfLabels, static_cast( 1 + range.m_UpperBound ) ); } DebugOutput( 9 ) << "Determined number of labels to be " << this->m_NumberOfLabels << "\n"; } this->m_NumberOfPixels = this->m_LabelImages[0]->GetNumberOfPixels(); this->m_LabelFlags.resize( this->m_NumberOfLabels, false ); for ( size_t k = 0; k < this->m_LabelImages.size(); ++k ) { const cmtk::TypedArray& data = *(this->m_LabelImages[k]->GetData()); cmtk::Types::DataItem l; for ( size_t i = 0; i < this->m_NumberOfPixels; ++i ) { if ( data.Get( l, i ) ) this->m_LabelFlags[static_cast( l )] = true; } } } TypedArray::SmartPtr LabelCombinationShapeBasedAveraging::GetResult( const bool detectOutliers ) const { cmtk::TypedArray::SmartPtr result( cmtk::TypedArray::Create( cmtk::TYPE_USHORT, this->m_NumberOfPixels ) ); result->BlockSet( 0 /*value*/, 0 /*idx*/, this->m_NumberOfPixels /*len*/ ); result->SetDataClass( DATACLASS_LABEL ); std::vector totalDistance( this->m_NumberOfPixels, 0.0 ); std::vector labelDistanceMap( this->m_NumberOfPixels ); for ( int label = 0; label < this->m_NumberOfLabels; ++label ) { /// skip labels that are not in any image. if ( ! this->m_LabelFlags[label] ) continue; cmtk::DebugOutput( 1 ) << "Processing label #" << label << "\r"; std::fill( labelDistanceMap.begin(), labelDistanceMap.end(), static_cast( 0 ) ); if ( detectOutliers ) { // if this is the first label, write directly to totalDistance, otherwise labelDistanceMap this->ProcessLabelExcludeOutliers( label, (label == 0) ? totalDistance : labelDistanceMap ); } else { // if this is the first label, write directly to totalDistance, otherwise labelDistanceMap this->ProcessLabelIncludeOutliers( label, (label == 0) ? totalDistance : labelDistanceMap ); } // compare sum over all inputs of this label's distance maps pixel by pixel with current total distance map. // Set result map to this label where it is closer than previous closest label. if ( label ) { #pragma omp parallel for for ( int i = 0; i < static_cast( this->m_NumberOfPixels ); ++i ) { if ( labelDistanceMap[i] < totalDistance[i] ) { totalDistance[i] = labelDistanceMap[i]; result->Set( label, i ); } else { if ( !(labelDistanceMap[i] > totalDistance[i]) ) { result->Set( this->m_NumberOfLabels, i ); } } } } } return result; } void LabelCombinationShapeBasedAveraging::ProcessLabelExcludeOutliers ( const Self::LabelIndexType label, std::vector& labelDistanceMap ) const { const size_t nLabelMaps = this->m_LabelImages.size(); std::vector signedDistanceMaps( nLabelMaps ); for ( size_t k = 0; k < nLabelMaps; ++k ) { signedDistanceMaps[k] = cmtk::UniformDistanceMap( *(this->m_LabelImages[k]), DistanceMap::VALUE_EXACT + DistanceMap::SIGNED, label ).Get(); } std::vector distances( nLabelMaps ); for ( int i = 0; i < static_cast( this->m_NumberOfPixels ); ++i ) { for ( size_t k = 0; k < nLabelMaps; ++k ) { distances[k] = static_cast( signedDistanceMaps[k]->GetDataAt( i ) ); } // sort distance std::sort( distances.begin(), distances.end() ); // determine 1st and 3rd quartile values const double Q1 = distances[static_cast( 0.25 * nLabelMaps )]; const double Q3 = distances[static_cast( 0.75 * nLabelMaps )]; // compute thresholds from quartiles and inter-quartile range const double lThresh = Q1 - 1.5 * (Q3-Q1); const double uThresh = Q3 + 1.5 * (Q3-Q1); for ( size_t k = 0; k < nLabelMaps; ++k ) { if ( (distances[k] >= lThresh) && (distances[k] <= uThresh) ) labelDistanceMap[i] += distances[k]; } } } void LabelCombinationShapeBasedAveraging::ProcessLabelIncludeOutliers ( const Self::LabelIndexType label, std::vector& labelDistanceMap ) const { for ( size_t k = 0; k < this->m_LabelImages.size(); ++k ) { cmtk::UniformVolume::SmartPtr signedDistanceMap = cmtk::UniformDistanceMap( *(this->m_LabelImages[k]), DistanceMap::VALUE_EXACT + DistanceMap::SIGNED, label ).Get(); const Self::DistanceMapRealType* signedDistancePtr = static_cast( signedDistanceMap->GetData()->GetDataPtr() ); #pragma omp parallel for for ( int i = 0; i < static_cast( this->m_NumberOfPixels ); ++i ) { labelDistanceMap[i] += signedDistancePtr[i]; } } } } // namespace cmtk cmtk-3.3.1/libs/Segmentation/cmtkLabelCombinationShapeBasedAveraging.h000066400000000000000000000066671276303427400260670ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2012 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 3951 $ // // $LastChangedDate: 2012-02-29 14:03:22 -0800 (Wed, 29 Feb 2012) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkLabelCombinationShapeBasedAveraging_h_included_ #define __cmtkLabelCombinationShapeBasedAveraging_h_included_ #include #include #include #include #include namespace cmtk { /** \addtogroup Segmentation */ //@{ /** Label image combination by Shape Based Averaging. *\see T. Rohlfing and C. R. Maurer, Jr., "Shape-based averaging," IEEE Transactions on Image Processing, vol. 16, no. 1, pp. 153-161, 2007. http://dx.doi.org/10.1109/TIP.2006.884936 */ class LabelCombinationShapeBasedAveraging { public: /// This class. typedef LabelCombinationShapeBasedAveraging Self; /// Smart pointer to this class. typedef SmartPointer SmartPtr; /// Smart pointer to const this class. typedef SmartConstPointer SmartConstPtr; /// Label index type. typedef unsigned short LabelIndexType; /// Real-value type for distance maps. typedef float DistanceMapRealType; /// Constructor: compute label combination. LabelCombinationShapeBasedAveraging( const std::vector& labelImages /*!< Input label images. */, const Self::LabelIndexType numberOfLabels = 0 /*!< Number of labels. If zero, the highest label index is determined from the data */ ); /// Get result. TypedArray::SmartPtr GetResult( const bool detectOutliers = false /*!< Flag for local outlier detection.*/ ) const; protected: /// Number of labels. Self::LabelIndexType m_NumberOfLabels; /// Vector of label images. const std::vector& m_LabelImages; /// Number of pixels per image. size_t m_NumberOfPixels; /// Flags for which labels actually exist in the data. std::vector m_LabelFlags; private: /// Handle one label and include outliers. void ProcessLabelIncludeOutliers( const Self::LabelIndexType label /*!< Current label */, std::vector& labelDistanceMap /*!< Distance map array for the current label */ ) const; /// Handle one label and exclude outliers. void ProcessLabelExcludeOutliers( const Self::LabelIndexType label /*!< Current label */, std::vector& labelDistanceMap /*!< Distance map array for the current label */ ) const; }; } // namespace cmtk #endif // #ifndef __cmtkLabelCombinationShapeBasedAveraging_h_included_ cmtk-3.3.1/libs/Segmentation/cmtkLabelCombinationShapeBasedAveragingInterpolation.cxx000066400000000000000000000116021276303427400311730ustar00rootroot00000000000000/* // // Copyright 1997-2012 Torsten Rohlfing // // Copyright 2004-2014 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 5268 $ // // $LastChangedDate: 2014-03-28 11:41:16 -0700 (Fri, 28 Mar 2014) $ // // $LastChangedBy: torstenrohlfing $ // */ #include "cmtkLabelCombinationShapeBasedAveragingInterpolation.h" #include #include #include #include #include #include #include #include #include #include namespace cmtk { /** \addtogroup Segmentation */ //@{ LabelCombinationShapeBasedAveragingInterpolation::LabelCombinationShapeBasedAveragingInterpolation ( const std::vector& labelImages, const std::vector& xformsToLabelImages, const UniformVolume::SmartConstPtr& targetGrid, const Self::LabelIndexType numberOfLabels ) : LabelCombinationShapeBasedAveraging( labelImages, numberOfLabels ), m_TargetGrid( targetGrid ), m_Transformations( xformsToLabelImages ) { if ( this->m_LabelImages.size() != this->m_Transformations.size() ) { StdErr << "ERROR: number of transformations does not match number of input images\n"; throw ExitException( 1 ); } this->m_NumberOfPixels = this->m_TargetGrid->GetNumberOfPixels(); } TypedArray::SmartPtr LabelCombinationShapeBasedAveragingInterpolation::GetResult() const { const cmtk::DataGrid::IndexType& targetDims = this->m_TargetGrid->GetDims(); cmtk::TypedArray::SmartPtr result( cmtk::TypedArray::Create( cmtk::TYPE_USHORT,this->m_NumberOfPixels ) ); result->BlockSet( 0 /*value*/, 0 /*idx*/,this->m_NumberOfPixels /*len*/ ); result->SetDataClass( DATACLASS_LABEL ); std::vector totalDistance( this->m_NumberOfPixels, 0.0 ); std::vector labelDistance( this->m_NumberOfPixels ); for ( int label = 0; label < this->m_NumberOfLabels; ++label ) { /// skip labels that are not in any image. if ( ! this->m_LabelFlags[label] ) continue; cmtk::DebugOutput( 1 ) << "Processing label #" << label << "\r"; std::fill( labelDistance.begin(), labelDistance.end(), static_cast( 0 ) ); for ( size_t k = 0; k < this->m_LabelImages.size(); ++k ) { cmtk::UniformVolume::SmartPtr signedDistanceMap = cmtk::UniformDistanceMap( *(this->m_LabelImages[k]), cmtk::DistanceMap::VALUE_EXACT + cmtk::DistanceMap::SIGNED, label ).Get(); cmtk::UniformVolumeInterpolator interpolator( *signedDistanceMap ); // accumulate interpolated distances for this label #pragma omp parallel for for ( int z = 0; z < targetDims[2]; ++z ) { cmtk::Vector3D v; cmtk::Types::DataItem dvalue; size_t i = z * targetDims[0] * targetDims[1]; for ( int y = 0; y < targetDims[1]; ++y ) { for ( int x = 0; x < targetDims[0]; ++x, ++i ) { this->m_Transformations[k]->GetTransformedGrid( v, x, y, z ); if ( interpolator.GetDataAt( v, dvalue ) ) { labelDistance[i] += static_cast( dvalue ); } } } } } // if this is not the first label, compare this label's sum distance map // (over all volumes) pixel by pixel and set this label where it is // closer than previous closest label if ( label ) { #pragma omp parallel for for ( int i = 0; i < static_cast(this->m_NumberOfPixels ); ++i ) { if ( labelDistance[i] < totalDistance[i] ) { totalDistance[i] = labelDistance[i]; result->Set( label, i ); } else { if ( !(labelDistance[i] > totalDistance[i]) ) { result->Set( this->m_NumberOfLabels, i ); } } } } else { // for label 0, simply copy map. for ( size_t i = 0; i m_NumberOfPixels; ++i ) { totalDistance[i] = labelDistance[i]; } } } return result; } } // namespace cmtk cmtk-3.3.1/libs/Segmentation/cmtkLabelCombinationShapeBasedAveragingInterpolation.h000066400000000000000000000065441276303427400306310ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2013 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4873 $ // // $LastChangedDate: 2013-09-24 12:53:12 -0700 (Tue, 24 Sep 2013) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkLabelCombinationShapeBasedAveragingInterpolation_h_included_ #define __cmtkLabelCombinationShapeBasedAveragingInterpolation_h_included_ #include #include #include #include #include #include #include namespace cmtk { /** \addtogroup Segmentation */ //@{ /** Label image combination by Shape Based Averaging. *\see T. Rohlfing and C. R. Maurer, Jr., "Shape-based averaging," IEEE Transactions on Image Processing, vol. 16, no. 1, pp. 153-161, 2007. http://dx.doi.org/10.1109/TIP.2006.884936 */ class LabelCombinationShapeBasedAveragingInterpolation : private LabelCombinationShapeBasedAveraging { public: /// This class. typedef LabelCombinationShapeBasedAveragingInterpolation Self; /// Parent class. typedef LabelCombinationShapeBasedAveraging Superclass; /// Smart pointer to this class. typedef SmartPointer SmartPtr; /// Smart pointer to const this class. typedef SmartConstPointer SmartConstPtr; /// Label index type. typedef Superclass::LabelIndexType LabelIndexType; /// Real-value type for distance maps. typedef Superclass::DistanceMapRealType DistanceMapRealType; /// Constructor: compute label combination. LabelCombinationShapeBasedAveragingInterpolation( const std::vector& labelImages /*!< Input label images. */, const std::vector& xformsToLabelImages /*!< Transformations with pre-assigned reference image grid.*/, const UniformVolume::SmartConstPtr& targetGrid /*!< Target grid for all transformations. */, const Self::LabelIndexType numberOfLabels = 0 /*!< Number of labels. If zero, the highest label index is determined from the data */ ); /// Get result. TypedArray::SmartPtr GetResult() const; private: /// Target grid for all transformations. const UniformVolume::SmartConstPtr m_TargetGrid; /// Vector of transformations to the label images. const std::vector m_Transformations; }; } // namespace cmtk #endif // #ifndef __cmtkLabelCombinationShapeBasedAveragingInterpolation_h_included_ cmtk-3.3.1/libs/Segmentation/cmtkLabelCombinationVoting.cxx000066400000000000000000000050671276303427400240560ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2011, 2014 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 5268 $ // // $LastChangedDate: 2014-03-28 11:41:16 -0700 (Fri, 28 Mar 2014) $ // // $LastChangedBy: torstenrohlfing $ // */ #include #include #include namespace cmtk { /** \addtogroup Segmentation */ //@{ LabelCombinationVoting::LabelCombinationVoting( const std::vector& data ) { const size_t nValues = data[ 0 ]->GetDataSize(); this->m_Result = TypedArray::SmartPtr( TypedArray::Create( TYPE_SHORT, nValues ) ); this->m_Result->SetDataClass( DATACLASS_LABEL ); size_t numberOfClasses = 1; for ( size_t k = 0; k < data.size(); ++k ) { const Types::DataItemRange range = data[k]->GetRange(); numberOfClasses = std::max( numberOfClasses, 1+static_cast( range.m_UpperBound ) ); } std::vector label( 1+numberOfClasses ); for ( size_t i = 0; i < nValues; ++i ) { std::fill( label.begin(), label.end(), 0 ); for ( size_t curr = 0; curr < data.size(); ++curr ) { Types::DataItem v; if ( data[ curr ]->Get( v, i ) ) { ++label[ std::min( numberOfClasses, static_cast( v ) ) ]; } } // Compute winner of label voting. short maxLab = 0; unsigned int maxCnt = 0; for ( size_t lab=0; lab < numberOfClasses; ++lab ) { // do something with tie case if ( label[ lab ] > maxCnt ) { maxCnt = label[ lab ]; maxLab = lab; } else { if ( label[lab] == maxCnt ) { maxLab = -1; } } } this->m_Result->Set( maxLab, i ); } } } // namespace cmtk cmtk-3.3.1/libs/Segmentation/cmtkLabelCombinationVoting.h000066400000000000000000000042431276303427400234760ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2011 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 2734 $ // // $LastChangedDate: 2011-01-14 10:23:45 -0800 (Fri, 14 Jan 2011) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkLabelCombinationVoting_h_included_ #define __cmtkLabelCombinationVoting_h_included_ #include #include #include #include namespace cmtk { /** \addtogroup Segmentation */ //@{ /** Label voting image combination. * This class implements combination of multiple multi-class or binary label images * using label voting. Each pixel in the output image is assigned the label that the * majority of input images assign to that pixel. Pixels with tied voting are assigned * a value of 256. The output image is allocated as 16bit short data to accommodate * this overflow value. *\attention All labels must be between 0 and 255. */ class LabelCombinationVoting { public: /// Constructor: compute label combination. LabelCombinationVoting( const std::vector& data ); /// Get result. TypedArray::SmartPtr& GetResult() { return this->m_Result; } private: /// Resulting data array. TypedArray::SmartPtr m_Result; }; } // namespace cmtk #endif // #ifndef __cmtkLabelCombinationVoting_h_included_ cmtk-3.3.1/libs/Segmentation/cmtkLeastSquaresPolynomialIntensityBiasField.cxx000066400000000000000000000102241276303427400276060ustar00rootroot00000000000000/* // // Copyright 2012, 2014 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 5189 $ // // $LastChangedDate: 2014-01-29 14:51:25 -0800 (Wed, 29 Jan 2014) $ // // $LastChangedBy: torstenrohlfing $ // */ #include "cmtkLeastSquaresPolynomialIntensityBiasField.h" #include #include #include #include namespace cmtk { LeastSquaresPolynomialIntensityBiasField::LeastSquaresPolynomialIntensityBiasField( const UniformVolume& image, const std::vector& mask, const int degree ) { const UniformVolume::CoordinateVectorType center = image.GetCenterCropRegion(); // first, compute average intensity over masked region Types::DataItem avg = 0; size_t nPixelsMask = 0; const DataGrid::RegionType region = image.GetWholeImageRegion(); for ( RegionIndexIterator it( region ); it != it.end(); ++it ) { const size_t ofs = image.GetOffsetFromIndex( it.Index() ); if ( mask[ofs] ) { avg += fabs( image.GetDataAt( ofs ) ); ++nPixelsMask; } } if ( !nPixelsMask ) throw Self::EmptyMaskException(); avg /= nPixelsMask; // set up least-squares problem const size_t nVars = PolynomialHelper::GetNumberOfMonomials( degree ); if ( nVars < 2 ) { // we ignore constant term, so we need at least 2 variables to be able to do anything meningful. for fewer, just use original data this->m_CorrectedData = image.GetData(); return; } std::vector dataVector( nPixelsMask ); Matrix2D uMatrix( nPixelsMask, nVars-1 ); // nVars-1 because we ignore zero-order term size_t cntPx = 0; for ( RegionIndexIterator it( region ); it != it.end(); ++it ) { const size_t ofs = image.GetOffsetFromIndex( it.Index() ); if ( mask[ofs] ) { const UniformVolume::CoordinateVectorType xyz = ComponentDivide( image.GetGridLocation( it.Index() ) - center, image.m_Size ); dataVector[cntPx] = image.GetDataAt( ofs ) / avg - 1.0; for ( size_t n = 1; n < nVars; ++n ) { uMatrix[cntPx][n-1] = Polynomial<4,Types::DataItem>::EvaluateMonomialAt( n, xyz[0], xyz[1], xyz[2] ); } ++cntPx; } } // solve least-squares problem const std::vector params = LeastSquares( uMatrix ).Solve( dataVector ); // apply solution this->m_CorrectedData = TypedArray::Create( image.GetData()->GetType(), image.GetNumberOfPixels() ); this->m_BiasData = TypedArray::Create( TYPE_ITEM, image.GetNumberOfPixels() ); for ( RegionIndexIterator it( region ); it != it.end(); ++it ) { const size_t ofs = image.GetOffsetFromIndex( it.Index() ); const UniformVolume::CoordinateVectorType xyz = ComponentDivide( image.GetGridLocation( it.Index() ) - center, image.m_Size ); Types::DataItem bias = 1.0; for ( size_t n = 1; n < nVars; ++n ) { bias += params[n-1] * Polynomial<4,Types::DataItem>::EvaluateMonomialAt( n, xyz[0], xyz[1], xyz[2] ); } this->m_BiasData->Set( bias, ofs ); Types::DataItem value; if ( image.GetData()->Get( value, ofs ) ) { this->m_CorrectedData->Set( value / bias, ofs ); } else { this->m_CorrectedData->SetPaddingAt( ofs ); } } } } // namespace cmtk cmtk-3.3.1/libs/Segmentation/cmtkLeastSquaresPolynomialIntensityBiasField.h000066400000000000000000000060431276303427400272370ustar00rootroot00000000000000/* // // Copyright 2012, 2014 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 5169 $ // // $LastChangedDate: 2014-01-13 16:14:38 -0800 (Mon, 13 Jan 2014) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkLeastSquaresPolynomialIntensityBiasField_h_included_ #define __cmtkLeastSquaresPolynomialIntensityBiasField_h_included_ #include #include #include #include // because this class may throw PolynomialHelper::DegreeUnsupported exception #include namespace cmtk { /** \addtogroup Segmentation */ //@{ /** Least-squares fit of a polynomial intensity bias field. */ class LeastSquaresPolynomialIntensityBiasField { public: /// This class. typedef LeastSquaresPolynomialIntensityBiasField Self; /// Smart pointer to this class. typedef SmartPointer SmartPtr; /// Smart const pointer to this class. typedef SmartConstPointer SmartConstPtr; /// Exception thrown if there are no non-zero mask pixels. class EmptyMaskException : public Exception {}; /// Constructor. LeastSquaresPolynomialIntensityBiasField( const UniformVolume& image /*!< Image for which bias field is estimated.*/, const std::vector& mask /*!< Mask vector - one bool per image pixel. Only pixels with "true" mask entry will be considered for bias estimation.*/, const int degree /*!< Polynomial degree of the estimated bias field.*/ ); /// Get estimated bias field data. TypedArray::SmartPtr GetBiasData() { return this->m_BiasData; } /// Get estimated bias field data. TypedArray::SmartConstPtr GetBiasData() const { return this->m_BiasData; } /// Get estimated bias field-corrected data. TypedArray::SmartPtr GetCorrectedData() { return this->m_CorrectedData; } /// Get estimated bias field-corrected data. TypedArray::SmartConstPtr GetCorrectedData() const { return this->m_CorrectedData; } private: /// The estimated bias field data. TypedArray::SmartPtr m_BiasData; /// The corrected image data. TypedArray::SmartPtr m_CorrectedData; }; //@} } // namespace cmtk #endif // #ifndef __cmtkLeastSquaresPolynomialIntensityBiasField_h_included_ cmtk-3.3.1/libs/Segmentation/cmtkMultiChannelEntropyMinimizationIntensityCorrectionFunctional.h000066400000000000000000000143231276303427400334230ustar00rootroot00000000000000/* // // Copyright 1997-2012 Torsten Rohlfing // // Copyright 2004-2012 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 3517 $ // // $LastChangedDate: 2011-10-27 12:42:15 -0700 (Thu, 27 Oct 2011) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkMultiChannelEntropyMinimizationIntensityCorrectionFunctional_h_included_ #define __cmtkMultiChannelEntropyMinimizationIntensityCorrectionFunctional_h_included_ #include #include #include #include #include #include namespace cmtk { /** \addtogroup Segmentation */ //@{ /** Functional to correct MR intensity bias by miniming image entropy. */ template class MultiChannelEntropyMinimizationIntensityCorrectionFunctional : /// Inherit from base class. public Functional { public: /// This class type. typedef MultiChannelEntropyMinimizationIntensityCorrectionFunctional Self; /// Class for single-channel functional type. typedef EntropyMinimizationIntensityCorrectionFunctional SingleChannelFunctionalType; /// Pointer to this class. typedef SmartPointer SmartPtr; /// Superclass type. typedef Functional Superclass; /// Constructor. MultiChannelEntropyMinimizationIntensityCorrectionFunctional( std::vector& inputImages ) : m_InputImages( inputImages ) { this->m_SingleChannelFunctionals.resize( this->m_InputImages.size() ); for ( size_t idx = 0; idx < this->m_SingleChannelFunctionals.size(); ++idx ) { this->m_SingleChannelFunctionals[idx] = SingleChannelFunctionalType::SmartPtr( new SingleChannelFunctionalType ); this->m_SingleChannelFunctionals[idx]->SetInputImage( this->m_InputImages[idx] ); } } /// Set foreground mask for all input images. virtual void SetForegroundMask( const UniformVolume& foregroundMask ) { for ( size_t idx = 0; idx < this->m_SingleChannelFunctionals.size(); ++idx ) { this->m_SingleChannelFunctionals[idx]->SetForegroundMask( foregroundMask ); } } /// Virtual destructor. virtual ~MultiChannelEntropyMinimizationIntensityCorrectionFunctional() {} /// Return parameter vector length. virtual size_t ParamVectorDim() const { return SingleChannelFunctionalType::NumberOfParameters * this->m_InputImages.size(); } /// Return parameter stepping. #pragma GCC diagnostic ignored "-Wtype-limits" virtual Types::Coordinate GetParamStep( const size_t idx, const Types::Coordinate mmStep = 1 ) const { // delegate to corresponding single-channel functional, although they are really all the same. return this->m_SingleChannelFunctionals[idx / SingleChannelFunctionalType::NumberOfParameters]->GetParamStep( idx % SingleChannelFunctionalType::NumberOfParameters ); } /// Copy parameters to the two correction polynomials. virtual void SetParamVector( CoordinateVector& v ) { this->m_ParameterVector = v; for ( size_t idx = 0; idx < this->m_SingleChannelFunctionals.size(); ++idx ) { } } /// Extract parameter vector from the two correction polynomials. virtual void GetParamVector( CoordinateVector& v ) { v = this->m_ParameterVector; } /** Fast implementation of gradient evaluation. * This function only updates either the additive of the multiplicative bias * field, depending on which gradient component is being determined. */ virtual typename Self::ReturnType EvaluateWithGradient( CoordinateVector& v, CoordinateVector& g, const Types::Coordinate step ); private: /// Keep a copy of the current parameter vector. CoordinateVector m_ParameterVector; /// Vector of input images. std::vector m_InputImages; /// Vector of single-channel functionals. std::vector m_SingleChannelFunctionals; }; /// Create functional templated over polynomial degrees. template MultiChannelEntropyMinimizationIntensityCorrectionFunctionalBase::SmartPtr CreateMultiChannelEntropyMinimizationIntensityCorrectionFunctional ( const unsigned int polynomialDegreeAdd ); /// Create functional templated over polynomial degrees. MultiChannelEntropyMinimizationIntensityCorrectionFunctionalBase::SmartPtr CreateMultiChannelEntropyMinimizationIntensityCorrectionFunctional ( const unsigned int polynomialDegreeAdd, const unsigned int polynomialDegreeMul ); /** Create functional templated over polynomial degrees with initialization from old functional. * This function creates a new functional and copies the polynomial coefficients from an existing * functional of equal or lower polynomial degrees into the correct locations of the new functional's * parameter vector. This is for incremental computation. */ MultiChannelEntropyMinimizationIntensityCorrectionFunctionalBase::SmartPtr CreateMultiChannelEntropyMinimizationIntensityCorrectionFunctional ( const unsigned int polynomialDegreeAdd, const unsigned int polynomialDegreeMul, MultiChannelEntropyMinimizationIntensityCorrectionFunctionalBase::SmartPtr oldFunctional ); //@} } // namespace cmtk #include "cmtkMultiChannelEntropyMinimizationIntensityCorrectionFunctional.txx" #endif // #ifndef __cmtkMultiChannelEntropyMinimizationIntensityCorrectionFunctional_h_included_ cmtk-3.3.1/libs/Segmentation/cmtkOverlapMeasures.cxx000066400000000000000000000162161276303427400226000ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2011 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 3097 $ // // $LastChangedDate: 2011-04-06 13:07:22 -0700 (Wed, 06 Apr 2011) $ // // $LastChangedBy: torstenrohlfing $ // */ #include #include #ifdef _OPENMP # include #endif namespace cmtk { /** \addtogroup Segmentation */ //@{ OverlapMeasures::OverlapMeasures( const std::vector& dataArrays ) { this->m_DataArrays = dataArrays; this->m_MaxLabelValue = 0; for ( size_t i = 0; i < this->m_DataArrays.size(); ++i ) { const Types::DataItemRange range = this->m_DataArrays[i]->GetRange(); this->m_MaxLabelValue = std::max( this->m_MaxLabelValue, static_cast( range.m_UpperBound ) ); } // set size limits this->m_NumberOfImages = this->m_DataArrays.size(); this->m_NumberOfPixels = this->m_DataArrays[0]->GetDataSize(); for ( size_t i = 1; i < this->m_NumberOfImages; ++i ) { this->m_NumberOfPixels = std::min( this->m_NumberOfPixels, this->m_DataArrays[i]->GetDataSize() ); } } size_t OverlapMeasures::ComputeGroupwiseOverlap ( const int firstLabel, const int numberOfLabels, double& overlapEqualWeighted, double& overlapVolumeWeighted, double& overlapInverseWeighted ) const { // compute label volumes per image std::vector< std::vector< unsigned int > > volumeTable( numberOfLabels ); for ( int label = 0; label < numberOfLabels; ++label ) { volumeTable[label].resize( this->m_NumberOfImages, 0 ); } std::vector labelExists( numberOfLabels ); std::fill( labelExists.begin(), labelExists.end(), false ); for ( size_t i = 0; i < this->m_NumberOfImages; ++i ) { for ( size_t px = 0; px < this->m_NumberOfPixels; ++px ) { Types::DataItem l; if ( this->m_DataArrays[i]->Get( l, px ) ) { const int thisLabel = static_cast(l) - firstLabel; if ( (thisLabel >= 0) && (thisLabel < numberOfLabels) ) { ++volumeTable[thisLabel][i]; labelExists[thisLabel] = true; } } } } size_t numberOfLabelsIncluded = 0; for ( int label = 0; label < numberOfLabels; ++label ) { if ( labelExists[label] ) ++numberOfLabelsIncluded; } if ( ! numberOfLabelsIncluded ) return numberOfLabelsIncluded; // run overlap computation const size_t progressPixels = 100000; Progress::Begin( 0, this->m_NumberOfPixels, progressPixels, "Overlap computation" ); #ifdef _OPENMP const size_t numberOfThreads = omp_get_max_threads(); #else const size_t numberOfThreads = 1; #endif std::vector labelsPerPixel( numberOfThreads * this->m_NumberOfImages ); const size_t sumsPerThread = numberOfLabels * this->m_NumberOfImages * (this->m_NumberOfImages-1) / 2; std::vector sumsMin( numberOfThreads * sumsPerThread, 0.0 ); std::vector sumsMax( numberOfThreads * sumsPerThread, 0.0 ); #pragma omp parallel for for ( int px = 0; px < static_cast( this->m_NumberOfPixels ); ++px ) { if ( (px % progressPixels) == 0 ) #ifdef _OPENMP if ( !omp_get_thread_num() ) #endif Progress::SetProgress( px ); #ifdef _OPENMP const size_t labelsPerPixelOfs = this->m_NumberOfImages * omp_get_thread_num(); #else const size_t labelsPerPixelOfs = 0; #endif Types::DataItem l; for ( size_t i = 0; i < this->m_NumberOfImages; ++i ) { labelsPerPixel[labelsPerPixelOfs+i] = -1; if ( this->m_DataArrays[i]->Get( l, px ) ) { const int thisLabel = static_cast(l) - firstLabel; if ( (thisLabel >= 0) && (thisLabel < numberOfLabels) ) labelsPerPixel[labelsPerPixelOfs+i] = thisLabel; } } #ifdef _OPENMP size_t ofs = sumsPerThread * omp_get_thread_num(); #else size_t ofs = 0; #endif for ( int label = 0; label < numberOfLabels; ++label ) { if ( labelExists[label] ) { for ( size_t i = 0; i < this->m_NumberOfImages; ++i ) { const int wi = (labelsPerPixel[labelsPerPixelOfs+i] == label)?1:0; for ( size_t j = 0; j < i; ++j, ++ofs ) { const int wj = (labelsPerPixel[labelsPerPixelOfs+j] == label)?1:0; sumsMin[ofs] += std::min( wi, wj ); sumsMax[ofs] += std::max( wi, wj ); } } } } } #ifdef _OPENMP size_t dstOfs = sumsPerThread; for ( size_t thread = 1; thread < numberOfThreads; ++thread ) { size_t thrOfs = 0; for ( size_t i = 0; i < sumsPerThread; ++i, ++thrOfs, ++dstOfs ) { sumsMin[thrOfs] += sumsMin[dstOfs]; sumsMax[thrOfs] += sumsMax[dstOfs]; } } #endif Progress::Done(); // rest should go fast double overlap_min_equal = 0, overlap_max_equal = 0; double overlap_min_volume = 0, overlap_max_volume = 0; double overlap_min_inverse = 0, overlap_max_inverse = 0; size_t ofs = 0; for ( int label = 0; label < numberOfLabels; ++label ) { if ( labelExists[label] ) { for ( size_t i = 0; i < this->m_NumberOfImages; ++i ) { const unsigned int vi = volumeTable[label][i]; for ( size_t j = 0; j < i; ++j, ++ofs ) { overlap_min_volume += sumsMin[ofs]; overlap_max_volume += sumsMax[ofs]; const unsigned int vij = vi + volumeTable[label][j]; if ( vij ) { overlap_min_equal += sumsMin[ofs] / vij; overlap_max_equal += sumsMax[ofs] / vij; overlap_min_inverse += (sumsMin[ofs] / vij) / vij; overlap_max_inverse += (sumsMax[ofs] / vij) / vij; } } } } } if ( overlap_max_equal ) overlapEqualWeighted = overlap_min_equal / overlap_max_equal; if ( overlap_max_volume ) overlapVolumeWeighted = overlap_min_volume / overlap_max_volume; if ( overlap_max_inverse ) overlapInverseWeighted = overlap_min_inverse / overlap_max_inverse; return numberOfLabelsIncluded; } double OverlapMeasures::ComputePairwiseOverlapMinMax ( double& overlap_min, double& overlap_max, const TypedArray::SmartPtr& data0, const TypedArray::SmartPtr& data1, const int label ) const { overlap_min = overlap_max = 0; for ( size_t i = 0; i < this->m_NumberOfPixels; ++i ) { Types::DataItem l0, l1; if ( ! data0->Get( l0, i ) ) l0 = -1; if ( ! data1->Get( l1, i ) ) l1 = -1; const int w0 = (l0 == label)?1:0; const int w1 = (l1 == label)?1:0; overlap_min += std::min( w0, w1 ); overlap_max += std::max( w0, w1 ); } return 0; } } // namespace cmtk cmtk-3.3.1/libs/Segmentation/cmtkOverlapMeasures.h000066400000000000000000000067631276303427400222330ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2011 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 3276 $ // // $LastChangedDate: 2011-07-15 16:25:02 -0700 (Fri, 15 Jul 2011) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkOverlapMeasures_h_included_ #define __cmtkOverlapMeasures_h_included_ #include #include #include namespace cmtk { /** \addtogroup Segmentation */ //@{ /// Class for overlap measures between multiple segmentations. class OverlapMeasures { public: /// Constructor: allocate local data structures and do precomputations (e.g., count labels, etc). OverlapMeasures( const std::vector& dataArrays ); /** Compute groupwise overlap with advanced options. *\return Number of labels included in computation. If this is zero, the resulting overlap values are invalid. */ size_t ComputeGroupwiseOverlap( const int firstLabel /*!< Analysis starts with this label.*/, const int numberOfLabels /*!< Analysis covers these labels*/, double& overlapEqualWeighted /*!< Equal-weighted overlap score is returned herein.*/, double& overlapVolumeWeighted /*!< Volume-weighted overlap score is returned herein.*/, double& overlapInverseWeighted /*!< Inverse volume-weighted overlap score is returned herein.*/ ) const; /** Compute simple groupwise overlap. *\return Number of labels included in computation. If this is zero, the resulting overlap values are invalid. */ size_t ComputeGroupwiseOverlap( double& overlapEqualWeighted /*!< Equal-weighted overlap score is returned herein.*/, double& overlapVolumeWeighted /*!< Volume-weighted overlap score is returned herein.*/, double& overlapInverseWeighted /*!< Inverse volume-weighted overlap score is returned herein.*/ ) const { return this->ComputeGroupwiseOverlap( 0, this->m_MaxLabelValue+1, overlapEqualWeighted, overlapVolumeWeighted, overlapInverseWeighted ); } /// Return maximum label value used in data. unsigned int GetMaxLabelValue() const { return this->m_MaxLabelValue; } private: /// Number of images. size_t m_NumberOfImages; /// Number of pixels: the minimum number over all images. size_t m_NumberOfPixels; /// Maximum label value used in the data. unsigned int m_MaxLabelValue; /// Data arrays. std::vector m_DataArrays; /// Compute pairwise overlap minimum. double ComputePairwiseOverlapMinMax( double& overlap_min, double& overlap_max, const TypedArray::SmartPtr& data0, const TypedArray::SmartPtr& data1, const int label ) const; }; //@} } // namespace cmtk #endif // #ifndef __cmtkOverlapMeasures_h_included_ cmtk-3.3.1/libs/Segmentation/cmtkSimpleLevelset.cxx000066400000000000000000000112771276303427400224220ustar00rootroot00000000000000/* // // Copyright 2010-2014 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 5268 $ // // $LastChangedDate: 2014-03-28 11:41:16 -0700 (Fri, 28 Mar 2014) $ // // $LastChangedBy: torstenrohlfing $ // */ #include "cmtkSimpleLevelset.h" #include #include #include #include #include #include #include #ifdef CMTK_BUILD_DEMO # include #endif // #ifdef CMTK_BUILD_DEMO void cmtk::SimpleLevelset ::InitializeCenteredSphere() { this->m_Levelset = this->m_Volume->CloneGrid(); this->m_Levelset->CreateDataArray( TYPE_FLOAT ); this->m_Levelset->GetData()->Fill( -1.0 ); FixedVector<3,int> center( this->m_Volume->GetDims() ); center /= 2; UniformVolumePainter painter( this->m_Levelset ); painter.DrawSphere( center, this->m_ScaleInitialSphere * ((this->m_Levelset->GetDims()[0]+this->m_Levelset->GetDims()[1]+this->m_Levelset->GetDims()[2])/6), 1.0 ); } void cmtk::SimpleLevelset ::Evolve( const int numberOfIterations, const bool forceIterations ) { const size_t numberOfPixels = this->m_Volume->GetNumberOfPixels(); size_t nInsideOld = 0, nInside = 1; Progress::Begin( 0, numberOfIterations, 1, "Levelset Evolution" ); for ( int it = 0; (it < numberOfIterations) && ((nInside!=nInsideOld) || forceIterations); ++it ) { Progress::SetProgress( it ); nInsideOld = nInside; nInside = 0; Types::DataItem insideSum = 0, outsideSum = 0; this->m_Levelset->SetData( UniformVolumeGaussianFilter( this->m_Levelset ).GetFiltered3D( this->m_FilterSigma ) ); #pragma omp parallel for reduction(+:nInside) reduction(+:insideSum) reduction(+:outsideSum) for ( int n = 0; n < static_cast( numberOfPixels ); ++n ) { if ( this->m_Levelset->GetDataAt( n ) > 0 ) { insideSum += this->m_Volume->GetDataAt( n ); ++nInside; } else outsideSum += this->m_Volume->GetDataAt( n ); } if ( nInside == 0 ) throw Self::DegenerateLevelsetException(); const size_t nOutside = numberOfPixels - nInside; if ( nOutside == 0 ) throw Self::DegenerateLevelsetException(); const Types::DataItem ratioInOut = 1.0 * nInside / nOutside; const Types::DataItem mInside = insideSum / nInside; const Types::DataItem mOutside = outsideSum / nOutside; DebugOutput( 1 ) << it << " IN: " << nInside << " " << mInside << " OUT: " << nOutside << " " << mOutside << "\r"; #pragma omp parallel for for ( int n = 0; n < static_cast( numberOfPixels ); ++n ) { const Types::DataItem data = this->m_Volume->GetDataAt( n ); const Types::DataItem zInside = fabs( mInside - data ); const Types::DataItem zOutside = fabs( mOutside - data ); Types::DataItem newLevel = this->m_Levelset->GetDataAt( n ); if ( zInside>zOutside ) { newLevel -= this->m_TimeDelta * ratioInOut; } else { newLevel += this->m_TimeDelta / ratioInOut; } this->m_Levelset->SetDataAt( std::min( this->m_LevelsetThreshold, std::max( -this->m_LevelsetThreshold, newLevel ) ), n ); } #ifdef CMTK_BUILD_DEMO UniformVolume::SmartConstPtr slice = this->m_Levelset->ExtractSlice( AXIS_Z, this->m_Levelset->m_Dims[2] / 2 ); char path[PATH_MAX]; snprintf( path, PATH_MAX, "levelset-%03d.nii", it ); VolumeIO::Write( *slice, path ); #endif } Progress::Done(); } cmtk::UniformVolume::SmartPtr& cmtk::SimpleLevelset ::GetLevelset( const bool binarize, const float threshold ) { if ( binarize ) { this->m_Levelset->GetData()->Binarize( threshold ); this->m_Levelset->SetData( TypedArray::SmartPtr( this->m_Levelset->GetData()->Convert( TYPE_BYTE ) ) ); this->m_Levelset->GetData()->SetDataClass( DATACLASS_LABEL ); } return this->m_Levelset; } cmtk-3.3.1/libs/Segmentation/cmtkSimpleLevelset.h000066400000000000000000000073631276303427400220500ustar00rootroot00000000000000/* // // Copyright 2010-2011, 2013 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4922 $ // // $LastChangedDate: 2013-10-03 14:51:17 -0700 (Thu, 03 Oct 2013) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkSimpleLevelset_h_included_ #define __cmtkSimpleLevelset_h_included_ #include #include #include #include namespace cmtk { /** \addtogroup Segmentation */ //@{ /** Class for computing a simple two-phase levelset evolution. */ class SimpleLevelset { public: /// This class. typedef SimpleLevelset Self; /// Exception that is thrown for degenerate levelsets, i.e., all foreground or all background. class DegenerateLevelsetException : public Exception {}; /// Constructor. SimpleLevelset( UniformVolume::SmartConstPtr& volume ) : m_Volume( volume ), m_ScaleInitialSphere( 1.0 ), m_TimeDelta( 1.0 ), m_LevelsetThreshold( 0.0 ) {} /// Set initial sphere scale factor. void SetScaleInitialSphere( const Types::Coordinate scale ) { this->m_ScaleInitialSphere = scale; } /// Set filter sigma parameter. void SetFilterSigma( const Units::GaussianSigma filterSigma ) { this->m_FilterSigma = filterSigma; } /// Set evolution time delta. void SetTimeDelta( const Types::Coordinate timeDelta ) { this->m_TimeDelta = timeDelta; } /// Set levelset threshold. void SetLevelsetThreshold( const Types::Coordinate levelsetThreshold ) { this->m_LevelsetThreshold = levelsetThreshold; } /// Initialize levelset with a centered sphere. void InitializeCenteredSphere(); /// Levelset evolution. virtual void Evolve( const int numberOfIterations /*!< Number of iterations */, const bool forceIterations = false /*!< If this is set, evolution continues until maximum iteration count is reached, even when convergence is detected */ ); /** Return levelset, optionally converting to a binarized byte pixel representation. *\warning If the levelset is retrieved with the "binarize" flag set, then the * levelset stored in this object will remain binarized after the call and * should be re-initialized before calling "Evolve" again. */ UniformVolume::SmartPtr& GetLevelset( const bool binarize = false, /*!< If set, levelset is binarized and converted to byte data */ const float threshold = 0.5 /*!< Threshold for optional binarization */ ); protected: /// The volume to compute a levelset segmentation for. UniformVolume::SmartConstPtr m_Volume; /// The evolving levelset. UniformVolume::SmartPtr m_Levelset; /// Initial sphere scale factor. Types::Coordinate m_ScaleInitialSphere; /// Sigma parameter of the Gaussian filter kernel. Units::GaussianSigma m_FilterSigma; /// Delta time constant. Types::Coordinate m_TimeDelta; /// Levelset threshold. Types::Coordinate m_LevelsetThreshold; }; } // namespace cmtk #endif // #ifndef __cmtkSimpleLevelset_h_included_ cmtk-3.3.1/libs/Segmentation/cmtkSimpleLevelsetCommandLine.h000066400000000000000000000063021276303427400241470ustar00rootroot00000000000000/* // // Copyright 1997-2010 Torsten Rohlfing // // Copyright 2004-2011, 2013 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 5129 $ // // $LastChangedDate: 2014-01-09 13:48:15 -0800 (Thu, 09 Jan 2014) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkSimpleLevelsetCommandLine_h_included_ #define __cmtkSimpleLevelsetCommandLine_h_included_ #include #include #include #include #include #ifdef CMTK_USE_SQLITE # include #endif namespace cmtk { /** \addtogroup Segmentation */ //@{ /** Command line interface class template for simple levelset segmentation with a particular implementation (CPU or GPU). */ template class SimpleLevelsetCommandLine : public SimpleLevelsetCommandLineBase { public: /// This class. typedef SimpleLevelsetCommandLine Self; /// The actual levelset implementation. typedef TImpl SimpleLevelsetImplementation; /// Execute levelset segmentation. void Execute() { // Instantiate programm progress indicator. cmtk::ProgressConsole progressIndicator( "LevelsetSegmentation" ); SimpleLevelsetImplementation levelset( this->m_Volume ); levelset.SetScaleInitialSphere( this->m_ScaleInitialSphere ); levelset.SetFilterSigma( cmtk::Units::GaussianSigma( this->m_FilterSigma ) ); levelset.SetTimeDelta( this->m_TimeDelta ); levelset.SetLevelsetThreshold( this->m_LevelsetThreshold ); levelset.InitializeCenteredSphere(); try { levelset.Evolve( this->m_NumberOfIterations, this->m_ForceIterations ); } catch ( const SimpleLevelset::DegenerateLevelsetException& ) { StdErr << "ERROR: degenerate levelset (all foreground or all background).\n"; throw ExitException( 1 ); } cmtk::VolumeIO::Write( *levelset.GetLevelset( this->m_Binarize ), this->m_OutFile ); #ifdef CMTK_USE_SQLITE if ( this->m_UpdateDB ) { try { cmtk::ImageXformDB db( this->m_UpdateDB ); db.AddImage( this->m_OutFile, this->m_InFile ); } catch ( const cmtk::SQLite::Exception& ex ) { StdErr << "ERROR: cmtk::SQLite threw exception - " << ex.what() << "\n"; } } #endif } }; } // namespace cmtk #endif // #ifndef __cmtkSimpleLevelsetCommandLine_h_included_ cmtk-3.3.1/libs/Segmentation/cmtkSimpleLevelsetCommandLineBase.cxx000066400000000000000000000107021276303427400253140ustar00rootroot00000000000000/* // // Copyright 1997-2010 Torsten Rohlfing // // Copyright 2004-2010, 2013 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4835 $ // // $LastChangedDate: 2013-09-12 15:48:47 -0700 (Thu, 12 Sep 2013) $ // // $LastChangedBy: torstenrohlfing $ // */ #include "cmtkSimpleLevelsetCommandLineBase.h" #include #include cmtk::SimpleLevelsetCommandLineBase::SimpleLevelsetCommandLineBase() : m_ScaleInitialSphere( 1.0 ), m_FilterSigma( 2.0 ), m_TimeDelta( 0.1 ), m_LevelsetThreshold( 1.0 ), m_NumberOfIterations( 100 ), m_ForceIterations( false ), m_Binarize( false ), m_CommandLine( cmtk::CommandLine::PROPS_XML ) { #ifdef CMTK_USE_SQLITE this->m_UpdateDB = NULL; #endif this->m_CommandLine.SetProgramInfo( CommandLine::PRG_TITLE, "Levelset segmentation" ); this->m_CommandLine.SetProgramInfo( CommandLine::PRG_DESCR, "Levelset-type segmentation of foreground/background using minimum regional variance energy" ); this->m_CommandLine.SetProgramInfo( CommandLine::PRG_CATEG, "CMTK.Segmentation" ); typedef CommandLine::Key Key; this->m_CommandLine.AddSwitch( Key( 'b', "binarize" ), &this->m_Binarize, true, "Binarize levelset and write as byte mask, rather than write floating-point levelset function itself." ); this->m_CommandLine.BeginGroup( "Levelset Initialization", "These parameters control the initialization of the levelset function" )->SetProperties( CommandLine::PROPS_ADVANCED ); this->m_CommandLine.AddOption( Key( "scale-initial-sphere" ), &this->m_ScaleInitialSphere, "Scale factor to reduce or increase the size of the initial foreground region sphere." ); this->m_CommandLine.EndGroup(); this->m_CommandLine.BeginGroup( "Levelset Evolution Parameters", "These parameters control the evolution of the levelset function" )->SetProperties( CommandLine::PROPS_ADVANCED ); this->m_CommandLine.AddOption( Key( 'n', "iterations" ), &this->m_NumberOfIterations, "Maximum number of iterations" ); this->m_CommandLine.AddSwitch( Key( 'f', "force-iterations" ), &this->m_ForceIterations, true, "Force given number of iterations, even when convergence has been detected" ); this->m_CommandLine.AddOption( Key( 's', "filter-sigma" ), &this->m_FilterSigma, "Gaussian filter sigma in world coordinate units (e.g., mm)" ); this->m_CommandLine.AddOption( Key( 'd', "delta" ), &this->m_TimeDelta, "Time constant for levelset evolution; must be > 0; larger is faster" ); this->m_CommandLine.AddOption( Key( 't', "levelset-threshold" ), &this->m_LevelsetThreshold, "Levelset threshold: levelset function is truncated at +/- this value" ); this->m_CommandLine.EndGroup(); #ifdef CMTK_USE_SQLITE this->m_CommandLine.BeginGroup( "Database", "Image/Transformation Database" ); this->m_CommandLine.AddOption( Key( "db" ), &this->m_UpdateDB, "Path to image/transformation database that should be updated with the newly created image." ); this->m_CommandLine.EndGroup(); #endif this->m_CommandLine.AddParameter( &this->m_InFile, "InputImage", "Input image path" )->SetProperties( CommandLine::PROPS_IMAGE ); this->m_CommandLine.AddParameter( &this->m_OutFile, "OutputImage", "Output image path" )->SetProperties( CommandLine::PROPS_IMAGE | CommandLine::PROPS_LABELS | CommandLine::PROPS_OUTPUT ); } int cmtk::SimpleLevelsetCommandLineBase::Init( const int argc, const char* argv[] ) { try { this->m_CommandLine.Parse( argc, argv ); } catch ( const cmtk::CommandLine::Exception& ex ) { cmtk::StdErr << ex; return 1; } this->m_Volume = cmtk::VolumeIO::ReadOriented( this->m_InFile ); if ( !this->m_Volume ) return 1; return 0; } cmtk-3.3.1/libs/Segmentation/cmtkSimpleLevelsetCommandLineBase.h000066400000000000000000000056411276303427400247470ustar00rootroot00000000000000/* // // Copyright 1997-2010 Torsten Rohlfing // // Copyright 2004-2011 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4834 $ // // $LastChangedDate: 2013-09-12 15:15:51 -0700 (Thu, 12 Sep 2013) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkSimpleLevelsetCommandLineBase_h_included_ #define __cmtkSimpleLevelsetCommandLineBase_h_included_ #include #include #include namespace cmtk { /** \addtogroup Segmentation */ //@{ /** Command line interface base class for simple levelset segmentation. */ class SimpleLevelsetCommandLineBase { public: /// This class. typedef SimpleLevelsetCommandLineBase Self; /// Default constructor. SimpleLevelsetCommandLineBase(); /// Initialize from command line arguments. int Init( const int argc, const char* argv[] ); /// Reference to command line object. CommandLine& GetCommandLine() { return this->m_CommandLine; } protected: /// Initial sphere scale factor. Types::Coordinate m_ScaleInitialSphere; /// Gaussian smoothing kernel sigma in mm. Types::Coordinate m_FilterSigma; /// Levelset evolution time constant. Types::Coordinate m_TimeDelta; /// Levelset threshold: the levelset function is truncated to plus/minus this value at each iteration. Types::Coordinate m_LevelsetThreshold; /// Number of levelset evolution iterations. int m_NumberOfIterations; /// Flag to force given number of iterations even when premature (discrete) convergence is detected. bool m_ForceIterations; /// Binarize levelset before output. bool m_Binarize; /// Input image path. std::string m_InFile; /// Output image path. std::string m_OutFile; /// The input image volume. UniformVolume::SmartConstPtr m_Volume; #ifdef CMTK_USE_SQLITE /// Update this image/transformation database with the newly created levelset image. const char* m_UpdateDB; #endif private: /// The command line parser object. cmtk::CommandLine m_CommandLine; }; } // namespace cmtk #endif // #ifndef __cmtkSimpleLevelsetCommandLineBase_h_included_ cmtk-3.3.1/libs/Segmentation/cmtkSphereDetectionBipolarMatchedFilterFFT.cxx000066400000000000000000000127351276303427400270570ustar00rootroot00000000000000/* // // Copyright 2012, 2013 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4928 $ // // $LastChangedDate: 2013-10-04 10:16:38 -0700 (Fri, 04 Oct 2013) $ // // $LastChangedBy: torstenrohlfing $ // */ #include "cmtkSphereDetectionBipolarMatchedFilterFFT.h" cmtk::SphereDetectionBipolarMatchedFilterFFT::SphereDetectionBipolarMatchedFilterFFT( const UniformVolume& image ) : m_NumberOfPixels( image.GetNumberOfPixels() ), m_ImageDims( image.m_Dims ), m_PixelSize( image.m_Delta ) { this->m_ImageFT = static_cast( fftw_malloc( sizeof( fftw_complex ) * this->m_NumberOfPixels ) ); this->m_FilterFT = static_cast( fftw_malloc( sizeof( fftw_complex ) * this->m_NumberOfPixels ) ); this->m_PlanFilter = fftw_plan_dft_3d( this->m_ImageDims[2], this->m_ImageDims[1], this->m_ImageDims[0], this->m_FilterFT, this->m_FilterFT, FFTW_FORWARD, FFTW_ESTIMATE ); this->m_PlanBackward = fftw_plan_dft_3d( this->m_ImageDims[2], this->m_ImageDims[1], this->m_ImageDims[0], this->m_FilterFT, this->m_FilterFT, FFTW_BACKWARD, FFTW_ESTIMATE ); // initialize image FT fftw_plan plan_image = fftw_plan_dft_3d( this->m_ImageDims[2], this->m_ImageDims[1], this->m_ImageDims[0], this->m_ImageFT, this->m_ImageFT, FFTW_FORWARD, FFTW_ESTIMATE ); for ( size_t n = 0; n < this->m_NumberOfPixels; ++n ) { this->m_ImageFT[n][0] = image.GetDataAt( n ); this->m_ImageFT[n][1] = 0; } fftw_execute( plan_image ); fftw_destroy_plan( plan_image ); } cmtk::SphereDetectionBipolarMatchedFilterFFT::~SphereDetectionBipolarMatchedFilterFFT() { fftw_destroy_plan( this->m_PlanBackward ); fftw_destroy_plan( this->m_PlanFilter ); fftw_free( this->m_FilterFT ); fftw_free( this->m_ImageFT ); } inline void multiply( fftw_complex& lhs, const fftw_complex& rhs ) { fftw_complex product; product[0] = lhs[0]*rhs[0] - lhs[1]*rhs[1]; product[1] = lhs[1]*rhs[0] + lhs[0]*rhs[1]; lhs[0] = product[0]; lhs[1] = product[1]; } cmtk::TypedArray::SmartPtr cmtk::SphereDetectionBipolarMatchedFilterFFT::GetFilteredImageData( const Types::Coordinate sphereRadius, const int marginWidth ) { memset( this->m_FilterFT, 0, sizeof( fftw_complex ) * this->m_NumberOfPixels ); const size_t cnt = this->MakeFilter( sphereRadius, marginWidth ); if ( cnt ) { // compute filter kernel FT fftw_execute( this->m_PlanFilter ); // apply FT'ed filter to FT'ed image for ( size_t n = 0; n < this->m_NumberOfPixels; ++n ) { this->m_FilterFT[n][1] *= -1; // correlation is multiplication with complex conjugate of filter, but we can also just conjugate the image multiply( this->m_FilterFT[n], this->m_ImageFT[n] ); this->m_FilterFT[n][0] /= cnt; this->m_FilterFT[n][1] /= cnt; } // transform filtered spectral data back into space domain fftw_execute( this->m_PlanBackward ); } // return filtered magnitude image TypedArray::SmartPtr result = TypedArray::Create( cmtk::TYPE_ITEM, this->m_NumberOfPixels ); for ( size_t n = 0; n < this->m_NumberOfPixels; ++n ) { result->Set( FFTW::Magnitude( this->m_FilterFT[n] ) / this->m_NumberOfPixels, n ); } return result; } size_t cmtk::SphereDetectionBipolarMatchedFilterFFT::MakeFilter( const Types::Coordinate sphereRadius, const int marginWidth ) { const int nRadius[3] = { 1 + marginWidth + static_cast( sphereRadius / this->m_PixelSize[0] ), 1 + marginWidth + static_cast( sphereRadius / this->m_PixelSize[1] ), 1 + marginWidth + static_cast( sphereRadius / this->m_PixelSize[2] ) }; // count number of non-zero filter elements for rough normalization size_t cnt = 0; // create a filter kernel for the sphere for ( int k = 0; k < nRadius[2]; ++k ) for ( int j = 0; j < nRadius[1]; ++j ) for ( int i = 0; i < nRadius[0]; ++i ) { const cmtk::Types::Coordinate distance = sqrt( cmtk::MathUtil::Square( k * this->m_PixelSize[2] ) + cmtk::MathUtil::Square( j * this->m_PixelSize[1] ) + cmtk::MathUtil::Square( i * this->m_PixelSize[0] ) ); if ( distance <= sphereRadius+marginWidth ) { cmtk::Types::DataItem value = 0; if ( distance >= sphereRadius-marginWidth ) { value = 1; } if ( (distance > sphereRadius) ) { value = -1; } if ( value != 0 ) { for ( int kk = k; kk < this->m_ImageDims[2]; kk += (this->m_ImageDims[2]-1-2*k) ) for ( int jj = j; jj < this->m_ImageDims[1]; jj += (this->m_ImageDims[1]-1-2*j) ) for ( int ii = i; ii < this->m_ImageDims[0]; ii += (this->m_ImageDims[0]-1-2*i) ) { this->m_FilterFT[ii+this->m_ImageDims[0] * (jj+this->m_ImageDims[1]*kk)][0] = value; ++cnt; } } } } return cnt; } cmtk-3.3.1/libs/Segmentation/cmtkSphereDetectionBipolarMatchedFilterFFT.h000066400000000000000000000076001276303427400264770ustar00rootroot00000000000000/* // // Copyright 2012 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4217 $ // // $LastChangedDate: 2012-04-18 11:18:25 -0700 (Wed, 18 Apr 2012) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkSphereDetectionBipolarMatchedFilterFFT_h_included_ #define __cmtkSphereDetectionBipolarMatchedFilterFFT_h_included_ #include #include #include namespace cmtk { /** Detect spheres in an image using FFT-based matched bipolar filter. * An object of this class can be used to detect spheres of different sizes with only a single FFT applied to the test image (each sphere size * does require a different filter kernel and thus a repeated FFT of the kernel). * * The filter kernel is bipolar, i.e., +1 inside the sphere and -1 outside the sphere, each within a user-provided margin inside and outside the * sphere surface. This makes the filter robust to intensity differences across the images, although probably not to the same degree as a truely * self-normalizing filter (e.g., Padberg's FFT-based NCC). *\note This class requires CMTK to be configured with FFTW3 support ("CMTK_USE_FFTW" CMake option). *\todo The current implementation does not take advantage of the real-valued image and filter data, which could be used to reduce the storage * requirement of the FT data (and probably the computational cost of the transform) by almost 50%. On the other hand, capitalizing on these * savings would either require out-of-place, rather than in-place, transforms, or substantially complicate memory layout of the input data. */ class SphereDetectionBipolarMatchedFilterFFT { public: /// Constructor: initialize FFTW plans and compute image FT. SphereDetectionBipolarMatchedFilterFFT( const UniformVolume& image ); /// Destructor: destroy FFTW plans and de-allocate transformed image memories. virtual ~SphereDetectionBipolarMatchedFilterFFT(); /// Get image filtered with spherical matched filter kernel. cmtk::TypedArray::SmartPtr GetFilteredImageData( const Types::Coordinate sphereRadius /*!< Radius of detected spheres in world coordinate units (e.g., mm) */, const int marginWidth = 1 /*!< Half width of the filter margin in pixels: positive filter coefficients in a band of this width inside radius, negative coeffiecients outside radius.*/ ); private: /// Image number of pixels. size_t m_NumberOfPixels; /// Image dimensions. DataGrid::IndexType m_ImageDims; /// Image pixel size. UniformVolume::SpaceVectorType m_PixelSize; /// The Fourier-transformed image. fftw_complex* m_ImageFT; /// The Fourier-transformed matched filter. fftw_complex* m_FilterFT; /// The filter FFT plan. fftw_plan m_PlanFilter; /// The backward (filtered data to space domain) FFT plan. fftw_plan m_PlanBackward; /** Make the filter kernel. *\return Number of non-zero elements in the filter kernel. */ size_t MakeFilter( const Types::Coordinate sphereRadius, const int marginWidth ); }; } // namespace cmtk #endif // #ifndef __cmtkSphereDetectionBipolarMatchedFilterFFT_h_included_ cmtk-3.3.1/libs/Segmentation/cmtkSphereDetectionNormalizedBipolarMatchedFilterFFT.cxx000066400000000000000000000214641276303427400311030ustar00rootroot00000000000000/* // // Copyright 2012-2014 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 5247 $ // // $LastChangedDate: 2014-03-20 14:46:13 -0700 (Thu, 20 Mar 2014) $ // // $LastChangedBy: torstenrohlfing $ // */ #include "cmtkSphereDetectionNormalizedBipolarMatchedFilterFFT.h" cmtk::SphereDetectionNormalizedBipolarMatchedFilterFFT::SphereDetectionNormalizedBipolarMatchedFilterFFT( const UniformVolume& image ) : m_NumberOfPixels( image.GetNumberOfPixels() ), m_ImageDims( image.m_Dims ), m_PixelSize( image.m_Delta ), m_SphereRadius( 0 ), m_MarginWidth( -1 ), m_SumFilter( 0.0 ), m_SumFilterMask( 0.0 ) { this->m_ImageFT = static_cast( fftw_malloc( sizeof( fftw_complex ) * this->m_NumberOfPixels ) ); this->m_ImageSquareFT = static_cast( fftw_malloc( sizeof( fftw_complex ) * this->m_NumberOfPixels ) ); this->m_FilterFT = static_cast( fftw_malloc( sizeof( fftw_complex ) * this->m_NumberOfPixels ) ); this->m_FilterMaskFT = static_cast( fftw_malloc( sizeof( fftw_complex ) * this->m_NumberOfPixels ) ); this->m_FilterMaskFT2 = static_cast( fftw_malloc( sizeof( fftw_complex ) * this->m_NumberOfPixels ) ); this->m_PlanFilter = fftw_plan_dft_3d( this->m_ImageDims[2], this->m_ImageDims[1], this->m_ImageDims[0], this->m_FilterFT, this->m_FilterFT, FFTW_FORWARD, FFTW_ESTIMATE ); this->m_PlanFilterMask = fftw_plan_dft_3d( this->m_ImageDims[2], this->m_ImageDims[1], this->m_ImageDims[0], this->m_FilterMaskFT, this->m_FilterMaskFT, FFTW_FORWARD, FFTW_ESTIMATE ); this->m_PlanBackward = fftw_plan_dft_3d( this->m_ImageDims[2], this->m_ImageDims[1], this->m_ImageDims[0], this->m_FilterFT, this->m_FilterFT, FFTW_BACKWARD, FFTW_ESTIMATE ); this->m_PlanBackwardMask = fftw_plan_dft_3d( this->m_ImageDims[2], this->m_ImageDims[1], this->m_ImageDims[0], this->m_FilterMaskFT, this->m_FilterMaskFT, FFTW_BACKWARD, FFTW_ESTIMATE ); this->m_PlanBackwardMask2 = fftw_plan_dft_3d( this->m_ImageDims[2], this->m_ImageDims[1], this->m_ImageDims[0], this->m_FilterMaskFT2, this->m_FilterMaskFT2, FFTW_BACKWARD, FFTW_ESTIMATE ); // initialize image FT fftw_plan plan_image = fftw_plan_dft_3d( this->m_ImageDims[2], this->m_ImageDims[1], this->m_ImageDims[0], this->m_ImageFT, this->m_ImageFT, FFTW_FORWARD, FFTW_ESTIMATE ); fftw_plan plan_image_square = fftw_plan_dft_3d( this->m_ImageDims[2], this->m_ImageDims[1], this->m_ImageDims[0], this->m_ImageSquareFT, this->m_ImageSquareFT, FFTW_FORWARD, FFTW_ESTIMATE ); #pragma omp parallel for for ( int n = 0; n < static_cast( this->m_NumberOfPixels ); ++n ) { this->m_ImageFT[n][0] = image.GetDataAt( n ); this->m_ImageFT[n][1] = 0; this->m_ImageSquareFT[n][0] = this->m_ImageFT[n][0] * this->m_ImageFT[n][0]; this->m_ImageSquareFT[n][1] = 0; } fftw_execute( plan_image ); fftw_execute( plan_image_square ); fftw_destroy_plan( plan_image ); fftw_destroy_plan( plan_image_square ); } cmtk::SphereDetectionNormalizedBipolarMatchedFilterFFT::~SphereDetectionNormalizedBipolarMatchedFilterFFT() { fftw_destroy_plan( this->m_PlanBackward ); fftw_destroy_plan( this->m_PlanBackwardMask ); fftw_destroy_plan( this->m_PlanBackwardMask2 ); fftw_destroy_plan( this->m_PlanFilter ); fftw_destroy_plan( this->m_PlanFilterMask ); fftw_free( this->m_FilterMaskFT2 ); fftw_free( this->m_FilterMaskFT ); fftw_free( this->m_FilterFT ); fftw_free( this->m_ImageFT ); } cmtk::TypedArray::SmartPtr cmtk::SphereDetectionNormalizedBipolarMatchedFilterFFT::GetFilteredImageData( const Types::Coordinate sphereRadius, const int marginWidth ) { // check if the requested kernel parameters are the same we previously used. if ( (sphereRadius == this->m_SphereRadius) && (marginWidth == this->m_MarginWidth ) ) return this->m_FilterResponse; this->m_SphereRadius = sphereRadius; this->m_MarginWidth = marginWidth; memset( this->m_FilterFT, 0, sizeof( fftw_complex ) * this->m_NumberOfPixels ); memset( this->m_FilterMaskFT, 0, sizeof( fftw_complex ) * this->m_NumberOfPixels ); this->MakeFilter( sphereRadius, marginWidth ); const Types::DataItem denom2 = sqrt( this->m_SumFilterMask - (this->m_SumFilter*this->m_SumFilter) / this->m_SumFilterMask ); // compute filter kernel FT fftw_execute( this->m_PlanFilter ); fftw_execute( this->m_PlanFilterMask ); // apply FT'ed filter to FT'ed image #pragma omp parallel for for ( int n = 0; n < static_cast( this->m_NumberOfPixels ); ++n ) { this->m_FilterMaskFT2[n][0] = this->m_FilterMaskFT[n][0]; this->m_FilterMaskFT2[n][1] = this->m_FilterMaskFT[n][1]; FFTW::MultiplyInPlace( this->m_FilterMaskFT[n], this->m_ImageFT[n] ); FFTW::MultiplyInPlace( this->m_FilterMaskFT2[n], this->m_ImageSquareFT[n] ); FFTW::MultiplyInPlace( this->m_FilterFT[n], this->m_ImageFT[n] ); } // transform filtered spectral data back into space domain fftw_execute( this->m_PlanBackward ); fftw_execute( this->m_PlanBackwardMask ); fftw_execute( this->m_PlanBackwardMask2 ); const double invNumberOfPixels = 1.0 / this->m_NumberOfPixels; #pragma omp parallel for for ( int n = 0; n < static_cast( this->m_NumberOfPixels ); ++n ) { for ( int c = 0; c < 2; ++c ) { this->m_FilterMaskFT[n][c] *= invNumberOfPixels; this->m_FilterMaskFT2[n][c] *= invNumberOfPixels; this->m_FilterFT[n][c] *= invNumberOfPixels; } } // return filter response data if ( !this->m_FilterResponse ) this->m_FilterResponse = TypedArray::Create( cmtk::TYPE_ITEM, this->m_NumberOfPixels ); #pragma omp parallel for for ( int n = 0; n < static_cast( this->m_NumberOfPixels ); ++n ) { const Types::DataItem num = FFTW::Magnitude( this->m_FilterFT[n] ) - (FFTW::Magnitude( this->m_FilterMaskFT[n] ) * this->m_SumFilter / this->m_SumFilterMask ); const Types::DataItem denom1 = sqrt( std::max( 0, FFTW::Magnitude( this->m_FilterMaskFT2[n] ) - FFTW::SumOfSquares( this->m_FilterMaskFT[n] ) / this->m_SumFilterMask ) ); if ( (num == 0) || (denom1 ==0) ) this->m_FilterResponse->Set( 0, n ); else this->m_FilterResponse->Set( num / (denom1*denom2), n ); } return this->m_FilterResponse; } void cmtk::SphereDetectionNormalizedBipolarMatchedFilterFFT::MakeFilter( const Types::Coordinate sphereRadius, const int marginWidth ) { const int nRadius[3] = { 1 + marginWidth + static_cast( sphereRadius / this->m_PixelSize[0] ), 1 + marginWidth + static_cast( sphereRadius / this->m_PixelSize[1] ), 1 + marginWidth + static_cast( sphereRadius / this->m_PixelSize[2] ) }; Types::DataItem sumFilter = 0, sumFilterMask = 0; const Types::Coordinate sphereRadiusSq = MathUtil::Square( sphereRadius ); const Types::Coordinate outerRadiusSq = MathUtil::Square( sphereRadius+marginWidth ); // create a filter kernel for the sphere #pragma omp parallel for reduction(+:sumFilter) reduction(+:sumFilterMask) for ( int k = 0; k < nRadius[2]; ++k ) { const Types::Coordinate dzSq = MathUtil::Square( k * this->m_PixelSize[2] ); for ( int j = 0; j < nRadius[1]; ++j ) { const Types::Coordinate dyzSq = dzSq + MathUtil::Square( j * this->m_PixelSize[1] ); for ( int i = 0; i < nRadius[0]; ++i ) { const Types::Coordinate distanceSq = dyzSq + MathUtil::Square( i * this->m_PixelSize[0] ); if ( distanceSq <= outerRadiusSq ) { Types::DataItem value = 1; if ( (distanceSq > sphereRadiusSq) ) { value = -1; } for ( int kk = k; kk < this->m_ImageDims[2]; kk += (this->m_ImageDims[2]-1-2*k) ) for ( int jj = j; jj < this->m_ImageDims[1]; jj += (this->m_ImageDims[1]-1-2*j) ) for ( int ii = i; ii < this->m_ImageDims[0]; ii += (this->m_ImageDims[0]-1-2*i) ) { const size_t ofs = ii+this->m_ImageDims[0] * (jj+this->m_ImageDims[1]*kk); this->m_FilterFT[ofs][0] = value; this->m_FilterMaskFT[ofs][0] = 1; sumFilter += value; sumFilterMask += 1; } } } } } this->m_SumFilter = sumFilter; this->m_SumFilterMask = sumFilterMask; } cmtk-3.3.1/libs/Segmentation/cmtkSphereDetectionNormalizedBipolarMatchedFilterFFT.h000066400000000000000000000127301276303427400305240ustar00rootroot00000000000000/* // // Copyright 2012, 2013 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4770 $ // // $LastChangedDate: 2013-05-28 14:10:06 -0700 (Tue, 28 May 2013) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkSphereDetectionNormalizedBipolarMatchedFilterFFT_h_included_ #define __cmtkSphereDetectionNormalizedBipolarMatchedFilterFFT_h_included_ #include #include #include namespace cmtk { /** Detect spheres in an image using FFT-based matched bipolar filter. * An object of this class can be used to detect spheres of different sizes with only two FFTs applied to the test image and its square. * Each detected sphere size does require a different filter kernel and thus three repeated FFTs of the kernel, its mask, and its square. * * The filter kernel is bipolar, i.e., +1 inside the sphere and -1 outside the sphere, each within a user-provided margin inside and outside the * sphere surface. This makes the filter robust to intensity differences across the images. * * Because the filter values are either +1 or -1 or 0, the squared filter is identical to the filter mask. This simplifies the computation and * saves FT of the squared filter. * *\see D. Padfield, "Masked object registration in the Fourier domain," IEEE Transactions on Image Processing, vol. 21, no. 5, pp. 2706-2718, 2012. * http://dx.doi.org/10.1109/TIP.2011.2181402 * http://ieeexplore.ieee.org/stamp/stamp.jsp?tp=&arnumber=6111478&isnumber=4358840 * *\note This class requires CMTK to be configured with FFTW3 support ("CMTK_USE_FFTW" CMake option). *\todo The current implementation does not take advantage of the real-valued image and filter data, which could be used to reduce the storage * requirement of the FT data (and probably the computational cost of the transform) by almost 50%. On the other hand, capitalizing on these * savings would either require out-of-place, rather than in-place, transforms, or substantially complicate memory layout of the input data. * *\todo Currently, the FFT calls within the class do not take advantage of the redundancy in the real-to-complex FT applied to the * real-valued image and mask. Properly considering this could potentially save about 1/2 of memory and CPU time. */ class SphereDetectionNormalizedBipolarMatchedFilterFFT { public: /// Constructor: initialize FFTW plans and compute image FT. SphereDetectionNormalizedBipolarMatchedFilterFFT( const UniformVolume& image ); /// Destructor: destroy FFTW plans and de-allocate transformed image memories. virtual ~SphereDetectionNormalizedBipolarMatchedFilterFFT(); /// Get image filtered with spherical matched filter kernel. cmtk::TypedArray::SmartPtr GetFilteredImageData( const Types::Coordinate sphereRadius /*!< Radius of detected spheres in world coordinate units (e.g., mm) */, const int marginWidth = 1 /*!< Half width of the filter margin in pixels: positive filter coefficients in a band of this width inside radius, negative coeffiecients outside radius.*/ ); private: /// Image number of pixels. size_t m_NumberOfPixels; /// Image dimensions. DataGrid::IndexType m_ImageDims; /// Image pixel size. UniformVolume::SpaceVectorType m_PixelSize; /// Store previous sphere radius to avoid unnecessary recomputation. Types::Coordinate m_SphereRadius; /// Store previous filter margin to avoid unnecessary recomputation. int m_MarginWidth; /// Store computed filter response. TypedArray::SmartPtr m_FilterResponse; /// The Fourier-transformed image. fftw_complex* m_ImageFT; /// The Fourier-transformed squared image. fftw_complex* m_ImageSquareFT; /// The Fourier-transformed matched filter. fftw_complex* m_FilterFT; /// The Fourier-transformed matched filter mask. fftw_complex* m_FilterMaskFT; /// Copy of the Fourier-transformed matched filter mask for computing a separate product. fftw_complex* m_FilterMaskFT2; /// The filter FFT plan. fftw_plan m_PlanFilter; /// The filter mask FFT plan. fftw_plan m_PlanFilterMask; /// The backward (filtered data to space domain) FFT plan. fftw_plan m_PlanBackward; /// The backward FFT plan for the mask. fftw_plan m_PlanBackwardMask; /// The backward FFT plan for the copy of the multiplied mask. fftw_plan m_PlanBackwardMask2; /// Sum of filter elements. Types::DataItem m_SumFilter; /// Sum of filter mask elements (number of non-zero elements). Types::DataItem m_SumFilterMask; /** Make the filter kernel. */ void MakeFilter( const Types::Coordinate sphereRadius, const int marginWidth ); }; } // namespace cmtk #endif // #ifndef __cmtkSphereDetectionNormalizedBipolarMatchedFilterFFT_h_included_ cmtk-3.3.1/libs/Segmentation/doxygen.h000066400000000000000000000023611276303427400177020ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2011 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 2734 $ // // $LastChangedDate: 2011-01-14 10:23:45 -0800 (Fri, 14 Jan 2011) $ // // $LastChangedBy: torstenrohlfing $ // */ /** \defgroup Segmentation cmtkSegmentation Library * This library provides classes for atlas-based image segmentation and * correction of MR intensity bias field artifacts. */ cmtk-3.3.1/libs/System/000077500000000000000000000000001276303427400147015ustar00rootroot00000000000000cmtk-3.3.1/libs/System/CMakeLists.txt000066400000000000000000000061231276303427400174430ustar00rootroot00000000000000## ## Copyright 1997-2010 Torsten Rohlfing ## ## Copyright 2004-2013 SRI International ## ## This file is part of the Computational Morphometry Toolkit. ## ## http://www.nitrc.org/projects/cmtk/ ## ## The Computational Morphometry Toolkit is free software: you can ## redistribute it and/or modify it under the terms of the GNU General Public ## License as published by the Free Software Foundation, either version 3 of ## the License, or (at your option) any later version. ## ## The Computational Morphometry Toolkit is distributed in the hope that it ## will be useful, but WITHOUT ANY WARRANTY; without even the implied ## warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License along ## with the Computational Morphometry Toolkit. If not, see ## . ## ## $Revision: 4672 $ ## ## $LastChangedDate: 2013-01-10 15:36:19 -0800 (Thu, 10 Jan 2013) $ ## ## $LastChangedBy: torstenrohlfing $ ## # Sources of non-templated classes. SET(cmtkSystem_SRCS cmtkCommandLine.cxx cmtkCommandLineCallback.cxx cmtkCommandLineItem.cxx cmtkCommandLineKeyToAction.cxx cmtkCommandLineKeyToActionEnum.cxx cmtkCommandLineKeyToActionSingle.cxx cmtkCommandLineNonOptionParameter.cxx cmtkCommandLineNonOptionParameterVector.cxx cmtkCommandLineHelp.cxx cmtkCommandLineMan.cxx cmtkCommandLineXML.cxx cmtkCommandLineWiki.cxx cmtkCompressedStream.cxx cmtkCompressedStreamFile.cxx cmtkCompressedStreamPipe.cxx cmtkCompressedStreamReaderBase.cxx cmtkCompressedStreamZlib.cxx cmtkConsole.cxx cmtkFileUtils.cxx cmtkMemory.cxx cmtkMountPoints.cxx cmtkProgress.cxx cmtkProgressConsole.cxx cmtkRegressionTracker.cxx cmtkStackBacktrace cmtkStrUtility.cxx cmtkThreads.cxx cmtkThreadPoolThreads.cxx cmtkThreadSemaphore.cxx cmtkTimers.cxx ) IF(CMTK_USE_BZIP2) LIST(APPEND cmtkSystem_SRCS cmtkCompressedStreamBZip2.cxx) ENDIF(CMTK_USE_BZIP2) IF(CMTK_USE_LZMA) LIST(APPEND cmtkSystem_SRCS cmtkCompressedStreamLZMA.cxx) ENDIF(CMTK_USE_LZMA) IF(CMTK_USE_GCD) LIST(APPEND cmtkSystem_SRCS cmtkSafeCounterGCD.cxx cmtkThreadPoolGCD.cxx) ENDIF(CMTK_USE_GCD) ADD_LIBRARY(cmtkSystem ${cmtkSystem_SRCS}) SET(cmtkSystem_LIBS ${CMTK_BZIP2_LIBS} ${CMTK_LZMA_LIBS} ${CMTK_ZLIB_LIB} ${ZLIB_LIBRARIES} ${MXML_LIBRARIES}) IF(CMTK_USE_FFTW_FOUND) LIST(APPEND cmtkSystem_LIBS ${CMTK_FFTW_LIBRARIES}) ENDIF(CMTK_USE_FFTW_FOUND) TARGET_LINK_LIBRARIES(cmtkSystem ${cmtkSystem_LIBS}) IF(CMTK_LIBRARY_PROPERTIES) SET_TARGET_PROPERTIES(cmtkSystem PROPERTIES ${CMTK_LIBRARY_PROPERTIES}) ENDIF(CMTK_LIBRARY_PROPERTIES) INSTALL(TARGETS cmtkSystem RUNTIME DESTINATION ${CMTK_INSTALL_BIN_DIR} COMPONENT runtime LIBRARY DESTINATION ${CMTK_INSTALL_LIB_DIR} COMPONENT runtime ARCHIVE DESTINATION ${CMTK_INSTALL_LIB_DIR} COMPONENT libraries) FILE(GLOB files_h "${CMAKE_CURRENT_SOURCE_DIR}/*.h") FILE(GLOB files_txx "${CMAKE_CURRENT_SOURCE_DIR}/*.txx") INSTALL(FILES ${files_h} ${files_txx} DESTINATION ${CMTK_INSTALL_INCLUDE_DIR}/System COMPONENT headers) cmtk-3.3.1/libs/System/cmtkCannotBeCopied.h000066400000000000000000000034231276303427400205500ustar00rootroot00000000000000/* // // Copyright 2010 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 1411 $ // // $LastChangedDate: 2010-04-02 10:36:26 -0700 (Fri, 02 Apr 2010) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkCannotBeCopied_h_included_ #define __cmtkCannotBeCopied_h_included_ #include namespace cmtk { /** \addtogroup System */ //@{ /** Base class to prevent copying of derived classes. * This class can be inherited by derived classes and will thus prevent * the compiler from generating default copy constructor and assignment * operator (see Effective C++, 3rd ed.) */ class CannotBeCopied { protected: /// Default constructor. CannotBeCopied() {}; /// Default destructor. ~CannotBeCopied() {}; private: /// Undefined copy constructor. CannotBeCopied( const CannotBeCopied& ); /// Undefined assignment operator. CannotBeCopied& operator=( const CannotBeCopied& ); }; } #endif // #ifndef __cmtkCannotBeCopied_h_included_ cmtk-3.3.1/libs/System/cmtkCommandLine.cxx000066400000000000000000000227671276303427400205100ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2013 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4879 $ // // $LastChangedDate: 2013-09-26 15:08:17 -0700 (Thu, 26 Sep 2013) $ // // $LastChangedBy: torstenrohlfing $ // */ #include "cmtkCommandLine.h" #include #include #include #include #include namespace cmtk { /** \addtogroup System */ //@{ CommandLine::CommandLine( const int properties ) : ArgC( 0 ), ArgV( NULL ), m_Properties( properties ), Index( 0 ) { this->SetDefaultInfo(); this->BeginGroup( "MAIN", "Main Options" ); } CommandLine::~CommandLine() { if ( this->Index < this->ArgC-1 ) { StdErr << "WARNING: the following command line arguments were not used: \n"; for ( size_t i = this->Index; i < this->ArgC; ++i ) { StdErr << this->ArgV[i] << " "; } StdErr << "\n"; } } void CommandLine::SetDefaultInfo() { this->m_ProgramInfo[PRG_LCNSE] = "http://www.fsf.org/licensing/licenses/gpl.html"; this->m_ProgramInfo[PRG_CNTRB] = "Torsten Rohlfing, with contributions from Michael P. Hasak, Greg Jefferis, Calvin R. Maurer, Daniel B. Russakoff, and Yaroslav Halchenko"; this->m_ProgramInfo[PRG_ACKNL] = "CMTK is developed with support from the NIAAA under Grant AA021697, National Consortium on Alcohol and Neurodevelopment in Adolescence (N-CANDA): Data Integration Component. From April 2009 through September 2011, CMTK development and maintenance was supported by the NIBIB under Grant EB008381."; this->m_ProgramInfo[PRG_CATEG] = "CMTK.Miscellaneous"; this->m_ProgramInfo[PRG_DOCUM] = "https://neuro.sri.com/cmtk/wiki/"; this->m_ProgramInfo[PRG_VERSN] = CMTK_VERSION_STRING; this->BeginGroup( "GLOBAL", "Global Toolkit Options (these are shared by all CMTK tools)" )->SetProperties( Self::PROPS_NOXML ); this->AddCallback( Self::Key( "help" ), &Self::CallbackInternal, "Write list of basic command line options to standard output." ); this->AddCallback( Self::Key( "help-all" ), &Self::CallbackInternal, "Write complete list of basic and advanced command line options to standard output." ); this->AddCallback( Self::Key( "wiki" ), &Self::CallbackInternal, "Write list of command line options to standard output in MediaWiki markup." )->SetProperties( Self::PROPS_ADVANCED ); this->AddCallback( Self::Key( "man" ), &Self::CallbackInternal, "Write man page source in 'nroff' markup to standard output." )->SetProperties( Self::PROPS_ADVANCED ); if (! (this->m_Properties & PROPS_NOXML) ) this->AddCallback( Self::Key( "xml" ), &Self::CallbackInternal, "Write command line syntax specification in XML markup (for Slicer integration)." )->SetProperties( Self::PROPS_ADVANCED ); this->AddCallback( Self::Key( "version" ), &Self::CallbackInternal, "Write toolkit version to standard output." )->SetProperties( Self::PROPS_ADVANCED ); this->AddCallback( Self::Key( "echo" ), &Self::CallbackInternal, "Write the current command line to standard output." )->SetProperties( Self::PROPS_ADVANCED ); this->AddCallback( Self::Key( "verbose-level" ), &DebugOutput::SetGlobalLevel, "Set verbosity level." )->SetProperties( Self::PROPS_ADVANCED ); this->AddCallback( Self::Key( 'v', "verbose" ), &DebugOutput::IncGlobalLevel, "Increment verbosity level by 1 (deprecated; supported for backward compatibility)." )->SetProperties( Self::PROPS_ADVANCED ); this->AddCallback( Self::Key( "threads" ), &Threads::SetNumberOfThreads, "Set maximum number of parallel threads (for POSIX threads and OpenMP)." )->SetProperties( Self::PROPS_ADVANCED ); this->EndGroup(); } void CommandLine::CallbackInternal() { StdErr << "ERROR: cmtk::CommandLine::CallbackInternal should never be called.\n"; throw ExitException( 1 ); } CommandLine::KeyActionGroupType::SmartPtr& CommandLine ::BeginGroup( const std::string& name, const std::string& description ) { this->m_KeyActionGroupList.push_back( KeyActionGroupType::SmartPtr( new KeyActionGroupType( name, description ) ) ); this->m_KeyActionList = &(this->m_KeyActionGroupList.back()->m_KeyActionList); return this->m_KeyActionGroupList.back(); } void CommandLine ::EndGroup() { this->m_KeyActionList = &(this->m_KeyActionGroupList.front()->m_KeyActionList); } bool CommandLine::Parse( const int argc, const char* argv[] ) throw( ExitException, Self::Exception ) { this->ArgC = argc; this->ArgV = argv; this->Index = 1; while ( (this->Index < this->ArgC) && (this->ArgV[this->Index][0] == '-') ) { // Break at first non-switch argument. if ( this->ArgV[this->Index][0] != '-' ) return true; // Like POSIX, break at "--" terminator. Also take "-" by itself as argument. if ( !strcmp( this->ArgV[this->Index], "--" ) || !strcmp( this->ArgV[this->Index], "-" ) ) { ++this->Index; break; } bool found = false; if ( this->ArgV[this->Index][1] == '-' ) { // Check for "--version" special option, which prints the CMTK version that this tool is part of. if ( !strcmp( this->ArgV[this->Index], "--version" ) ) { StdOut << this->m_ProgramInfo[PRG_VERSN] << "\n"; throw ExitException( 0 ); } // Check for "--xml" special option, which produces self description according to Slicer execution model. if ( !strcmp( this->ArgV[this->Index], "--xml" ) && !(this->m_Properties & PROPS_NOXML) ) { this->WriteXML(); throw ExitException( 0 ); } // Check for "--help" special option, which produces textual description of all command line options if ( !strcmp( this->ArgV[this->Index], "--help" ) || !strcmp( this->ArgV[this->Index], "--help-all" ) ) { this->PrintHelp( strcmp( this->ArgV[this->Index], "--help-all" ) == 0 ); throw ExitException( 0 ); } // Check for "--wiki" special option, which produces Wiki-markup description of all command line options if ( !strcmp( this->ArgV[this->Index], "--wiki" ) ) { this->PrintWiki(); throw ExitException( 0 ); } // Check for "--man" special option, which produces nroff-markup man page source if ( !strcmp( this->ArgV[this->Index], "--man" ) ) { this->PrintMan( argv[0] ); throw ExitException( 0 ); } // Check for "--echo" special option, which echoes the command line to stderr. This does not exit the program automatically. if ( !strcmp( this->ArgV[this->Index], "--echo" ) ) { for ( size_t i = 0; i < this->ArgC; ++i ) { std::cerr << this->ArgV[i] << " "; } std::cerr << std::endl; found = true; } if ( !found ) { for ( KeyActionListType::iterator it = this->m_KeyActionListComplete.begin(); !found && (it != this->m_KeyActionListComplete.end()); ++it ) { found = (*it)->MatchAndExecute( std::string( this->ArgV[this->Index]+2 ), this->ArgC, this->ArgV, this->Index ); } } if ( ! found ) throw( Exception( std::string("Unknown option: ") + std::string( this->ArgV[this->Index] ) ) ); } else { const char* optChar = this->ArgV[this->Index]+1; while ( *optChar ) { // reset in case of multiple short options found = false; // short option for ( KeyActionListType::iterator it = this->m_KeyActionListComplete.begin(); !found && (it != this->m_KeyActionListComplete.end()); ++it ) { found = (*it)->MatchAndExecute( *optChar, this->ArgC, this->ArgV, this->Index ); } if ( !found ) { const char opt[2] = { *optChar, 0 }; throw( Exception( std::string("Unknown option: -") + std::string(opt) ) ); } ++optChar; // next short option in this block, POSIX style. } } ++this->Index; } // while for ( NonOptionParameterListType::iterator it = this->m_NonOptionParameterList.begin(); it != this->m_NonOptionParameterList.end(); ++it, ++this->Index ) { if ( this->Index >= this->ArgC ) { if ( ! ((*it)->m_Properties & PROPS_OPTIONAL) ) throw( Exception( "Insufficient number of command line arguments", this->Index ) ); } else { (*it)->Evaluate( this->ArgC, this->ArgV, this->Index ); } } for ( NonOptionParameterVectorListType::iterator it = this->m_NonOptionParameterVectorList.begin(); it != this->m_NonOptionParameterVectorList.end(); ++it, ++this->Index ) { if ( this->Index >= this->ArgC ) { if ( ! ((*it)->m_Properties & PROPS_OPTIONAL) ) throw( Exception( "Insufficient number of command line arguments", this->Index ) ); } else { (*it)->Evaluate( this->ArgC, this->ArgV, this->Index ); } } return true; } Console& operator<<( Console& console, CommandLine::Exception e ) { console << e.Message << " [argument #" << e.Index << "]\n"; return console; } } // namespace cmtk cmtk-3.3.1/libs/System/cmtkCommandLine.h000066400000000000000000001235221276303427400201240ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2013 SRI International // // Copyright 2015 Google, Inc. // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 5382 $ // // $LastChangedDate: 2015-01-31 20:53:03 -0800 (Sat, 31 Jan 2015) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkCommandLine_h_included_ #define __cmtkCommandLine_h_included_ #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace cmtk { /** \addtogroup System */ //@{ /** Command line argument parser. * This class provides functionality for command line argument parsing, including automatic * generation of help texts and XML self-description according to the specification of the * Slicer3 execution model * (http://www.slicer.org/slicerWiki/index.php/Slicer3:Execution_Model_Documentation) * * The class handles the following types of command line arguments: * *\section secSwitch Switches * * A command line switch does not have an argument itself but internally sets a variable * to a pre-defined value. * * Example: * * \code * bool verbose; * cl.AddSwitch( CommandLine::Key( "verbose" ), &verbose, true, "Verbose mode ); * \endcode * * Effect: "--verbose" sets the "verbose" variable to "true". * *\section secOption Options * * A command line option has an argument, which is evaluated and stored in a variable. * * Example: * * \code * int iterations = 0; * cl.AddOption( CommandLine::Key( "iterations" ), &iterations, "Number of iterations" ); * \endcode * * Effect: "--iterations 10" sets "iterations" to 10. * *\section secCallback Callbacks * * A callback is linked to a user-defined C function, which is called when the * associated key appears on the command line. * * Example: * * \code * const char* callback( const char* arg ) * { * std::cerr << "callback!" << std::endl; * } * cl.AddCallback( Key( "do-something" ), &callback, "Do something using a callback function" ); * \endcode * *\section secEnumeration Enumerations * * An enumeration is a group of options that modify the same variable by setting it to * different values. * * Examples: * * \code * int InterleaveAxis = 1; * cmtk::CommandLine::EnumGroup::SmartPtr interleaveGroup = cl.AddEnum( "interleave-axis", &InterleaveAxis, "Define interleave axis." ); * interleaveGroup->AddSwitch( Key( "guess-from-input" ), -1, "Guess from input image" ); * interleaveGroup->AddSwitch( Key( 'a', "axial" ), 2, "Interleaved axial images" ); * interleaveGroup->AddSwitch( Key( 'c', "coronal" ), 1, "Interleaved coronal images" ); * interleaveGroup->AddSwitch( Key( 's', "sagittal" ), 0, "Interleaved sagittal images" ); * \endcode * * \code * std::string channel( "spgr" ); * cmtk::CommandLine::EnumGroup::SmartPtr channelGroup = cl.AddEnum( "RegistrationChannel", &channel, "MR channel." ); * channelGroup->AddSwitch( Key( "spgr" ), "spgr", "SPGR (T1-weighted) structural channel" ); * channelGroup->AddSwitch( Key( "early-fse" ), "erly", "Early-echo (PD-weighted) fast spin echo channel" ); * channelGroup->AddSwitch( Key( "late-fse" ), "late", "Late-echo (T2-weighted) fast spin echo channel" ); * \endcode */ class CommandLine : /// Make class uncopyable via inheritance. private CannotBeCopied { public: /// This class. typedef CommandLine Self; /// Enum for program properties. typedef enum { /// Program title. PRG_TITLE = 0, /// Program description. PRG_DESCR = 1, /// Program category. PRG_CATEG = 2, /// Program acknowledgement. PRG_ACKNL = 3, /// Program license (URL). PRG_LCNSE = 4, /// Program contributor. PRG_CNTRB = 5, /// Program documentation (URL). PRG_DOCUM = 6, /// Program version (default: CMTK version). PRG_VERSN = 7, /// Program syntax. PRG_SYNTX = 100 } ProgramProperties; /// Enum for command line item properties. typedef enum { /// No properties, but supports XML (NOXML is off).. PROPS_XML = 0, /// Item is an advanced option. PROPS_ADVANCED = 1, /// Item can appear repeatedly PROPS_MULTIPLE = 2, /// Item is not included in XML output. PROPS_NOXML = 4, /// Item is a generic file name PROPS_DIRNAME = 8, /// Item is a generic directory name PROPS_FILENAME = 16, /// Item is an image file name PROPS_IMAGE = 32, /// When used with PROPS_IMAGE, this means the expected image is a label map. PROPS_LABELS = 64, /// Item is a transformation file name PROPS_XFORM = 128, /// This parameter refers to an output, not an input. PROPS_OUTPUT = 256, /// This parameter (non-option argument) is optional. PROPS_OPTIONAL = 512 } ItemProperties; /// Set program title, description, an category. void SetProgramInfo( const ProgramProperties key, const std::string& value ) { this->m_ProgramInfo[key] = value; } /// Exception for parser error reporting. class Exception { public: /// Constructor. Exception( const char* message = NULL, const size_t index = 0 ) : Message( message ), Index( index ) {} /// Constructor. Exception( const std::string& message, const size_t index = 0 ) : Message( message ), Index( index ) {} /// The error message describing the parsing condition. std::string Message; /// The parameter index where the condition occured. size_t Index; }; /// Callback function. typedef void (*CallbackFunc)(); /// Callback function with an argument. typedef void (*CallbackFuncArg)( const char* ); /// Callback function with an integer argument. typedef void (*CallbackFuncIntArg)( const long int ); /// Callback function with a double-precision floating point argument. typedef void (*CallbackFuncDblArg)( const double ); /// Callback function with multiple arguments. typedef void (*CallbackFuncMultiArg)( const char**, int& argsUsed ); /** Command line key: short and long option. * A key is the character (short options) or string (long options) that is associated * with a given action. When the key appears on the command line, the action is executed. * * The following are examples of Key initializers: * \code * Key( 'v', "verbose" ); * Key( "number-of-iterations" ); * Key( 'q' ); * \endcode */ class Key { public: /// Long option constructor. explicit Key( const std::string& keyString ) : m_KeyChar( 0 ), m_KeyString( keyString ) {} /// Short and long option constructor. explicit Key( const char keyChar, const std::string& keyString ) : m_KeyChar( keyChar ), m_KeyString( keyString ) {} /// Short option key. char m_KeyChar; /// Long option key. std::string m_KeyString; }; /// Virtual base class for command line item. class Item { public: /// Smart pointer to this class. typedef SmartPointer SmartPtr; /// Constructor. Item() : m_Properties( PROPS_XML ) {}; /// Virtual destructor. virtual ~Item() {} /// Set item properties. virtual Item* SetProperties( const long int properties ) { this->m_Properties = properties; return this; } /// Get item properties. virtual long int GetProperties() const { return this->m_Properties; } /// Set an attribute. virtual Item* SetAttribute( const std::string& key, const std::string& value ) { this->m_Attributes[key] = value; return this; } /// Virtual function: evaluate switch or option. virtual void Evaluate( const size_t argc, const char* argv[], size_t& index ) = 0; /// Virtual function that returns an XML tree describing this option. virtual mxml_node_t* MakeXML( mxml_node_t *const parent /*!< Parent in the XML tree for the new node.*/ ) const = 0; /// Virtual function returns a string that describes the parameter type associated with this option (derived classes only) virtual std::string GetParamTypeString() const { return ""; } /// Format additional help information. virtual std::ostringstream& PrintHelp( std::ostringstream& fmt /*!< Stream that the additional help information is formatted into*/ ) const { // by default, simply return stream unchanged. return fmt; } /// Format additional Wiki information. virtual void PrintWiki() const { // by default, do nothing } /// Format additional man page information. virtual void PrintMan() const { // by default, do nothing } /// Return true if and only if this item is the default for the associated action or variable. virtual bool IsDefault() const { return false; } protected: /// Item properties. long int m_Properties; /// Item attributes. These are free-form string key/value pairs. std::map m_Attributes; /// Safely convert string argument to long integer. static long int ConvertStrToLong( const char* str ); /// Safely convert string argument to double. static double ConvertStrToDouble( const char* str ); /// Convert function. template T Convert( const char* str ); /// Helper class to avoid function template template class Helper { public: /// Make a basic XML node for a generic item based on type, properties, attribute, etc. static mxml_node_t* MakeXML( const Item* item, mxml_node_t *const parent ); /// Return a textual description of the parameter associated with this option static std::string GetParamTypeString( const Item* item ); }; private: /// Allow command line class full access. friend class CommandLine; }; private: /// Command line switch. template class Switch : /// Inherit from generic command line item. public Item { public: /// Constructor. Switch( T *const field, const T value ) : m_Field( field ), m_Value( value ) { } /// Virtual destructor. virtual ~Switch() {} /// Evaluate and set associated flag. virtual void Evaluate( const size_t, const char*[], size_t& ) { *this->m_Field = this->m_Value; } /// Virtual function that returns an XML tree describing this option. virtual mxml_node_t* MakeXML( mxml_node_t *const parent ) const { if ( ! (this->m_Properties & PROPS_NOXML) ) { return mxmlNewElement( parent, "boolean" ); } return NULL; } /// Format additional help information (e.g., default values). virtual std::ostringstream& PrintHelp( std::ostringstream& fmt /*!< Stream that the additional help information is formatted into*/ ) const { if ( this->IsDefault() ) fmt << "\n[This is the default]"; return fmt; } /// Format additional help information (e.g., default values). virtual void PrintWiki() const { if ( this->IsDefault() ) StdOut << " '''[This is the default]'''"; } /// Format additional man page information (e.g., default values). virtual void PrintMan() const { if ( this->IsDefault() ) StdOut << "\\fB[This is the default]\\fR\n"; } /// Return true if and only if this item is the default for the associated action or variable. virtual bool IsDefault() const { return ( *(this->m_Field) == this->m_Value ); } private: /// Pointer to field handled by this switch. T* m_Field; /// Value to set field to when switch is encountered. const T m_Value; }; /// Command line option with argument. template class Option : /// Inherit from generic command line item. public Item { public: /// Constructor. Option( T *const var, bool *const flag ) : Var( var ), Flag( flag ) {} /// Virtual destructor. virtual ~Option() {} /// Evaluate and set associated option. virtual void Evaluate( const size_t argc, const char* argv[], size_t& index ); /// Virtual function that returns an XML tree describing this option. virtual mxml_node_t* MakeXML( mxml_node_t *const parent ) const; /// Return a textual description of the parameter associated with this option virtual std::string GetParamTypeString() const; /// Format additional help information (e.g., default values). virtual std::ostringstream& PrintHelp( std::ostringstream& fmt /*!< Stream that the additional help information is formatted into*/ ) const; /// Format additional Wiki information (e.g., default values). virtual void PrintWiki() const; /// Format additional man page information (e.g., default values). virtual void PrintMan() const; protected: /// Pointer to associated variable. T* Var; /// Pointer to (optional) flag that will be set when this option occurs. bool* Flag; }; /// Command line option with list argument: repeated calls will add to list template class List : /// Inherit from generic command line item. public Item { public: /// Constructor. List( std::list& list ) : m_pList( &list ) {} /// Virtual destructor. virtual ~List() {} /// Evaluate and set associated option. virtual void Evaluate( const size_t argc, const char* argv[], size_t& index ); /// Virtual function that returns an XML tree describing this option. virtual mxml_node_t* MakeXML( mxml_node_t *const parent ) const; private: /// Pointer to associated variable. std::list* m_pList; }; /** Command line option with vector argument. *\note For backward compatibility, repeated use of a vector option appends * the subsequent vector elements onto the previously set ones. That is, the * vector is only cleared upon the first option use, to remove potentially * present default elements. */ template class Vector : /// Inherit from generic command line item. public Item { public: /// Constructor. Vector( std::vector& vector ) : m_pVector( &vector ), m_HasBeenUsed( false ) {} /// Virtual destructor. virtual ~Vector() {} /// Evaluate and set associated option. virtual void Evaluate( const size_t argc, const char* argv[], size_t& index ); /// Virtual function that returns an XML tree describing this option. virtual mxml_node_t* MakeXML( mxml_node_t *const parent ) const; /// Return a textual description of the parameter associated with this option virtual std::string GetParamTypeString() const; private: /// Pointer to associated variable. std::vector* m_pVector; /// Has this vector option been used already? This is so we clear the vector exactly once. bool m_HasBeenUsed; }; /// Command line callback. class Callback : /// Inherit from generic command line item. public Item { public: /// Constructor for simple callback. Callback( CallbackFunc func ) : m_Func( func ), m_FuncArg( NULL ), m_FuncIntArg( NULL ), m_FuncDblArg( NULL ), m_FuncMultiArg( NULL ) {} /// Constructor for callback with argument. Callback( CallbackFuncArg funcArg ) : m_Func( NULL ), m_FuncArg( funcArg ), m_FuncIntArg( NULL ), m_FuncDblArg( NULL ), m_FuncMultiArg( NULL ) {} /// Constructor for callback with integer argument. Callback( CallbackFuncIntArg funcIntArg ) : m_Func( NULL ), m_FuncArg( NULL ), m_FuncIntArg( funcIntArg ), m_FuncDblArg( NULL ), m_FuncMultiArg( NULL ) {} /// Constructor for callback with integer argument. Callback( CallbackFuncDblArg funcDblArg ) : m_Func( NULL ), m_FuncArg( NULL ), m_FuncIntArg( NULL ), m_FuncDblArg( funcDblArg ), m_FuncMultiArg( NULL ) {} /// Constructor for callback with multiple arguments. Callback( CallbackFuncMultiArg funcMultiArg ) : m_Func( NULL ), m_FuncArg( NULL ), m_FuncIntArg( NULL ), m_FuncDblArg( NULL ), m_FuncMultiArg( funcMultiArg ) {} /// Virtual destructor. virtual ~Callback() {} /// Evaluate and set associated option. virtual void Evaluate( const size_t argc, const char* argv[], size_t& index ); /// Virtual function that returns an XML tree describing this option. virtual mxml_node_t* MakeXML( mxml_node_t *const parent ) const; /// Virtual function returns a string that describes the parameter type associated with this callback. virtual std::string GetParamTypeString() const; private: /// Pointer to simple callback. CallbackFunc m_Func; /// Pointer to callback with argument. CallbackFuncArg m_FuncArg; /// Pointer to callback with integer argument. CallbackFuncIntArg m_FuncIntArg; /// Pointer to callback with floating point argument. CallbackFuncDblArg m_FuncDblArg; /// Pointer to callback with multiple arguments. CallbackFuncMultiArg m_FuncMultiArg; }; /// Non-option parameter. class NonOptionParameter : /// This is like a standalone string option, so inherit from that. public Option { public: /// This class. typedef NonOptionParameter Self; /// Smart pointer. typedef SmartPointer SmartPtr; /// Superclass. typedef Option Superclass; /// Constructor. NonOptionParameter( std::string *const var, const std::string& name, const std::string& comment, bool *const flag ) : Superclass( var, flag ), m_Name( name ), m_Comment( comment ) {}; /// Evaluate and set associated variable. virtual void Evaluate( const size_t argc, const char* argv[], size_t& index ); /// Returns an XML tree describing this parameter. virtual mxml_node_t* MakeXMLWithIndex( mxml_node_t *const parent /*!< Parent in the XML tree for the new node.*/, const int index /*!< Running index [0,1,...] of this argument in the argument list.*/ ) const; /// Return a textual description of the parameter associated with this option virtual std::string GetParamTypeString() const; /// Format additional help information (e.g., default values). virtual std::ostringstream& PrintHelp( std::ostringstream& fmt /*!< Stream that the additional help information is formatted into*/ ) const { // by default, simply return stream unchanged. if ( this->Var && !this->Var->empty() ) fmt << "\n[Default: " << *(this->Var) << "]"; else fmt << "\n[There is no default for this parameter]"; return fmt; } /// Format additional Wiki information (e.g., default values). virtual void PrintWiki() const { // by default, simply return stream unchanged. if ( this->Var && !this->Var->empty() ) StdOut << " '''[Default: " << *(this->Var) << "]'''\n"; else StdOut << " '''[There is no default for this parameter]'''\n"; } /// Format additional man page information (e.g., default values). virtual void PrintMan() const { // by default, simply return stream unchanged. if ( this->Var && !this->Var->empty() ) StdOut << "\\fB[Default: " << *(this->Var) << "]\\fR\n"; else StdOut << "\\fB[There is no default for this parameter]\\fR\n"; } /// Name of this parameter. const std::string m_Name; /// Comment (description) of this parameter. const std::string m_Comment; }; /// Non-option parameter. class NonOptionParameterVector : /// This is like a standalone string option, so inherit from that. public Option< std::vector > { public: /// This class. typedef NonOptionParameterVector Self; /// Smart pointer. typedef SmartPointer SmartPtr; /// Superclass. typedef Option< std::vector > Superclass; /// Constructor. NonOptionParameterVector( std::vector *pvec, const std::string& name, const std::string& comment, bool *const flag ) : Superclass( pvec, flag ), m_Name( name ), m_Comment( comment ) {}; /// Evaluate and set associated variable. virtual void Evaluate( const size_t argc, const char* argv[], size_t& index ); /// Returns an XML tree describing this parameter. virtual mxml_node_t* MakeXMLWithIndex( mxml_node_t *const parent /*!< Parent in the XML tree for the new node.*/, const int index /*!< Running index [0,1,...] of this argument in the argument list.*/ ) const; /// Return a textual description of the parameter associated with this option virtual std::string GetParamTypeString() const; /// Format additional help information (e.g., default values). virtual std::ostringstream& PrintHelp( std::ostringstream& fmt /*!< Stream that the additional help information is formatted into*/ ) const { if ( this->Var->size() ) { fmt << "\n[Default: ( \"" << (*this->Var)[0] << "\""; for ( size_t i = 1; i < this->Var->size(); ++i ) fmt << ", \"" << (*this->Var)[i] << "\" "; fmt << ") ]"; } else { fmt << "\n[Default: (empty)]"; } return fmt; } /// Format additional Wiki information (e.g., default values). virtual void PrintWiki() const { if ( this->Var->size() ) { StdOut << "'''[Default: ( \"" << (*this->Var)[0] << "\""; for ( size_t i = 1; i < this->Var->size(); ++i ) StdOut << ", \"" << (*this->Var)[i] << "\" "; StdOut << ") ]'''\n"; } else { StdOut << "'''[Default: (empty)]'''\n"; } } /// Format additional man page information (e.g., default values). virtual void PrintMan() const { if ( this->Var->size() ) { StdOut << "\\fB[Default: ( '" << (*this->Var)[0] << "'"; for ( size_t i = 1; i < this->Var->size(); ++i ) StdOut << ", '" << (*this->Var)[i] << "' "; StdOut << ") ]\\fR\n"; } else { StdOut << "\\fB[Default: (empty)]\\fR\n"; } } /// Name of this parameter. const std::string m_Name; /// Comment (description) of this parameter. const std::string m_Comment; }; public: /// Constructor using const inputs. CommandLine( const int properties = PROPS_NOXML ); /// Destructor: spit out a warning if there are unused extra arguments on the command line. ~CommandLine(); /// Forward declaration. class EnumGroupBase; /// Local class that connects command line options with their evaluators. class KeyToAction { public: /// Smart pointer to this class. typedef SmartPointer SmartPtr; /// Key for this key-action pair. const Key m_Key; /// Constructor. KeyToAction( const Key& key /*!< Key: long and/or short command line option for this action.*/, const std::string& comment /*!< Command line help comment for this action.*/ ) : m_Key( key ), m_Comment( comment ), m_Properties( PROPS_XML ) {} /// Virtual destructor. virtual ~KeyToAction() {}; /// Test long key from command line and execute if match. virtual bool MatchAndExecute( const std::string& key /*!< Key (long option) from the command line.*/, const size_t argc /*!< Total number of command line arguments.*/, const char* argv[] /*!< Command line argument list.*/, size_t& index /*!< Current index in command line list*/ ) = 0; /// Test short key from command line and execute if match. virtual bool MatchAndExecute( const char keyChar /*!< Key (long option) from the command line.*/, const size_t argc /*!< Total number of command line arguments.*/, const char* argv[] /*!< Command line argument list.*/, size_t& index /*!< Current index in command line list*/ ) = 0; /// Set action properties. virtual void SetProperties( const long int properties ); /// Get item properties. virtual long int GetProperties() const; /// Returns an XML tree describing this key and action. virtual mxml_node_t* MakeXML( mxml_node_t *const parent /*!< Parent in the XML tree for the new node.*/ ) const; /// Print help for this item. virtual void PrintHelp( const size_t globalIndent = 0 /*!< Indent by this many characters.*/, const bool advanced = false /*!< Flag: include advanced help content. */ ) const = 0; protected: /// Format help for key part of this key/action.. virtual void FormatHelp( std::ostringstream& fmt ) const; /// Print Wiki markup for this item. virtual void PrintWikiWithPrefix( const std::string& prefix = "" ) const; /// Print man page markup for this item. virtual void PrintManWithPrefix( const std::string& prefix = "" ) const; /// Get type info for action parameter (if any). virtual std::string GetActionTypeInfo() const { return ""; } /// Comment (description). std::string m_Comment; /// Group properties. long int m_Properties; /** Match two long options but be tolerant to hyphens, i.e., consider '-' and '_' the same. * This allows us to be tolerant with Slicer's requirement that there are no hyphens in * long options, while maintaining the ability to use them on the command line for * compatibility. *\return true is the two string match, or their only differences are hyphens vs. underlines. */ virtual bool MatchLongOption( const std::string& key ) const; /// Give command line class full access. friend class CommandLine; /// Give enum group class full access. friend class CommandLine::EnumGroupBase; }; /// Local class that connects command line options with single action evaluators. class KeyToActionSingle : /// Inherit from generic key-to-action class. public KeyToAction { public: /// Superclass. typedef KeyToAction Superclass; /// Smart pointer. typedef SmartPointer SmartPtr; /// Constructor. KeyToActionSingle( const Key& key /*!< Key: long and/or short command line option for this action.*/, Item::SmartPtr action /*!< The actual action (option, switch, callback, etc.)*/, const std::string& comment /*!< Command line help comment for this action.*/ ) : KeyToAction( key, comment ), m_Action( action ) {} /// Virtual destructor. virtual ~KeyToActionSingle() {}; /// Test long key from command line and execute if match. bool MatchAndExecute( const std::string& key /*!< Key (long option) from the command line.*/, const size_t argc /*!< Total number of command line arguments.*/, const char* argv[] /*!< Command line argument list.*/, size_t& index /*!< Current index in command line list*/ ); /// Test short key from command line and execute if match. bool MatchAndExecute( const char keyChar /*!< Key (long option) from the command line.*/, const size_t argc /*!< Total number of command line arguments.*/, const char* argv[] /*!< Command line argument list.*/, size_t& index /*!< Current index in command line list*/ ); /// Returns an XML tree describing this key and action. virtual mxml_node_t* MakeXML( mxml_node_t *const parent /*!< Parent in the XML tree for the new node.*/ ) const; /// Print help for this item. virtual void PrintHelp( const size_t globalIndent = 0 /*!< Indent by this many characters.*/, const bool advanced = false /*!< Flag: include advanced help content. */ ) const; /// Print wiki help for this item. virtual void PrintWikiWithPrefix( const std::string& prefix = "" ) const; /// Print man page markup for this item. virtual void PrintManWithPrefix( const std::string& prefix = "" ) const; /// Get type info for action parameter (if any). virtual std::string GetActionTypeInfo() const { return this->m_Action->GetParamTypeString(); } /// Action for simple key-action correspondence.. Item::SmartPtr m_Action; }; /// Base class for templated EnumGroup class. class EnumGroupBase : /// Inherit from STL list class. public std::list< SmartPointer > { public: /// Smart pointer. typedef SmartPointer SmartPtr; /// Constructor. EnumGroupBase() : m_Properties( PROPS_XML ) {}; /// Get key for the default value. std::string GetDefaultKey() const { for ( const_iterator it = this->begin(); it != this->end(); ++it ) { if ( (*it)->m_Action->IsDefault() ) { return std::string( (*it)->m_Key.m_KeyString ); } } return ""; } /// Virtual destructor. virtual ~EnumGroupBase() {} /// Set enum group properties. virtual EnumGroupBase* SetProperties( const long int properties ) { this->m_Properties = properties; return this; } /// Get item properties. virtual long int GetProperties() const { return this->m_Properties; } private: /// Enum group properties. long int m_Properties; }; /// Local class that connects command line options with enum group evaluators. class KeyToActionEnum : /// Inherit from generic key-to-action class. public KeyToAction { public: /// Superclass. typedef KeyToAction Superclass; /// Smart pointer. typedef SmartPointer SmartPtr; /** Constructor for enumeration parameter group. * An enumeration parameter group is a group of parameters that all modify the same variable * by setting it to different values. There is one command line parameter (i.e., key/action pair) * per value, plus a group parameter, which sets the variable based on the name of one of * the supported values. */ KeyToActionEnum( const Key& key /*!< Key: long and/or short command line option for this action.*/, EnumGroupBase::SmartPtr keyToActionEnum /*!< The definition of the enumeration keys and values.*/, const std::string& comment /*!< Command line help comment for this action.*/ ) : KeyToAction( key, comment ), m_EnumGroup( keyToActionEnum ) {} /// Virtual destructor. virtual ~KeyToActionEnum() {}; /// Test long key from command line and execute if match. bool MatchAndExecute( const std::string& key /*!< Key (long option) from the command line.*/, const size_t argc /*!< Total number of command line arguments.*/, const char* argv[] /*!< Command line argument list.*/, size_t& index /*!< Current index in command line list*/ ); /// Test short key from command line and execute if match. bool MatchAndExecute( const char keyChar /*!< Key (long option) from the command line.*/, const size_t argc /*!< Total number of command line arguments.*/, const char* argv[] /*!< Command line argument list.*/, size_t& index /*!< Current index in command line list*/ ); /// Returns an XML tree describing this key and action. virtual mxml_node_t* MakeXML( mxml_node_t *const parent /*!< Parent in the XML tree for the new node.*/ ) const; /// Print help for this item. virtual void PrintHelp( const size_t globalIndent = 0 /*!< Indent by this many characters.*/, const bool advanced = false /*!< Flag: include advanced help content. */ ) const; /// Print Wiki markup for this item in Wiki markup. virtual void PrintWikiWithPrefix( const std::string& prefix = "" ) const; /// Print man page markup for this item in Wiki markup. virtual void PrintManWithPrefix( const std::string& prefix = "" ) const; private: /// For enum parameter group, list of subkeys and action. EnumGroupBase::SmartPtr m_EnumGroup; /// Give enum group class full access. friend class EnumGroupBase; }; /// Key-to-action list for enumeration parameter groups. template class EnumGroup : /// Inherit from base class. public EnumGroupBase { public: /// Smart pointer to this class. typedef SmartPointer< EnumGroup > SmartPtr; /// Constructor. EnumGroup( TDataType *const variable /*!< The variable handled by this enum group.*/ ) : m_Variable( variable ) { } /// Add switch to this group. Item::SmartPtr& AddSwitch( const Key& key, const TDataType& value, const std::string& comment ) { KeyToActionSingle::SmartPtr keyToAction( new KeyToActionSingle( key, Item::SmartPtr( new Switch( this->m_Variable, value ) ), comment ) ); this->push_back( keyToAction ); return keyToAction->m_Action; } private: /// Pointer to the variable that holds the enum value. TDataType *m_Variable; }; /// Add an enum group. template typename EnumGroup::SmartPtr AddEnum( const std::string& name, TDataType *const variable, const std::string& comment ) { typename EnumGroup::SmartPtr enumGroup( new EnumGroup( variable ) ); KeyToActionEnum::SmartPtr keyToAction( new KeyToActionEnum( Key( name ), enumGroup, comment ) ); this->m_KeyActionList->push_back( keyToAction ); this->m_KeyActionListComplete.push_back( keyToAction ); return enumGroup; } /// Add switch. template Item::SmartPtr AddSwitch( const Key& key, T *const var, const T value, const std::string& comment ) { return this->AddKeyAction( KeyToActionSingle::SmartPtr( new KeyToActionSingle( key, Item::SmartPtr( new Switch( var, value ) ), comment ) ) )->m_Action; } /// Add option. template Item::SmartPtr AddOption( const Key& key, T *const var, const std::string& comment, bool *const flag = NULL ) { return this->AddKeyAction( KeyToActionSingle::SmartPtr( new KeyToActionSingle( key, Item::SmartPtr( new Option( var, flag ) ), comment ) ) )->m_Action; } /// Add list option (put arguments from repeated uses into a FIFO list). template Item::SmartPtr AddList( const Key& key, std::list& list, const std::string& comment ) { return this->AddKeyAction( KeyToActionSingle::SmartPtr( new KeyToActionSingle( key, Item::SmartPtr( new List( list ) ), comment ) ) )->m_Action; } /// Add vector option (breaks argument into elements of a vector). template Item::SmartPtr AddVector( const Key& key, std::vector& vector, const std::string& comment ) { return this->AddKeyAction( KeyToActionSingle::SmartPtr( new KeyToActionSingle( key, Item::SmartPtr( new Vector( vector ) ), comment ) ) )->m_Action; } /// Add callback. Item::SmartPtr AddCallback( const Key& key, CallbackFunc func, const std::string& comment ) { return this->AddKeyAction( KeyToActionSingle::SmartPtr( new KeyToActionSingle( key, Item::SmartPtr( new Callback( func ) ), comment ) ) )->m_Action; } /// Add callback with a single argument. template Item::SmartPtr AddCallback( const Key& key, void (*funcArg)( const TArg ), const std::string& comment ) { return this->AddKeyAction( KeyToActionSingle::SmartPtr( new KeyToActionSingle( key, Item::SmartPtr( new Callback( funcArg ) ), comment ) ) )->m_Action; } /// Add callback with multiple arguments. Item::SmartPtr AddCallback( const Key& key, CallbackFuncMultiArg funcMultiArg, const std::string& comment ) { return this->AddKeyAction( KeyToActionSingle::SmartPtr( new KeyToActionSingle( key, Item::SmartPtr( new Callback( funcMultiArg ) ), comment ) ) )->m_Action; } /// Add single non-option parameter. Item::SmartPtr AddParameter( std::string *const var, const std::string& name, const std::string& comment, bool *const flag = NULL ) { NonOptionParameter::SmartPtr parameter( new NonOptionParameter( var, name, comment, flag ) ); this->m_NonOptionParameterList.push_back( parameter ); return parameter; } /// Add vector of non-option parameters. Item::SmartPtr AddParameterVector( std::vector* pvec, const std::string& name, const std::string& comment, bool *const flag = NULL ) { NonOptionParameterVector::SmartPtr vparameter( new NonOptionParameterVector( pvec, name, comment, flag ) ); this->m_NonOptionParameterVectorList.push_back( vparameter ); return vparameter; } /// Type for key/action lists. typedef std::vector KeyActionListType; /// Reference to current key/action list (current group). KeyActionListType* m_KeyActionList; /// Reference to complete key/action list (all groups combined), which is used for key lookup. KeyActionListType m_KeyActionListComplete; /// Add an item to applicable key/action lists. KeyToActionSingle::SmartPtr AddKeyAction( const KeyToActionSingle::SmartPtr& keyAction ) { this->m_KeyActionList->push_back( keyAction ); this->m_KeyActionListComplete.push_back( keyAction ); return keyAction; } /// Add an item to applicable key/action lists. KeyToActionEnum::SmartPtr AddKeyAction( const KeyToActionEnum::SmartPtr& keyAction ) { this->m_KeyActionList->push_back( keyAction ); this->m_KeyActionListComplete.push_back( keyAction ); return keyAction; } /// Type for action groups, which map a group name to the group's key-action list. class KeyActionGroupType { public: /// Smart pointer to this class. typedef SmartPointer SmartPtr; /// Constructor: set name and description. KeyActionGroupType( const std::string& name, const std::string& description ) : m_Name( name ), m_Description( description ), m_Properties( PROPS_XML ) {}; /// Virtual destructor. virtual ~KeyActionGroupType() {} /// Group name. const std::string m_Name; /// Group description. const std::string m_Description; /// Key-action list for this group. KeyActionListType m_KeyActionList; /// Set group properties. virtual void SetProperties( const long int properties ) { this->m_Properties = properties; } /// Get group properties. virtual long int GetProperties() const { return this->m_Properties; } private: /// Group properties. long int m_Properties; }; /// Begin parameter group. KeyActionGroupType::SmartPtr& BeginGroup( const std::string& name, const std::string& description ); /// End last parameter group. void EndGroup(); /// Parse command line. bool Parse( const int argc, const char* argv[] ) throw( ExitException, Self::Exception ); /// Help text indentation. static const int HelpTextIndent = 10; /// Print help text. void PrintHelp( const bool advanced = false /*!< Flag: include advanced help content.*/ ) const; /// Print help text. void PrintWiki() const; /// Print help text. void PrintMan( const char* argv0 /*!< The first command line argument - this is the program path. */ ) const; /// Get next parameter index. size_t GetNextIndex() const { return this->Index; } /** Get next command line argument. * An exception is generated if no further arguments are available. */ const char* GetNext() { if ( Index >= ArgC ) throw( Exception( "Out of arguments.", Index ) ); return ArgV[Index++]; } /** Get next command line argument. * A null pointer is returned if no further arguments are available. */ const char* GetNextOptional() { if ( Index >= ArgC ) return NULL; return ArgV[Index++]; } /** Write XML self description according to Slice3 execution model. *\see */ void WriteXML() const; private: /// Total number of arguments. size_t ArgC; /// Array of argument pointers. const char** ArgV; /// Global properties of the command line. long int m_Properties; /// Index of current argument. size_t Index; /// Type for group list. typedef std::vector KeyActionGroupListType; /// List of command line keys with associated actions. KeyActionGroupListType m_KeyActionGroupList; /// Type for no-option parameter list. typedef std::vector NonOptionParameterListType; /// List of non-option parameters (i.e., "the rest of the command line"). NonOptionParameterListType m_NonOptionParameterList; /// Type for no-option parameter vector list. These come after the scalar non-option parameters. typedef std::vector NonOptionParameterVectorListType; /// List of non-option parameters (i.e., "the rest of the command line"). NonOptionParameterVectorListType m_NonOptionParameterVectorList; /// Map type for program meta information. typedef std::map ProgramPropertiesMapType; /// Program meta information. ProgramPropertiesMapType m_ProgramInfo; /// Add program info item to XML tree. mxml_node_t* AddProgramInfoXML( mxml_node_t *const parent /*!< Parent node for new entry in XML tree.*/, const ProgramProperties key /*!< Key code for program property.*/, const char* name /*!< Name of XML tag for this property.*/ ) const; /// Set default values for meta information. void SetDefaultInfo(); /// Make options class friend. template friend class Option; /// Make switch class friend. template friend class Switch; /// Make callback class friend. friend class Callback; /// Dummy callback function for internal options. static void CallbackInternal(); }; /// Output of command line exception. Console& operator<<( Console& console, CommandLine::Exception e ); //@} } // namespace cmtk #include "cmtkCommandLineItem.txx" #include "cmtkCommandLineOption.txx" #include "cmtkCommandLineConvert.txx" #include "cmtkCommandLineList.txx" #include "cmtkCommandLineVector.txx" #endif // #ifndef __cmtkCommandLine_h_included_ cmtk-3.3.1/libs/System/cmtkCommandLineCallback.cxx000066400000000000000000000101101276303427400221000ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2009, 2013 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4898 $ // // $LastChangedDate: 2013-09-30 16:10:27 -0700 (Mon, 30 Sep 2013) $ // // $LastChangedBy: torstenrohlfing $ // */ #include "cmtkCommandLine.h" #include void cmtk::CommandLine::Callback::Evaluate ( const size_t argc, const char* argv[], size_t& index ) { // callback with argument? if ( this->m_FuncArg ) { if ( index+1 < argc ) { try { this->m_FuncArg( argv[index+1] ); } catch ( const char* error ) { throw( Exception( error, index ) ); } ++index; } else { throw( Exception( "Option needs an argument.", index ) ); } } else // callback with integer argument? if ( this->m_FuncIntArg ) { if ( index+1 < argc ) { try { this->m_FuncIntArg( ConvertStrToLong( argv[index+1] ) ); } catch ( const char* error ) { throw( Exception( error, index ) ); } ++index; } else { throw( Exception( "Option needs an integer argument.", index ) ); } } else // callback with double argument? if ( this->m_FuncDblArg ) { if ( index+1 < argc ) { try { this->m_FuncDblArg( ConvertStrToDouble( argv[index+1] ) ); } catch ( const char* error ) { throw( Exception( error, index ) ); } ++index; } else { throw( Exception( "Option needs a floating point argument.", index ) ); } } else // multiple arguments to callback? if ( this->m_FuncMultiArg ) { if ( index+1 < argc ) { int argsUsed = 0; try { this->m_FuncMultiArg( argv+index+1, argsUsed ); } catch ( const char* error ) { throw( Exception( error, index ) ); } index += argsUsed; } else { throw( Exception( "Option needs an argument", index ) ); } } else { // no argument to callback try { this->m_Func(); } catch ( const char* error ) { throw( Exception( error, index ) ); } } } mxml_node_t* cmtk::CommandLine::Callback ::MakeXML( mxml_node_t *const parent ) const { mxml_node_t* node = NULL; if ( this->m_Func ) { node = mxmlNewElement( parent, "boolean" ); mxml_node_t *dflt = mxmlNewElement( node, "default" ); Coverity::FakeFree( mxmlNewText( dflt, 0, "false" ) ); } else if ( this->m_FuncArg ) { node = mxmlNewElement( parent, "string" ); } else if ( this->m_FuncIntArg ) { node = mxmlNewElement( parent, "integer" ); } else if ( this->m_FuncDblArg ) { node = mxmlNewElement( parent, "double" ); } else if ( this->m_FuncMultiArg ) { node = mxmlNewElement( parent, "string-vector" ); } mxmlElementSetAttr( node, "multiple", "true" ); return node; } std::string cmtk::CommandLine::Callback ::GetParamTypeString() const { if ( this->m_FuncArg ) { return Item::Helper::GetParamTypeString( this ); } else if ( this->m_FuncIntArg ) { return Item::Helper::GetParamTypeString( this ); } else if ( this->m_FuncDblArg ) { return Item::Helper::GetParamTypeString( this ); } else if ( this->m_FuncMultiArg ) { return ""; } return ""; } cmtk-3.3.1/libs/System/cmtkCommandLineConvert.txx000066400000000000000000000106221276303427400220550ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2013 SRI International // // Copyright 2015 Google, Inc. // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 5382 $ // // $LastChangedDate: 2015-01-31 20:53:03 -0800 (Sat, 31 Jan 2015) $ // // $LastChangedBy: torstenrohlfing $ // */ namespace cmtk { /** \addtogroup System */ //@{ /// Convert string to bool. template<> inline bool CommandLine::Item::Convert( const char* str ) { if ( ! str ) return false; else return !strcmp( str, "yes" ); } /// Convert string to float. template<> inline float CommandLine::Item::Convert( const char* str ) { return static_cast( this->ConvertStrToDouble( str ) ); } /// Convert string to double. template<> inline double CommandLine::Item::Convert( const char* str ) { return static_cast( this->ConvertStrToDouble( str ) ); } /// Convert string to long int. template<> inline long int CommandLine::Item::Convert( const char* str ) { return static_cast( this->ConvertStrToLong( str ) ); } /// Convert string to int. template<> inline int CommandLine::Item::Convert( const char* str ) { return static_cast( this->ConvertStrToLong( str ) ); } /// Convert string to unsigned int. template<> inline unsigned int CommandLine::Item::Convert( const char* str ) { return static_cast( this->ConvertStrToLong( str ) ); } /// Convert string to unsigned long template<> inline unsigned long int CommandLine::Item::Convert( const char* str ) { return static_cast( this->ConvertStrToLong( str ) ); } /// Convert string to unsigned long long template<> inline unsigned long long int CommandLine::Item::Convert( const char* str ) { return static_cast( this->ConvertStrToLong( str ) ); } /// Convert string to signed long long template<> inline signed long long int CommandLine::Item::Convert( const char* str ) { return static_cast( this->ConvertStrToLong( str ) ); } /// Convert string to char. template<> inline char CommandLine::Item::Convert( const char* str ) { return static_cast( this->ConvertStrToLong( str ) ); } /// Convert string to unsigned char. template<> inline unsigned char CommandLine::Item::Convert( const char* str ) { return static_cast( this->ConvertStrToLong( str ) ); } /// Convert string to signed char. template<> inline signed char CommandLine::Item::Convert( const char* str ) { return static_cast( this->ConvertStrToLong( str ) ); } /// Convert string to short integer. template<> inline short CommandLine::Item::Convert( const char* str ) { return static_cast( this->ConvertStrToLong( str ) ); } /// Convert string to unsigned short integer. template<> inline unsigned short CommandLine::Item::Convert( const char* str ) { return static_cast( this->ConvertStrToLong( str ) ); } /// Convert string to string. template<> inline const char* CommandLine::Item::Convert( const char* str ) { return str; } /// Convert string to STL string. template<> inline std::string CommandLine::Item::Convert( const char* str ) { return std::string(str); } /// Convert string to vector. template<> inline std::vector CommandLine::Item::Convert< std::vector >( const char* str ) { std::vector v; v.push_back( std::string( str ) ); return v; } } // namespace cmtk cmtk-3.3.1/libs/System/cmtkCommandLineHelp.cxx000066400000000000000000000111571276303427400213100ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2012 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 5332 $ // // $LastChangedDate: 2014-04-25 16:38:49 -0700 (Fri, 25 Apr 2014) $ // // $LastChangedBy: torstenrohlfing $ // */ #include #include "cmtkCommandLine.h" #include namespace cmtk { /** \addtogroup System */ //@{ void CommandLine::PrintHelp ( const bool advanced ) const { const size_t lineWidth = StdOut.GetLineWidth(); ProgramPropertiesMapType::const_iterator ppit = this->m_ProgramInfo.find(PRG_TITLE); if ( ppit != this->m_ProgramInfo.end() ) { StdOut << "TITLE:\n\n"; StdOut.FormatText( ppit->second, 5 ) << "\n"; } ppit = this->m_ProgramInfo.find(PRG_DESCR); if ( ppit != this->m_ProgramInfo.end() ) { StdOut << "\nDESCRIPTION:\n\n"; StdOut.FormatText( ppit->second, 5 ) << "\n"; } ppit = this->m_ProgramInfo.find(PRG_SYNTX); if ( ppit != this->m_ProgramInfo.end() ) { StdOut << "\nSYNTAX:\n\n"; StdOut.FormatText( ppit->second, 5 ) << "\n"; } else { if ( this->m_NonOptionParameterList.size() || this->m_NonOptionParameterVectorList.size() ) { StdOut << "\nSYNTAX:\n\n"; std::ostringstream fmt; fmt << "[options] "; for ( NonOptionParameterListType::const_iterator it = this->m_NonOptionParameterList.begin(); it != this->m_NonOptionParameterList.end(); ++it ) { if ( ! (*it)->m_Comment.empty() ) { if ( (*it)->m_Properties & PROPS_OPTIONAL ) fmt << "[" << (*it)->m_Name << "] "; else fmt << (*it)->m_Name << " "; } } for ( NonOptionParameterVectorListType::const_iterator it = this->m_NonOptionParameterVectorList.begin(); it != this->m_NonOptionParameterVectorList.end(); ++it ) { if ( ! (*it)->m_Comment.empty() ) { if ( (*it)->m_Properties & PROPS_OPTIONAL ) fmt << "[" << (*it)->m_Name << " ... ] "; else fmt << (*it)->m_Name << " ... "; } } StdOut.FormatText( fmt.str(), 5, lineWidth ); StdOut << "\n where\n\n"; const int indent = 20; for ( NonOptionParameterListType::const_iterator it = this->m_NonOptionParameterList.begin(); it != this->m_NonOptionParameterList.end(); ++it ) { if ( ! (*it)->m_Comment.empty() ) { fmt.str(""); fmt << (*it)->m_Name << " = "; if ( fmt.str().length() > static_cast( indent-2 ) ) fmt << "\n"; else { while ( fmt.str().length() < static_cast( indent ) ) fmt << " "; } fmt << (*it)->m_Comment; StdOut.FormatText( fmt.str(), 5+indent, lineWidth, -indent ) << "\n"; } } for ( NonOptionParameterVectorListType::const_iterator it = this->m_NonOptionParameterVectorList.begin(); it != this->m_NonOptionParameterVectorList.end(); ++it ) { if ( ! (*it)->m_Comment.empty() ) { fmt.str(""); fmt << (*it)->m_Name << " = "; if ( fmt.str().length() > static_cast( indent-2 ) ) fmt << "\n"; else { while ( fmt.str().length() < static_cast( indent ) ) fmt << " "; } fmt << (*it)->m_Comment; StdOut.FormatText( fmt.str(), 5+indent, lineWidth, -indent ) << "\n"; } } } } StdOut << "\nLIST OF SUPPORTED OPTIONS:\n\n"; for ( KeyActionGroupListType::const_iterator grp = this->m_KeyActionGroupList.begin(); grp != this->m_KeyActionGroupList.end(); ++grp ) { if ( ! (*grp)->m_KeyActionList.empty() ) { if ( (((*grp)->GetProperties() & Self::PROPS_ADVANCED)==0) || advanced ) { StdOut << (*grp)->m_Description << "\n\n"; const size_t indent = 2; const KeyActionListType& kal = (*grp)->m_KeyActionList; for ( KeyActionListType::const_iterator it = kal.begin(); it != kal.end(); ++it ) { (*it)->PrintHelp( indent, advanced ); } } } } StdOut << "\n"; } } // namespace cmtk cmtk-3.3.1/libs/System/cmtkCommandLineItem.cxx000066400000000000000000000032411276303427400213110ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2009 SRI International // // Copyright 2015 Google, Inc. // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 5382 $ // // $LastChangedDate: 2015-01-31 20:53:03 -0800 (Sat, 31 Jan 2015) $ // // $LastChangedBy: torstenrohlfing $ // */ #include "cmtkCommandLine.h" long int cmtk::CommandLine::Item ::ConvertStrToLong( const char* str ) { char* endptr; const long int value = strtol( str, &endptr, 0 ); if ( (endptr == str) || *endptr ) { throw( Exception( "Option expects an integer argument" ) ); } return value; } double cmtk::CommandLine::Item ::ConvertStrToDouble( const char* str ) { char* endptr; const double value = strtod( str, &endptr ); if ( (endptr == str) || *endptr ) { throw( Exception( "Option expects a floating point argument" ) ); } return value; } cmtk-3.3.1/libs/System/cmtkCommandLineItem.txx000066400000000000000000000066151276303427400213420ustar00rootroot00000000000000/* // // Copyright 2009, 2013 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4898 $ // // $LastChangedDate: 2013-09-30 16:10:27 -0700 (Mon, 30 Sep 2013) $ // // $LastChangedBy: torstenrohlfing $ // */ #include #include template mxml_node_t* cmtk::CommandLine::Item::Helper ::MakeXML( const Item* item, mxml_node_t *const parent ) { if ( ! (item->m_Properties & PROPS_NOXML) ) { const char* typeName = CommandLineTypeTraits::GetName(); mxml_node_t *node = NULL; if ( std::string( typeName ) == "string" ) { if ( item->m_Properties & PROPS_IMAGE ) { node = mxmlNewElement( parent, "image" ); if ( item->m_Properties & PROPS_LABELS ) mxmlElementSetAttr( node, "type", "label" ); else mxmlElementSetAttr( node, "type", "scalar" ); } else if ( item->m_Properties & PROPS_XFORM ) { node = mxmlNewElement( parent, "transform" ); mxmlElementSetAttr( node, "fileExtensions", ".txt" ); } else if ( item->m_Properties & PROPS_FILENAME ) node = mxmlNewElement( parent, "file" ); else if ( item->m_Properties & PROPS_DIRNAME ) node = mxmlNewElement( parent, "directory" ); else node = mxmlNewElement( parent, "string" ); if ( item->m_Properties & PROPS_OUTPUT ) Coverity::FakeFree( mxmlNewText( mxmlNewElement( node, "channel" ), 0, "output" ) ); else Coverity::FakeFree( mxmlNewText( mxmlNewElement( node, "channel" ), 0, "input" ) ); } else node = mxmlNewElement( parent, typeName ); // write any attributes the user might have set for ( std::map::const_iterator attrIt = item->m_Attributes.begin(); attrIt != item->m_Attributes.end(); ++attrIt ) { mxmlElementSetAttr( node, attrIt->first.c_str(), attrIt->second.c_str() ); } return node; } return NULL; } template std::string cmtk::CommandLine::Item::Helper ::GetParamTypeString( const Item* item ) { const std::string& typeName = CommandLineTypeTraits::GetName(); if ( typeName == "string" ) { if ( item->m_Properties & PROPS_IMAGE ) { if ( item->m_Properties & PROPS_LABELS ) return ""; else return ""; } else if ( item->m_Properties & PROPS_XFORM ) { return ""; } else if ( item->m_Properties & PROPS_FILENAME ) return ""; else if ( item->m_Properties & PROPS_DIRNAME ) return ""; else return ""; } return std::string("<")+typeName+std::string(">"); } cmtk-3.3.1/libs/System/cmtkCommandLineKeyToAction.cxx000066400000000000000000000127751276303427400226200ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2011, 2013 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4898 $ // // $LastChangedDate: 2013-09-30 16:10:27 -0700 (Mon, 30 Sep 2013) $ // // $LastChangedBy: torstenrohlfing $ // */ #include "cmtkCommandLine.h" #include #include void cmtk::CommandLine::KeyToAction ::SetProperties( const long int properties ) { this->m_Properties = properties; } long int cmtk::CommandLine::KeyToAction ::GetProperties() const { return this->m_Properties; } mxml_node_t* cmtk::CommandLine::KeyToAction ::MakeXML( mxml_node_t *const node ) const { if ( ! (this->m_Properties & PROPS_NOXML) ) { // for some reason Slicer does not accept long options that contain hyphens ("-"), so we replace them. std::string xmlKeyStr = this->m_Key.m_KeyString; for ( size_t i = 0; i < xmlKeyStr.length(); ++i ) { if ( xmlKeyStr[i] == '-' ) xmlKeyStr[i] = '_'; } if ( this->m_Comment.length() ) { Coverity::FakeFree( mxmlNewText( mxmlNewElement( node, "description" ), 0, this->m_Comment.c_str() ) ); } if ( this->m_Key.m_KeyString.length() ) { Coverity::FakeFree( mxmlNewText( mxmlNewElement( node, "name" ), 0, xmlKeyStr.c_str() ) ); Coverity::FakeFree( mxmlNewText( mxmlNewElement( node, "label" ), 0, xmlKeyStr.c_str() ) ); } if ( this->m_Key.m_KeyChar ) { const char keyStr[] = { '-', this->m_Key.m_KeyChar, 0 }; Coverity::FakeFree( mxmlNewText( mxmlNewElement( node, "flag" ), 0, keyStr ) ); } if ( this->m_Key.m_KeyString.length() ) { Coverity::FakeFree( mxmlNewText( mxmlNewElement( node, "longflag" ), 0, (std::string( "--" ) + xmlKeyStr).c_str() ) ); } return node; } return NULL; } void cmtk::CommandLine::KeyToAction ::FormatHelp( std::ostringstream& fmt ) const { if ( this->m_Comment.length() ) { const std::string& typeInfo = this->GetActionTypeInfo(); if ( this->m_Key.m_KeyString.size() ) { fmt << "--" << this->m_Key.m_KeyString; if ( typeInfo.length() ) { fmt << " " << typeInfo; } } if ( this->m_Key.m_KeyChar && this->m_Key.m_KeyString.size() ) { fmt << ", "; } if ( this->m_Key.m_KeyChar ) { fmt << "-" << this->m_Key.m_KeyChar; if ( typeInfo.length() ) { fmt << " " << typeInfo; } } if ( fmt.str().length() > static_cast( CommandLine::HelpTextIndent-2 ) ) fmt << "\n"; else { while ( fmt.str().length() < static_cast( CommandLine::HelpTextIndent ) ) fmt << " "; } fmt << this->m_Comment; } } void cmtk::CommandLine::KeyToAction ::PrintWikiWithPrefix( const std::string& prefix ) const { if ( this->m_Comment.length() ) { const std::string& typeInfo = this->GetActionTypeInfo(); StdOut << prefix << "; "; if ( this->m_Key.m_KeyString.size() ) { StdOut << "--" << this->m_Key.m_KeyString << ""; if ( typeInfo.length() ) { StdOut << " " << typeInfo << ""; } } if ( this->m_Key.m_KeyChar && this->m_Key.m_KeyString.size() ) { StdOut << ", "; } if ( this->m_Key.m_KeyChar ) { StdOut << "-" << this->m_Key.m_KeyChar << ""; if ( typeInfo.length() ) { StdOut << " " << typeInfo << ""; } } StdOut << " : " << this->m_Comment; } } void cmtk::CommandLine::KeyToAction ::PrintManWithPrefix( const std::string& prefix ) const { if ( this->m_Comment.length() ) { const std::string& typeInfo = this->GetActionTypeInfo(); StdOut << prefix; if ( this->m_Key.m_KeyString.size() ) { StdOut << ".TP 5\n"; StdOut << "\\fB\\-\\-" << this->m_Key.m_KeyString << "\\fR"; if ( typeInfo.length() ) { StdOut << " \\fI" << typeInfo << "\\fR"; } } if ( this->m_Key.m_KeyChar && this->m_Key.m_KeyString.size() ) { StdOut << ", "; } if ( this->m_Key.m_KeyChar ) { StdOut << "\\fB\\-" << this->m_Key.m_KeyChar << "\\fR"; if ( typeInfo.length() ) { StdOut << " \\fI" << typeInfo << "\\fR"; } } StdOut << "\n" << this->m_Comment << "\n"; } } bool cmtk::CommandLine::KeyToAction ::MatchLongOption( const std::string& key ) const { if ( key.length() != this->m_Key.m_KeyString.length() ) return false; for ( size_t i = 0; i < key.length(); ++i ) { if ( (key[i] == '-' || key[i] == '_') && (this->m_Key.m_KeyString[i] == '-' || this->m_Key.m_KeyString[i] == '_') ) continue; if ( key[i] != this->m_Key.m_KeyString[i] ) return false; } return true; } cmtk-3.3.1/libs/System/cmtkCommandLineKeyToActionEnum.cxx000066400000000000000000000137431276303427400234410ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2011, 2013 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4903 $ // // $LastChangedDate: 2013-10-01 10:15:08 -0700 (Tue, 01 Oct 2013) $ // // $LastChangedBy: torstenrohlfing $ // */ #include "cmtkCommandLine.h" #include void cmtk::CommandLine::KeyToActionEnum ::PrintHelp( const size_t globalIndent, const bool advanced ) const { if ( ((this->m_Properties & Self::PROPS_ADVANCED)==0) || advanced ) { std::ostringstream fmt; this->Superclass::FormatHelp( fmt ); fmt << "\nSupported values: "; for ( EnumGroupBase::const_iterator it = this->m_EnumGroup->begin(); it != this->m_EnumGroup->end(); ++it ) { fmt << "\"" << (*it)->m_Key.m_KeyString << "\", "; } const std::string defaultKey = this->m_EnumGroup->GetDefaultKey(); if ( defaultKey.length() ) { fmt << "where the default is \"" << defaultKey << "\", "; } fmt << "or use one of the following"; StdOut.FormatText( fmt.str(), CommandLine::HelpTextIndent + globalIndent, StdErr.GetLineWidth(), -CommandLine::HelpTextIndent ) << "\n"; for ( EnumGroupBase::const_iterator it = this->m_EnumGroup->begin(); it != this->m_EnumGroup->end(); ++it ) { (*it)->PrintHelp( globalIndent + 10 ); } } } void cmtk::CommandLine::KeyToActionEnum ::PrintWikiWithPrefix( const std::string& prefix ) const { this->Superclass::PrintWikiWithPrefix( prefix ); StdOut << "Supported values: "; for ( EnumGroupBase::const_iterator it = this->m_EnumGroup->begin(); it != this->m_EnumGroup->end(); ++it ) { StdOut << "\"" << (*it)->m_Key.m_KeyString << "\", "; } const std::string defaultKey = this->m_EnumGroup->GetDefaultKey(); if ( defaultKey.length() ) { StdOut << "where the default is \"" << defaultKey << "\", "; } StdOut << "or use one of the following:\n"; for ( EnumGroupBase::const_iterator it = this->m_EnumGroup->begin(); it != this->m_EnumGroup->end(); ++it ) { (*it)->PrintWikiWithPrefix( ":" ); } } void cmtk::CommandLine::KeyToActionEnum ::PrintManWithPrefix( const std::string& prefix ) const { this->Superclass::PrintManWithPrefix( prefix ); StdOut << "Supported values: "; for ( EnumGroupBase::const_iterator it = this->m_EnumGroup->begin(); it != this->m_EnumGroup->end(); ++it ) { StdOut << "\"" << (*it)->m_Key.m_KeyString << "\", "; } const std::string defaultKey = this->m_EnumGroup->GetDefaultKey(); if ( defaultKey.length() ) { StdOut << "where the default is \"" << defaultKey << "\", "; } StdOut << "or use one of the following:\n.RS 5\n"; for ( EnumGroupBase::const_iterator it = this->m_EnumGroup->begin(); it != this->m_EnumGroup->end(); ++it ) { (*it)->PrintManWithPrefix(); } StdOut << ".RE\n"; } mxml_node_t* cmtk::CommandLine::KeyToActionEnum ::MakeXML( mxml_node_t *const parent ) const { if ( ! (this->m_Properties & PROPS_NOXML) ) { mxml_node_t *node = mxmlNewElement( parent, "string-enumeration" ); mxml_node_t* defaultElement = mxmlNewElement( node, "default" ); Coverity::FakeFree( mxmlNewText( defaultElement, 0, this->m_EnumGroup->GetDefaultKey().c_str() ) ); for ( EnumGroupBase::const_iterator it = this->m_EnumGroup->begin(); it != this->m_EnumGroup->end(); ++it ) { mxml_node_t* element = mxmlNewElement( node, "element" ); Coverity::FakeFree( mxmlNewText( element, 0, (*it)->m_Key.m_KeyString.c_str() ) ); } return this->Superclass::MakeXML( node ); } return NULL; } bool cmtk::CommandLine::KeyToActionEnum ::MatchAndExecute( const std::string& key, const size_t argc, const char* argv[], size_t& index ) { if ( this->MatchLongOption( std::string( key ) ) ) { // check if optional argument matches suboption if ( this->m_EnumGroup ) { for ( EnumGroupBase::iterator it = this->m_EnumGroup->begin(); it != this->m_EnumGroup->end(); ++it ) { size_t ii = index+1; if ( (*it)->MatchAndExecute( argv[ii], argc, argv, ii ) ) { index = ii; return true; } } } } // also check for direct matches with the suboptions if ( this->m_EnumGroup ) { for ( EnumGroupBase::iterator it = this->m_EnumGroup->begin(); it != this->m_EnumGroup->end(); ++it ) { if ( (*it)->MatchAndExecute( key, argc, argv, index ) ) { return true; } } } return false; } bool cmtk::CommandLine::KeyToActionEnum ::MatchAndExecute( const char keyChar, const size_t argc, const char* argv[], size_t& index ) { // check if optional argument matches suboption for ( EnumGroupBase::iterator it = this->m_EnumGroup->begin(); it != this->m_EnumGroup->end(); ++it ) { size_t ii = index+1; if ( (*it)->MatchAndExecute( argv[ii], argc, argv, ii ) ) { index = ii; return true; } } // also check for direct matches with the suboptions for ( EnumGroupBase::iterator it = this->m_EnumGroup->begin(); it != this->m_EnumGroup->end(); ++it ) { if ( (*it)->MatchAndExecute( keyChar, argc, argv, index ) ) { return true; } } return false; } cmtk-3.3.1/libs/System/cmtkCommandLineKeyToActionSingle.cxx000066400000000000000000000053421276303427400237520ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2011 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 3474 $ // // $LastChangedDate: 2011-10-19 15:09:57 -0700 (Wed, 19 Oct 2011) $ // // $LastChangedBy: torstenrohlfing $ // */ #include "cmtkCommandLine.h" mxml_node_t* cmtk::CommandLine::KeyToActionSingle ::MakeXML( mxml_node_t *const parent ) const { if ( ! (this->m_Properties & PROPS_NOXML) ) { return this->Superclass::MakeXML( this->m_Action->MakeXML( parent ) ); } return NULL; } void cmtk::CommandLine::KeyToActionSingle ::PrintHelp( const size_t globalIndent, const bool advanced ) const { std::ostringstream fmt; this->Superclass::FormatHelp( fmt ); if ( ((this->m_Action->GetProperties() & Self::PROPS_ADVANCED)==0) || advanced ) { this->m_Action->PrintHelp( fmt ); StdOut.FormatText( fmt.str(), CommandLine::HelpTextIndent + globalIndent, StdErr.GetLineWidth(), -CommandLine::HelpTextIndent ) << "\n"; } } void cmtk::CommandLine::KeyToActionSingle ::PrintWikiWithPrefix( const std::string& prefix ) const { this->Superclass::PrintWikiWithPrefix( prefix ); this->m_Action->PrintWiki(); StdOut << "\n"; } void cmtk::CommandLine::KeyToActionSingle ::PrintManWithPrefix( const std::string& prefix ) const { this->Superclass::PrintManWithPrefix( prefix ); this->m_Action->PrintMan(); } bool cmtk::CommandLine::KeyToActionSingle ::MatchAndExecute( const std::string& key, const size_t argc, const char* argv[], size_t& index ) { if ( this->MatchLongOption( std::string( key ) ) ) { this->m_Action->Evaluate( argc, argv, index ); return true; } return false; } bool cmtk::CommandLine::KeyToActionSingle ::MatchAndExecute( const char keyChar, const size_t argc, const char* argv[], size_t& index ) { if ( this->m_Key.m_KeyChar == keyChar ) { this->m_Action->Evaluate( argc, argv, index ); return true; } return false; } cmtk-3.3.1/libs/System/cmtkCommandLineList.txx000066400000000000000000000032561276303427400213550ustar00rootroot00000000000000/* // // Copyright 2009 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 418 $ // // $LastChangedDate: 2009-08-07 23:00:10 -0700 (Fri, 07 Aug 2009) $ // // $LastChangedBy: torstenrohlfing $ // */ #include template void cmtk::CommandLine::List ::Evaluate( const size_t argc, const char* argv[], size_t& index ) { if ( index+1 < argc ) { ++index; m_pList->push_back( this->Convert( argv[index] ) ); } else { throw( Exception( "List command line option needs an argument.", index ) ); } } template mxml_node_t* cmtk::CommandLine::List ::MakeXML( mxml_node_t *const parent ) const { if ( ! (this->m_Properties & PROPS_NOXML) ) { mxml_node_t *node = Item::Helper::MakeXML( this, parent ); mxmlElementSetAttr( node, "multiple", "true" ); return node; } return NULL; } cmtk-3.3.1/libs/System/cmtkCommandLineMan.cxx000066400000000000000000000074131276303427400211330ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2011 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4506 $ // // $LastChangedDate: 2012-09-07 11:43:50 -0700 (Fri, 07 Sep 2012) $ // // $LastChangedBy: torstenrohlfing $ // */ #include #include "cmtkCommandLine.h" #include namespace cmtk { /** \addtogroup System */ //@{ void CommandLine::PrintMan ( const char* argv0 ) const { // determine program file name const char* cmd = strrchr( argv0, '/' ); if ( cmd ) { ++cmd; } else { cmd = argv0; } ProgramPropertiesMapType::const_iterator ppit = this->m_ProgramInfo.find(PRG_VERSN); StdOut << ".TH " << cmd << " \"1\" \"" << __DATE__ << "\" \"CMTK " << ppit->second << "\" \"The Computational Morphometry Toolkit\"\n"; ppit = this->m_ProgramInfo.find(PRG_TITLE); if ( ppit != this->m_ProgramInfo.end() ) { StdOut << ".SH NAME\n" << cmd << " \\- " << ppit->second << "\n"; } ppit = this->m_ProgramInfo.find(PRG_SYNTX); if ( ppit != this->m_ProgramInfo.end() ) { StdOut << ".SH SYNOPSIS\n"; StdOut << ppit->second << "\n"; } else { if ( this->m_NonOptionParameterList.size() || this->m_NonOptionParameterVectorList.size() ) { StdOut << ".SH SYNOPSIS\n\\fB" << cmd << "\\fR "; for ( NonOptionParameterListType::const_iterator it = this->m_NonOptionParameterList.begin(); it != this->m_NonOptionParameterList.end(); ++it ) { StdOut << (*it)->m_Name << " "; } for ( NonOptionParameterVectorListType::const_iterator it = this->m_NonOptionParameterVectorList.begin(); it != this->m_NonOptionParameterVectorList.end(); ++it ) { StdOut << (*it)->m_Name << " "; } StdOut << "\n"; } } ppit = this->m_ProgramInfo.find(PRG_DESCR); if ( ppit != this->m_ProgramInfo.end() ) { StdOut << ".SH DESCRIPTION\n"; StdOut << ppit->second << "\n"; } StdOut << ".SH OPTIONS\n"; for ( KeyActionGroupListType::const_iterator grp = this->m_KeyActionGroupList.begin(); grp != this->m_KeyActionGroupList.end(); ++grp ) { if ( ! (*grp)->m_KeyActionList.empty() ) { StdOut << ".SS " << (*grp)->m_Description << "\n"; const KeyActionListType& kal = (*grp)->m_KeyActionList; for ( KeyActionListType::const_iterator it = kal.begin(); it != kal.end(); ++it ) { (*it)->PrintManWithPrefix(); } } } ppit = this->m_ProgramInfo.find(PRG_CNTRB); if ( ppit != this->m_ProgramInfo.end() ) { StdOut << ".SH AUTHORS\n" << ppit->second << "\n"; } ppit = this->m_ProgramInfo.find(PRG_LCNSE); if ( ppit != this->m_ProgramInfo.end() ) { StdOut << ".SH LICENSE\n" << ppit->second << "\n"; } StdOut << ".SH BUGS\nReport bugs at http://nitrc.org/projects/cmtk/\n"; ppit = this->m_ProgramInfo.find(PRG_ACKNL); if ( ppit != this->m_ProgramInfo.end() ) { StdOut << ".SH ACKNOWLEDGMENTS\n" << ppit->second << "\n"; } } } // namespace cmtk cmtk-3.3.1/libs/System/cmtkCommandLineNonOptionParameter.cxx000066400000000000000000000047011276303427400242010ustar00rootroot00000000000000/* // // Copyright 2009-2011, 2013-2014 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 5333 $ // // $LastChangedDate: 2014-04-25 16:39:07 -0700 (Fri, 25 Apr 2014) $ // // $LastChangedBy: torstenrohlfing $ // */ #include "cmtkCommandLine.h" #include #include void cmtk::CommandLine::NonOptionParameter ::Evaluate( const size_t argc, const char* argv[], size_t& index ) { if ( this->Flag ) *this->Flag = true; if ( index < argc ) { *this->Var = argv[index]; } else { if ( ! (this->m_Properties & PROPS_OPTIONAL) ) throw( Exception( "Argument missing", index ) ); } } mxml_node_t* cmtk::CommandLine::NonOptionParameter ::MakeXMLWithIndex( mxml_node_t *const parent, const int index ) const { mxml_node_t *node = Item::Helper::MakeXML( this, parent ); if ( node ) { if ( ! this->m_Name.empty() ) { Coverity::FakeFree( mxmlNewText( mxmlNewElement( node, "name" ), 0, this->m_Name.c_str() ) ); Coverity::FakeFree( mxmlNewText( mxmlNewElement( node, "label" ), 0, this->m_Name.c_str() ) ); } if ( ! this->m_Comment.empty() ) { Coverity::FakeFree( mxmlNewText( mxmlNewElement( node, "description" ), 0, this->m_Comment.c_str() ) ); } if ( index >= 0 ) { std::ostringstream strm; strm << index; Coverity::FakeFree( mxmlNewText( mxmlNewElement( node, "index" ), 0, strm.str().c_str() ) ); } } return node; } std::string cmtk::CommandLine::NonOptionParameter ::GetParamTypeString() const { return Item::Helper::GetParamTypeString( this ); } cmtk-3.3.1/libs/System/cmtkCommandLineNonOptionParameterVector.cxx000066400000000000000000000052251276303427400253660ustar00rootroot00000000000000/* // // Copyright 2009-2011, 2013 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4898 $ // // $LastChangedDate: 2013-09-30 16:10:27 -0700 (Mon, 30 Sep 2013) $ // // $LastChangedBy: torstenrohlfing $ // */ #include "cmtkCommandLine.h" #include #include void cmtk::CommandLine::NonOptionParameterVector ::Evaluate( const size_t argc, const char* argv[], size_t& index ) { if ( this->Flag ) *this->Flag = true; if ( index < argc ) { while ( (index < argc) && strcmp( argv[index], "--" ) ) { this->Var->push_back( std::string( argv[index++] ) ); } if (index < argc) ++index; // skip "--" separator } else { if ( ! (this->m_Properties & PROPS_OPTIONAL) ) throw( Exception( "Non-option vector missing at least one parameter", index ) ); } } mxml_node_t* cmtk::CommandLine::NonOptionParameterVector ::MakeXMLWithIndex( mxml_node_t *const parent, const int index ) const { mxml_node_t *node = Item::Helper::MakeXML( this, parent ); if ( node ) { if ( ! this->m_Name.empty() ) { Coverity::FakeFree( mxmlNewText( mxmlNewElement( node, "name" ), 0, this->m_Name.c_str() ) ); Coverity::FakeFree( mxmlNewText( mxmlNewElement( node, "label" ), 0, this->m_Name.c_str() ) ); } if ( ! this->m_Comment.empty() ) { Coverity::FakeFree( mxmlNewText( mxmlNewElement( node, "description" ), 0, this->m_Comment.c_str() ) ); } if ( index >= 0 ) { std::ostringstream strm; strm << index; Coverity::FakeFree( mxmlNewText( mxmlNewElement( node, "index" ), 0, strm.str().c_str() ) ); } } return node; } std::string cmtk::CommandLine::NonOptionParameterVector ::GetParamTypeString() const { return Item::Helper::GetParamTypeString( this ); } cmtk-3.3.1/libs/System/cmtkCommandLineOption.txx000066400000000000000000000061201276303427400217030ustar00rootroot00000000000000/* // // Copyright 2009, 2011, 2013 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4898 $ // // $LastChangedDate: 2013-09-30 16:10:27 -0700 (Mon, 30 Sep 2013) $ // // $LastChangedBy: torstenrohlfing $ // */ #include #include template void cmtk::CommandLine::Option ::Evaluate( const size_t argc, const char* argv[], size_t& index ) { if ( this->Flag ) *(this->Flag) = true; if ( index+1 < argc ) { *(this->Var) = this->Convert( argv[index+1] ); ++index; } else { throw( Exception( "Option needs an argument.", index ) ); } } template mxml_node_t* cmtk::CommandLine::Option ::MakeXML( mxml_node_t *const parent ) const { if ( ! (this->m_Properties & PROPS_NOXML) ) { if ( Flag ) // if there is a flag monitoring this option, then we need to create an additional boolean toggle { } mxml_node_t *node = Item::Helper::MakeXML( this, parent ); if ( !Flag ) // if there is no flag monitoring this option, then there must be a valid default value { mxml_node_t *dflt = mxmlNewElement( node, "default" ); Coverity::FakeFree( mxmlNewText( dflt, 0, CommandLineTypeTraits::ValueToStringMinimal( *(this->Var) ).c_str() ) ); } return node; } return NULL; } template std::string cmtk::CommandLine::Option ::GetParamTypeString() const { return Item::Helper::GetParamTypeString( this ); } template std::ostringstream& cmtk::CommandLine::Option ::PrintHelp( std::ostringstream& fmt ) const { if ( this->Flag && !(*this->Flag) ) fmt << "\n[Default: disabled]"; else fmt << "\n[Default: " << CommandLineTypeTraits::ValueToString( *(this->Var) ) << "]"; return fmt; } template void cmtk::CommandLine::Option ::PrintWiki() const { if ( this->Flag && !(*this->Flag) ) StdOut << " '''[Default: disabled]'''"; else StdOut << " '''[Default: " << CommandLineTypeTraits::ValueToString( *(this->Var) ) << "]'''"; } template void cmtk::CommandLine::Option ::PrintMan() const { if ( this->Flag && !(*this->Flag) ) StdOut << ".B [Default: disabled]\n"; else StdOut << ".B [Default: " << CommandLineTypeTraits::ValueToString( *(this->Var) ) << "]\n"; } cmtk-3.3.1/libs/System/cmtkCommandLineTypeTraits.h000066400000000000000000000152421276303427400221540ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2013 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4835 $ // // $LastChangedDate: 2013-09-12 15:48:47 -0700 (Thu, 12 Sep 2013) $ // // $LastChangedBy: torstenrohlfing $ // */ #include #include #include namespace cmtk { /** \addtogroup System */ //@{ template class CommandLineTypeTraitsBase { public: /// Convert a value of this type to string. static std::string ValueToString( const T& value ) { std::ostringstream stream; stream << value; return stream.str(); } /// Convert a value of this type to string with minimal added markup (for XML output). static std::string ValueToStringMinimal( const T& value ) { std::ostringstream stream; stream << value; return stream.str(); } }; /// Template for traits to handle command line arguments of different types. template class CommandLineTypeTraits /// Inherit generic template members : public CommandLineTypeTraitsBase { public: /// Return name of the parameter type (for XML). static const char* GetName() { return "none"; } }; template<> class CommandLineTypeTraits /// Inherit generic template members : public CommandLineTypeTraitsBase { public: static const char* GetName() { return "string"; } static std::string ValueToString( const char*& value ) { std::ostringstream stream; if ( value ) stream << "\"" << value << "\""; else stream << "NONE"; return stream.str(); } static std::string ValueToStringMinimal( const char*& value ) { std::ostringstream stream; if ( value ) stream << value; return stream.str(); } }; template<> class CommandLineTypeTraits /// Inherit generic template members : public CommandLineTypeTraitsBase { public: static const char* GetName() { return "string"; } static std::string ValueToString( const std::string& value ) { std::ostringstream stream; if ( value.length() ) stream << "\"" << value << "\""; else stream << "NONE"; return stream.str(); } static std::string ValueToStringMinimal( const std::string& value ) { std::ostringstream stream; stream << value; return stream.str(); } }; template<> class CommandLineTypeTraits< std::vector > /// Inherit generic template members : public CommandLineTypeTraitsBase< std::vector > { public: static const char* GetName() { return "vector"; } static std::string ValueToString( const std::vector& value ) { std::ostringstream stream; for ( size_t i = 0; i < value.size(); ++i ) stream << value[i] << " "; return stream.str(); } /// Convert a value of this type to string with minimal added markup (for XML output). static std::string ValueToStringMinimal( const std::vector& value ) { return ValueToString( value ); } }; template<> class CommandLineTypeTraits /// Inherit generic template members : public CommandLineTypeTraitsBase { public: static const char* GetName() { return "integer"; } }; template<> class CommandLineTypeTraits /// Inherit generic template members : public CommandLineTypeTraitsBase { public: static const char* GetName() { return "integer"; } }; template<> class CommandLineTypeTraits /// Inherit generic template members : public CommandLineTypeTraitsBase { public: static const char* GetName() { return "integer"; } }; template<> class CommandLineTypeTraits /// Inherit generic template members : public CommandLineTypeTraitsBase { public: static const char* GetName() { return "integer"; } }; template<> class CommandLineTypeTraits /// Inherit generic template members : public CommandLineTypeTraitsBase { public: /// Convert a value of this type to numerical string. static std::string ValueToString( const signed char& value ) { std::ostringstream stream; stream << static_cast( value ); return stream.str(); } /// Convert a value of this type to numerical string with minimal added markup (for XML output). static std::string ValueToStringMinimal( const signed char& value ) { std::ostringstream stream; stream << static_cast( value ); return stream.str(); } static const char* GetName() { return "integer"; } }; template<> class CommandLineTypeTraits : /// Inherit generic template members public CommandLineTypeTraitsBase { public: /// Convert a value of this type to numerical string. static std::string ValueToString( const unsigned char& value ) { std::ostringstream stream; stream << static_cast( value ); return stream.str(); } /// Convert a value of this type to numerical string with minimal added markup (for XML output). static std::string ValueToStringMinimal( const unsigned char& value ) { std::ostringstream stream; stream << static_cast( value ); return stream.str(); } static const char* GetName() { return "integer"; } }; template<> class CommandLineTypeTraits /// Inherit generic template members : public CommandLineTypeTraitsBase { public: static const char* GetName() { return "float"; } }; template<> class CommandLineTypeTraits /// Inherit generic template members : public CommandLineTypeTraitsBase { public: static const char* GetName() { return "double"; } }; template<> class CommandLineTypeTraits /// Inherit generic template members : public CommandLineTypeTraitsBase { public: static const char* GetName() { return "boolean"; } }; } // namespace cmtk cmtk-3.3.1/libs/System/cmtkCommandLineVector.txx000066400000000000000000000056221276303427400217030ustar00rootroot00000000000000/* // // Copyright 2009 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 779 $ // // $LastChangedDate: 2009-11-16 15:33:49 -0800 (Mon, 16 Nov 2009) $ // // $LastChangedBy: torstenrohlfing $ // */ #include #include template void cmtk::CommandLine::Vector ::Evaluate( const size_t argc, const char* argv[], size_t& index ) { if ( !this->m_HasBeenUsed ) { this->m_pVector->resize(0); this->m_HasBeenUsed = true; } if ( index+1 < argc ) { ++index; // first, replace all commas with spaces, so we can simply use a stringstream for parsing the vector elements std::string str( argv[index] ); for ( size_t i = 0; i < str.length(); ++i ) { if ( str[i] == ',' ) str[i] = ' '; } // new read values from the whitespaced argument std::istringstream strm( str ); while ( strm.good() && ! strm.eof() ) { T nextValue; strm >> nextValue; this->m_pVector->push_back( nextValue ); } } else { throw( Exception( "Vector command line option needs an argument.", index ) ); } } template mxml_node_t* cmtk::CommandLine::Vector ::MakeXML( mxml_node_t *const parent ) const { if ( ! (this->m_Properties & PROPS_NOXML) ) { const std::string typeName = std::string ( CommandLineTypeTraits::GetName() ) + std::string( "-vector" ); mxml_node_t *node = mxmlNewElement( parent, typeName.c_str() ); // write any attributes the user might have set for ( std::map::const_iterator attrIt = this->m_Attributes.begin(); attrIt != this->m_Attributes.end(); ++attrIt ) { mxmlElementSetAttr( node, attrIt->first.c_str(), attrIt->second.c_str() ); } mxmlElementSetAttr( node, "multiple", "true" ); return node; } return NULL; } template std::string cmtk::CommandLine::Vector ::GetParamTypeString() const { const std::string& singleItemString = Item::Helper::GetParamTypeString( this ); return singleItemString+std::string("[,")+singleItemString+std::string(",...]"); } cmtk-3.3.1/libs/System/cmtkCommandLineWiki.cxx000066400000000000000000000070261276303427400213230ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2011 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 3473 $ // // $LastChangedDate: 2011-10-19 11:36:13 -0700 (Wed, 19 Oct 2011) $ // // $LastChangedBy: torstenrohlfing $ // */ #include #include "cmtkCommandLine.h" #include namespace cmtk { /** \addtogroup System */ //@{ void CommandLine::PrintWiki () const { ProgramPropertiesMapType::const_iterator ppit = this->m_ProgramInfo.find(PRG_TITLE); if ( ppit != this->m_ProgramInfo.end() ) { StdOut << "== Title ==\n\n"; StdOut << ppit->second << "\n\n"; } ppit = this->m_ProgramInfo.find(PRG_DESCR); if ( ppit != this->m_ProgramInfo.end() ) { StdOut << "== Description ==\n\n"; StdOut << ppit->second << "\n\n"; } ppit = this->m_ProgramInfo.find(PRG_SYNTX); if ( ppit != this->m_ProgramInfo.end() ) { StdOut << "== Syntax ==\n\n"; StdOut << ppit->second << "\n\n"; } else { if ( this->m_NonOptionParameterList.size() || this->m_NonOptionParameterVectorList.size() ) { StdOut << "== Syntax ==\n\n"; StdOut << ": [options] "; for ( NonOptionParameterListType::const_iterator it = this->m_NonOptionParameterList.begin(); it != this->m_NonOptionParameterList.end(); ++it ) { StdOut << (*it)->m_Name << " "; } for ( NonOptionParameterVectorListType::const_iterator it = this->m_NonOptionParameterVectorList.begin(); it != this->m_NonOptionParameterVectorList.end(); ++it ) { StdOut << (*it)->m_Name << " "; } StdOut << "\n\nwhere\n"; for ( NonOptionParameterListType::const_iterator it = this->m_NonOptionParameterList.begin(); it != this->m_NonOptionParameterList.end(); ++it ) { StdOut << "\n"; StdOut << "; " << (*it)->m_Name << " : "; StdOut << (*it)->m_Comment << "\n";; } for ( NonOptionParameterVectorListType::const_iterator it = this->m_NonOptionParameterVectorList.begin(); it != this->m_NonOptionParameterVectorList.end(); ++it ) { StdOut << "\n"; StdOut << "; " << (*it)->m_Name << " : "; StdOut << (*it)->m_Comment << "\n";; } } } StdOut << "\n== List of Supported Options ==\n\n"; for ( KeyActionGroupListType::const_iterator grp = this->m_KeyActionGroupList.begin(); grp != this->m_KeyActionGroupList.end(); ++grp ) { if ( ! (*grp)->m_KeyActionList.empty() ) { StdOut << "=== " << (*grp)->m_Description << " ===\n\n"; const KeyActionListType& kal = (*grp)->m_KeyActionList; for ( KeyActionListType::const_iterator it = kal.begin(); it != kal.end(); ++it ) { (*it)->PrintWikiWithPrefix(); StdOut << "\n"; } } } StdOut << "\n"; } } // namespace cmtk cmtk-3.3.1/libs/System/cmtkCommandLineXML.cxx000066400000000000000000000112131276303427400210510ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2011, 2013 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4904 $ // // $LastChangedDate: 2013-10-01 10:58:09 -0700 (Tue, 01 Oct 2013) $ // // $LastChangedBy: torstenrohlfing $ // */ #include #include "cmtkCommandLine.h" #include #include #include namespace cmtk { /** \addtogroup System */ //@{ mxml_node_t* CommandLine::AddProgramInfoXML( mxml_node_t *const parent, const ProgramProperties key, const char* name ) const { ProgramPropertiesMapType::const_iterator it = this->m_ProgramInfo.find( key ); if ( it != this->m_ProgramInfo.end() ) { mxml_node_t *node = mxmlNewElement( parent, name ); Coverity::FakeFree( mxmlNewText( node, 0, it->second.c_str() ) ); return node; } return NULL; } const char * cmtkWhitespaceWriteMiniXML( mxml_node_t*, int where) { switch ( where ) { case MXML_WS_BEFORE_OPEN: return NULL; case MXML_WS_AFTER_OPEN: return "\n"; case MXML_WS_BEFORE_CLOSE: return "\n"; case MXML_WS_AFTER_CLOSE: return "\n"; } return NULL; } void CommandLine::WriteXML () const { // check for globally disabled XML support (some tools should not be used as Slicer plugins, for example) if ( ! (this->m_Properties & PROPS_NOXML) ) { // The following is what // mxml_node_t *xml = mxmlNewXML("1.0"); // would do using MiniXML 2.6, but this would be missing the "encoding" property on earlier versions. // So we'll just do it "by hand." mxml_node_t *xml = mxmlNewElement( NULL, "?xml version=\"1.0\" encoding=\"utf-8\"?" ); // now build XML description for Slicer. mxml_node_t *x_exec = mxmlNewElement(xml, "executable"); this->AddProgramInfoXML( x_exec, PRG_CATEG, "category" ); this->AddProgramInfoXML( x_exec, PRG_TITLE, "title" ); this->AddProgramInfoXML( x_exec, PRG_DESCR, "description" ); this->AddProgramInfoXML( x_exec, PRG_LCNSE, "license" ); this->AddProgramInfoXML( x_exec, PRG_CNTRB, "contributor" ); this->AddProgramInfoXML( x_exec, PRG_ACKNL, "acknowledgements" ); this->AddProgramInfoXML( x_exec, PRG_DOCUM, "documentation-url" ); this->AddProgramInfoXML( x_exec, PRG_VERSN, "version" ); for ( KeyActionGroupListType::const_iterator grp = this->m_KeyActionGroupList.begin(); grp != this->m_KeyActionGroupList.end(); ++grp ) { if ( ! ((*grp)->GetProperties() & PROPS_NOXML) && ! (*grp)->m_KeyActionList.empty() ) { mxml_node_t *parameterGroup = mxmlNewElement( x_exec, "parameters" ); if ( (*grp)->GetProperties() & PROPS_ADVANCED ) mxmlElementSetAttr( parameterGroup, "advanced", "true" ); const std::string& name = (*grp)->m_Name; if ( name == "MAIN" ) { Coverity::FakeFree( mxmlNewText( mxmlNewElement( parameterGroup, "label" ), 0, "General" ) ); Coverity::FakeFree( mxmlNewText( mxmlNewElement( parameterGroup, "description" ), 0, "General Parameters" ) ); int index = 0; for ( NonOptionParameterListType::const_iterator it = this->m_NonOptionParameterList.begin(); it != this->m_NonOptionParameterList.end(); ++it ) { (*it)->MakeXMLWithIndex( parameterGroup, index++ ); } } else { Coverity::FakeFree( mxmlNewText( mxmlNewElement( parameterGroup, "label" ), 0, name.c_str() ) ); Coverity::FakeFree( mxmlNewText( mxmlNewElement( parameterGroup, "description" ), 0, (*grp)->m_Description.c_str() ) ); } const KeyActionListType& kal = (*grp)->m_KeyActionList; for ( KeyActionListType::const_iterator it = kal.begin(); it != kal.end(); ++it ) { (*it)->MakeXML( parameterGroup ); } } } mxmlSaveFile( xml, stdout, cmtkWhitespaceWriteMiniXML ); fputs( "\n", stdout ); // Slicer's XML parser needs an extra \n after the last line mxmlDelete( xml ); } } } // namespace cmtk cmtk-3.3.1/libs/System/cmtkCompressedStream.cxx000066400000000000000000000135101276303427400215640ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2014 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 5360 $ // // $LastChangedDate: 2014-06-24 17:33:04 -0700 (Tue, 24 Jun 2014) $ // // $LastChangedBy: torstenrohlfing $ // */ #include #include #include #include #include #include #include #include #include #ifdef HAVE_SYS_TYPES_H # include #endif #ifdef _MSC_VER # include #endif namespace cmtk { /** \addtogroup System */ //@{ const CompressedStream::ArchiveLookupEntry CompressedStream::ArchiveLookup[] = { #ifdef _MSC_VER {".Z", "gunzip -cd %s > %s"}, {".gz", "gzip -cd %s > %s"}, {".bz", "bzip -Q -cd %s > %s"}, {".bz2", "bzip2 -cd %s > %s"}, {".lzma", "xz -cd %s > %s"}, {".xz", "xz -cd %s > %s"}, #else {".Z", "gunzip -c %s"}, {".gz", "gzip -cd %s"}, {".bz", "bzip -Q -cd %s"}, {".bz2", "bzip2 -cd %s"}, {".lzma", "xz -cd %s"}, {".xz", "xz -cd %s"}, #endif { NULL, NULL} }; CompressedStream::CompressedStream ( const std::string& filename ) : m_Reader( NULL ), m_Compressed( false ) { this->Open( MountPoints::Translate( filename ) ); } CompressedStream::~CompressedStream () { this->Close(); } bool CompressedStream::Open ( const std::string& filename ) { this->Close(); if ( Self::Stat( filename.c_str() ) == 2 ) { StdErr << "WARNING: file '" << filename << "' exists both compressed and uncompressed!\n"; } this->m_Compressed = false; std::string suffix = ""; const size_t period = filename.rfind( '.' ); if ( period != std::string::npos ) { suffix = filename.substr( period, std::string::npos ); for ( int i=0; ArchiveLookup[i].suffix && !this->m_Compressed; ++i ) this->m_Compressed = this->m_Compressed || ( suffix == ArchiveLookup[i].suffix ); } try { if ( !this->m_Compressed ) { this->m_Reader = ReaderBase::SmartPtr( new Self::File( filename ) ); } } catch (...) { } try { if ( ! this->m_Reader ) { bool result = false; for ( int i=0; ArchiveLookup[i].suffix && !result; ++i ) result = this->OpenDecompressionPipe( filename, suffix, ArchiveLookup[i].command, ArchiveLookup[i].suffix ); this->m_Compressed = true; } } catch ( ... ) { this->m_Reader = ReaderBase::SmartPtr( NULL ); } return this->IsValid(); } void CompressedStream::Close() { if ( this->m_Reader ) { this->m_Reader->Close(); this->m_Reader = ReaderBase::SmartPtr( NULL ); } } bool CompressedStream::OpenDecompressionPipe ( const std::string& filename, const std::string& suffix, const char* command, const char* compressedSuffix ) { std::string fname = filename; if ( suffix != compressedSuffix ) fname = fname + compressedSuffix; #ifdef _MSC_VER std::replace( fname.begin(), fname.end(), '/', '\\' ); #endif Self::StatType buf; if ( (! #ifdef CMTK_USE_STAT64 stat64( fname.c_str(), &buf ) #else stat( fname.c_str(), &buf ) #endif ) && ( (buf.st_mode & S_IFREG) == S_IFREG ) ) { if ( !strcmp( compressedSuffix, ".gz" ) ) { this->m_Reader = ReaderBase::SmartPtr( new Self::Zlib( fname ) ); } #ifdef CMTK_USE_BZIP2 else if ( !strcmp( compressedSuffix, ".bz2" ) ) { this->m_Reader = ReaderBase::SmartPtr( new Self::BZip2( fname ) ); } #endif #ifdef CMTK_USE_LZMA else if ( !strcmp( compressedSuffix, ".lzma" ) ) { this->m_Reader = ReaderBase::SmartPtr( new Self::LZMA( fname ) ); } #endif else { this->m_Reader = ReaderBase::SmartPtr( new Self::Pipe( fname, command ) ); } } return this->IsValid(); } std::string CompressedStream::GetBaseName( const std::string& path ) { const size_t suffixPos = path.rfind( '.' ); if ( suffixPos != std::string::npos ) { const std::string fileSuffix = path.substr( suffixPos, std::string::npos ); for ( int i = 0; ArchiveLookup[i].suffix; ++i ) { if ( fileSuffix == ArchiveLookup[i].suffix ) { return path.substr( 0, suffixPos ); } } } return path; } int CompressedStream::Stat( const std::string& path, Self::StatType* buf ) { const std::string baseName = CompressedStream::GetBaseName( MountPoints::Translate( path ) ); Self::StatType statbuf; if ( ! buf ) buf = &statbuf; #ifdef CMTK_USE_STAT64 const bool existsUncompressed = ! stat64( baseName.c_str(), buf ); #else const bool existsUncompressed = ! stat( baseName.c_str(), buf ); #endif for ( int i = 0; ArchiveLookup[i].suffix; ++i ) { const std::string cpath = baseName + std::string( ArchiveLookup[i].suffix ); if ( ! #ifdef CMTK_USE_STAT64 stat64( cpath.c_str(), buf ) #else stat( cpath.c_str(), buf ) #endif ) return existsUncompressed ? 2 : 1; } return existsUncompressed ? 0 : -1; } } // namespace cmtk cmtk-3.3.1/libs/System/cmtkCompressedStream.h000066400000000000000000000336171276303427400212230ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2013 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4876 $ // // $LastChangedDate: 2013-09-24 13:09:58 -0700 (Tue, 24 Sep 2013) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkCompressedStream_h_included_ #define __cmtkCompressedStream_h_included_ #include #include #include #include #include #include #ifdef CMTK_USE_BZIP2 # include #endif #ifdef CMTK_USE_LZMA # include #endif #ifdef HAVE_SYS_STAT_H # include #endif #include #if defined(_MSC_VER) #define CMTK_FILE_MODE "rb" #else #define CMTK_FILE_MODE "r" #endif namespace cmtk { /** \addtogroup System */ //@{ /// Stream with on-the-fly decompression class CompressedStream : /// Make class uncopyable via inheritance. private CannotBeCopied { public: /// This class. typedef CompressedStream Self; /// Type for stat() buffer #ifdef CMTK_USE_STAT64 typedef struct stat64 StatType; #else typedef struct stat StatType; #endif /// Create stream object without opening any files. CompressedStream() : m_Reader( NULL ), m_Compressed( false ) {}; /// Create stream from filename. CompressedStream ( const std::string& filename ); /// Dispose stream object. ~CompressedStream (); /// Return validity of stream. bool IsValid() const { return (this->m_Reader != NULL); } /// Return flag whether this stream is actually using decompression or simply reads a file as-is. bool IsCompressed() const { return this->m_Compressed; } /// Open new stream from filename. bool Open( const std::string& filename ); /// Close current file stream. void Close(); /** Set filepointer to beginning of file. */ void Rewind() { this->m_Reader->Rewind(); } /** Set filepointer. * If the object represents a pipe with on-the-fly decompression, only * the set mode "SEEK_CUR" is supported and will be simulated by successive * reading of 8kB data blocks from the pipe. *\param offset Offset the file pointer is set to, depending on the value of * whence. *\param whence File pointer set mode as defined for fseek. */ int Seek ( const long int offset, int whence ) { return this->m_Reader->Seek( offset, whence ); } /// Read block of data. size_t Read ( void *data, size_t size, size_t count ) { return this->m_Reader->Read( data, size, count ); } /// Read a single character from the stream. int Get ( char &c) { return this->m_Reader->Get( c ); } /// Return number of bytes read from stream. int Tell () const { return this->m_Reader->Tell(); } /// Return 1 if and only if end of file reached. bool Feof () const { return this->m_Reader->Feof(); } /** Return base name of a path without compression suffix. */ static std::string GetBaseName( const std::string& path ); /** Do stat() on compressed file. *\return -1 if the file does not exist; 0 if the file exists under the given name; 1 if the file exists * with an additional suffix corresponding to one of the supported compression schemes, e.g., ".gz"; 2 if * the file exists with both its plain name AND at least one compressed suffix. The last case indicates a * potential consistency problem because it is not clear, which file should be read. */ static int Stat( const std::string& path, Self::StatType *const buf = NULL ); private: /** Open decompressing pipe. * A suffix is appended to the desired filename, unless the name has * has already been given this suffix. Afterwards, a pipe through a * user-defined command is opened. *\param filename The name of the file to read through the pipe. *\param suffix Actual suffix of the file to be read through the pipe. *\param command The command to pipe the file through. This should be the * name of a program that reads the file with the given name (referenced by * "%s" in the command string) and writes its output to stdout. *\param compressedSuffix The file suffix corresponding to input files for the given * pipe command, e.g. ".gz" for gzip. */ bool OpenDecompressionPipe ( const std::string& filename, const std::string& suffix, const char* command, const char* compressedSuffix ); /** Entry for the suffix-to-archiver assignment table. */ typedef struct { /// A compressed file suffix such as .gz const char *suffix; /** Command to decompress a file with this suffix to a pipe. * For .gz, this command would be "gzip -df". */ const char *command; } ArchiveLookupEntry; /// The suffix-to-archiver assignment table. static const ArchiveLookupEntry ArchiveLookup[]; private: /// Abstract base class for low-level reader engines. class ReaderBase { public: /// This class. typedef ReaderBase Self; /// Smart pointer to this class. typedef SmartPointer SmartPtr; /// Default constructor. ReaderBase() : m_BytesRead( 0 ) {} /// Virtual destructor. virtual ~ReaderBase() {} /// Close current file stream. virtual void Close() = 0; /// Reset read pointer to beginning of stream. virtual void Rewind() { this->m_BytesRead = 0; } /** Set filepointer. * This class implements a naive seek that optionally calls "this->Rewind()" (if * "whence" is SEEK_SET, then reads "offset" bytes from the input to position * read pointer. *\param offset Offset the file pointer is set to, depending on the value of * whence. *\param whence File pointer set mode as defined for fseek. */ virtual int Seek ( const long int offset, int whence ); /// Read block of data. virtual size_t Read ( void *data, size_t size, size_t count ) = 0; /// Read a single character from the stream. virtual bool Get ( char &c) = 0; /// Return number of bytes read from stream. virtual int Tell () const = 0; /// Return 1 if and only if end of file reached. virtual bool Feof () const = 0; private: /// Block size for fake seek() operation. static const size_t SeekBlockSize = 8192; protected: /// Count number of bytes read from file or pipe. size_t m_BytesRead; }; /// Class for uncompressed file-based reader engine. class File : public ReaderBase { public: /// This class. typedef File Self; /// Smart pointer to this class. typedef SmartPointer SmartPtr; /// Open new stream from filename. File( const std::string& filename ); /// Virtual destructor. virtual ~File(); /// Close current file stream. virtual void Close(); /// Reset read pointer to beginning of stream. virtual void Rewind(); /** Set filepointer. * If the object represents a pipe with on-the-fly decompression, only * the set mode "SEEK_CUR" is supported and will be simulated by successive * reading of 8kB data blocks from the pipe. *\param offset Offset the file pointer is set to, depending on the value of * whence. *\param whence File pointer set mode as defined for fseek. */ virtual int Seek ( const long int offset, int whence ); /// Read block of data. virtual size_t Read ( void *data, size_t size, size_t count ); /// Read a single character from the stream. virtual bool Get ( char &c); /// Return number of bytes read from stream. virtual int Tell () const; /// Return 1 if and only if end of file reached. virtual bool Feof () const; private: /// File pointer. FILE* m_File; }; /// Class for reader engine using pipe. class Pipe : public ReaderBase { public: /// This class. typedef Pipe Self; /// Smart pointer to this class. typedef SmartPointer SmartPtr; /// Open new pipe from filename. Pipe( const std::string& filename, const char* command ); /// Virtual destructor. virtual ~Pipe(); /// Close current file stream. virtual void Close(); /// Reset read pointer to beginning of stream. virtual void Rewind(); /// Read block of data. virtual size_t Read ( void *data, size_t size, size_t count ); /// Read a single character from the stream. virtual bool Get ( char &c); /// Return number of bytes read from stream. virtual int Tell () const; /// Return 1 if and only if end of file reached. virtual bool Feof () const; private: /// File pointer. FILE* m_File; #ifdef _MSC_VER /** Temporary filename. * On platforms where no on-the-fly decompression by via a pipe is possible * (i.e. Windows), this holds the name of a temporary file used to hold * the decompressed input file. */ char m_TempName[256]; #endif }; #ifdef CMTK_USE_BZIP2 /// Class for BZip2-based reader engine. class BZip2 : public ReaderBase { public: /// This class. typedef BZip2 Self; /// Smart pointer to this class. typedef SmartPointer SmartPtr; /// Open new stream from filename. BZip2( const std::string& filename ); /// Virtual destructor. virtual ~BZip2() {} /// Close current file stream. virtual void Close(); /// Reset read pointer to beginning of stream. virtual void Rewind(); /// Read block of data. virtual size_t Read ( void *data, size_t size, size_t count ); /// Read a single character from the stream. virtual bool Get ( char &c); /// Return number of bytes read from stream. virtual int Tell () const; /// Return 1 if and only if end of file reached. virtual bool Feof () const; private: /// BZip2 file pointer. BZFILE* m_BzFile; /// BZip2 error variable. int m_BzError; }; #endif #ifdef CMTK_USE_LZMA /// Class for Zlib-based reader engine. class LZMA : public ReaderBase { public: /// This class. typedef LZMA Self; /// Smart pointer to this class. typedef SmartPointer SmartPtr; /// Open new stream from filename. LZMA( const std::string& filename ); /// Virtual destructor. virtual ~LZMA() {} /// Close current file stream. virtual void Close(); /// Reset read pointer to beginning of stream. virtual void Rewind(); /// Read block of data. virtual size_t Read ( void *data, size_t size, size_t count ); /// Read a single character from the stream. virtual bool Get ( char &c); /// Return number of bytes read from stream. virtual int Tell () const; /// Return 1 if and only if end of file reached. virtual bool Feof () const; private: /// LZMA file pointer. lzmadec_FILE* m_File; }; #endif // #ifdef CMTK_USE_LZMA /// The low-level reader object. ReaderBase::SmartPtr m_Reader; /// Flag whether current stream is from a compressed source. bool m_Compressed; public: /// Class for Zlib-based reader engine. class Zlib : public ReaderBase { public: /// This class. typedef Zlib Self; /// Smart pointer to this class. typedef SmartPointer SmartPtr; /// Open new stream from filename. Zlib( const std::string& filename ); /// Virtual destructor. virtual ~Zlib() {} /// Close current file stream. virtual void Close(); /// Reset read pointer to beginning of stream. virtual void Rewind(); /** Set filepointer. * If the object represents a pipe with on-the-fly decompression, only * the set mode "SEEK_CUR" is supported and will be simulated by successive * reading of 8kB data blocks from the pipe. *\param offset Offset the file pointer is set to, depending on the value of * whence. *\param whence File pointer set mode as defined for fseek. */ virtual int Seek ( const long int offset, int whence ); /** Read block of data. * This function, unlike gzread(), is safe to use on very large files. *\see http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=587912 *\remark Yaroslav Halchenko pointed this out. */ virtual size_t Read ( void *data, size_t size, size_t count ); /** Safe, static write function. * This is simply here as a service to other classes, which may use this as a plug-in replacement * for gzwrite() to work around a large-file problem in zlib. *\see http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=587912 *\remark Yaroslav Halchenko pointed this out. */ static size_t StaticSafeWrite ( gzFile file, const void *data, size_t size ); /// Read a single character from the stream. virtual bool Get ( char &c ); /// Return number of bytes read from stream. virtual int Tell () const; /// Return 1 if and only if end of file reached. virtual bool Feof () const; private: /// Zlib file pointer. gzFile m_GzFile; }; }; //@} } // namespace cmtk #endif // #ifndef __cmtkCompressedStream_h_included_ cmtk-3.3.1/libs/System/cmtkCompressedStreamBZip2.cxx000066400000000000000000000046661276303427400224470ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2013 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4875 $ // // $LastChangedDate: 2013-09-24 12:59:43 -0700 (Tue, 24 Sep 2013) $ // // $LastChangedBy: torstenrohlfing $ // */ #include "cmtkCompressedStream.h" #include #include #include namespace cmtk { CompressedStream::BZip2::BZip2( const std::string& filename ) : m_BzError( 0 ) { this->m_BzFile = BZ2_bzopen( filename.c_str(), CMTK_FILE_MODE ); if ( !this->m_BzFile ) { StdErr << "ERROR: CompressedStream::BZip2 could not open file '" << filename << "'\n"; throw ExitException( 1 ); } } void CompressedStream::BZip2::Close() { BZ2_bzclose( this->m_BzFile ); } void CompressedStream::BZip2::Rewind() { StdErr << "CompressedStream::BZip2::Rewind() is not implemented\n"; throw ExitException( 1 ); } size_t CompressedStream::BZip2::Read ( void *data, size_t size, size_t count ) { const size_t bytesRead = BZ2_bzRead( &this->m_BzError, this->m_BzFile, data, size * count ); if ( this->m_BzError < 0 ) { StdErr << "BZ2_bzRead() returned error " << this->m_BzError << "\n"; throw( ExitException( 1 ) ); } this->m_BytesRead += bytesRead; return bytesRead / size; } bool CompressedStream::BZip2::Get ( char &c) { if ( this->Feof() || !this->Read( &c, sizeof(char), 1 ) ) return false; return true; } int CompressedStream::BZip2::Tell () const { return this->m_BytesRead; } bool CompressedStream::BZip2::Feof () const { return this->m_BzError == BZ_STREAM_END; } } // namespace cmtk cmtk-3.3.1/libs/System/cmtkCompressedStreamFile.cxx000066400000000000000000000046551276303427400223760ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2012 SRI International // // Copyright 2011 Greg Jefferis // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4830 $ // // $LastChangedDate: 2013-09-11 14:23:35 -0700 (Wed, 11 Sep 2013) $ // // $LastChangedBy: torsten_at_home $ // */ #include #include #include #include namespace cmtk { /** \addtogroup System */ //@{ CompressedStream::File::File( const std::string& filename ) { this->m_File = fopen( filename.c_str(), CMTK_FILE_MODE ); if ( !this->m_File ) { throw 0; } } CompressedStream::File::~File() { this->Close(); } void CompressedStream::File::Close() { if ( this->m_File ) { fclose( this->m_File ); this->m_File = NULL; } } void CompressedStream::File::Rewind () { rewind( this->m_File ); this->CompressedStream::ReaderBase::Rewind(); } int CompressedStream::File::Seek ( const long int offset, int whence ) { return fseek( this->m_File, offset, whence ); } size_t CompressedStream::File::Read( void *data, size_t size, size_t count ) { const size_t itemsRead = fread( data, size, count, this->m_File ); this->m_BytesRead += itemsRead * size; return itemsRead; } bool CompressedStream::File::Get ( char &c) { const int data = fgetc( this->m_File ); if ( data != EOF ) { c=(char) data; ++this->m_BytesRead; return true; } return false; } int CompressedStream::File::Tell () const { return ftell( this->m_File ); } bool CompressedStream::File::Feof () const { return (feof( this->m_File ) != 0); } } // namespace cmtk cmtk-3.3.1/libs/System/cmtkCompressedStreamLZMA.cxx000066400000000000000000000044551276303427400222600ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2012 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4260 $ // // $LastChangedDate: 2012-04-25 16:34:20 -0700 (Wed, 25 Apr 2012) $ // // $LastChangedBy: torstenrohlfing $ // */ #include #include #include #include namespace cmtk { CompressedStream::LZMA::LZMA( const std::string& filename ) { this->m_File = lzmadec_open( filename.c_str() ); if ( !this->m_File ) { StdErr << "ERROR: lzmadec_open() failed for file '" << filename << "'\n"; throw ExitException( 1 ); } } void CompressedStream::LZMA::Close() { lzmadec_close( this->m_File ); } size_t CompressedStream::LZMA::Read ( void *data, size_t size, size_t count ) { const size_t result = lzmadec_read( this->m_File, reinterpret_cast( data ), size * count ); this->m_BytesRead += result; return result / size; } bool CompressedStream::LZMA::Get ( char &c) { const int data = lzmadec_getc( this->m_File ); if ( data != EOF ) { c=(char) data; ++this->m_BytesRead; return true; } return false; } void CompressedStream::LZMA::Rewind () { lzmadec_rewind( this->m_File ); this->CompressedStream::ReaderBase::Rewind(); } int CompressedStream::LZMA::Tell () const { return lzmadec_tell( this->m_File ); } bool CompressedStream::LZMA::Feof () const { return lzmadec_eof( this->m_File ); } } // namespace cmtk cmtk-3.3.1/libs/System/cmtkCompressedStreamPipe.cxx000066400000000000000000000070541276303427400224100ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2013 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 5128 $ // // $LastChangedDate: 2014-01-09 13:47:58 -0800 (Thu, 09 Jan 2014) $ // // $LastChangedBy: torstenrohlfing $ // */ #include "cmtkCompressedStream.h" #include #include #include #include #include #include namespace cmtk { /** \addtogroup System */ //@{ CompressedStream::Pipe::Pipe( const std::string& filename, const char* command ) { char cmd[PATH_MAX]; #ifndef _MSC_VER if ( static_cast( snprintf( cmd, sizeof( cmd ), command, filename.c_str() ) ) >= sizeof( cmd ) ) { StdErr << "WARNING: length of path exceeds system PATH_MAX in CompressedStream::OpenDecompressionPipe and will be truncated.\n"; } errno = 0; this->m_File = popen( cmd, CMTK_FILE_MODE ); if ( !this->m_File ) { fprintf( stderr, "ERROR: popen(\"%s\") returned NULL (errno=%d).\n", cmd, errno ); perror( "System message" ); throw 0; } #else this->m_TempName[0] = 0; if ( snprintf( cmd, sizeof( cmd ), command, filename, tmpnam( this->m_TempName) ) >= sizeof( cmd ) ) { StdErr << "WARNING: length of path exceeds system PATH_MAX in CompressedStream::OpenDecompressionPipe and will be truncated.\n"; } _flushall(); int sysReturn = system( cmd ); if ( sysReturn ) { fprintf( stderr, "Command %s returned %d\n", cmd, sysReturn ); fprintf( stderr, "Errno = %d\n", errno ); } this->m_File = fopen( this->m_TempName, CMTK_FILE_MODE); if ( !this->m_File ) { throw 0; } #endif this->m_BytesRead = 0; } CompressedStream::Pipe::~Pipe() { this->Close(); } void CompressedStream::Pipe::Close() { #ifndef _MSC_VER if ( this->m_File ) { pclose( this->m_File ); this->m_File = NULL; } #else if ( this->m_TempName[0] ) { remove( this->m_TempName ); this->m_TempName[0] = 0; } #endif // # ifndef _MSC_VER } void CompressedStream::Pipe::Rewind() { StdErr << "CompressedStream::Pipe::Rewind() is not implemented\n"; throw ExitException( 1 ); } size_t CompressedStream::Pipe::Read( void *data, size_t size, size_t count ) { const size_t result = fread( data, size, count, this->m_File ); this->m_BytesRead += result; return result / size; } bool CompressedStream::Pipe::Get ( char &c) { const int data = fgetc( this->m_File ); if ( data != EOF ) { c=(char) data; ++this->m_BytesRead; return true; } return false; } int CompressedStream::Pipe::Tell () const { return this->m_BytesRead; } bool CompressedStream::Pipe::Feof () const { return (feof( this->m_File ) != 0); } } // namespace cmtk cmtk-3.3.1/libs/System/cmtkCompressedStreamReaderBase.cxx000066400000000000000000000032331276303427400235030ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2010, 2013 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4875 $ // // $LastChangedDate: 2013-09-24 12:59:43 -0700 (Tue, 24 Sep 2013) $ // // $LastChangedBy: torstenrohlfing $ // */ #include "cmtkCompressedStream.h" int cmtk::CompressedStream::ReaderBase::Seek( const long int offset, int whence ) { if ( whence == SEEK_SET ) this->Rewind(); char buffer[Self::SeekBlockSize]; for ( int stillToRead = offset; stillToRead > 0; ) { if ( static_cast( stillToRead ) < Self::SeekBlockSize ) { this->Read( buffer, sizeof(char), stillToRead ); stillToRead = 0; } else { this->Read( buffer, sizeof(char), Self::SeekBlockSize ); stillToRead -= Self::SeekBlockSize; } } return this->m_BytesRead; } cmtk-3.3.1/libs/System/cmtkCompressedStreamZlib.cxx000066400000000000000000000063371276303427400224160ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2011 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4260 $ // // $LastChangedDate: 2012-04-25 16:34:20 -0700 (Wed, 25 Apr 2012) $ // // $LastChangedBy: torstenrohlfing $ // */ #include #include namespace cmtk { /** \addtogroup System */ //@{ CompressedStream::Zlib::Zlib( const std::string& filename ) { this->m_GzFile = gzopen( filename.c_str(), CMTK_FILE_MODE ); if ( !this->m_GzFile ) { throw 0; } } void CompressedStream::Zlib::Close() { gzclose( this->m_GzFile ); } void CompressedStream::Zlib::Rewind () { gzrewind( this->m_GzFile ); this->CompressedStream::ReaderBase::Rewind(); } int CompressedStream::Zlib::Seek ( const long int offset, int whence ) { return gzseek( this->m_GzFile, offset, whence ); } size_t CompressedStream::Zlib::Read ( void *data, size_t size, size_t count ) { // See http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=587912 // Thanks to Yaroslav Halchenko for pointing this out. #define BIG (((unsigned)(-1)>>2)+1) size_t result = 0; size_t total = size * count; while ( total ) { unsigned n = (total > BIG) ? BIG : (unsigned)total; int got = gzread( this->m_GzFile, data, n ); if (got < 0) return got; result += got; total -= got; data = reinterpret_cast( data ) + got; if (got < (int)n) break; } this->m_BytesRead += result; return result / size; #undef BIG } size_t CompressedStream::Zlib::StaticSafeWrite ( gzFile file, const void *data, size_t size ) { #define BIG (((unsigned)(-1)>>2)+1) size_t result = 0; while ( size ) { unsigned n = (size > BIG) ? BIG : (unsigned)size; int written = gzwrite( file, data, n ); if (written < 0) return written; result += written; size -= written; data = reinterpret_cast( data ) + written; if (written < (int)n) break; } return result; } bool CompressedStream::Zlib::Get ( char &c) { const int data = gzgetc( this->m_GzFile ); if ( data != EOF ) { c=(char) data; ++this->m_BytesRead; return true; } return false; } int CompressedStream::Zlib::Tell () const { return gztell( this->m_GzFile ); } bool CompressedStream::Zlib::Feof () const { return (gzeof( this->m_GzFile ) != 0); } } // namespace cmtk cmtk-3.3.1/libs/System/cmtkConsole.cxx000066400000000000000000000077751276303427400177260ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2012 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 3810 $ // // $LastChangedDate: 2012-02-02 15:38:48 -0800 (Thu, 02 Feb 2012) $ // // $LastChangedBy: torstenrohlfing $ // */ #include "cmtkConsole.h" #ifdef HAVE_UNISTD_H # include #endif #ifdef HAVE_SYS_IOCTL_H # include #endif #ifdef HAVE_TERMIOS_H # include #endif #include #include #include namespace cmtk { Console StdErr( &std::cerr ); Console StdOut( &std::cout ); Console StdNull( NULL ); size_t Console::GetLineWidth() const { // Allow override by user const char *env = getenv( "CMTK_CONSOLE_LINE_WIDTH" ); if ( env ) { const size_t width = atoi( env ); if ( width ) return width; } #if defined(HAVE_SYS_IOCTL_H) && defined(TIOCGWINSZ) struct winsize sz; if ( ioctl(0, TIOCGWINSZ, &sz) >= 0 ) return sz.ws_col; #endif // default to 80 return 80; } Console& Console::FormatText( const std::string& text, const size_t margin, const size_t width, const int firstLine ) { // Set current indentation to first line. size_t currentIndent = static_cast( std::max( 0, margin + firstLine ) ); // set the actual width const size_t actualWidth = width ? width : this->GetLineWidth(); // the effective length of a line size_t length = static_cast( std::max( 1, actualWidth - currentIndent ) ) - 1; // loop while text left to write std::string remaining = text; while ( remaining.length() > length ) { // first, see if we have forced line breaks within the first "length" characters. size_t breakAt = remaining.find_first_of( "\n\r", 0 ); // nothing forced in range, check for available spaces before max length if ( (breakAt == std::string::npos) || (breakAt >= length) ) breakAt = remaining.find_last_of( " ", length+1 ); // if no more spaces within available length, look for first available if ( breakAt == std::string::npos ) breakAt = remaining.find_first_of( " ", length+1 ); // if no more spaces at all, bail if ( breakAt == std::string::npos ) break; this->Indent( currentIndent ); (*this) << remaining.substr( 0, breakAt ) << "\n"; remaining.erase( 0, breakAt+1 ); currentIndent = margin; length = static_cast( std::max( 1, actualWidth - currentIndent ) ) - 1; } size_t breakAt = remaining.find_first_of( "\n\r", 0 ); while ( breakAt != std::string::npos ) { this->Indent( currentIndent ); (*this) << remaining.substr( 0, breakAt ) << "\n"; remaining.erase( 0, breakAt+1 ); breakAt = remaining.find_first_of( "\n\r", 0 ); currentIndent = margin; } this->Indent( currentIndent ); return (*this) << remaining << "\n"; } void Console::printf( const char* format, ... ) { char buffer[1024]; va_list args; va_start(args, format); vsnprintf( buffer, sizeof( buffer ), format, args ); va_end(args); this->Indent(); *this << buffer; } void Console::Indent ( size_t level ) { if ( ! level ) level = this->IndentLevel; for ( size_t i = 0; i < level; ++i ) (*this) << " "; } } // namespace cmtk cmtk-3.3.1/libs/System/cmtkConsole.h000066400000000000000000000074701276303427400173430ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2012 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4541 $ // // $LastChangedDate: 2012-10-04 12:56:38 -0700 (Thu, 04 Oct 2012) $ // // $LastChangedBy: torsten_at_home $ // */ #ifndef __cmtkConsole_h_included_ #define __cmtkConsole_h_included_ #include #include #include #include #include #include #include namespace cmtk { /** \addtogroup System */ //@{ /// Standard error output console for library. class Console : /// Make class uncopyable via inheritance. private CannotBeCopied { public: /// Constructor. Console( std::ostream* stream ) : m_StreamP( stream ) { this->IndentLevel = 0; } /** Get terminal line width, if possible. * The line width is determined using an ioctl() call, if available. Line width can be * set (or overridden) by the user by setting the "CMTK_CONSOLE_LINE_WIDTH" environment * variable to the desired number of characters per line. */ size_t GetLineWidth() const; /// Format text with line breaks etc. Console& FormatText( const std::string& text /*!< The text to format with line breaks.*/, const size_t margin = 0 /*!< Left margin: this many space characters are printed at the beginning of each line.*/, const size_t width = 0 /*m_StreamP ) { LockingPtr pStream( *this->m_StreamP, this->m_MutexLock ); pStream->flush(); } } /// Output stream operator. template Console& operator << ( const T data ) { if ( this->m_StreamP ) { LockingPtr pStream( *this->m_StreamP, this->m_MutexLock ); *pStream << data; } return *this; } /// Increment indentation level. void indent() { IndentLevel += 2; } /// Decrement indentation level. void unindent() { IndentLevel -= 2; } /// Implicit conversion to C++ ostream. operator std::ostream&() { assert( this->m_StreamP ); // will fail for StdNull! return *(this->m_StreamP); } private: /// The system stream that we're attaching to. std::ostream* m_StreamP; /// Indentiation level. size_t IndentLevel; /// Perform indentation. void Indent( size_t level = 0 ); /// Mutex lock for thread safety. MutexLock m_MutexLock; }; /// Standard error output for the library. extern Console StdErr; /// Standard output for the library. extern Console StdOut; /// Standard output for the library. extern Console StdNull; //@} } // namespace cmtk #endif // #ifndef __cmtkConsole_h_included_ cmtk-3.3.1/libs/System/cmtkCoverity.h000066400000000000000000000025601276303427400175400ustar00rootroot00000000000000/* // // Copyright 2013 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4898 $ // // $LastChangedDate: 2013-09-30 16:10:27 -0700 (Mon, 30 Sep 2013) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkCoverity_h_included_ #define __cmtkCoverity_h_included_ #include namespace cmtk { class Coverity { public: /// Pretend to free allocated memory to suppress CoverityScan false positives. // coverity[+free : arg-0] static void FakeFree( void *const ) {} }; } // namespace cmtk #endif // #ifndef __cmtkCoverity_h_included_ cmtk-3.3.1/libs/System/cmtkDebugOutput.h000066400000000000000000000050041276303427400201770ustar00rootroot00000000000000/* // // Copyright 2011, 2013 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4737 $ // // $LastChangedDate: 2013-05-14 14:10:27 -0700 (Tue, 14 May 2013) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkDebugOutput_h_included_ #define __cmtkDebugOutput_h_included_ #include #include namespace cmtk { /** \addtogroup System */ //@{ /// Class for debug output with different levels of detail. class DebugOutput { public: /// This class. typedef DebugOutput Self; /// Constructor. DebugOutput( const int level = 0 ) : m_Level( level ) {} /// Output operator. template Console& operator<<( const T data ) const { return this->GetStream() << data; } /// Flush the appropriate stream for this output object. void Flush() { if ( this->m_Level > Self::GetGlobalLevel() ) StdNull.flush(); else StdOut.flush(); } /// Get the appropriate stream for this output object. Console& GetStream() const { if ( this->m_Level > Self::GetGlobalLevel() ) return StdNull; else return StdOut; } /// Set global debug level. static void SetGlobalLevel( const long int level ) { Self::GetGlobalLevel() = level; } /// Increment global debug level by 1. static void IncGlobalLevel() { ++Self::GetGlobalLevel(); } /// Get global debug level (reference to static variable). static int& GetGlobalLevel() { static int globalLevel = 0; return globalLevel; } private: /** Level for this instance. * Output is suppressed if this is higher than the global level. */ int m_Level; }; //@} } // namespace cmtk #endif // #ifndef __cmtkDebugOutput_h_included_ cmtk-3.3.1/libs/System/cmtkException.h000066400000000000000000000054751276303427400177020ustar00rootroot00000000000000/* // // Copyright 1997-2010 Torsten Rohlfing // // Copyright 2004-2011 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 2752 $ // // $LastChangedDate: 2011-01-17 11:33:31 -0800 (Mon, 17 Jan 2011) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkException_h_included_ #define __cmtkException_h_included_ #include #include #include #include #include namespace cmtk { /** \addtogroup System */ //@{ /** Exception class. * Instances of this class are "thrown" in case of severe program errors. * They can be given an optional error message and a pointer to the object * that encountered the fatal condition. */ class Exception : /// Inherit from C++ standard exception. public std::exception { public: /** Constructor. *\param errorMsg An optional error message describing the condition causing * the exception. *\param fromObject An optional pointer to the object that encountered the * condition causing the exception. */ Exception( const std::string& errorMsg = "", const void *const fromObject = NULL ) { this->m_ErrorMsg = errorMsg; this->m_FromObject = fromObject; } /// Virtual destructor. virtual ~Exception() throw() {}; /** Return pointer to the error message. */ const std::string& GetErrorMsg() const { return this->m_ErrorMsg; } /** Return pointer to the object that encountered the error condition. */ const void* GetFromObject() const { return this->m_FromObject; } /// Overwrite inherited "what" member. virtual const char* what() const throw() { return this->m_ErrorMsg.c_str(); } private: /// Pointer to a string describing the condition causing the exception. std::string m_ErrorMsg; /// Pointer to the object that encountered the error condition. const void* m_FromObject; }; //@} /// Output of command line exception. Console& operator<<( Console& console, Exception e ); } // namespace cmtk #endif // #ifndef __cmtkException_h_included_ cmtk-3.3.1/libs/System/cmtkExitException.h000066400000000000000000000053041276303427400205230ustar00rootroot00000000000000/* // // Copyright 1997-2010 Torsten Rohlfing // // Copyright 2004-2010 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 2398 $ // // $LastChangedDate: 2010-10-05 14:54:37 -0700 (Tue, 05 Oct 2010) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkExitException_h_included_ #define __cmtkExitException_h_included_ #include #include #include #include #include namespace cmtk { /** \addtogroup System */ //@{ /** Program exit exception class. * Throwing an object of this class should return control to the global main() function, * which should exit() with the given exit status. This should ensure that all local * automatic as well as global static objects have their destructors called. * * The global main() function can make use of this exception via the following wrapper * construct, *\code * int * main( const int argc, const char* argv[] ) * { * int exitCode = 0; * try * { * exitCode = doMain( argc, argv ); * } * catch ( const cmtk::ExitException& ex ) * { * exitCode = ex.ExitCode(); * } * return exitCode; * } *\endcode * where the actual function of the program is moved into the doMain() implementation function. */ class ExitException : /// Inherit from C++ standard exception. public std::exception { public: /** Constructor. */ ExitException( const int exitCode = 0 /*!< Program exit code. */ ) : m_ExitCode( exitCode ) {} /// Virtual destructor. virtual ~ExitException() throw() {}; /// Overwrite inherited "what" member. virtual const char* what() const throw() { return "cmtk::ExitException"; } /// Return exit code. int ExitCode() const { return this->m_ExitCode; } private: /// Program exit code. const int m_ExitCode; }; //@} } // namespace cmtk #endif // #ifndef __cmtkExitException_h_included_ cmtk-3.3.1/libs/System/cmtkFFTW.h000066400000000000000000000053621276303427400165050ustar00rootroot00000000000000/* // // Copyright 2012 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4233 $ // // $LastChangedDate: 2012-04-19 14:41:15 -0700 (Thu, 19 Apr 2012) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkFFTW_h_included_ #define __cmtkFFTW_h_included_ #include #include #include namespace cmtk { /** \addtogroup System */ //@{ /** Interface class to FFTW library for fast Fourier transforms. * Among other things, this class handles the initialization and destruction of global FFTW properties, such as threads support. */ class FFTW { public: /// This class. typedef FFTW Self; /// Get static instance. static Self& GetStatic() { static Self staticInstance; return staticInstance; } /// In-place complex multiplication. static void MultiplyInPlace( fftw_complex& lhs, const fftw_complex& rhs ) { fftw_complex product; product[0] = lhs[0]*rhs[0] - lhs[1]*rhs[1]; product[1] = lhs[1]*rhs[0] + lhs[0]*rhs[1]; lhs[0] = product[0]; lhs[1] = product[1]; } /// Return sum of squares of real and imaginary parts of a complex number. static double SumOfSquares( const fftw_complex& c ) { return c[0]*c[0] + c[1]*c[1]; } /// Return magnitude of complex number. static double Magnitude( const fftw_complex& c ) { return sqrt( Self::SumOfSquares( c ) ); } /** Set number of threads for FFTW plans. * The number of threads applies only to subsequently created plans. Previously created plans * retain their original number of threads when executed. */ void SetNumberOfThreads( const int nThreads ) { #ifdef CMTK_USE_SMP fftw_plan_with_nthreads( nThreads ); #endif } protected: /// Constructor. FFTW() { #ifdef CMTK_USE_SMP fftw_init_threads(); #endif } /// Destructor. ~FFTW() { #ifdef CMTK_USE_SMP fftw_cleanup_threads(); #endif } }; } // namespace cmtk #endif // #ifndef __cmtkFFTW_h_included_ cmtk-3.3.1/libs/System/cmtkFileUtils.cxx000066400000000000000000000104071276303427400202060ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2013 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 5377 $ // // $LastChangedDate: 2015-01-16 18:42:56 -0800 (Fri, 16 Jan 2015) $ // // $LastChangedBy: torstenrohlfing $ // */ #include "cmtkFileUtils.h" #include #include #include #include #ifdef HAVE_UNISTD_H # include #endif #ifdef HAVE_SYS_TYPES_H # include #endif #ifdef HAVE_SYS_STAT_H # include #endif #ifdef _MSC_VER # include # include #endif #include namespace cmtk { /** \addtogroup System */ //@{ namespace FileUtils { int RecursiveMkDir( const std::string& filename, const int permissions ) { const int result = RecursiveMkPrefixDir( filename, permissions ); if ( result) return result; #ifdef _MSC_VER return _mkdir( filename.c_str() ); #else return mkdir( filename.c_str(), permissions ); #endif } int RecursiveMkPrefixDir ( const std::string& filename, const int permissions ) { char prefix[PATH_MAX]; for ( unsigned i=0; filename[i]; ++i ) { if ( (filename[i] == CMTK_PATH_SEPARATOR) || (filename[i] == '/') ) { prefix[i+1] = 0; if ( i ) // do not delete single "/" or "\" prefix[i] = 0; else prefix[i] = CMTK_PATH_SEPARATOR; #ifdef _MSC_VER if ( (i > 0) && (prefix[i-1] == ':') ) { prefix[i] = '\\'; } #endif #ifdef _MSC_VER int result = 0; // NOTE(fschulze@vrvis.at): prevent to call _mkdir on drive letters // (e.g. "C:\") because this would fail and subsequentually make the // whole function fail Furthermore drives are not folders and cannot // be created. // The current prefix describes a drive if the second letter is a colon // and we only have 3 letters const bool isDrive = (prefix[i-1]==':') && (i==2); if (!isDrive) { result = _mkdir( prefix ); } #else const int result = mkdir( prefix, permissions ); #endif if ( result && errno != EEXIST && errno != EISDIR ) { return result; } } prefix[i] = filename[i]; } return 0; } std::string GetAbsolutePath( const std::string& relPath ) { #ifdef _MSC_VER char absPath[PATH_MAX]; GetFullPathName( relPath.c_str(), PATH_MAX, absPath, NULL ); return std::string( absPath ); #else if ( relPath[0] == CMTK_PATH_SEPARATOR ) { return relPath; } else { char absPath[PATH_MAX]; getcwd( absPath, PATH_MAX ); if ( absPath[ strlen( absPath )-1 ] != CMTK_PATH_SEPARATOR ) strcat( absPath, CMTK_PATH_SEPARATOR_STR ); return std::string( absPath ) + relPath; } #endif } std::string Basename( const std::string& path, const std::string& suffix ) { std::string basename = path; // if given, and if present, remove suffix if ( ! suffix.empty() && (basename.length() >= suffix.length() ) ) { if ( basename.compare( basename.length() - suffix.length(), suffix.length(), suffix ) ) { basename = basename.substr( 0, basename.length() - suffix.length() ); } } // split into dirname and basename now const size_t separator = basename.rfind( CMTK_PATH_SEPARATOR ); if ( separator == std::string::npos ) { // no separator, return the whole thing return basename; } else { // seprator - return whatever comes after return basename.substr( separator+1 ); } } } // namespace FileUtils } // namespace cmtk cmtk-3.3.1/libs/System/cmtkFileUtils.h000066400000000000000000000042651276303427400176400ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2013 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 5084 $ // // $LastChangedDate: 2013-12-03 15:22:17 -0800 (Tue, 03 Dec 2013) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkFileUtil_h_included_ #define __cmtkFileUtil_h_included_ #include #include namespace cmtk { /** \addtogroup System */ //@{ /** Utility functions for file and directory access. */ namespace FileUtils { /** Recursively create a new directory. *\return Zero if operation was successful, mkdir() error code otherwise. */ int RecursiveMkDir( const std::string& filename, const int permissions = 0755 ); /** Recursively create all directories on a full filename's prefix. *\return Zero if operation was successful, mkdir() error code otherwise. */ int RecursiveMkPrefixDir( const std::string& filename, const int permissions = 0755 ); /// Make an absolute path name from a (possibly) relative path. std::string GetAbsolutePath( const std::string& relPath ); /// Return basename of a given path. std::string Basename( const std::string& path /*!< Complete path with directory and file name parts. */, const std::string& suffix = "" /*!< Optional argument: if given, this suffix is removed from the basename, if present. */ ); } //@} } // namespace cmtk #endif // #ifndef __cmtkFileUtil_h_included_ cmtk-3.3.1/libs/System/cmtkLockingPtr.h000066400000000000000000000042741276303427400200140ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2011 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 2941 $ // // $LastChangedDate: 2011-03-04 10:31:31 -0800 (Fri, 04 Mar 2011) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkLockingPtr_h_included_ #define __cmtkLockingPtr_h_included_ #include #include namespace cmtk { /** \addtogroup System */ //@{ /** Class for mutually exclusive access to objects. * This class is an adapted version of a concept by Andrei Alexandrescu of * RealNetworks Inc. *\see http://www.cuj.com/experts/1902/alexandr.htm?topic=experts */ template class LockingPtr { public: /// Create locking pointer and lock mutex. LockingPtr( T& object, MutexLock& mutexLock ) : m_Object( &object ), m_MutexLock( &mutexLock ) { this->m_MutexLock->Lock(); } /// Destroy locking pointer and unlock mutex. ~LockingPtr() { this->m_MutexLock->Unlock(); } /// Dereferencing operator. T& operator*() { return *this->m_Object; } /// Member access operator. T* operator->() { return this->m_Object; } private: /// Pointer to the accessed object. T* m_Object; /// The mutex lock. MutexLock* m_MutexLock; }; //@} } // namespace cmtk #endif // #ifndef __cmtkLockingPtr_h_included_ cmtk-3.3.1/libs/System/cmtkMemory.cxx000066400000000000000000000041011276303427400175500ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2010 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 2478 $ // // $LastChangedDate: 2010-10-20 15:07:14 -0700 (Wed, 20 Oct 2010) $ // // $LastChangedBy: torstenrohlfing $ // */ #include "cmtkMemory.h" #ifdef HAVE_MALLOC_H # include #endif #include // thanks to Hans Johnson for pointing this out #include namespace cmtk { namespace Memory { size_t GetNextPowerOfTwo( size_t k ) { // http://en.wikipedia.org/wiki/Power_of_two#Algorithm_to_find_the_next-highest_power_of_two if (k == 0) return 1; k--; for (size_t i=1; i> i; return k+1; } size_t Used () { #ifdef HAVE_MALLINFO struct mallinfo stats = mallinfo(); return stats.uordblks + stats.usmblks; #else return 0; #endif } void Info ( const char *msg ) { const int used = Used(); if (msg ) printf("%d bytes in use %s\n",used,msg); else printf("%d bytes in use.\n",used); } void Diff ( const size_t before, const char *msg ) { const int diff = Used()-before; if (diff<0) printf("%s freed %d bytes.\n",msg,-diff); else printf("%s allocated %d bytes.\n",msg,diff); } } // namespace Memory } // namespace cmtk cmtk-3.3.1/libs/System/cmtkMemory.h000066400000000000000000000120721276303427400172030ustar00rootroot00000000000000/* // // Copyright 1997-2010 Torsten Rohlfing // // Copyright 2004-2011 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 3579 $ // // $LastChangedDate: 2011-11-21 12:58:43 -0800 (Mon, 21 Nov 2011) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkMemory_h_included_ #define __cmtkMemory_h_included_ #include #include namespace cmtk { /** \addtogroup System */ //@{ /// Memory-related helper functions. namespace Memory { /** Utility function: get next power of two. * http://en.wikipedia.org/wiki/Power_of_two#Algorithm_to_find_the_next-highest_power_of_two */ size_t GetNextPowerOfTwo( size_t k ); /// Memory deallocation fucntion pointer. typedef void (*DeallocatorFunctionPointer)( void *const ); /** Memory allocation for C-style array. */ class ArrayC { public: /** Allocator function: calls malloc() *\warning Objects in the array will not be instantiated. */ template static T* Allocate( const size_t size ) { return static_cast( malloc( size * sizeof( T ) ) ); } /// Delete an array allocated using Allocate(). template static void Delete( T *const array ) { free( array ); } /** Delete an array allocated using Allocate(), but referred to by a void*. * This function provides a universal signature that we can use for function pointers. */ static void DeleteWrapper( void *const array ) { free( array ); } }; /** Memory allocation for C++-style array. */ class ArrayCXX { public: /** Allocator function: calls new[]() *\warning Objects in the array will not be instantiated. */ template static T* Allocate( const size_t size ) { return new T[size]; } /// Delete an array allocated using Allocate(). template static void Delete( T *const array ) { delete[]( array ); } /** Delete an array allocated using Allocate(), but referred to by a void*. * This function provides a universal signature that we can use for function pointers. */ template static void DeleteWrapper( void *const array ) { delete[]( static_cast( array ) ); } }; /** Set (fill) memory region with given value. * This is the templated version of memset() */ template inline void Set( T* const to, const T value, const size_t length ) { for ( size_t i = 0; i < length; ++i ) to[i] = value; } /** Copy memory region. * This is the templated version of memcpy() */ template inline void Copy( T* const to, const T* from, const size_t length ) { for ( size_t i = 0; i < length; ++i ) to[i] = from[i]; } /// Byte-swap arbitrary value. template T ByteSwap( T value ) { char *const cptr = reinterpret_cast( &value ); unsigned int j = sizeof(T)-1; for ( unsigned int i = 0; i < j; ++i, --j ) { char tmp = cptr[i]; cptr[i] = cptr[j]; cptr[j] = tmp; } return value; } /// Byte-swap arbitrary value in place. template void ByteSwapInPlace( T& value ) { char *const cptr = reinterpret_cast( &value ); unsigned int j = sizeof(T)-1; for ( unsigned int i = 0; i < j; ++i, --j ) { char tmp = cptr[i]; cptr[i] = cptr[j]; cptr[j] = tmp; } } /**\name Functions for tracing memory allocation and de-allocation. * These functions can be used to watch the amount of memory allocated and * freed by other functions. Memory holes can be identified and located. */ //@{ /** Get amount of memory used. *\return The number of bytes allocated by the process the calling function * is located in. */ size_t Used(); /** Print memory usage. *\param msg An optional message to be printed with the amount of bytes * allocated by the current process. This allows location of memory holes. * The parameter may be omitted or given as NULL if no additional message is * required. */ void Info ( const char *msg = NULL ); /** Print difference of memory usage. *\param before Number of bytes allocated before the inspected operation. This * parameter can be retrieved by al call to memused(). *\param msg Name of the operation the memory allocation of which was * inspected. */ void Diff ( const size_t before, const char *msg ); } // namespace Memory //@} } // namespace cmtk #endif // #ifndef __cmtkMemory_h_included_ cmtk-3.3.1/libs/System/cmtkMountPoints.cxx000066400000000000000000000062271276303427400206120ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2013 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4950 $ // // $LastChangedDate: 2013-10-08 14:27:35 -0700 (Tue, 08 Oct 2013) $ // // $LastChangedBy: torstenrohlfing $ // */ #include "cmtkMountPoints.h" #include #include #include namespace cmtk { /** \addtogroup System */ //@{ std::string MountPoints::Translate( const std::string& path ) { // Get environment variable defining mount points. const char *mountpoints = getenv( CMTK_MOUNTPOINTSVAR ); if ( ! mountpoints ) { mountpoints = getenv( IGS_MOUNTPOINTSVAR ); // Not defined: Return path unmodified if ( ! mountpoints ) return path; } std::string buffer = path; const char *nextRule = mountpoints; while ( nextRule ) { // find the equation sign between pattern and replacement const char* delim = strchr( nextRule, '=' ); if ( delim ) { const int cplen = delim - nextRule; const std::string pattern = std::string( nextRule ).substr( 0, cplen ); std::string replacement = std::string( delim+1 ); // see if there's another replacement rule following nextRule = strchr( delim, ',' ); if ( nextRule ) { /// if there is, remove it from the replacement string const int cplenNext = nextRule - delim - 1; replacement = replacement.substr( 0, cplenNext ); nextRule++; } else { nextRule = NULL; } // check for beginning-of-line token bool checkPrefixOnly = false; if ( pattern[0] == '^' ) { checkPrefixOnly = true; } if ( checkPrefixOnly ) { // Check if rule applies to given path. if ( path.substr( 0, pattern.length() - 1 ) == pattern.substr( 1 ) ) { // Yes, it does: Substitute prefix accordingly and return pointer // to buffer containing modified path. buffer = buffer.replace( 0, pattern.length() - 1, replacement ); } } else { // Substitute non-prefix occurences as well size_t found = buffer.find( pattern ); while ( found != std::string::npos ) { buffer = buffer.replace( found, pattern.length(), replacement ); found = buffer.find( pattern, found + replacement.length() ); // search after replaced string to avoid infinite recursive replacement } } } } return buffer; } } // namespace cmtk cmtk-3.3.1/libs/System/cmtkMountPoints.h000066400000000000000000000070071276303427400202340ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2012 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4260 $ // // $LastChangedDate: 2012-04-25 16:34:20 -0700 (Wed, 25 Apr 2012) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkMountPoints_h_included_ #define __cmtkMountPoints_h_included_ #include #include #include namespace cmtk { /** \addtogroup System */ //@{ /** Name of the shell variable defining the directory translation. * This variable can be set to contain a list of substitution rules. Every * rule has the form "search=replace", where all appearances of "search" are * to be replaced by "replace" in all filesystem paths. * * Several of these rules may be concatenated by ",". An example is "/cdrom=j:,/home=k:". This * results in all paths relative to "/cdrom" being relative to "j:" after * substituion. The same holds for "k:" and "/home". This example could be * used to read data on a PC mounting Unix filesystems to network drives. * * Conversely, the Unix box would define "j:=/cdrom,k:=/home" in order to be * able to read data written by this PC. *\see MountPoints */ const char* const CMTK_MOUNTPOINTSVAR = "CMTK_MOUNTPOINTS"; /** Legacy environment variable. * This is what CMTK_MOUNTPOINTS used to be called in an earlier life. We * continue to check for it so we don't break older scripts. */ const char* const IGS_MOUNTPOINTSVAR = "IGS_MOUNTPOINTS"; /** Directory translation. * This class implements a translation for file system paths. This is used to * dynamically change references to the file system when using data from one * system on another without changing the actual files. This is useful, for * example, when reading cross-referenced files from a CDROM on several * machines with different CD mount points. It is also useful for exchanging * files containing paths between Unix and Windows systems. */ class MountPoints { public: /** Perform directory substitutions. *\param path The original path before substitions. *\return A pointer to a static buffer holding the path after all substitions * have been done. The buffer is guaranteed to remain unchanged until and * only until the next time Translate() is called. *\todo There is really no reason why we parse the environment variable every time a * substitution is (potentially) performed. Instead, it should be parsed once and then * simply re-applied. *\see CMTK_MOUNTPOINTSVAR */ static std::string Translate ( const std::string& path ); private: /// Static buffer holding paths after pattern substitution. static char Buffer[PATH_MAX]; }; //@} } // namespace cmtk #endif // #ifndef __cmtkMountPoints_h_included_ cmtk-3.3.1/libs/System/cmtkMutexLock.h000066400000000000000000000054251276303427400176520ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2011 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 3288 $ // // $LastChangedDate: 2011-07-26 16:27:51 -0700 (Tue, 26 Jul 2011) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkMutexLock_h_included_ #define __cmtkMutexLock_h_included_ #include #include #if defined(CMTK_USE_PTHREADS) # include #endif #ifdef _MSC_VER # include #endif namespace cmtk { /** \addtogroup System */ //@{ /** Generic mutex lock. * This class represents a thread-model independent wrapper for locks on data * that requires mutually exclusive access. */ class MutexLock : /// Make class uncopyable via inheritance. private CannotBeCopied { public: /// Constructor: initialize low-level lock. MutexLock() { #if defined(CMTK_USE_PTHREADS) pthread_mutex_init( &this->m_MutexLock, NULL ); #else #ifdef _MSC_VER InitializeCriticalSection( &this->m_MutexObject ); #endif #endif } /// Destructor: clean up low-level lock. ~MutexLock() { #if defined(CMTK_USE_PTHREADS) pthread_mutex_destroy( &this->m_MutexLock ); #else #ifdef _MSC_VER DeleteCriticalSection( &this->m_MutexObject ); #endif #endif } /// Lock: if already locked, wait until unlocked, then lock. void Lock() { #if defined(CMTK_USE_PTHREADS) pthread_mutex_lock( &this->m_MutexLock ); #else #ifdef _MSC_VER EnterCriticalSection( &this->m_MutexObject ); #endif #endif } /// Unlock. void Unlock() { #if defined(CMTK_USE_PTHREADS) pthread_mutex_unlock( &this->m_MutexLock ); #else #ifdef _MSC_VER LeaveCriticalSection( &this->m_MutexObject ); #endif #endif } #if defined(CMTK_USE_PTHREADS) protected: /** Low-level mutex lock for POSIX threads. */ pthread_mutex_t m_MutexLock; #else #ifdef _MSC_VER CRITICAL_SECTION m_MutexObject; #endif #endif }; //@} } // namespace cmtk #endif // #ifndef __cmtkMutexLock_h_included_ cmtk-3.3.1/libs/System/cmtkProgress.cxx000066400000000000000000000060151276303427400201120ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2011 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 2999 $ // // $LastChangedDate: 2011-03-15 16:13:20 -0700 (Tue, 15 Mar 2011) $ // // $LastChangedBy: torstenrohlfing $ // */ #include "cmtkProgress.h" #include namespace cmtk { /** \addtogroup System */ //@{ Progress* Progress::ProgressInstance = 0; void Progress::Begin ( const double start, const double end, const double increment, const std::string& taskName ){ if ( ProgressInstance ) { ProgressInstance->BeginVirtual( start, end, increment, taskName ); } } void Progress::BeginVirtual ( const double start, const double end, const double increment, const std::string& taskName ) { this->m_RangeStack.push_front( Self::Range( start, end, increment, taskName ) ); } void Progress::SetProgressCurrent( const double progress ) { RangeStackType::iterator current = this->m_RangeStack.begin(); if ( current != this->m_RangeStack.end() ) { current->m_Current = progress; } } Progress::ResultEnum Progress::SetProgress( const double progress ) { if ( ProgressInstance ) { ProgressInstance->SetProgressCurrent( progress ); return ProgressInstance->UpdateProgress(); } else return Self::OK; } void Progress::Done() { if ( ProgressInstance ) ProgressInstance->DoneVirtual(); } void Progress::DoneVirtual() { if ( this->m_RangeStack.begin() != this->m_RangeStack.end() ) this->m_RangeStack.pop_front(); } double Progress::Range::GetFractionComplete( const double nestedFraction ) const { return ( ( this->m_Current + nestedFraction * this->m_Increment ) - this->m_Start) / (this->m_End - this->m_Start); } double Progress::GetFractionComplete() const { double fraction = 0; for ( RangeStackType::const_iterator it = this->m_RangeStack.begin(); it != this->m_RangeStack.end(); ++it ) { fraction = it->GetFractionComplete( fraction ); } return fraction; } const std::string Progress::GetCurrentTaskName() const { RangeStackType::const_reverse_iterator it = this->m_RangeStack.rbegin(); if ( it != this->m_RangeStack.rend() ) return it->m_TaskName; return std::string(""); } } // namespace cmtk cmtk-3.3.1/libs/System/cmtkProgress.h000066400000000000000000000104541276303427400175410ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2011 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 2994 $ // // $LastChangedDate: 2011-03-14 11:27:30 -0700 (Mon, 14 Mar 2011) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkProgress_h_included_ #define __cmtkProgress_h_included_ #include #include #include namespace cmtk { /** \addtogroup System */ //@{ /** Generic class for progress indication. */ class Progress { public: /// Status code returned by SetPercentDone() method. typedef enum { /// Everything okay; continue as usual. OK, /// User requests interrupt of operation. INTERRUPT, /// Interrupt generated by timeout. TIMEOUT, /// Something went wrong. FAILED } ResultEnum; /// This class. typedef Progress Self; /// Constructor. Progress() { Self::SetProgressInstance( this ); } /// Virtual (dummy) destructor. virtual ~Progress() {}; /// Set total number of steps to complete. static void Begin( const double start, const double end, const double increment, const std::string& taskName = std::string("") ); /// Set number of tasks completed. static ResultEnum SetProgress( const double progress ); /// Done with progress indicator. static void Done(); /// Set number of tasks completed. virtual ResultEnum UpdateProgress() = 0; /// Set number of tasks completed. void SetProgressCurrent( const double progress ); /// Set progress handler instance. static void SetProgressInstance( Self *const progressInstance ) { Self::ProgressInstance = progressInstance; } protected: /// Check if we're at the top level of the task hierarchy. bool IsTopLevel() const { return m_RangeStack.size() == 1; } /// Return the name of the current task (at the lowest level of nested ranges). const std::string GetCurrentTaskName() const; /// Compute current completion fraction from range stack. double GetFractionComplete() const; /// Set total number of steps to complete. virtual void BeginVirtual( const double start, const double end, const double increment, const std::string& taskName = std::string("") ); /** Clean up progress output. * This member function can be overriden by derived classes. */ virtual void DoneVirtual(); private: /// Instance of a derived class that handles GUI interaction etc. static Self* ProgressInstance; /// Class to current progress range, which can be nested. class Range { public: /// Constructor. Range( const double start, const double end, const double increment, const std::string& taskName = std::string("") ) : m_Start( start ), m_End( end ), m_Increment( increment ), m_TaskName( taskName ) { this->m_Current = this->m_Start; } /// Get complete fraction for this range. double GetFractionComplete( const double nestedFraction = 0 /*!< For nexted ranges, this is the fraction achieved in he next level down.*/ ) const; /// Start of this range. double m_Start; /// End of this range. double m_End; /// Increment between steps. double m_Increment; /// Current position in the range. double m_Current; /// Name of this task. const std::string m_TaskName; }; /// Type for stack of nested progress ranges. typedef std::deque RangeStackType; /// Stack of nested progress ranges. RangeStackType m_RangeStack; }; //@} } // namespace cmtk #endif // #ifndef __cmtkProgress_h_included_ cmtk-3.3.1/libs/System/cmtkProgressConsole.cxx000066400000000000000000000060331276303427400214350ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2011, 2013 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4875 $ // // $LastChangedDate: 2013-09-24 12:59:43 -0700 (Tue, 24 Sep 2013) $ // // $LastChangedBy: torstenrohlfing $ // */ #include "cmtkProgressConsole.h" #include #include #include #include namespace cmtk { /** \addtogroup System */ //@{ ProgressConsole::ProgressConsole( const std::string& programName ) : m_ProgramName( programName ), m_TimeAtStart( 0.0 ) { this->m_InsideSlicer3 = ( getenv( "Slicer3_HOME" ) != NULL ); if ( this->m_InsideSlicer3 ) { std::cout << "\n" << "" << this->m_ProgramName << "\n" << " \"" << this->m_ProgramName << "\" \n" << "\n"; std::cout.flush(); } } ProgressConsole::~ProgressConsole() { if ( this->m_InsideSlicer3 ) { std::cout << "\n" << "" << this->m_ProgramName << "\n" << "" << Timers::GetTimeProcess() - this->m_TimeAtStart << "\n" << "\n"; std::cout.flush(); } } Progress::ResultEnum ProgressConsole::UpdateProgress() { const double fraction = this->GetFractionComplete(); if ( this->m_InsideSlicer3 ) { std::cout << "" << fraction << "\n"; std::cout.flush(); } else { const std::string& currentTaskName = this->GetCurrentTaskName(); if ( currentTaskName.length() ) { DebugOutput( 2 ).GetStream().printf( "%s: %d %%\r", currentTaskName.c_str(), static_cast( 100.0 * fraction ) ); } else { DebugOutput( 2 ).GetStream().printf( "%d %%\r", static_cast( 100.0 * fraction ) ); } } return Self::OK; } void ProgressConsole:: BeginVirtual ( const double start, const double end, const double increment, const std::string& taskName ) { this->Superclass::BeginVirtual( start, end, increment, taskName ); if ( this->IsTopLevel() ) { this->m_TimeAtStart = Timers::GetTimeProcess(); } } } // namespace cmtk cmtk-3.3.1/libs/System/cmtkProgressConsole.h000066400000000000000000000051561276303427400210670ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2011 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 2731 $ // // $LastChangedDate: 2011-01-13 16:22:47 -0800 (Thu, 13 Jan 2011) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkProgressConsole_h_included_ #define __cmtkProgressConsole_h_included_ #include #include #include /** \addtogroup System */ //@{ namespace cmtk { /** Progress indicator with console output. * This class displays the programm progress on the console, using cmtk::StdErr. * If this process is being run from inside Slicer3, output is based on Slicer's * XML-type progress reporting instead, and this is written to std::cout. * *\see http://www.slicer.org/slicerWiki/index.php/Slicer3:Execution_Model_Documentation#Showing_Progress_in_an_Application */ class ProgressConsole : /// Inherit generic progress indicator interface. public Progress { public: /// This class. typedef ProgressConsole Self; /// Superclass. typedef Progress Superclass; /// Default constructor: connect to progress indicator. ProgressConsole( const std::string& programName = std::string("") ); /// Destructor: finish things up. virtual ~ProgressConsole(); /// Output progress to console. virtual ResultEnum UpdateProgress(); protected: /// Begin a new level of progress reporting. virtual void BeginVirtual( const double start, const double end, const double increment, const std::string& taskName ); private: /// Name of this program. std::string m_ProgramName; /// Process time at start of task. double m_TimeAtStart; /// Flag that indicates whether we're running inside of Slicer3. bool m_InsideSlicer3; }; } // namespace cmtk //@} #endif // #ifndef __cmtkProgressConsole_h_included_ cmtk-3.3.1/libs/System/cmtkRegressionTracker.cxx000066400000000000000000000041121276303427400217360ustar00rootroot00000000000000/* // // Copyright 2012, 2013 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4908 $ // // $LastChangedDate: 2013-10-01 13:06:26 -0700 (Tue, 01 Oct 2013) $ // // $LastChangedBy: torstenrohlfing $ // */ #include "cmtkRegressionTracker.h" #include cmtk::RegressionTracker::RegressionTracker() : m_File( NULL ), m_WriteFlag( false ) { const char *env = getenv( "CMTK_RTRACKER" ); if ( env ) { this->m_File = fopen( env, "r" ); if ( this->m_File ) this->m_WriteFlag = false; else { this->m_File = fopen( env, "w" ); this->m_WriteFlag = true; } } } cmtk::RegressionTracker::~RegressionTracker() { if ( this->m_File ) { fclose( this->m_File ); } } void cmtk::RegressionTracker::CompareChecksum( const unsigned char *const data, size_t nBytes ) { unsigned int checksum = 0; for ( size_t n = 0; n < nBytes; ++n ) { checksum = ((checksum & 255) << 24) | (checksum >> 8); checksum ^= data[n]; } if ( this->m_WriteFlag ) { fprintf( this->m_File, "%u\n", checksum ); } else { unsigned int baseline; if ( 1 != fscanf( this->m_File, "%20u", &baseline ) ) { this->Trap(); } if ( checksum != baseline ) this->Trap(); } } cmtk-3.3.1/libs/System/cmtkRegressionTracker.h000066400000000000000000000044731276303427400213750ustar00rootroot00000000000000/* // // Copyright 2012 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 3968 $ // // $LastChangedDate: 2012-03-06 11:29:26 -0800 (Tue, 06 Mar 2012) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkRegressionTracker_h_included_ #define __cmtkRegressionTracker_h_included_ #include #include #include namespace cmtk { /** \addtogroup System */ //@{ /// Class for generating and following regression tracks. class RegressionTracker { public: /// This class. typedef RegressionTracker Self; /// Constructor. RegressionTracker(); /// Virtual destructor. virtual ~RegressionTracker(); /// Instantiate and return static instance. static Self& Static() { static Self tracker; return tracker; } /// Advance tracker: compute checksum and either write to baseline file or compare against existing file. template void Advance( const T* data, const size_t nBytes ) { this->CompareChecksum( reinterpret_cast( data ), nBytes ); } private: /// Regression trace file. FILE* m_File; /// Flag for writing. bool m_WriteFlag; /// Compare using checksum void CompareChecksum( const unsigned char *const data, size_t nBytes ); protected: /// Member fuction that is called when divergence from previous trace is detected. virtual void Trap() { StdErr << "Detected regression divergence.\n"; } }; //@} } // namespace cmtk #endif // #ifndef __cmtkRegressionTracker_h_included_ cmtk-3.3.1/libs/System/cmtkSafeCounter.h000066400000000000000000000026771276303427400201630ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2011 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 2934 $ // // $LastChangedDate: 2011-03-04 09:56:27 -0800 (Fri, 04 Mar 2011) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkSafeCounter_h_included_ #define __cmtkSafeCounter_h_included_ #include #ifdef CMTK_USE_GCD # include "cmtkSafeCounterGCD.h" namespace cmtk { typedef SafeCounterGCD SafeCounter; } #else # include "cmtkSafeCounterMutex.h" namespace cmtk { typedef SafeCounterMutex SafeCounter; } #endif // #ifdef CMTK_USE_GCD #endif // #ifndef __cmtkSafePtr_h_included_ cmtk-3.3.1/libs/System/cmtkSafeCounterGCD.cxx000066400000000000000000000031321276303427400210370ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2011 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 2941 $ // // $LastChangedDate: 2011-03-04 10:31:31 -0800 (Fri, 04 Mar 2011) $ // // $LastChangedBy: torstenrohlfing $ // */ #include "cmtkSafeCounterGCD.h" unsigned int cmtk::SafeCounterGCD::Get() const { __block unsigned int result; dispatch_sync( this->m_Queue, ^{ result = this->m_Counter; } ); return result; } unsigned int cmtk::SafeCounterGCD::Increment() { __block unsigned int result; dispatch_sync( this->m_Queue, ^{ result = ++(this->m_Counter); } ); return result; } unsigned int cmtk::SafeCounterGCD::Decrement() { __block unsigned int result; dispatch_sync( this->m_Queue, ^{ result = --(this->m_Counter); } ); return result; } cmtk-3.3.1/libs/System/cmtkSafeCounterGCD.h000066400000000000000000000040421276303427400204650ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2011 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 2944 $ // // $LastChangedDate: 2011-03-04 13:23:25 -0800 (Fri, 04 Mar 2011) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkSafeCounterGCD_h_included_ #define __cmtkSafeCounterGCD_h_included_ #include #include namespace cmtk { /** \addtogroup System */ //@{ /** Thread-safe counter using Grand Central Dispatch FIFO queue. */ class SafeCounterGCD { public: /// Constructor. SafeCounterGCD( const unsigned int counter = 0 ) : m_Counter( counter ), m_Queue( dispatch_queue_create( "SafeCounterGCD", NULL) ) {} /// Destructor. ~SafeCounterGCD() { dispatch_release( this->m_Queue ); } /// Retrieve counter value. unsigned int Get() const; /// Increment and return new counter value. unsigned int Increment(); /// Decrement and return new counter value. unsigned int Decrement(); private: /// The actual counter. unsigned int m_Counter; /// GCD for thread-safe exclusive access to counter. mutable dispatch_queue_t m_Queue; }; //@} } // namespace cmtk #endif // #ifndef __cmtkSafeCounterGCD_h_included_ cmtk-3.3.1/libs/System/cmtkSafeCounterMutex.h000066400000000000000000000047541276303427400212040ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2011 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 2941 $ // // $LastChangedDate: 2011-03-04 10:31:31 -0800 (Fri, 04 Mar 2011) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkSafeCounterMutex_h_included_ #define __cmtkSafeCounterMutex_h_included_ #include #include #include namespace cmtk { /** \addtogroup System */ //@{ /** Thread-safe counter using POSIX mutex lock. * If the library is compiled without SMP support, then this class implements * only an interface to a simple unsigned int counter. With SMP support, a * mutex lock supplied by the MutexLock class is used to protect the * counter from concurrent access of multiple threads. */ class SafeCounterMutex { public: /// Constructor. SafeCounterMutex( const unsigned int counter = 0 ) : m_Counter( counter ) {} /// Retrieve counter value. unsigned int Get() const { LockingPtr counter( this->m_Counter, this->m_Mutex ); return *counter; } /// Increment and return new counter value. unsigned int Increment() { LockingPtr counter( this->m_Counter, this->m_Mutex ); return ++(*counter); } /// Decrement and return new counter value. unsigned int Decrement() { LockingPtr counter( this->m_Counter, this->m_Mutex ); return --(*counter); } private: /// The actual counter. unsigned int m_Counter; /// Mutex for thread-safe exclusive access to counter. mutable MutexLock m_Mutex; }; //@} } // namespace cmtk #endif // #ifndef __cmtkSafeCounterMutex_h_included_ cmtk-3.3.1/libs/System/cmtkSmartConstPtr.h000066400000000000000000000152161276303427400205210ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2012 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4188 $ // // $LastChangedDate: 2012-04-13 14:26:11 -0700 (Fri, 13 Apr 2012) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkSmartConstPtr_h_included_ #define __cmtkSmartConstPtr_h_included_ #include #ifndef NULL # define NULL 0 #endif #include #include #include namespace cmtk { /** \addtogroup System */ //@{ /** Smart pointer with reference counting. */ template class SmartConstPointer { public: /// This class instance. typedef SmartConstPointer Self; /// The underlying raw pointer type. typedef const T* PointerType; /// Reference to static null object. static Self& Null() { static Self null; return null; } /// Get current reference counter value: use with caution! unsigned int GetReferenceCount() const { return this->m_ReferenceCount->Get(); } /** Default constructor. * This needs to be separate because the constructor that takes a dumb pointer * must be explicit, but the default constructor should not be. */ SmartConstPointer() : m_ReferenceCount( new SafeCounter( 1 ) ) { this->m_Object.ptrConst = NULL; } /** Construct from dumb pointer. * Note that you MUST NEVER use this constructor more than once for each * dumb pointer, other than NULL! */ explicit SmartConstPointer( T *const object ) : m_ReferenceCount( new SafeCounter( 1 ) ) { this->m_Object.ptrConst = object; } /** Copy constructor template. * Increment reference counter in the process. */ template SmartConstPointer( const SmartConstPointer& ptr ) : m_ReferenceCount( ptr.m_ReferenceCount ) { this->m_Object.ptrConst = ptr.m_Object.ptrConst; this->m_ReferenceCount->Increment(); } /** Copy constructor to prevent compiler-generated copy constructor. * Increment reference counter in the process. */ SmartConstPointer( const Self& ptr ) : m_ReferenceCount( ptr.m_ReferenceCount ) { this->m_Object.ptrConst = ptr.m_Object.ptrConst; this->m_ReferenceCount->Increment(); } /// Destruct: decrease reference pointer and free dumb pointer if necessary. ~SmartConstPointer() { assert( this->m_ReferenceCount != NULL ); // we may have m_Object=NULL, but m_ReferenceCount should never be! if ( ! this->m_ReferenceCount->Decrement() ) { delete this->m_ReferenceCount; #ifdef DEBUG this->m_ReferenceCount = NULL; #endif if ( this->m_Object.ptrConst ) { delete this->m_Object.ptrConst; #ifdef DEBUG this->m_Object.ptrConst = NULL; #endif } } } /// Get dumb pointer. const T* GetPtr() const { return this->m_Object.ptrConst; } /// De-referencing operator (returns constant object). const T& operator*() const { return *this->m_Object.ptrConst; } /// De-referencing operator (returns constant object pointer). const T* operator->() const { return this->m_Object.ptrConst; } /// Implicit conversion to constant pointer. operator const T*() const { return m_Object.ptrConst; } /// Explicit conversion to constant pointer. const T* GetConstPtr() const { return this->m_Object.ptrConst; } /** Release control of this pointer. *\note This is a dangerous function. Be sure you know what you are doing! */ const T* ReleasePtr() { const T* object = this->m_Object.ptrConst; this->m_Object.ptrConst = NULL; return object; } /** Assignment operator. * This is implemented using the std::swap function. *\warning The "other" parameter HAS TO USE CALL BY VALUE for this function to work, * because we are not creating an explicit copy of the original object before * calling Swap() (see Effective C++, 3rd, Item 11, p.56). *\warning Open question: given that we pass the parameter by value, not reference, does * this really prevent the definition of a compiler-generated assignment (which passes * parameter by reference)? */ const Self& operator= ( const Self other ) const { using std::swap; swap( this->m_ReferenceCount, other.m_ReferenceCount ); swap( this->m_Object.ptrConst, other.m_Object.ptrConst ); return *this; } /** Equality operator using pointer. */ bool operator== ( const Self& other ) const { return (this->m_Object.ptrConst == other.m_Object.ptrConst); } /** Inequality operator. */ bool operator!= ( const Self& other ) const { return (this->m_Object.ptrConst != other.m_Object.ptrConst); } /** "Smaller than" operator (necessay for storing objects in std::map). */ bool operator< ( const Self& other ) const { return ( this->m_Object.ptrConst < other.m_Object.ptrConst ); } ///Dynamic cast between smart pointer types. template static Self DynamicCastFrom( const T2& from_P ) { return Self( dynamic_cast( from_P.GetConstPtr() ), from_P.m_ReferenceCount ); } protected: /// Pointer to detached reference counter for this object. mutable SafeCounter* m_ReferenceCount; /// Union for const and non-const pointer for reference-counted object. mutable union { /// Pointer to const. const T* ptrConst; /// Pointer to non-const. T* ptr; } m_Object; /** Construct from dumb pointer and existing reference counter. * The reference counter is increased in the process. */ SmartConstPointer( const T *const object, SafeCounter *const counter ) { this->m_Object.ptrConst = object; this->m_ReferenceCount = counter; this->m_ReferenceCount->Increment(); } /// Make all template instances friends for easy type casting. template friend class SmartConstPointer; }; //@} } // namespace cmtk #endif // define __cmtkSmartConstPtr_h_included_ cmtk-3.3.1/libs/System/cmtkSmartPtr.h000066400000000000000000000073001276303427400175050ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2011 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 3024 $ // // $LastChangedDate: 2011-03-21 14:04:20 -0700 (Mon, 21 Mar 2011) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkSmartPtr_h_included_ #define __cmtkSmartPtr_h_included_ #include #ifndef NULL # define NULL 0 #endif #include #include #include namespace cmtk { /** \addtogroup System */ //@{ /** Smart pointer with reference counting. */ template class SmartPointer : /// Inherit from smart pointer-to-const. public SmartConstPointer { public: /// This class instance. typedef SmartPointer Self; /// This class instance. typedef SmartConstPointer Superclass; /// Use "const" inherited member functions. using Superclass::operator*; using Superclass::operator->; using Superclass::ReleasePtr; /// The underlying raw pointer type. typedef T* PointerType; /// Reference to static null object. static Self& Null() { static Self null; return null; } /** Default constructor. */ SmartPointer() {} /** Construct from dumb pointer. * Note that you MUST NEVER use this constructor more than once for each * dumb pointer, other than NULL! */ explicit SmartPointer( T *const object ) : SmartConstPointer( object ) {} /** Copy constructor template. */ template SmartPointer( const SmartPointer& ptr ) : SmartConstPointer( ptr ) {} /** Copy constructor to prevent compiler-generated copy constructor. */ SmartPointer( const Self& ptr ) : SmartConstPointer( ptr ) {} /// De-referencing operator (returns non-constant object). T& operator*() { return *this->m_Object.ptr; } /// De-referencing operator (returns non-constant object pointer). T* operator->() { return this->m_Object.ptr; } /// Explicit conversion to non-constant pointer. T* GetPtr() const { return this->m_Object.ptr; } /** Release control of this pointer. *\note This is a dangerous function. Be sure you know what you are doing! */ T* ReleasePtr() { T* object = this->m_Object.ptr; this->m_Object.ptr = NULL; return object; } ///Dynamic cast between smart pointer types. template static Self DynamicCastFrom( const T2& from_P ) { return Self( dynamic_cast( from_P.GetPtr() ), from_P.m_ReferenceCount ); } private: /** Construct from dumb pointer and existing reference counter. * The reference counter is increased in the process. */ SmartPointer( T *const object, SafeCounter *const counter ) : Superclass( object, counter ) {} /// Make all template instances friends for easy type casting. template friend class SmartPointer; }; //@} } // namespace cmtk #endif // define __cmtkSmartPtr_h_included_ cmtk-3.3.1/libs/System/cmtkStackBacktrace.cxx000066400000000000000000000053101276303427400211500ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2010 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 2523 $ // // $LastChangedDate: 2010-11-01 15:41:02 -0700 (Mon, 01 Nov 2010) $ // // $LastChangedBy: torstenrohlfing $ // */ #include "cmtkStackBacktrace.h" #include #include #ifdef HAVE_EXECINFO_H # include #endif namespace cmtk { /** \addtogroup System */ //@{ StackBacktrace ::StackBacktrace() { #ifndef _MSC_VER struct sigaction sa; sa.sa_sigaction = cmtkStackBacktraceSignalHandler; sigemptyset (&sa.sa_mask); sa.sa_flags = SA_RESTART | SA_SIGINFO; sigaction(SIGSEGV, &sa, NULL); sigaction(SIGUSR1, &sa, NULL); #else #endif // #ifndef _MSC_VER } void StackBacktrace ::PrintBacktrace( const int levels ) { #ifdef HAVE_EXECINFO_H void *trace[16]; const int trace_size = backtrace(trace, 16); char *const *messages = backtrace_symbols( trace, trace_size ); /* skip first stack frame (points here) */ printf("[stack] Execution path:\n"); const int printLevels = levels ? levels+1 : trace_size; for ( int i=1; isi_addr ); } else { printf( "Caught signal %d\n", sig); } cmtk::StackBacktrace::PrintBacktrace(); exit( cmtk::StackBacktrace::ExitCode ); } #else // #ifndef _MSC_VER void cmtkStackBacktraceSignalHandler ( int sig ) { exit( cmtk::StackBacktrace::ExitCode ); } #endif // #ifndef _MSC_VER cmtk-3.3.1/libs/System/cmtkStackBacktrace.h000066400000000000000000000046671276303427400206130ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2012 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 3968 $ // // $LastChangedDate: 2012-03-06 11:29:26 -0800 (Tue, 06 Mar 2012) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkStackBacktrace_h_included_ #define __cmtkStackBacktrace_h_included_ #include #include namespace cmtk { /** \addtogroup System */ //@{ /** Class for printing stack backtrace in the event of a crash. * A static instance of this class is used to catch SEGFAULT and other * termination signals and prints a stack trace. *\author http://www.linuxjournal.com/article/6391 */ class StackBacktrace { public: /// This class. typedef StackBacktrace Self; /// Constructor: register signal handler. StackBacktrace(); /// Set exit code used after catching SEGFAULT or other signals. static void SetExitCode( const int code = 1 ) { Self::ExitCode = code; } /** Exit code. * This defaults to "1" but can be set to "0" for CTest testing. */ static int ExitCode; /// Print current stack backtrace. static void PrintBacktrace( const int levels = 0 /*!< Maximum number of levels to display (default: 0 = no limit). */ ); /// Get static stack backtrace object instance. static Self& Static() { static Self instance; return instance; } }; //@} } // namespace cmtk #ifndef _MSC_VER /// Signal handler. extern "C" void cmtkStackBacktraceSignalHandler( int sig, siginfo_t *info, void *secret ); #else extern "C" void cmtkStackBacktraceSignalHandler( int sig ); #endif #endif // #ifndef __cmtkStackBacktrace_h_included_ cmtk-3.3.1/libs/System/cmtkStrUtility.cxx000066400000000000000000000070251276303427400204440ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2014 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 5196 $ // // $LastChangedDate: 2014-02-07 14:56:12 -0800 (Fri, 07 Feb 2014) $ // // $LastChangedBy: torstenrohlfing $ // */ #include "cmtkStrUtility.h" #include namespace cmtk { /** \addtogroup System */ //@{ int StrCmp( const char* s1, const char* s2 ) { if ( s1 == NULL ) { if ( s2 == NULL ) return 0; else return -1; } else { if ( s2 == NULL ) return 1; else return strcmp( s1, s2 ); } } const char* StrNStr( const char* haystack, const size_t nBytes, const char* needle ) { for ( size_t hofs = 0; hofs < nBytes; ++hofs ) { size_t hidx = hofs; const char* nchr = needle; while ( (*nchr!=0) && (hidx& rules, const bool multiple ) { std::string result = str; std::map::const_iterator it = rules.begin(); while ( it != rules.end() ) { bool replaced = true; while ( replaced ) { replaced = false; std::string::size_type pos = result.find( it->first ); while ( pos != std::string::npos ) { result.replace( pos, it->first.length(), it->second ); replaced = true; pos = result.find( it->first ); if ( ! multiple ) break; } if ( ! multiple ) break; } ++it; } return result; } std::string StrReplace( const std::string& str, const std::string& search, const std::string& replace ) { std::string result = str; if ( ! search.empty() ) { for ( size_t b = result.find(search, 0); b != result.npos; b = result.find(search, b) ) { result.replace( b, search.size(), replace ); b += (replace.size() - search.size()); } } return result; } std::string StrMakeLegalInPath( const std::string& s ) { std::string result = s; result = StrReplace( result, " ", "_" ); result = StrReplace( result, ":", "_" ); return result; } std::vector StrSplit( const std::string& s, const std::string separators ) { std::vector result; if ( ! s.empty() ) { size_t prev = 0; while ( prev != std::string::npos ) { size_t next = s.find_first_of( separators, prev ); if ( next != std::string::npos ) { result.push_back( s.substr( prev, next-prev ) ); prev = next+1; } else { result.push_back( s.substr( prev ) ); prev = next; } } } return result; } } // namespace cmtk cmtk-3.3.1/libs/System/cmtkStrUtility.h000066400000000000000000000057611276303427400200760ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2014 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 5196 $ // // $LastChangedDate: 2014-02-07 14:56:12 -0800 (Fri, 07 Feb 2014) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkStrUtility_h_included_ #define __cmtkStrUtility_h_included_ #include #include #include #include namespace cmtk { /** \addtogroup System */ //@{ /** Safe search for a string within a non-null-terminated string. */ const char* StrNStr( const char* haystack, const size_t nBytes, const char* needle ); /** Safe string comparison. * This function is a wrapper for the standard library's "strcmp" function. * This wrapper is safe in that it accepts any combination of NULL and non-NULL * pointers to both arguments. *\return 0 if the strings pointed to by "s1" and "s2" are equal, a value * greater than zero if s1 is greater than s2, and a value smaller than zero * if s1 is smaller than s2. NULL pointers are considered smaller than any * other string except the NULL pointer itself. */ int StrCmp( const char *s1, const char* s2 ); /** Replace string components. *\todo This is highly unsafe, since we're not checking for infinite loops and * the likes. We should really work on this sometimes... or be REALLY carefull. */ std::string StrReplaceByRules( const std::string& str, const std::map& rules, const bool multiple = false ); /// Replace a search string with a replacement string. std::string StrReplace( const std::string& str /*!< The string to replace in */, const std::string& search /*!< Substring to replace */, const std::string& replace /*!< String to replace search string with */ ); /// Make a string legal in a path by replacing spaces and colons with "_". std::string StrMakeLegalInPath( const std::string& s ); /// Split a string into a vector of strings. std::vector StrSplit( const std::string& s /*. // // $Revision: 1881 $ // // $LastChangedDate: 2010-06-23 11:38:13 -0700 (Wed, 23 Jun 2010) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkTestFunctionMap_h_included_ #define __cmtkTestFunctionMap_h_included_ #include #include #include #include namespace cmtk { /** \addtogroup System */ //@{ /// Map from test name to test function. class TestFunctionMap { public: /// Test function pointer. typedef int (*TestFunctionPtr)(); /// Add test. void AddTest( const std::string& testName, TestFunctionPtr testFunction ) { this->m_Map[testName] = testFunction; } /// Run a test by name. int RunTestByName( const std::string& testName ) { MapType::iterator test = this->m_Map.find( testName ); if ( test == this->m_Map.end() ) { std::cerr << "Test '" << testName << "' not found."; return 2; } return test->second(); } private: /// Map from test name to test pointer. typedef std::map< std::string, TestFunctionPtr > MapType; /// Map from test name to test function. MapType m_Map; }; //@} } // namespace cmtk #endif // #ifndef __cmtkTestFunctionMap_h_included_ cmtk-3.3.1/libs/System/cmtkThreadParameterArray.h000066400000000000000000000064241276303427400220060ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2010 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4343 $ // // $LastChangedDate: 2012-05-11 11:47:31 -0700 (Fri, 11 May 2012) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkThreadParameterArray_h_included_ #define __cmtkThreadParameterArray_h_included_ #include #include #include namespace cmtk { /** \addtogroup System */ //@{ /** Array of thread parameters. * This array initializes the non-template type specific fields of the thread * parameter structure. */ template > class ThreadParameterArray { public: /** Constructor. * Allocate array and initialize generic fields. */ ThreadParameterArray ( TClass *const thisObject, const size_t numberOfThreads ) { this->m_NumberOfThreads = numberOfThreads; this->m_Ptr = Memory::ArrayC::Allocate( numberOfThreads ); for ( size_t i = 0; i < numberOfThreads; ++i ) { this->m_Ptr[i].thisObject = thisObject; this->m_Ptr[i].ThisThreadIndex = i; this->m_Ptr[i].NumberOfThreads = numberOfThreads; this->m_Ptr[i].m_ThreadID = 0; } } /// Destructor. ~ThreadParameterArray() { Memory::ArrayC::Delete( this->m_Ptr ); } /// Constant access operator. const TParam& operator[]( const size_t i ) const { return this->m_Ptr[i]; } /// Access operator. TParam& operator[]( const size_t i ) { return this->m_Ptr[i]; } /// Return pointer to array. TParam* GetPtr() { return this->m_Ptr; } /// Return constant pointer to array. const TParam* GetPtr() const { return this->m_Ptr; } /// Return number of threads. size_t GetNumberOfThreads() const { return this->m_NumberOfThreads; } /// Run thread function in parallel. void RunInParallel( ThreadFunction threadCall ) { Threads::RunThreads( threadCall, this->GetNumberOfThreads(), this->GetPtr(), sizeof( TParam ) ); } /// Run thread functions using a static FIFO scheduler. void RunInParallelFIFO(ThreadFunction threadCall, const size_t numberOfThreadsTotal, const size_t firstThreadIdx = 0 ); private: /// Store number of threads and entries in parameter array. size_t m_NumberOfThreads; /// Pointer to parameter block array. TParam* m_Ptr; }; //@} } // namespace cmtk #include "cmtkThreadParameterArray.txx" #endif // #ifndef __cmtkThreadParameterArray_h_included_ cmtk-3.3.1/libs/System/cmtkThreadParameterArray.txx000066400000000000000000000122221276303427400223730ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2011, 2013 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4862 $ // // $LastChangedDate: 2013-09-23 16:41:36 -0700 (Mon, 23 Sep 2013) $ // // $LastChangedBy: torstenrohlfing $ // */ #include #ifdef _OPENMP # include #endif namespace cmtk { /** \addtogroup System */ //@{ template void ThreadParameterArray ::RunInParallelFIFO ( ThreadFunction threadCall, const size_t numberOfThreadsTotal, const size_t firstThreadIdx ) { #ifdef _OPENMP const int nThreadsOMP = std::max( 1, 1+GetNumberOfThreads()-this->m_NumberOfThreads ); omp_set_num_threads( nThreadsOMP ); #endif #ifndef CMTK_USE_PTHREADS // we're not actually using threads, so simply run everything "by hand". for ( size_t threadIdx = 0; threadIdx < numberOfThreadsTotal; ++threadIdx ) { this->m_Ptr[0].ThisThreadIndex = firstThreadIdx + threadIdx; threadCall( this->m_Ptr ); } #else if ( this->m_NumberOfThreads == 1 ) { for ( size_t threadIdx = 0; threadIdx < numberOfThreadsTotal; ++threadIdx ) { this->m_Ptr[0].ThisThreadIndex = firstThreadIdx + threadIdx; threadCall( this->m_Ptr ); } } else { #ifdef _MSC_VER #else pthread_attr_t attr; pthread_attr_init(&attr); pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM); #endif // #ifdef _MSC_VER /* Initialization phase -- start first batch of parallel threads. */ size_t threadIdx = 0; for ( ; (threadIdx < this->m_NumberOfThreads) && (threadIdx < numberOfThreadsTotal); ++threadIdx ) { this->m_Ptr[threadIdx].ThisThreadIndex = firstThreadIdx + threadIdx; #ifdef _MSC_VER this->m_Ptr[threadIdx].m_Handle = CreateThread( NULL /*default security attributes*/, 0 /*use default stack size*/, (LPTHREAD_START_ROUTINE) threadCall, &this->m_Ptr[threadIdx], 0 /*use default creation flags*/, &this->m_Ptr[threadIdx].m_ThreadID ); if ( this->m_Ptr[threadIdx].m_Handle == NULL ) { fprintf( stderr, "Creation of thread #%d failed.\n", (int)threadIdx ); exit( 1 ); } #else int status = pthread_create( &this->m_Ptr[threadIdx].m_ThreadID, &attr, threadCall, &this->m_Ptr[threadIdx] ); if ( status ) { fprintf( stderr, "Creation of thread #%d failed with status %d.\n", (int)threadIdx, (int)status ); exit( 1 ); } #endif } /* Sustained phase -- start new thread whenever oldest one completes. */ size_t nextThreadToJoin = 0; while ( threadIdx < numberOfThreadsTotal ) { #ifndef _MSC_VER void *resultThread; #endif if ( this->m_Ptr[threadIdx].m_ThreadID ) { #ifdef _MSC_VER #else pthread_join( this->m_Ptr[threadIdx].m_ThreadID, &resultThread ); #endif } this->m_Ptr[nextThreadToJoin].ThisThreadIndex = firstThreadIdx + threadIdx; #ifdef _MSC_VER this->m_Ptr[nextThreadToJoin].m_Handle = CreateThread( NULL /*default security attributes*/, 0 /*use default stack size*/, (LPTHREAD_START_ROUTINE) threadCall, &this->m_Ptr[nextThreadToJoin],0 /*use default creation flags*/, &this->m_Ptr[nextThreadToJoin].m_ThreadID ); if ( this->m_Ptr[nextThreadToJoin].m_Handle == NULL) { fprintf( stderr, "Creation of thread #%d failed.\n", (int)threadIdx ); exit( 1 ); } #else int status = pthread_create( &this->m_Ptr[nextThreadToJoin].m_ThreadID, &attr, threadCall, &this->m_Ptr[nextThreadToJoin] ); if ( status ) { fprintf( stderr, "Creation of thread #%d failed with status %d.\n", (int)threadIdx, (int)status ); exit( 1 ); } #endif ++threadIdx; nextThreadToJoin = (nextThreadToJoin + 1) % this->m_NumberOfThreads; } /* Cleanup phase -- Collect remaining thread results. */ for ( threadIdx = 0; (threadIdx < this->m_NumberOfThreads) && (threadIdx < numberOfThreadsTotal); ++threadIdx ) { #ifndef _MSC_VER void *resultThread; #endif if ( this->m_Ptr[nextThreadToJoin].m_ThreadID ) { #ifdef _MSC_VER #else pthread_join( this->m_Ptr[nextThreadToJoin].m_ThreadID, &resultThread ); #endif } nextThreadToJoin = (nextThreadToJoin + 1) % this->m_NumberOfThreads; } #ifdef _MSC_VER #else pthread_attr_destroy(&attr); #endif } #endif // #ifndef CMTK_USE_PTHREADS #ifdef _OPENMP omp_set_num_threads( GetNumberOfThreads() ); #endif } } // namespace cmtk cmtk-3.3.1/libs/System/cmtkThreadParameters.h000066400000000000000000000040361276303427400211670ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2009, 2013 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4812 $ // // $LastChangedDate: 2013-09-09 14:26:32 -0700 (Mon, 09 Sep 2013) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkThreadParameters_h_included_ #define __cmtkThreadParameters_h_included_ #include namespace cmtk { /** \addtogroup System */ //@{ /** Base class for thread parameter blocks. * This doesn't hurt to have, even if we're building without thread support. */ template class ThreadParameters { public: /// Template pointer to the object starting this thread. T* thisObject; /// Unique index of this thread instance among all threads. unsigned int ThisThreadIndex; /// Total number of threads created. unsigned int NumberOfThreads; /// Thread ID of a started thread. ThreadIDType m_ThreadID; #ifdef _MSC_VER void* m_Handle; #endif /// Default constructor. ThreadParameters() : thisObject( NULL ), ThisThreadIndex( 0 ), NumberOfThreads( 0 ) { this->m_ThreadID = 0; #ifdef _MSC_VER this->m_Handle = NULL; #endif } }; //@} } // namespace cmtk #endif // #ifndef __cmtkThreadParameters_h_included_ cmtk-3.3.1/libs/System/cmtkThreadPool.h000066400000000000000000000025471276303427400200020ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2011 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: -1 $ // // $LastChangedDate: $ // // $LastChangedBy: $ // */ #ifndef __cmtkThreadPool_h_included_ #define __cmtkThreadPool_h_included_ #include #ifdef CMTK_USE_GCD # include "cmtkThreadPoolGCD.h" namespace cmtk { typedef ThreadPoolGCD ThreadPool; } #else # include "cmtkThreadPoolThreads.h" namespace cmtk { typedef ThreadPoolThreads ThreadPool; } #endif #endif // #ifndef __cmtkThreadPool_h_included_ cmtk-3.3.1/libs/System/cmtkThreadPoolGCD.cxx000066400000000000000000000055271276303427400206740ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2011 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 2963 $ // // $LastChangedDate: 2011-03-07 14:58:55 -0800 (Mon, 07 Mar 2011) $ // // $LastChangedBy: torstenrohlfing $ // */ #include "cmtkThreadPoolGCD.h" #include #include namespace cmtk { ThreadPoolGCD::ThreadPoolGCD( const size_t nThreads ) { if ( ! nThreads ) this->m_NumberOfThreads = cmtk::Threads::GetNumberOfThreads(); else this->m_NumberOfThreads = nThreads; this->m_Queues.resize( this->m_NumberOfThreads ); for ( size_t i = 0; i < this->m_NumberOfThreads; ++i ) { this->m_Queues[i] = dispatch_queue_create( "ThreadPoolGCD", 0 ); } } ThreadPoolGCD::~ThreadPoolGCD() { for ( size_t i = 0; i < this->m_NumberOfThreads; ++i ) { dispatch_release( this->m_Queues[i] ); } } void ThreadPoolGCD::Dispatch( Self::TaskFunction taskFunction, std::vector& taskParameters, const size_t numberOfTasksOverride ) { const size_t numberOfTasks = numberOfTasksOverride ? numberOfTasksOverride : taskParameters.size(); if ( ! numberOfTasks ) { StdErr << "ERROR: trying to run zero tasks on thread pool. Did you forget to resize the parameter vector?\n"; exit( 1 ); } const std::vector& vParams = taskParameters; const size_t nQueues = this->m_Queues.size(); for ( size_t taskIdx = 0; taskIdx < numberOfTasks; ) { for ( size_t queueIdx = 0; queueIdx < nQueues; ++queueIdx, ++taskIdx ) { if ( taskIdx < numberOfTasks ) { dispatch_async( this->m_Queues[queueIdx], ^{ taskFunction( (void*)( vParams[taskIdx] ), taskIdx, numberOfTasks, queueIdx, nQueues ); } ); } } } // wait for all queues to finish for ( size_t queueIdx = 0; queueIdx < nQueues; ++queueIdx ) { dispatch_sync( this->m_Queues[queueIdx], ^{} ); } } ThreadPoolGCD& ThreadPoolGCD::GetGlobalThreadPool() { static ThreadPoolGCD globalThreadPoolGCD; return globalThreadPoolGCD; } } cmtk-3.3.1/libs/System/cmtkThreadPoolGCD.h000066400000000000000000000110541276303427400203110ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2011 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 2953 $ // // $LastChangedDate: 2011-03-04 14:53:37 -0800 (Fri, 04 Mar 2011) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkThreadPoolGCD_h_included_ #define __cmtkThreadPoolGCD_h_included_ #include #include #include #include #include namespace cmtk { /** \addtogroup System */ //@{ /** Class that provides access to Grand Central Dispatch through a thread pool-like API. */ class ThreadPoolGCD : /// Make class uncopyable via inheritance. private CannotBeCopied { public: /// This class. typedef ThreadPoolGCD Self; /// Smart pointer. typedef SmartPointer SmartPtr; /** Task function: this is the interface for the functions called by the pooled threads to do the actual work. * The task function receives five parameters: a) a pointer to its parameter black, b) the index of the task, * c) the number of tasks, d) the index of the thread within the pool that is calling the task, and e) the * number of threads in the pool. Whereas the function should use b) and c) to determine what portion of work * it needs to do, d) and e) must be used to determine, for example, what local memory should be used, if * temporary storage has been allocated for each thread. Because the number of tasks typically exceeds the * number of threads, this is more efficient than allocating temporary storage for each task. */ typedef void (*TaskFunction)( void *const args /*!< Pointer to parameter block for this task.*/, const size_t taskIdx /*!< Index of this task.*/, const size_t taskCnt /*!< Number of tasks.*/, const size_t threadIdx /*!< Index of the thread that is running this task.*/, const size_t threadCnt /*!< Number of threads in this pool.*/ ); /** Constructor: create a pool of nThreads FIFO queues. */ ThreadPoolGCD( const size_t nThreads = 0 /*!< Number of FIFO queues. This is the maximum number of simultaneously running tasks. */ ); /// Destructor. ~ThreadPoolGCD(); /// Return number of threads in the pool. size_t GetNumberOfThreads() const { return this->m_NumberOfThreads; } /// Run actual worker functions through running threads. template void Run( Self::TaskFunction taskFunction /*!< Pointer to task function.*/, std::vector& taskParameters /*!< Vector of task parameter blocks, one per task.*/, const size_t numberOfTasksOverride = 0 /*!< This can be used to run a smaller number of tasks than taskParameters.size(), which is useful to allow re-use of larger, allocated vector.*/ ); /** Get reference to global thread pool. * This is shared by all functions in the process and allows re-use of the same "physical" threads * for all types of computations. The thread pool itself is a local static instance within this * function, thus making sure it is initialized properly (see Effective C++, 3rd, Item 4). */ static Self& GetGlobalThreadPool(); private: /// Number of running threads. size_t m_NumberOfThreads; /// Thread handles. std::vector m_Queues; /// Low-level dispatch of the jobs to run. void Dispatch( Self::TaskFunction taskFunction /*!< Pointer to task function.*/, std::vector& taskParameters /*!< Vector of task parameter blocks, one per task.*/, const size_t numberOfTasksOverride = 0 /*!< This can be used to run a smaller number of tasks than taskParameters.size(), which is useful to allow re-use of larger, allocated vector.*/ ); }; } // namespace cmtk #include "cmtkThreadPoolGCD.txx" #endif // #ifndef __cmtkThreadPoolGCD_h_included_ cmtk-3.3.1/libs/System/cmtkThreadPoolGCD.txx000066400000000000000000000027661276303427400207170ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2011 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 2953 $ // // $LastChangedDate: 2011-03-04 14:53:37 -0800 (Fri, 04 Mar 2011) $ // // $LastChangedBy: torstenrohlfing $ // */ #include template void cmtk::ThreadPoolGCD::Run ( const Self::TaskFunction taskFunction, std::vector& taskParameters, const size_t numberOfTasksOverride ) { std::vector taskArgPtr( taskParameters.size() ); for ( size_t i = 0; i < taskParameters.size(); ++i ) { taskArgPtr[i] = &taskParameters[i]; } this->Dispatch( taskFunction, taskArgPtr, numberOfTasksOverride ); } cmtk-3.3.1/libs/System/cmtkThreadPoolThreads.cxx000066400000000000000000000133041276303427400216610ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2011, 2013 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4875 $ // // $LastChangedDate: 2013-09-24 12:59:43 -0700 (Tue, 24 Sep 2013) $ // // $LastChangedBy: torstenrohlfing $ // */ #include "cmtkThreadPoolThreads.h" #include #include #include /// This is the actual low-level thread function. It calls ThreadFunction() for the cmtk::ThreadPoolThreads instance given as the function parameter. extern "C" CMTK_THREAD_RETURN_TYPE cmtkThreadPoolThreadFunction( CMTK_THREAD_ARG_TYPE arg ) { static_cast( arg )->m_Pool->ThreadFunction( static_cast( arg )->m_Index ); return CMTK_THREAD_RETURN_VALUE; } namespace cmtk { ThreadPoolThreads::ThreadPoolThreads( const size_t nThreads ) : m_NumberOfTasks( 0 ), m_NextTaskIndex( 0 ), m_TaskFunction( NULL ), m_ThreadsRunning( false ), m_ContinueThreads( true ) { if ( ! nThreads ) this->m_NumberOfThreads = cmtk::Threads::GetNumberOfThreads(); else this->m_NumberOfThreads = nThreads; #ifdef CMTK_USE_SMP this->m_ThreadID.resize( this->m_NumberOfThreads, 0 ); #ifdef _MSC_VER this->m_ThreadHandles.resize( this->m_NumberOfThreads, 0 ); #endif #endif this->m_ThreadArgs.resize( this->m_NumberOfThreads ); } void ThreadPoolThreads::StartThreads() { if ( !this->m_ThreadsRunning ) { #ifdef CMTK_USE_SMP #ifdef CMTK_USE_PTHREADS pthread_attr_t attr; pthread_attr_init(&attr); pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM); for ( size_t idx = 0; idx < this->m_NumberOfThreads; ++idx ) { // set thread function arguments this->m_ThreadArgs[idx].m_Pool = this; this->m_ThreadArgs[idx].m_Index = idx; // start thread const int status = pthread_create( &this->m_ThreadID[idx], &attr, cmtkThreadPoolThreadFunction, static_cast( &this->m_ThreadArgs[idx] ) ); if ( status ) { StdErr.printf( "Creation of pooled thread #%u failed with status %d.\n", idx, status ); exit( 1 ); } } pthread_attr_destroy(&attr); #elif defined(_MSC_VER) for ( size_t idx = 0; idx < this->m_NumberOfThreads; ++idx ) { // set thread function arguments this->m_ThreadArgs[idx].m_Pool = this; this->m_ThreadArgs[idx].m_Index = idx; // nothing happened yet, so set status to OK int status = 0; // start thread this->m_ThreadHandles[idx] = CreateThread( NULL /*default security attributes*/, 0/*use default stack size*/, (LPTHREAD_START_ROUTINE) cmtkThreadPoolThreadFunction, static_cast( &this->m_ThreadArgs[idx] ), 0/*use default creation flags*/, &this->m_ThreadID[idx] ); if ( this->m_ThreadHandles[idx] == NULL ) { status = -1; } if ( status ) { StdErr.printf( "Creation of pooled thread #%u failed with status %d.\n", idx, status ); exit( 1 ); } } #endif // #ifdef CMTK_USE_PTHREADS #endif // #ifdef CMTK_USE_SMP this->m_ThreadsRunning = true; } } ThreadPoolThreads::~ThreadPoolThreads() { this->EndThreads(); } void ThreadPoolThreads::EndThreads() { if ( this->m_ThreadsRunning ) { #ifdef CMTK_USE_PTHREADS // set flag to terminate threads and post one semaphore per actual thread this->m_ContinueThreads = false; this->m_TaskWaitingSemaphore.Post( this->m_NumberOfThreads ); for ( size_t idx = 0; idx < this->m_NumberOfThreads; ++idx ) { if ( this->m_ThreadID[idx] ) { pthread_join( this->m_ThreadID[idx], NULL ); this->m_ThreadID[idx] = 0; } } #elif defined(_MSC_VER) for ( size_t idx = 0; idx < this->m_NumberOfThreads; ++idx ) { DWORD resultThread = 0; TerminateThread( this->m_ThreadHandles[idx], resultThread ); } #endif this->m_ThreadsRunning = false; } } void ThreadPoolThreads::ThreadFunction( const size_t threadIdx ) { #ifdef _OPENMP // Disable OpenMP inside thread omp_set_num_threads( 1 ); #endif #ifdef CMTK_USE_SMP // wait for task waiting this->m_TaskWaitingSemaphore.Wait(); while ( this->m_ContinueThreads ) { // lock, get, increment next task index this->m_NextTaskIndexLock.Lock(); const size_t taskIdx = this->m_NextTaskIndex; ++this->m_NextTaskIndex; this->m_NextTaskIndexLock.Unlock(); // call task function this->m_TaskFunction( this->m_TaskParameters[taskIdx], taskIdx, this->m_NumberOfTasks, threadIdx, this->m_NumberOfThreads ); // post "task done, thread waiting" this->m_ThreadWaitingSemaphore.Post(); // wait for task waiting this->m_TaskWaitingSemaphore.Wait(); } #endif // #ifdef CMTK_USE_SMP } ThreadPoolThreads& ThreadPoolThreads::GetGlobalThreadPool() { static ThreadPoolThreads globalThreadPoolThreads; return globalThreadPoolThreads; } } cmtk-3.3.1/libs/System/cmtkThreadPoolThreads.h000066400000000000000000000221711276303427400213100ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2011 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 3288 $ // // $LastChangedDate: 2011-07-26 16:27:51 -0700 (Tue, 26 Jul 2011) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkThreadPoolThreads_h_included_ #define __cmtkThreadPoolThreads_h_included_ #include #include #include #include #include #include #include namespace cmtk { /** \addtogroup System */ //@{ /** Class that provides a pool of continuously running threads that can be used for reducing overhead in SMP computations. * * Every instance of this class starts a pool of threads upon initialization, which can later be used to execute arbitrary tasks. * The threads are held via a semaphore, which are used to signal the availability of an arbitrary number of tasks. Once the * task semaphores have been posted, the calling function waits for the running threads to signal back the same number of * completed tasks via a second semaphore. * * The main advantages of this thread execution framework are: * * 1. On platforms with inefficient, slow thread creation and joining (Win32), the use and re-use of a persistent set of threads * greatly improves run-time efficiency and reduces overhead. * * 2. A single computation can be broken into more tasks than there are threads running, i.e., more tasks than there are CPUs * available. This allows for load balancing, because tasks that complete faster than others will free the executing thread, which * is then available to process the next available task without waiting for any other threads. * * This class provides a global thread pool, which can (and should) be shared by all computations in a process. Creating additional * thread pool instances should hardly ever be necessary. * * To run tasks on the global thread pool, simply create a std::vector that contains a parameter block for each task. The size of the * vector also determines the number of tasks to run. For example, the thread parameter could simply be a pointer to the current * instance ("this") of a class that acts as the client that requests a parallel computation: * *\code * #include * #include * * class ComputationClass * { * public: * typedef ComputationClass Self; * * void ComputeUsingSMP() * { * // run approximately four times as many tasks as there are threads (only one task for single thread) * const size_t numberOfTasks = 4 * ThreadPool::GlobalThreadPool.GetNumberOfThreads() - 3; * * std::vector taskParamaters( numberOfTasks, this ); * cmtk::ThreadPool::GlobalThreadPool.Run( ComputeTask, taskParameters ); * } * * void ComputeTask( void *const arg, const size_t taskIdx, const size_t taskCnt, const size_t threadIdx, const size_t threadCnt ) * { * Self* Caller = static_cast( arg ); * // more things to do for "Caller" * // taskIdx is the index of this task; taskCnt is the total number of tasks. These two determine what part of the total work must be done. * // threadIdx is the index of the "physical" thread out of threadCnt threads that are running in this pool. If temporary memory is allocated * // for this function, then threadIdx can be used to index this temporary storage, thus allowing us to get by with threadCnt many spaces, * // rather than taskCnt many, which is usuallu much larger. * } * }; *\endcode */ class ThreadPoolThreads : /// Make class uncopyable via inheritance. private CannotBeCopied { public: /// This class. typedef ThreadPoolThreads Self; /// Smart pointer. typedef SmartPointer SmartPtr; /** Task function: this is the interface for the functions called by the pooled threads to do the actual work. * The task function receives five parameters: a) a pointer to its parameter black, b) the index of the task, * c) the number of tasks, d) the index of the thread within the pool that is calling the task, and e) the * number of threads in the pool. Whereas the function should use b) and c) to determine what portion of work * it needs to do, d) and e) must be used to determine, for example, what local memory should be used, if * temporary storage has been allocated for each thread. Because the number of tasks typically exceeds the * number of threads, this is more efficient than allocating temporary storage for each task. */ typedef void (*TaskFunction)( void *const args /*!< Pointer to parameter block for this task.*/, const size_t taskIdx /*!< Index of this task.*/, const size_t taskCnt /*!< Number of tasks.*/, const size_t threadIdx /*!< Index of the thread that is running this task.*/, const size_t threadCnt /*!< Number of threads in this pool.*/ ); /** Constructor: create a pool of nThreads running threads. *\param nThreads Number of threads to create for this pool. By default, the number * of threads created is the current number of available threads, i.e., typically * the number of CPUs minus the number of currently running threads, if any. */ ThreadPoolThreads( const size_t nThreads = 0 ); /// Destructor: stop all running threads. ~ThreadPoolThreads(); /// Return number of threads in the pool. size_t GetNumberOfThreads() const { return this->m_NumberOfThreads; } /// Run actual worker functions through running threads. template void Run( Self::TaskFunction taskFunction /*!< Pointer to task function.*/, std::vector& taskParameters /*!< Vector of task parameter blocks, one per task.*/, const size_t numberOfTasksOverride = 0 /*!< This can be used to run a smaller number of tasks than taskParameters.size(), which is useful to allow re-use of larger, allocated vector.*/ ); /** Get reference to global thread pool. * This is shared by all functions in the process and allows re-use of the same "physical" threads * for all types of computations. The thread pool itself is a local static instance within this * function, thus making sure it is initialized properly (see Effective C++, 3rd, Item 4). */ static Self& GetGlobalThreadPool(); /** Thread function arguments: identify pool and index of thread in it. * Cannot be "private" because we need this in a "C" linkage function, which cannot be * a "friend" of this class. */ class ThreadPoolThreadsArg { public: /// The thread pool. ThreadPoolThreads* m_Pool; /// Index of thread in pool. size_t m_Index; }; /** This function is run as a thread. * Cannot be "private" because we need to call this from a "C" linkage function, which cannot be * a "friend" of this class. */ void ThreadFunction( const size_t threadIdx /*!< Index of the actual thread in the pool. */ ); private: /// Semaphore to signal running threads when tasks are waiting. ThreadSemaphore m_TaskWaitingSemaphore; /// Semaphore that threads use to signal when tasks they are ready for the next task. ThreadSemaphore m_ThreadWaitingSemaphore; /// Total number of tasks to execute. size_t m_NumberOfTasks; /// Index of next available task. size_t m_NextTaskIndex; /// Lock to ensure exclusive access to the task index counter. MutexLock m_NextTaskIndexLock; /// The current task function. Self::TaskFunction m_TaskFunction; /// Task function parameter pointers. std::vector m_TaskParameters; /// Thread function parameters. std::vector m_ThreadArgs; /// Number of running threads. size_t m_NumberOfThreads; #ifdef CMTK_USE_SMP /// Thread handles. std::vector m_ThreadID; #ifdef _MSC_VER /// Windows thread handles std::vector m_ThreadHandles; #endif #endif /// Flag whether threads for this pool are running. bool m_ThreadsRunning; /** Flag whether threads should continue or terminate. * When the thread pool is destructed, this is set to "true" to wind down all running * threads gracefully. */ bool m_ContinueThreads; /// Start threads for this pool. void StartThreads(); /// End threads for this pool. void EndThreads(); }; } // namespace cmtk #include "cmtkThreadPoolThreads.txx" #endif // #ifndef __cmtkThreadPoolThreads_h_included_ cmtk-3.3.1/libs/System/cmtkThreadPoolThreads.txx000066400000000000000000000060251276303427400217040ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2011 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 3288 $ // // $LastChangedDate: 2011-07-26 16:27:51 -0700 (Tue, 26 Jul 2011) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifdef _OPENMP # include #endif #include #include template void cmtk::ThreadPoolThreads::Run ( const Self::TaskFunction taskFunction, std::vector& taskParameters, const size_t numberOfTasksOverride ) { if ( ! this->m_ThreadsRunning ) { this->StartThreads(); } const size_t numberOfTasks = numberOfTasksOverride ? numberOfTasksOverride : taskParameters.size(); if ( ! numberOfTasks ) { StdErr << "ERROR: trying to run zero tasks on thread pool. Did you forget to resize the parameter vector?\n"; exit( 1 ); } #ifdef _OPENMP // if OpenMP is also used in CMTK, reduce the number of OMP threads by the number of threads/tasks that we're about to run in parallel. const int nThreadsOMP = std::max( 1, 1+Threads::GetNumberOfThreads() - std::min( numberOfTasks, this->m_NumberOfThreads ) ); omp_set_num_threads( nThreadsOMP ); #endif #ifdef CMTK_USE_SMP // set task function this->m_TaskFunction = taskFunction; // initialize task index and count this->m_NumberOfTasks = numberOfTasks; this->m_TaskParameters.resize( this->m_NumberOfTasks ); this->m_NextTaskIndex = 0; // set parameter pointers and post semaphores for tasks waiting to supply all running threads. for ( size_t idx = 0; idx < numberOfTasks; ++idx ) { this->m_TaskParameters[idx] = &(taskParameters[idx]); } this->m_TaskWaitingSemaphore.Post( numberOfTasks ); // now wait for all tasks to complete, as signaled via the "thread waiting" semaphore. for ( size_t idx = 0; idx < numberOfTasks; ++idx ) { this->m_ThreadWaitingSemaphore.Wait(); } #else // without SMP, just run everything sequentially. for ( size_t idx = 0; idx < numberOfTasks; ++idx ) { taskFunction( &taskParameters[idx], idx, numberOfTasks, 0, 1 ); } #endif #ifdef _OPENMP // restore OpenMP thread count to maximum omp_set_num_threads( Threads::GetNumberOfThreads() ); #endif } cmtk-3.3.1/libs/System/cmtkThreadSemaphore.cxx000066400000000000000000000026341276303427400213640ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2011 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 3056 $ // // $LastChangedDate: 2011-03-28 11:24:31 -0700 (Mon, 28 Mar 2011) $ // // $LastChangedBy: torstenrohlfing $ // */ #include "cmtkThreadSemaphore.h" #ifdef _MSC_VER # include "cmtkThreadSemaphoreWindows.txx" #elif defined(__APPLE__) || defined(__CYGWIN__) # include "cmtkThreadSemaphoreAppleIsRetarded.txx" #elif defined(CMTK_USE_PTHREADS) # include "cmtkThreadSemaphorePOSIX.txx" #else # include "cmtkThreadSemaphoreNone.txx" #endif // #ifdef CMTK_USE_PTHREADS cmtk-3.3.1/libs/System/cmtkThreadSemaphore.h000066400000000000000000000052321276303427400210060ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2010 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 3288 $ // // $LastChangedDate: 2011-07-26 16:27:51 -0700 (Tue, 26 Jul 2011) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkThreadSemaphore_h_included_ #define __cmtkThreadSemaphore_h_included_ #include #include #if defined(CMTK_USE_PTHREADS) # if defined(__APPLE__) || defined(__CYGWIN__) # include # else # include # endif #elif defined(_MSC_VER) # include #endif namespace cmtk { /** \addtogroup System */ //@{ /** Semaphore for thread synchronization. * Because apparently Apple engineers are incapable of implementing an interface for unnamed * semaphores as provided by , we are building the semaphore ourselves on the * Mac OS platform using a mutex and a condition variable. */ class ThreadSemaphore : /// Make class uncopyable via inheritance. private CannotBeCopied { public: /// Initialize semaphore. ThreadSemaphore( const unsigned int initial = 0 ); /// Destroy semaphore object. ~ThreadSemaphore(); /// Post semaphore. void Post( const unsigned int increment = 1 ); /// Wait for semaphore. void Wait(); #if defined(CMTK_USE_PTHREADS) # if defined(__APPLE__) || defined(__CYGWIN__) private: /// Counter (Apple only). long int m_Counter; /// Counter mutex lock (Apple only). pthread_mutex_t m_Mutex; /// Condition variable (Apple only). pthread_cond_t m_Condition; # else // POSIX /// Opaque system semaphore object (POSIX only). sem_t m_Semaphore; # endif #elif defined(_MSC_VER) private: /// Opaque system semaphore object (Windows native only). HANDLE m_Semaphore; #endif }; //@} } // namespace cmtk #endif // #ifndef __cmtkThreadSemaphore_h_included_ cmtk-3.3.1/libs/System/cmtkThreadSemaphoreAppleIsRetarded.txx000066400000000000000000000035741276303427400243420ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2010 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 583 $ // // $LastChangedDate: 2009-10-13 13:22:25 -0700 (Tue, 13 Oct 2009) $ // // $LastChangedBy: torstenrohlfing $ // */ namespace cmtk { ThreadSemaphore::ThreadSemaphore( const unsigned int initial ) { this->m_Counter = initial; pthread_mutex_init( &this->m_Mutex, NULL ); pthread_cond_init( &this->m_Condition, NULL ); } ThreadSemaphore::~ThreadSemaphore() { pthread_mutex_destroy( &this->m_Mutex ); pthread_cond_destroy( &this->m_Condition ); } void ThreadSemaphore::Post( const unsigned int increment ) { pthread_mutex_lock( &this->m_Mutex ); this->m_Counter += increment; pthread_mutex_unlock( &this->m_Mutex ); pthread_cond_broadcast( &this->m_Condition ); } void ThreadSemaphore::Wait() { pthread_mutex_lock( &this->m_Mutex ); while ( !this->m_Counter ) pthread_cond_wait( &this->m_Condition, &this->m_Mutex ); --this->m_Counter; pthread_mutex_unlock( &this->m_Mutex ); } } // namespace cmtk cmtk-3.3.1/libs/System/cmtkThreadSemaphoreNone.txx000066400000000000000000000024131276303427400222200ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2011 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 3056 $ // // $LastChangedDate: 2011-03-28 11:24:31 -0700 (Mon, 28 Mar 2011) $ // // $LastChangedBy: torstenrohlfing $ // */ namespace cmtk { ThreadSemaphore::ThreadSemaphore( const unsigned int ) {} ThreadSemaphore::~ThreadSemaphore() {} void ThreadSemaphore::Post( const unsigned int ) {} void ThreadSemaphore::Wait() {} } cmtk-3.3.1/libs/System/cmtkThreadSemaphorePOSIX.txx000066400000000000000000000037471276303427400222360ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2010 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 583 $ // // $LastChangedDate: 2009-10-13 13:22:25 -0700 (Tue, 13 Oct 2009) $ // // $LastChangedBy: torstenrohlfing $ // */ #include #include #include namespace cmtk { ThreadSemaphore::ThreadSemaphore( const unsigned int initial ) { if ( sem_init( &this->m_Semaphore, 0, initial ) ) { std::cerr << "ERROR: sem_init failed with errno=" << errno << "\n"; exit( 1 ); } } ThreadSemaphore::~ThreadSemaphore() { if ( sem_destroy( &this->m_Semaphore ) ) { std::cerr << "ERROR: sem_destroy failed with errno=" << errno << "\n"; exit( 1 ); } } void ThreadSemaphore::Post( const unsigned int increment ) { for ( unsigned int idx = 0; idx < increment; ++idx ) { if ( sem_post( &this->m_Semaphore ) ) { std::cerr << "ERROR: sem_post failed with errno=" << errno << "\n"; exit( 1 ); } } } void ThreadSemaphore::Wait() { if ( sem_wait( &this->m_Semaphore ) ) { std::cerr << "ERROR: sem_wait failed with errno=" << errno << "\n"; exit( 1 ); } } } cmtk-3.3.1/libs/System/cmtkThreadSemaphoreWindows.txx000066400000000000000000000034071276303427400227570ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // Copyright 2004-2009 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 583 $ // // $LastChangedDate: 2009-10-13 13:22:25 -0700 (Tue, 13 Oct 2009) $ // // $LastChangedBy: torstenrohlfing $ // */ #include #include namespace cmtk { ThreadSemaphore::ThreadSemaphore( const unsigned int initial ) { this->m_Semaphore = CreateSemaphore( NULL /*default security attributes*/, initial, 32768 /*maximum count*/, NULL /*unnamed semaphore*/ ); if ( this->m_Semaphore == NULL) { std::cerr << "CreateSemaphore error: " << GetLastError() << std::endl; exit( 1 ); } } ThreadSemaphore::~ThreadSemaphore() { CloseHandle( this->m_Semaphore ); } void ThreadSemaphore::Post( const unsigned int increment ) { ReleaseSemaphore( this->m_Semaphore, increment, NULL); } void ThreadSemaphore::Wait() { WaitForSingleObject( this->m_Semaphore, INFINITE ); } } // namespace cmtk cmtk-3.3.1/libs/System/cmtkThreadSystemTypes.h000066400000000000000000000047331276303427400214010ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // Copyright 2004-2009 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 3288 $ // // $LastChangedDate: 2011-07-26 16:27:51 -0700 (Tue, 26 Jul 2011) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkThreadSystemTypes_h_included_ #define __cmtkThreadSystemTypes_h_included_ #ifndef _MSC_VER # if defined(CMTK_USE_PTHREADS) # include # endif /// Return type of a thread function. # define CMTK_THREAD_RETURN_TYPE void* /// Return type of a thread function. # define CMTK_THREAD_ARG_TYPE void* /// Return value of a thread function. # define CMTK_THREAD_RETURN_VALUE NULL #else // _MSC_VER # include # define CMTK_THREAD_RETURN_TYPE DWORD /// Return type of a thread function. # define CMTK_THREAD_ARG_TYPE LPVOID # define CMTK_THREAD_RETURN_VALUE NULL #endif // _MSC_VER namespace cmtk { /** \addtogroup System */ //@{ #ifndef _MSC_VER # if defined(CMTK_USE_PTHREADS) typedef pthread_t ThreadIDType; # else /// Dummy definition for non-threading builds. typedef int ThreadIDType; # endif #else // _MSC_VER typedef DWORD ThreadIDType; # endif // _MSC_VER /// Type of thread function typedef CMTK_THREAD_RETURN_TYPE (*ThreadFunction)(CMTK_THREAD_ARG_TYPE); #ifndef CMTK_MAX_THREADS /** Maximum number of threads supported. * This value determines the size of the statically allocated array of thread * IDs. If you need more than 256 parallel threads (default), you may use * -DCMTK_MAX_THREADS= as a preprocessor switch when compiling this * library. */ #define CMTK_MAX_THREADS 256 #endif //@} } // namespace cmtk #endif // #ifndef __cmtkThreadSystemTypes_h_included_ cmtk-3.3.1/libs/System/cmtkThreads.cxx000066400000000000000000000202771276303427400177060ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2013 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4918 $ // // $LastChangedDate: 2013-10-03 13:01:32 -0700 (Thu, 03 Oct 2013) $ // // $LastChangedBy: torstenrohlfing $ // */ #include "cmtkThreads.h" // GJ: use sysctl library to query cpu number on apple macosx #ifdef __APPLE__ #include #endif #ifdef HAVE_UNISTD_H # include #endif #ifdef CMTK_USE_PTHREADS # include # include #endif #ifdef _OPENMP # include #endif // _OPENMP #if defined(_OPENMP) && defined(__APPLE__) #include __attribute__((visibility("hidden"))) pthread_attr_t gomp_thread_attr; #endif #include #include #include #include #ifdef CMTK_USE_GCD # include #endif #ifndef _SC_NPROCESSORS_ONLN # ifdef _SC_NPROC_ONLN # define _SC_NPROCESSORS_ONLN _SC_NPROC_ONLN # endif #endif #ifdef CMTK_USE_FFTW_FOUND # include #endif namespace cmtk { /** \addtogroup System */ //@{ int Threads::NumberOfThreads = 0; int Threads::GetNumberOfThreads() { if ( !Threads::NumberOfThreads ) Threads::CheckEnvironment(); return Threads::NumberOfThreads; } void Threads::SetNumberOfThreads( const long int numberOfThreads ) { Threads::SetNumberOfThreads( numberOfThreads, true /*force*/ ); } int Threads ::SetNumberOfThreads ( const int numberOfThreads, const bool force ) { if ( numberOfThreads ) { if ( force ) { NumberOfThreads = std::min( numberOfThreads, Threads::GetMaxThreads() ); } else { NumberOfThreads = std::min( numberOfThreads, Threads::GetNumberOfProcessors() ); } } else NumberOfThreads = std::min( Threads::GetNumberOfProcessors(), Threads::GetMaxThreads() ); #ifdef _OPENMP omp_set_num_threads( NumberOfThreads ); #endif return NumberOfThreads; } int Threads::GetMaxThreads() { #ifdef _MSC_VER return CMTK_MAX_THREADS; # elif defined(_POSIX_THREAD_THREADS_MAX) return _POSIX_THREAD_THREADS_MAX; # elif defined(PTHREAD_THREADS_MAX) return PTHREAD_THREADS_MAX; // GJ added extra elif for Apple // not sure if this max is sensible, but can't find // anywhere to query it #elif defined(__APPLE__) && defined(CMTK_USE_PTHREADS) return std::min( CMTK_MAX_THREADS, CMTK_MAX_THREADS ); # elif defined(CMTK_USE_PTHREADS) const long sysconfNumThreads = sysconf( _SC_THREAD_THREADS_MAX ); if ( sysconfNumThreads == -1 ) return CMTK_MAX_THREADS; else return std::min( (int)sysconfNumThreads, CMTK_MAX_THREADS ); # else return 1; # endif } int Threads::GetNumberOfProcessors() { #ifdef _MSC_VER SYSTEM_INFO systemInfo; GetSystemInfo( &systemInfo ); return std::min( systemInfo.dwNumberOfProcessors, CMTK_MAX_THREADS ); #elif defined(__APPLE__) // use sysctl to get number of available cpus on apple. Copied from: // developer.apple.com/documentation/Porting/Conceptual/PortingUnix/index.html const char *name="hw.activecpu"; int nproc; size_t len=4; sysctlbyname(name, &nproc, &len, NULL, 0); return nproc; #elif defined(CMTK_USE_PTHREADS) return sysconf( _SC_NPROCESSORS_ONLN ); #else return 1; #endif } #ifdef CMTK_USE_GCD void Threads::RunThreads ( ThreadFunction threadCall, const unsigned numberOfThreads, void *const parameters, const size_t parameterSize ) { dispatch_apply( numberOfThreads, dispatch_get_global_queue(0, 0), ^(size_t i) { threadCall( ((char*)parameters)+i*parameterSize ); } ); } #else void Threads::RunThreads ( ThreadFunction threadCall, const unsigned numberOfThreads, void *const parameters, const size_t parameterSize ) { #ifdef _OPENMP const int nThreadsOMP = std::max( 1, 1+GetNumberOfThreads()-numberOfThreads ); omp_set_num_threads( nThreadsOMP ); #endif #ifdef CMTK_USE_SMP ThreadIDType Thread[CMTK_MAX_THREADS]; #ifdef _MSC_VER HANDLE ThreadHandles[CMTK_MAX_THREADS]; #endif #endif #ifdef CMTK_USE_PTHREADS pthread_attr_t attr; pthread_attr_init(&attr); pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM); #endif for ( unsigned threadIdx = 1; threadIdx < numberOfThreads; ++threadIdx ) { void *threadParameters = ((char*) parameters) + threadIdx * parameterSize; // nothing happened yet, so set status to OK int status = 0; // We do NOT run the last thread as a thread, but rather continue the // main thread. This should allow us to compute the actual real time // spent on the longest thread, at least approximately. #ifdef CMTK_USE_SMP #ifdef _MSC_VER ThreadHandles[threadIdx] = CreateThread( NULL /*default security attributes*/, 0/*use default stack size*/, (LPTHREAD_START_ROUTINE) threadCall, threadParameters, 0/*use default creation flags*/, &Thread[threadIdx] ); if ( ThreadHandles[threadIdx] == NULL ) { status = -1; } #else // _MSC_VER status = pthread_create( &Thread[threadIdx], &attr, threadCall, threadParameters ); #endif // _MSC_VER #else // we're not actually using SMP, so simply run everything "by hand". threadCall( threadParameters ); // this should never fail really #endif if ( status ) { fprintf( stderr, "Creation of thread #%u failed with status %d.\n", threadIdx, status ); #if defined(CMTK_USE_SMP) && defined(CMTK_USE_PTHREADS) Thread[threadIdx] = 0; #endif threadCall( threadParameters ); } } // Run thread #0 threadCall( parameters ); // Collect thread results in reverse order. Threads with higher numbers are // likely to have to do an iteration less than those with lower numbers. // So we collect the shorter running ones first and give more time to the // longer running ones while dealing with the administrational overhead of // the ones that are already finished. for ( unsigned threadIdx = numberOfThreads-1; threadIdx; --threadIdx ) { #ifdef CMTK_USE_SMP # ifdef _MSC_VER WaitForSingleObject( ThreadHandles[threadIdx], INFINITE /*no timeout*/ ); # else // _MSC_VER void *resultThread; if ( Thread[threadIdx] ) { pthread_join( Thread[threadIdx], &resultThread ); } # endif // _MSC_VER #endif } #ifdef CMTK_USE_SMP #ifndef _MSC_VER pthread_attr_destroy(&attr); #endif #endif #ifdef _OPENMP omp_set_num_threads( GetNumberOfThreads() ); #endif } #endif // CMTK_USE_GCD void Threads::CheckEnvironment() { const char *env = getenv( "CMTK_NUM_THREADS" ); // check legacy variable if ( ! env ) env = getenv( "IGS_NUM_THREADS" ); if ( env ) { const int numThreads = atoi( env ); if ( numThreads ) { SetNumberOfThreads( numThreads ); // cannot use StdErr here, because it may not be initialized yet std::cerr << "INFO: number of threads set to " << numThreads << " according to environment variable CMTK_NUM_THREADS\n"; } else { // cannot use StdErr here, because it may not be initialized yet std::cerr << "WARNING: environment variable CMTK_NUM_THREADS is set but does not seem to contain a number larger than 0.\n"; } } if ( ! NumberOfThreads ) { SetNumberOfThreads( std::min( Threads::GetNumberOfProcessors(), Threads::GetMaxThreads() ) ); } #ifdef CMTK_USE_FFTW_FOUND FFTW::GetStatic().SetNumberOfThreads( Threads::GetNumberOfThreads() ); #endif #ifdef _OPENMP // this is to force Apple's gcc on MacOS to link all OpenMP libraries #pragma omp parallel {} #endif } //@} } // namespace cmtk cmtk-3.3.1/libs/System/cmtkThreads.h000066400000000000000000000131301276303427400173210ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2011 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 5248 $ // // $LastChangedDate: 2014-03-20 14:47:06 -0700 (Thu, 20 Mar 2014) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkThreads_h_included_ #define __cmtkThreads_h_included_ #include #include #include #include #include #include #include namespace cmtk { /** \addtogroup System */ //@{ /** Thread-related utility functions and global configuration variables. */ namespace Threads { /// Check environment variables that control thread creation. void CheckEnvironment(); /// Check whether this system supports threads. bool Available(); /// Number of threads to run in parallel. extern int GetNumberOfThreads(); /** Set the number of threads to run in parallel. * If the given parameter is less than or equal to zero, the number of * threads is set to equal the number of currently available CPUs. This also * applies when this function is called without any parameter. *\param numberOfThreads Number of parallel threads to run. This is a * maximum value. Particular tasks may execute with less parallel threads * than this value. *\param force If this flag is off (default), then the number of parallel * threads is limited by the number of available CPUs on the current host. * If the flag is true, the limitation is not entirely lifted, but the * number of threads is limited by the maximum number of threads that * can be created by a process on the current operating system. *\return The actual number of threads that the library was set to. */ extern int SetNumberOfThreads( const int numberOfThreads, const bool force = false ); /// Helper function for setting number of threads from command line. extern void SetNumberOfThreads( const long int numberOfThreads ); /// Return number of threads allowed per process on this system. extern int GetMaxThreads(); /// Return number of processors currently online. extern int GetNumberOfProcessors(); /// (Maximum) number of threads to run in parallel. extern int NumberOfThreads; /** Specialized but more hands-on thread scheduling function. *\param threadCall Thread function to be called. *\param numberOfThreads Number of parallel threads. This parameter must not * exceed the number of threads set using and returned by SetNumberOfThreads. *\param parameters Pointer to an array of parameter blocks for all parallel * threads. *\param parameterSize Size in bytes of each thread parameter block in the * array pointed to by the previous parameter. */ void RunThreads( ThreadFunction threadCall, const unsigned numberOfThreads, void *const parameters, const size_t parameterSize ); /** Generic thread scheduling function. * This function created a given number of parallel threads with * user-provided thread-specific parameters. It then waits for all threads * to complete before returning. This function is a convenience wrapper for * the four-parameter function of the same name. *\param threadCall Thread function to be called. *\param numberOfThreads Number of parallel threads. This parameter must not * exceed the number of threads set using and returned by SetNumberOfThreads. *\param parameters Pointer to an array of parameter blocks for all parallel * threads. This is a template type parameter, so arbitrary parameter blocks * can be used. */ template void RunThreads( ThreadFunction threadCall, const unsigned numberOfThreads, T *const parameters ) { Threads::RunThreads( threadCall, numberOfThreads, parameters, sizeof( T ) ); } /** Compute stride for threaded loops. * This class computes stride and loop start and ends for threaded loops, based on * a trade-off between number of CPUs in the system and total loop iterations. */ class Stride { public: /// Constructor: compute stride based on 2 blocks per available CPU. Stride( const size_t totalIterations ) : m_TotalIterations( totalIterations ) { this->m_Stride = this->m_TotalIterations / (2 * cmtk::Threads::GetNumberOfProcessors() ); this->m_Blocks = ((this->m_TotalIterations-1) / this->m_Stride) + 1; } /// Return number of blocks. size_t NBlocks() const { return this->m_Blocks; } /// Loop "from" for a given block. size_t From( const size_t blockIdx ) const { return blockIdx * this->m_Stride; } size_t To( const size_t blockIdx ) const { return std::min( (1+blockIdx) * this->m_Stride, this->m_TotalIterations ); } private: /// Total number of loop iterations. size_t m_TotalIterations; /// Number of blocks. size_t m_Blocks; /// Stride. size_t m_Stride; }; } // namespace Threads //@} } // namespace cmtk #endif // #ifndef __cmtkThreads_h_included_ cmtk-3.3.1/libs/System/cmtkTimers.cxx000066400000000000000000000055601276303427400175550ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2010 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 3288 $ // // $LastChangedDate: 2011-07-26 16:27:51 -0700 (Tue, 26 Jul 2011) $ // // $LastChangedBy: torstenrohlfing $ // */ #include "cmtkTimers.h" #include #include #include #ifdef HAVE_SYS_IOCTL_H # include #endif #ifdef HAVE_FCNTL_H # include #endif #ifdef HAVE_SYS_PROCFS_H # include #endif #ifdef CMTK_USE_PTHREADS # include #endif double cmtk::Timers::GetTimeProcess() { #ifdef _MSC_VER return (double) clock(); #else struct tms t; if ( times(&t) ) #ifdef _SC_CLK_TCK return (double)( t.tms_utime + t.tms_cutime + t.tms_stime + t.tms_cstime )/sysconf(_SC_CLK_TCK); #else return (double)( t.tms_utime + t.tms_cutime + t.tms_stime + t.tms_cstime )/CLK_TCK; #endif else return 0; #endif // #ifdef _MSC_VER } double cmtk::Timers::GetWalltime() { return static_cast( time( NULL ) ); } double cmtk::Timers::GetTimeThread() { #ifndef _MSC_VER #ifdef HAVE_SYS_PROCFS_H char buffer[80]; snprintf( buffer, sizeof( buffer ), "/proc/%ld/usage", (long)getpid() ); FILE *fp = fopen( buffer, "r" ); if ( fp ) { #ifdef PRUSAGE_T_IN_SYS_PROCFS_H prusage_t usage; if ( fread( &usage, sizeof( usage ), 1, fp ) ) { fclose( fp ); // return ((double) usage.pr_rtime.tv_sec) + // ((double) usage.pr_rtime.tv_nsec) / NANOSEC; return ((double) usage.pr_utime.tv_sec) + ((double) usage.pr_utime.tv_nsec) / NANOSEC + ((double) usage.pr_stime.tv_sec) + ((double) usage.pr_stime.tv_nsec) / NANOSEC; } #endif fclose( fp ); } return 0; #else // #ifdef HAVE_SYS_PROCFS_H struct tms t; if ( times(&t) ) #ifdef _SC_CLK_TCK return (double)(t.tms_utime + t.tms_stime)/sysconf(_SC_CLK_TCK); #else return (double)(t.tms_utime + t.tms_stime)/CLK_TCK; #endif else return 0; #endif // #ifdef HAVE_SYS_PROCFS_H #else // ifndef _MSC_VER return (double) clock(); #endif // ifndef _MSC_VER } cmtk-3.3.1/libs/System/cmtkTimers.h000066400000000000000000000036301276303427400171760ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2010 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 2022 $ // // $LastChangedDate: 2010-07-21 15:26:03 -0700 (Wed, 21 Jul 2010) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkTimers_h_included_ #define __cmtkTimers_h_included_ #include #ifdef HAVE_SYS_TIME_H # include #endif #ifdef HAVE_SYS_TIMES_H # include #endif #ifdef HAVE_UNISTD_H # include #endif #include #include namespace cmtk { /** \addtogroup System */ //@{ /** Namespace that contains functions and variables for CPU time measurements. */ namespace Timers { /// Get CPU time for the current process. extern double GetTimeProcess(); /// Get CPU walltime for the current process. extern double GetWalltime(); /** Get CPU time for the current thread. *\todo We need to find an equivalent implementation of this under Windows. */ extern double GetTimeThread(); } // namespace Timers //@} } // namespace cmtk #endif // #ifndef __cmtkTimers_h_included_ cmtk-3.3.1/libs/System/doxygen.h000066400000000000000000000023661276303427400165360ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2011 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 2734 $ // // $LastChangedDate: 2011-01-14 10:23:45 -0800 (Fri, 14 Jan 2011) $ // // $LastChangedBy: torstenrohlfing $ // */ /** \defgroup System cmtkSystem Library * This library provides system-related classes, such as command line parsing, * memory allocation, threading, synchronization, and timing. */ cmtk-3.3.1/libs/Unstable/000077500000000000000000000000001276303427400151725ustar00rootroot00000000000000cmtk-3.3.1/libs/Unstable/CMakeLists.txt000066400000000000000000000035531276303427400177400ustar00rootroot00000000000000## ## Copyright 1997-2009 Torsten Rohlfing ## ## Copyright 2004-2012 SRI International ## ## This file is part of the Computational Morphometry Toolkit. ## ## http://www.nitrc.org/projects/cmtk/ ## ## The Computational Morphometry Toolkit is free software: you can ## redistribute it and/or modify it under the terms of the GNU General Public ## License as published by the Free Software Foundation, either version 3 of ## the License, or (at your option) any later version. ## ## The Computational Morphometry Toolkit is distributed in the hope that it ## will be useful, but WITHOUT ANY WARRANTY; without even the implied ## warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License along ## with the Computational Morphometry Toolkit. If not, see ## . ## ## $Revision: 3814 $ ## ## $LastChangedDate: 2012-02-03 09:46:58 -0800 (Fri, 03 Feb 2012) $ ## ## $LastChangedBy: torstenrohlfing $ ## # Sources of non-templated classes. SET(cmtkUnstable_SRCS dummy.cxx cmtkTypedArrayNoiseEstimatorBrummer.cxx cmtkTypedArrayNoiseEstimatorMaximumLikelihood.cxx cmtkFilterVolumeCoupe.cxx) ADD_LIBRARY(cmtkUnstable ${cmtkUnstable_SRCS}) IF(CMTK_LIBRARY_PROPERTIES) SET_TARGET_PROPERTIES(cmtkUnstable PROPERTIES ${CMTK_LIBRARY_PROPERTIES}) ENDIF(CMTK_LIBRARY_PROPERTIES) INSTALL(TARGETS cmtkUnstable RUNTIME DESTINATION ${CMTK_INSTALL_BIN_DIR} COMPONENT runtime LIBRARY DESTINATION ${CMTK_INSTALL_LIB_DIR} COMPONENT runtime ARCHIVE DESTINATION ${CMTK_INSTALL_LIB_DIR} COMPONENT libraries) FILE(GLOB files_h "${CMAKE_CURRENT_SOURCE_DIR}/*.h") FILE(GLOB files_txx "${CMAKE_CURRENT_SOURCE_DIR}/*.txx") INSTALL(FILES ${files_h} ${files_txx} DESTINATION ${CMTK_INSTALL_INCLUDE_DIR}/Unstable COMPONENT headers) cmtk-3.3.1/libs/Unstable/cmtkFilterVolumeCoupe.cxx000066400000000000000000000671101276303427400222130ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2011, 2013 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4876 $ // // $LastChangedDate: 2013-09-24 13:09:58 -0700 (Tue, 24 Sep 2013) $ // // $LastChangedBy: torstenrohlfing $ // */ #include "cmtkFilterVolumeCoupe.h" #define COUPE_BLOCK_SIZE 27 #define COUPE_BLOCK_RADIUS 1 namespace cmtk { Types::DataItem FilterVolumeCoupe::Mean ( TypedArray::SmartPtr items, const int numItems ) { Types::DataItem sum = 0.0; Types::DataItem curItem; for ( int i = 0; i < numItems; i++ ) { items->Get(curItem, i); sum += curItem; } return sum / numItems; } Types::DataItem FilterVolumeCoupe::Variance ( TypedArray::SmartPtr items, const int numItems, const Types::DataItem mean ) { Types::DataItem sum = 0.0; Types::DataItem curItem; for ( int i = 0; i < numItems; i++ ) { items->Get(curItem, i); sum += pow( curItem - mean, 2 ); } return sum / numItems; } void FilterVolumeCoupe::BlockAddInPlace ( TypedArray::SmartPtr v1, TypedArray::SmartPtr v2, const int blockSize ) { Types::DataItem item1; Types::DataItem item2; for ( int i = 0; i < blockSize; i++ ) { v1->Get(item1, i); v2->Get(item2, i); v1->Set( item1+item2, i ); } } void FilterVolumeCoupe::BlockSubtract ( TypedArray::SmartPtr diff, TypedArray::SmartPtr v1, TypedArray::SmartPtr v2, const int blockSize) { Types::DataItem item1; Types::DataItem item2; for ( int i = 0; i < blockSize; i++ ) { v1->Get(item1, i); v2->Get(item2, i); diff->Set( item1-item2, i ); } } void FilterVolumeCoupe::BlockConstMult ( TypedArray::SmartPtr prod, TypedArray::SmartPtr items, const Types::DataItem mult, const int blockSize ) { Types::DataItem curItem; for ( int i = 0; i < blockSize; i++ ) { items->Get(curItem, i); prod->Set( curItem * mult, i ); } } double FilterVolumeCoupe::BlockSquaredDistance ( TypedArray::SmartPtr centerBlock, TypedArray::SmartPtr outerBlock, const int blockSize ) { TypedArray::SmartPtr diff = TypedArray::Create( centerBlock->GetType(), blockSize ); BlockSubtract( diff, centerBlock, outerBlock, blockSize ); Types::DataItem sum = 0.0; Types::DataItem curItem; for ( int i = 0; i < blockSize; i++ ) { diff->Get(curItem, i); sum += pow( curItem, 2 ); } return ( sum ); // Coupe paper uses squared distance } void FilterVolumeCoupe::GetNeighborhood ( TypedArray::SmartPtr neighborhood, const int radius, const TypedArray* data, const int* dims, const int x, const int y, const int z ) { int curBlockSlot = 0; /* No bounds-checking here. The program should fail if * a block is requested too close to the edge of the image. */ for ( int k = z - radius; k <= z + radius; ++k ) for ( int j = y - radius; j <= y + radius; ++j ) for ( int i = x - radius; i <= x + radius; ++i ) { int offset = i + dims[AXIS_X] * ( j + dims[AXIS_Y] * k ); Types::DataItem value; data->Get( value, offset ); neighborhood->Set( value, curBlockSlot++ ); } } void FilterVolumeCoupe::GetCoupeBlock ( TypedArray::SmartPtr block, const TypedArray* data, const int* dims, const int x, const int y, const int z ) { FilterVolumeCoupe::GetNeighborhood( block, COUPE_BLOCK_RADIUS, data, dims, x, y, z ); } double FilterVolumeCoupe::ComputeCoupeWeight ( const Types::DataItem smoothingParam, TypedArray::SmartPtr centerBlock, TypedArray::SmartPtr outerBlock ) { const double numerator = BlockSquaredDistance( centerBlock, outerBlock, COUPE_BLOCK_SIZE ); double weight = 1.0; if ( numerator != 0 ) weight = exp( -(log(numerator) / smoothingParam) ); //std::cout << numerator << ", " << smoothingParam << std::endl; //std::cout << weight << "\n"; return weight; } void FilterVolumeCoupe::ComputeNLWithinWindow ( TypedArray::SmartPtr NL, const TypedArray* blockLocations, const TypedArray* data, const int* dims, const Types::DataItem smoothingParam, const int x, const int y, const int z, const int windowRadius, const float, // beta const TypedArray* localMeansMap, const TypedArray* localVariancesMap, TypedArray::SmartPtr centerBlock ) { /* These two constants were determined experimentally * by Coupe, et al. (Section V-C, Fig. 8.) */ const float Mu1 = 0.95; const float Sigma1SQ = 0.5; /* In this loop, build a set of weights from only * that portion of the neighborhood-window that falls * within the image boundaries. */ int curNeighbor = 0; int centerBlockPos = -1; const int windowSize = (2 * windowRadius + 1) * (2 * windowRadius + 1) * (2 * windowRadius + 1); const int blockRadius = COUPE_BLOCK_RADIUS; int centerOffset = x + dims[AXIS_X] * ( y + dims[AXIS_Y] * z ); Types::DataItem centerBlockMean, centerBlockVariance; localMeansMap->Get( centerBlockMean, centerOffset ); localVariancesMap->Get( centerBlockVariance, centerOffset ); TypedArray::SmartPtr curBlock = TypedArray::Create( data->GetType(), COUPE_BLOCK_SIZE ); int offset = 0; Types::DataItem blockAtCurVox = 0.0; //#ifdef CMTK_COMPILER_VAR_AUTO_ARRAYSIZE // Types::DataItem neighborBlocks[windowSize][COUPE_BLOCK_SIZE]; // Types::DataItem neighborWeights[windowSize]; //#else Matrix2D neighborBlocks( windowSize, COUPE_BLOCK_SIZE ); std::vector neighborWeights( windowSize ); //#endif Types::DataItem curBlockMean, curBlockVariance; Types::DataItem ratioBlockMean, ratioBlockVariance; for ( int k = z - windowRadius; k <= z + windowRadius; k++ ) if ( ( k >= blockRadius ) && ( k < dims[AXIS_Z] - blockRadius ) ) { for ( int j = y - windowRadius; j <= y + windowRadius; j++ ) if ( ( j >= blockRadius ) && ( j < dims[AXIS_Y] - blockRadius ) ) { for ( int i = x - windowRadius; i <= x + windowRadius; i++ ) if ( ( i >= blockRadius ) && ( i < dims[AXIS_X] - blockRadius ) ) { offset = i + dims[AXIS_X] * ( j + dims[AXIS_Y] * k ); blockLocations->Get( blockAtCurVox, offset ); if ( blockAtCurVox ) // if there's a block here { //FilterVolumeCoupe::GetCoupeBlock( curBlock, data, dims, i, j, k ); FilterVolumeCoupe::GetNeighborhood( curBlock, COUPE_BLOCK_RADIUS, data, dims, i, j, k ); localMeansMap->Get( curBlockMean, offset ); localVariancesMap->Get( curBlockVariance, offset ); ratioBlockMean = centerBlockMean / curBlockMean; /* This block is to handle the 0:0 ratio without divide-by-zero */ if ( curBlockVariance != 0 ) { ratioBlockVariance = centerBlockVariance / curBlockVariance; } else { ratioBlockVariance = ( centerBlockVariance == 0 ) ? 1.0 : 0.0; } if ( ( Mu1 <= ratioBlockMean ) && ( ratioBlockMean <= ( 1 / Mu1 ) ) && ( Sigma1SQ <= ratioBlockVariance ) && ( ratioBlockVariance <= ( 1 / Sigma1SQ ) ) ) { /* Handle blocks that aren't the center block here, * handle the center block in 'else' below */ if ( ( ( i != x ) || ( j != y ) || ( k != z ) ) ) { double weight = ComputeCoupeWeight( smoothingParam, centerBlock, curBlock ); if ( weight > 0 ) { if ( ( i == 91 ) && ( j == 1 ) && ( k == 93 ) ) { Types::DataItem tmp; data->Get( tmp, offset ); std::cout << i << "," << j << "," << k << "\t"; std::cout << tmp << "\t" << curBlockMean << "\t" << weight << "\n"; } memcpy( neighborBlocks[curNeighbor], curBlock, sizeof( curBlock ) ); neighborWeights[curNeighbor] = weight; } } /* Handle center block */ else { if ( ( i == 91 ) && ( j == 1 ) && ( k == 93 ) ) { Types::DataItem tmp; data->Get( tmp, offset ); std::cout << i << "," << j << "," << k << "\t" << tmp << "\t"; std::cout << curBlockMean << "\t..." << "\n"; } memcpy( neighborBlocks[curNeighbor], curBlock, sizeof( curBlock ) ); /* Store the position of the center block * w.r.t. the list of used neighbors, for use * in the weighting clause below. */ centerBlockPos = curNeighbor; } curNeighbor++; } } } } } int numNeighborsUsed = curNeighbor + 1; /* As per Manjon et al, 2008, set the weight of the center * block to equal the largest of the neighbor-weights. */ if ( numNeighborsUsed < 2 ) { neighborWeights[centerBlockPos] = 1.0; } else { Types::DataItem maxWt = 0.0; for ( int i = 0; i < numNeighborsUsed; i++ ) maxWt = ( neighborWeights[i] > maxWt ) ? neighborWeights[i] : maxWt; //neighborWeights[centerBlockPos] = 1.0; neighborWeights[centerBlockPos] = maxWt; } /* Sum the weights for normalization */ Types::DataItem weightsSum = 0.0; for ( int i = 0; i < numNeighborsUsed; i++ ) weightsSum += neighborWeights[i]; for ( int i = 0; i < COUPE_BLOCK_SIZE; i++ ) NL->Set( 0.0, i ); Types::DataItem curVal; //if ( weightsSum > 1e-9 ) if ( weightsSum != 0 ) { /* Multiply each neighborBlock by its normalized weight, * then add that to the output NL value. */ TypedArray::SmartPtr weightedCurBlock; Types::DataItem tmp = 0.0; for ( int i = 0; i < numNeighborsUsed; i++ ) { Types::DataItem normalizedWeight = 0; normalizedWeight = neighborWeights[i] / weightsSum; tmp += normalizedWeight; for (int b = 0; b < COUPE_BLOCK_SIZE; b++) { curVal = neighborBlocks[i][b]; curBlock->Set( curVal, b ); } BlockConstMult( weightedCurBlock, curBlock, normalizedWeight, COUPE_BLOCK_SIZE ); BlockAddInPlace( NL, weightedCurBlock, COUPE_BLOCK_SIZE ); } } else { for (int b = 0; b < COUPE_BLOCK_SIZE; b++) { curVal = neighborBlocks[centerBlockPos][b]; curBlock->Set( curVal, b ); } BlockAddInPlace( NL, curBlock, COUPE_BLOCK_SIZE ); } //std::cout <GetData(); if ( ! inputData ) return TypedArray::SmartPtr( NULL ); TypedArray::SmartPtr filtered = TypedArray::Create( inputData->GetType(), inputData->GetDataSize() ); const int* dims = volume->GetDims().begin(); const int dimX = dims[AXIS_X]; const int dimY = dims[AXIS_Y]; const int dimZ = dims[AXIS_Z]; const int blockRadius = COUPE_BLOCK_RADIUS; const int stepSize = BLOCKWISE_NLM ? 2 : 1; std::cout << "Block radius:\t" << COUPE_BLOCK_RADIUS << "\n"; std::cout << "Window radius:\t" << windowRadius << "\n"; std::cout << "Beta:\t\t" << beta << std::endl; std::cout << "Dimensions:\t" << dimX << " x " << dimY << " x " << dimZ << std::endl; std::vector< std::vector* > NLsPerVoxel; /* Initialize an array with a vector for each voxel, * to store a set of NL estimates for each voxel. */ for ( int i = 0; i < ( dimX * dimY * dimZ ); i++ ) { std::vector* tmp; NLsPerVoxel.push_back( tmp ); NLsPerVoxel[i] = new std::vector(); } /* Compute the smoothing parameter */ //Types::DataItem varianceEst = ( EstimateNoiseVariance( inputData, dims ) ); Types::DataItem stdDevEst = (Types::DataItem)3.70101; Types::DataItem varianceEst = stdDevEst * stdDevEst; std::cout << "varianceEst:\t" << varianceEst << std::endl; Types::DataItem smoothingParam = 2 * beta * varianceEst * COUPE_BLOCK_SIZE; //Types::DataItem smoothingParam = pow( 2.19037, 2 ); std::cout << "h:\t\t"<< sqrt(smoothingParam) << std::endl; /* Figure out where the blocks will be */ TypedArray::SmartPtr blockLocations = TypedArray::Create( TYPE_INT, inputData->GetDataSize() ); int blockCount = 0; for ( int z = blockRadius; z < dimZ - blockRadius; z++ ) for ( int y = blockRadius; y < dimY - blockRadius ; y++ ) for ( int x = blockRadius; x < dimX - blockRadius; x++ ) { int offset = x + dimX * ( y + dimY * z ); if ( ( ( x % stepSize ) == 0 ) && ( ( y % stepSize ) == 0 ) && ( ( z % stepSize ) == 0 ) ) { blockLocations->Set( 1.0, offset ); blockCount++; } else { blockLocations->Set( 0.0, offset ); } } std::cout << "Block count:\t" << blockCount << std::endl; /* Precompute the local means and local variances maps */ TypedArray::SmartPtr curBlock = TypedArray::Create( inputData->GetType(), COUPE_BLOCK_SIZE ); TypedArray::SmartPtr localMeansMap = TypedArray::Create( TYPE_DOUBLE, inputData->GetDataSize() ); TypedArray::SmartPtr localVariancesMap = TypedArray::Create( TYPE_DOUBLE, inputData->GetDataSize() ); for ( int z = blockRadius; z < dimZ - blockRadius; z++ ) for ( int y = blockRadius; y < dimY - blockRadius ; y++ ) for ( int x = blockRadius; x < dimX - blockRadius; x++ ) { int offset = x + dimX * ( y + dimY * z ); FilterVolumeCoupe::GetNeighborhood( curBlock, COUPE_BLOCK_RADIUS, inputData, dims, x, y, z ); //FilterVolumeCoupe::GetCoupeBlock( curBlock, inputData, dims, x, y, z ); Types::DataItem mean = FilterVolumeCoupe::Mean( curBlock, COUPE_BLOCK_RADIUS ); Types::DataItem variance = FilterVolumeCoupe::Variance( curBlock, COUPE_BLOCK_RADIUS, mean ); localMeansMap->Set( mean, offset); localVariancesMap->Set( variance, offset); } /* Loop through the image, computing NL estimates * for each voxel. */ Types::DataItem blockAtCurVox = 0.0; for ( int Cz = blockRadius; Cz < dimZ - blockRadius; Cz ++ ) { for ( int Cy = blockRadius; Cy < dimY - blockRadius; Cy ++ ) for ( int Cx = blockRadius; Cx < dimX - blockRadius; Cx ++ ) { int windowOffset = Cx + dimX * ( Cy + dimY * Cz ); blockLocations->Get( blockAtCurVox, windowOffset ); if ( blockAtCurVox ) // if there's a block here { Types::DataItem maxWeight = 0.0; // to hold the highest neighbor-weight below Types::DataItem weightsSum = 0.0; // to hold the sum of the neighbor weights below Types::DataItem outputTmp = 0.0; // to accumulate the neighbor weights in voxel-wise case Types::DataItem NLblock[COUPE_BLOCK_SIZE]; // to accumulate the weighted sum of neighbor blocks in block-wise case Types::DataItem flattenedBlock[COUPE_BLOCK_SIZE]; // to store 1-D representation of neighbor block in block-wise case /* Initialize accumulator block for current neighborhood */ if ( BLOCKWISE_NLM ) for ( int i = 0; i < COUPE_BLOCK_SIZE; i++ ) NLblock[i] = 0.0; /* Gather statistics on current voxel for use in the * similarity threshold below */ Types::DataItem centerMean, centerVariance; localMeansMap->Get( centerMean, windowOffset ); localVariancesMap->Get( centerVariance, windowOffset ); /* Iterate through the blocks of the window centered at Cx,Cy,Cz * ( Nx, Ny, Nz are the x,y,z coordinates of the current neighbor block ) */ for ( int Nz = std::max( Cz - windowRadius, 0 ); Nz < std::min( Cz + windowRadius, dimZ ); Nz++ ) for ( int Ny = std::max( Cy - windowRadius, 0 ); Ny < std::min( Cy + windowRadius, dimZ ); Ny++ ) for ( int Nx = std::max( Cx - windowRadius, 0 ); Nx < std::min( Cx + windowRadius, dimZ ); Nx++ ) { Types::DataItem neighborDist = 0.0; // Will hold Euclidean dist. of center and current neighbor intensities int neighborOffset = Nx + dimX * ( Ny + dimY * Nz ); blockLocations->Get( blockAtCurVox, neighborOffset ); if ( blockAtCurVox && ( ( Nx != Cx) || ( Ny != Cy ) || ( Nz != Cz ) ) ) // skip center block for now { /* Only use blocks falling within the similarity range. * So here, compute the ratio of the means of the center * block and the neighbor block, and the ratio of their * variances. Then proceed only if those values fall * within the desired range. */ Types::DataItem nbMean, nbVariance; localMeansMap->Get( nbMean, neighborOffset ); localVariancesMap->Get( nbVariance, neighborOffset ); Types::DataItem ratioMeans = centerMean / nbMean; Types::DataItem ratioVariance = centerVariance / nbVariance; Types::DataItem mu1 = (Types::DataItem)0.5; Types::DataItem sigma1 = (Types::DataItem)0.95; if ( ( ratioMeans > mu1 ) && ( ratioMeans <= 1 / mu1 ) && ( ratioVariance > sigma1 ) && ( ratioVariance <= 1 / sigma1 ) ) { /* Initialize 1-D array for applicaton of weight to current block */ if ( BLOCKWISE_NLM ) for ( int i = 0; i < COUPE_BLOCK_SIZE; i++ ) flattenedBlock[i] = 0.0; /* Iterate through center block and neighbor block in parallel, * calculating the squared Euclidean distance between the two. * Only use blocks that fall completely within the image. * ( Ix,Iy,Iz are the x,y,z coordinates of the center block voxels, * Jx,Jx,jZ are the x,y,z coordinates of the neighbor block voxels. ) */ int i = 0; for ( int Bz = -blockRadius; Bz <= blockRadius; Bz++ ) { int Iz = Cz + Bz; int Jz = Nz + Bz; if ( ( Iz >= 0 ) && ( Iz < dimZ ) && ( Jz >= 0 ) && ( Jz < dimZ ) ) for ( int By = -blockRadius; By <= blockRadius; By++ ) { int Iy = Cy + By; int Jy = Ny + By; if ( ( Iy >= 0 ) && ( Iy < dimY ) && ( Jy >= 0 ) && ( Jy < dimY ) ) for ( int Bx = -blockRadius; Bx <= blockRadius; Bx++ ) { int Ix = Cx + Bx; int Jx = Nx + Bx; if ( ( Ix >= 0 ) && ( Ix < dimX ) && ( Jx >= 0 ) && ( Jx < dimX ) ) { int BIoffset = Ix + dimX * ( Iy + dimY * Iz ); int BJoffset = Jx + dimX * ( Jy + dimY * Jz ); Types::DataItem BI, BJ; inputData->Get( BI, BIoffset ); inputData->Get( BJ, BJoffset ); Types::DataItem distIJ = BI - BJ; neighborDist += distIJ * distIJ; if ( BLOCKWISE_NLM ) flattenedBlock[i++] = BJ; } } } } Types::DataItem curWeight = exp( log(neighborDist) / smoothingParam ); weightsSum += curWeight; maxWeight = ( curWeight > maxWeight ) ? curWeight : maxWeight; if ( BLOCKWISE_NLM ) { /* Weight the neighbor block and add that * to the accumulator block */ for ( int ii = 0; ii < COUPE_BLOCK_SIZE; ii++) { NLblock[ii] += curWeight * flattenedBlock[ii]; //std::cout << curWeight << "\t" << flattenedBlock[ii] << "\n"; } } else // voxel-wise case { /* Weight the neighbor voxel and add that * to the accumulator value */ Types::DataItem neighbValue; inputData->Get( neighbValue, neighborOffset ); outputTmp += curWeight * neighbValue; } } // end similarity-threshold test } // end test for J != I } // end loop through blocks of current window /* Current center block gets weighted by * the maximum neighbor-weight */ Types::DataItem centerValue; inputData->Get( centerValue, windowOffset ); outputTmp += maxWeight * centerValue; weightsSum += maxWeight; if ( BLOCKWISE_NLM ) { /* Push the accumulator block into place for averaging * later * ( Ix,Iy,Iz are the coordinates the center block voxels ) */ int i = 0; for ( int Iz = Cz - blockRadius; Iz <= Cz + blockRadius; Iz++ ) { if ( ( Iz >= 0 ) && ( Iz < dimZ ) ) for ( int Iy = Cy - blockRadius; Iy <= Cy + blockRadius; Iy++ ) { if ( ( Iy >= 0 ) && ( Iy < dimY ) ) for ( int Ix = Cx - blockRadius; Ix <= Cx + blockRadius; Ix++ ) { if ( ( Ix >= 0 ) && ( Ix < dimX ) ) { int Voffset = Ix + dimX * ( Iy + dimY * Iz ); //std::cout << Voffset << "\t" << NLblock[i] << "\n"; NLsPerVoxel[Voffset]->push_back( NLblock[i] ); i++; } } } } } else { /* Set the output voxel */ filtered->Set( outputTmp / weightsSum, windowOffset ); } } // end if ( blockAtCurVox ) } // end loop through pixels of image // FilterVolumeCoupe::ComputeNLWithinWindow( curNL, blockLocations, inputData, dims, smoothingParam, // Cx, Cy, Cz, // windowRadius, // beta, // localMeansMap, // localVariancesMap, // centerBlock ); // /* Push the values from curNL into the vector // * containing the NL estimates for the respective // * corresponding voxels. (curNL contains an NL // * estimate for each voxel in this block). // */ // int curNLIndex = 0; // for ( int k = z - blockRadius; k <= z + blockRadius; ++k ) // if ( ( k >= 0 ) && ( k < dimZ ) ) // { // for ( int j = y - blockRadius; j <= y + blockRadius; ++j ) // if ( ( j >= 0 ) && ( j < dimY ) ) // { // for ( int i = x - blockRadius; i <= x + blockRadius; ++i ) // if ( ( i >= 0 ) && ( i < dimX ) ) // { // int offset = i + dimX * ( j + dimY * k ); // NLsPerVoxel[offset]->push_back( curNL[curNLIndex] ); // curNLIndex++; // } // } // } } if ( BLOCKWISE_NLM ) { /* Take the average of the NL estimates for each voxel */ int NLsPerVoxelHist[40] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 }; for ( int z = 0; z < dimZ; ++z ) for ( int y = 0; y < dimY; ++y ) for ( int x = 0; x < dimX; ++x ) { int offset = x + dimX * ( y + dimY * z ); NLsPerVoxelHist[NLsPerVoxel.at( offset )->size()]++; Types::DataItem curNLSum = 0.0; for ( std::vector::iterator it = NLsPerVoxel.at( offset )->begin(); it != NLsPerVoxel.at( offset )->end(); ++it ) curNLSum += *it; Types::DataItem restoredVoxel = curNLSum / NLsPerVoxel.at( offset )->size(); //std::cout << offset << "\t" << curNLSum << "\t" << NLsPerVoxel.at( offset )->size() << "\n"; filtered->Set( restoredVoxel , offset ); if ( ( x == 91 ) && ( y == 1 ) && ( z == 93 ) ) { std::cout << std::endl; std::cout << std::endl; for ( std::vector::iterator it = NLsPerVoxel.at( offset )->begin(); it != NLsPerVoxel.at( offset )->end(); ++it ) { std::cout << *it << "..." << "\n"; } std::cout << restoredVoxel << "." << "\n"; } } /* Print out the histogram of NL counts */ std::cout << std::endl; std::cout << "NL-count histogram:" << std::endl; std::cout << "0\t1\t2\t3\t4\t5\t6\t7\t8\t9\t10\t11\t12\t13\t14\t15\t16\t17\t18\t19" << std::endl; for ( int i = 0; i < 20; i++ ) std::cout << NLsPerVoxelHist[i] << "\t"; std::cout << std::endl; std::cout << std::endl; std::cout << "20\t21\t22\t23\t24\t25\t26\t27\t28\t29\t30\t31\t32\t33\t34\t35\t36\t37\t38\t39" << std::endl; for ( int i = 20; i < 40; i++ ) std::cout << NLsPerVoxelHist[i] << "\t"; std::cout << std::endl; std::cout << std::endl; } return filtered; } } // namespace cmtk cmtk-3.3.1/libs/Unstable/cmtkFilterVolumeCoupe.h000066400000000000000000000151031276303427400216330ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2012 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 3812 $ // // $LastChangedDate: 2012-02-02 15:52:58 -0800 (Thu, 02 Feb 2012) $ // // $LastChangedBy: torstenrohlfing $ // */ #ifndef __cmtkFilterVolumeCoupe_h_included_ #define __cmtkFilterVolumeCoupe_h_included_ #include #include #include #include namespace cmtk { /** \addtogroup Base */ //@{ /// Class for filtering volume images. class FilterVolumeCoupe { public: /** Apply Coupe denoising filter. *\param volume Input 3D image. *\param beta Smoothing adjustment parameter *\param windowRadius Distance from center voxel to outer edge of window *\return A newly allocated TypedArray object that can be used, for * example, to replace the one held by the input image. The data type of the * array is identical to the input array. */ static TypedArray::SmartPtr CoupeFilter ( const UniformVolume* volume, const int windowRadius, const float beta = 0.5 ); private: /** Return the mean value of a vector of Types::DataItems. *\param items Block of Types::DataItems. */ static Types::DataItem Mean( TypedArray::SmartPtr items, const int numItems ); /** Return the variance of a vector of Types::DataItems. *\param items Block of Types::DataItems. *\param mean The (pre-computed) mean value of the items vector. */ static Types::DataItem Variance( TypedArray::SmartPtr items, const int numItems, const Types::DataItem mean ); /** Fill an array with the neighborhood centered around a given voxel. * Note that this routine will only include existing * voxels. So asking for a block near the edge * of a volume will produce a truncated block. *\param block To store the newly-fetched block *\param data Data array from input 3D image. *\param dims Dimensions of the input 3D image. *\param x X-coordinate of block center. *\param y Y-coordinate of block center. *\param z Z-coordinate of block center. */ static void GetNeighborhood( TypedArray::SmartPtr neighborhood, const int radius, const TypedArray* data, const int* dims, const int x, const int y, const int z ); /** Fill a CoupeBlock with the block centered around a given voxel. * Note that this routine will only include existing * voxels. So asking for a block near the edge * of a volume will produce a truncated block. *\param block To store the newly-fetched block *\param data Data array from input 3D image. *\param dims Dimensions of the input 3D image. *\param x X-coordinate of block center. *\param y Y-coordinate of block center. *\param z Z-coordinate of block center. */ static void GetCoupeBlock( TypedArray::SmartPtr block, const TypedArray* data, const int* dims, const int x, const int y, const int z ); /** Compute the NL-weighting factor between two blocks. *\param smoothingParam Smoothing parameter for the weighting function *\param centerBlock The block to be restored using NL-means *\param outerBlock A block to contribute to the restoration of centerBlock */ static double ComputeCoupeWeight ( const Types::DataItem smoothingParam, TypedArray::SmartPtr centerBlock, TypedArray::SmartPtr outerBlock ); /** Add two blocks, in-place on the first block. *\param v1 First vector addend, becomes the sum *\param v2 Second vector addend *\param blockSize Size of the block. */ static void BlockAddInPlace( TypedArray::SmartPtr v1, TypedArray::SmartPtr v2, const int blockSize ); /** Subtract two blocks. *\param diff Block to contain the difference between v1 and v2 *\param v1 First block subtrahend *\param v2 Second block subtrahend *\param blockSize Size of the block. */ static void BlockSubtract( TypedArray::SmartPtr diff, TypedArray::SmartPtr v1, TypedArray::SmartPtr v2, const int blockSize ); /** Return the product of the input vector multiplied by a constant float. *\param prod An block to store the product *\param items An block containing the items to be multiplied *\param mult The constant to multiply by *\param blockSize Size of the block. */ static void BlockConstMult( TypedArray::SmartPtr prod, TypedArray::SmartPtr items, const Types::DataItem mult, const int blockSize ); /** Return the squared Euclidean distance between two blocks. */ static double BlockSquaredDistance( TypedArray::SmartPtr centerBlock, TypedArray::SmartPtr outerBlock, const int blockSize ); /** Process a window for NL-means accumulation. *\param NL Block in which to put the NL computation *\param blockLocations UNDOCUMENTED *\param data Data array from input 3D image. *\param dims Dimensions of the input 3D image. *\param smoothingParam Smoothing parameter for the weighting function *\param x X-coordinate of volume center. *\param y Y-coordinate of volume center. *\param z Z-coordinate of volume center. *\param windowRadius Distance from center voxel to outer edge of window *\param beta Smoothing adjustment parameter *\param localMeansMap Mean intensity values at each block *\param localVariancesMap Variance of the intensities of the center block *\param centerBlock The block to be restored using NL-means */ static void ComputeNLWithinWindow ( TypedArray::SmartPtr NL, const TypedArray* blockLocations, const TypedArray* data, const int* dims, const Types::DataItem smoothingParam, const int x, const int y, const int z, const int windowRadius, const float beta, const TypedArray* localMeansMap, const TypedArray* localVariancesMap, TypedArray::SmartPtr centerBlock ); }; //@} } // namespace cmtk #endif // #ifndef __cmtkFilterVolumeCoupe_h_included_ cmtk-3.3.1/libs/Unstable/cmtkTypedArrayNoiseEstimatorBrummer.cxx000066400000000000000000000256071276303427400251130ustar00rootroot00000000000000/* // // Copyright 2008-2013 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4876 $ // // $LastChangedDate: 2013-09-24 13:09:58 -0700 (Tue, 24 Sep 2013) $ // // $LastChangedBy: torstenrohlfing $ // */ #include "cmtkTypedArrayNoiseEstimatorBrummer.h" namespace cmtk { /* Brummer estimation of the noise variance. * (As described in the Sijbers paper.) */ TypedArrayNoiseEstimatorBrummer::TypedArrayNoiseEstimatorBrummer ( const TypedArray* data, const size_t histogramBins ) { UNUSED(histogramBins); const size_t histogramBinsActual = 255; Histogram::SmartPtr histogram( data->GetHistogram( histogramBinsActual ) ); // int numBinsToUse; double sampleMean = 0; for ( size_t i = 0; i < histogramBinsActual; i++ ) { sampleMean += (*histogram)[i] * histogram->BinToValue(i); } sampleMean /= histogram->SampleCount(); std::cout << " sampleMean: " << sampleMean << std::endl; std::cout << " sampleCount: " << histogram->SampleCount() << std::endl; std::cout << " 1 / sampleCount: " << (1.0 / histogram->SampleCount()) << std::endl; double sumSquaredSampleDiffs = 0; for ( size_t i = 0; i < histogramBinsActual; i++ ) { sumSquaredSampleDiffs += (*histogram)[i] * pow( ( histogram->BinToValue(i) - sampleMean ), 2 ); } std::cout << " sumSquaredSampleDiffs: " << sumSquaredSampleDiffs << std::endl; const double sampleStdDev = sqrt( ( 1.0 / histogram->SampleCount() ) * sumSquaredSampleDiffs ); std::cout << " sampleStdDev: " << sampleStdDev << std::endl; std::cout << " npow: " << pow( (double)histogram->SampleCount(), 1/5 ) << std::endl; /* Find an initial estimate of sigma using the Chang method. * (Chang L-C et al 2005 "An automatic method for estimating noise-induced signal * variance in magnitude-reconstructed magnetic resonance images") */ const double changH = 1.0 / ( 1.06 * sampleStdDev * pow( (double)histogram->SampleCount(), 1/5 ) ); double changSigma = 0; double sigmaUpperBound = 100; double valueToMaximize = std::numeric_limits::min(); for ( double maybeChangSigma = 1.0; maybeChangSigma < sigmaUpperBound; maybeChangSigma += 1.5 ) { double changSum = 0; for ( size_t i = 0; i < histogramBinsActual; i++ ) { const double valToAdd = ( (*histogram)[i] * ( 1.0 / sqrt(2.0 * M_PI) ) * exp( ( -1.0 * log( pow( (double)( maybeChangSigma - histogram->BinToValue(i) ) / changH, 2 ) / 2.0) ) ) ); changSum += valToAdd; // std::cout << " valToAdd: " << valToAdd << std::endl; } std::cout << " changSum: " << changSum << std::endl; double curValueToMaximize = ( 1.0 / ( histogram->SampleCount() * changH ) ) * changSum; std::cout << " curValueToMaximize: " << curValueToMaximize << std::endl; if ( curValueToMaximize > valueToMaximize ) { valueToMaximize = curValueToMaximize; changSigma = maybeChangSigma; std::cout << "New max: " << curValueToMaximize << " New changSigma: " << changSigma << std::endl; } } /* Using the Chang sigma as a starting estimate, use * the Brummer method to get a final sigma estimate. */ valueToMaximize = std::numeric_limits::min(); double sigma0 = changSigma; //double sigma0 = 27.0; double maxAmplitude = 2000; double brummerSigma = changSigma; for ( double maybeSigma = 0; maybeSigma < histogramBinsActual; maybeSigma += 1.0 ) { for ( double maybeAmplitude = 0; maybeAmplitude < maxAmplitude; maybeAmplitude += 10.0 ) { double brummerSum = 0; for ( int i = 0; i <= 2 * sigma0; i++ ) { brummerSum += log( pow( (double)(*histogram)[i] - maybeAmplitude * ( histogram->BinToValue(i) / pow( maybeSigma, 2) ) * exp( -1 * ( pow( (double)histogram->BinToValue(i),2) / (2 * pow( maybeSigma, 2 ) ) ) ) , 2 ) ); } std::cout << " sigma: " << maybeSigma << " K: " << maybeAmplitude << " brummerSum: " << brummerSum << std::endl; if ( brummerSum > valueToMaximize ) { valueToMaximize = brummerSum; brummerSigma = maybeSigma; std::cout << "new valueToMaximize: " << valueToMaximize << std::endl; std::cout << "new brummerSigma: " << brummerSigma << std::endl; } } } std::cerr << brummerSigma << std::endl; //return static_cast( maxLikelySigma ); this->m_NoiseLevelSigma = static_cast( 0 ); } double TypedArrayNoiseEstimatorBrummer::SijbersBiasHat ( const Histogram::SmartPtr histogram, const double sigmaHat, const int numBinsToUse ) { double K = numBinsToUse; /* * Count the number of samples in the partial histogram * (bins 0 thru K-1 of histogram) */ int partialHistCount = 0; for ( int i = 0; i < K; i++ ) { partialHistCount += (*histogram)[i]; } /* * Pre-calculate some recurring quantities */ const double twoSigmaSquared = 2 * sigmaHat * sigmaHat; const double denom = exp( -1 * ( log( (double)pow( histogram->BinToValue( 0 ), 2 ) / twoSigmaSquared ) ) ) - exp( -1 * ( log( (double)pow( histogram->BinToValue( K ), 2 ) / twoSigmaSquared ) ) ) ; // This will be a denominator below, needs to be != 0 assert ( denom != 0 ); /* * lambdaStar is composed of two sums: one using * bins 0 thru K-1 of histogram, and * one using bins K through the end of the * histogram. This loop computes the first sum. */ double lambdaStar = 0.0; for ( int i = 1; i < K; i++ ) { const double quotient = ( exp( -1 * ( log( (double)pow( histogram->BinToValue(i-1), 2 ) ) ) / twoSigmaSquared ) - exp( -1 * ( log( pow( (double)histogram->BinToValue( i ), 2 ) ) ) / twoSigmaSquared ) ) / denom; const double f = partialHistCount * quotient; assert ( f != 0 ); lambdaStar += pow( f - (*histogram)[i], 2 ) / f; } /* * Second sum for lambdaStar */ for ( size_t i = static_cast( K ); i < histogram->GetNumberOfBins(); i++ ) { const double quotient = ( exp( -1 * ( log( pow( (double)histogram->BinToValue(i-1), 2 ) ) ) / twoSigmaSquared ) - exp( -1 * ( log( pow( (double)histogram->BinToValue( i ), 2 ) ) ) / twoSigmaSquared ) ) / denom; const double f = partialHistCount * quotient; double numerator = ( f - (*histogram)[i] ); if ( numerator < 0 ) numerator = 0 ; assert ( f != 0 ); lambdaStar += pow( numerator, 2 ) / f; } /* * Compute M (variable name same as in Sijbers) */ int M = 0; for ( size_t i = static_cast( K ); i < histogram->GetNumberOfBins(); i++ ) { const double quotient = ( exp( -1 * ( log( pow( (double)histogram->BinToValue(i-1), 2 ) ) / twoSigmaSquared ) ) - exp( -1 * ( log( pow( (double)histogram->BinToValue( i ), 2 ) ) / twoSigmaSquared ) ) ) / denom; const double f = partialHistCount * quotient; const double applyStepFilterTo = ( f - (*histogram)[i] ); M += ( applyStepFilterTo >= 0 ) ? 1 : 0; } // std::cout << "M: " << M << std::endl; /* * biasHat is a simple formula using the results * from above. */ const double biasHat = ( lambdaStar - ( K - 2 + M ) ) / sqrt( K - 2 + M ); std::cout << "biasHat: " << biasHat << std::endl; //return biasHat; return 0; } double TypedArrayNoiseEstimatorBrummer::SijbersLogLikelihood ( const Histogram::SmartPtr histogram, const double sigma, const int numBinsToUse ) { double s2 = 2 * sigma * sigma; int K = numBinsToUse; double logLikelihood = 0.0; for ( int i = 0; i < K; i++ ) { const double curCount = (*histogram)[i]; const double quotient = ( exp( -1 * ( log( pow( (double)histogram->BinToValue(i-1), 2 ) ) / s2 ) ) - exp( -1 * ( log( pow( (double)histogram->BinToValue( i ), 2 ) ) / s2 ) ) ) / ( exp( -1 * ( log( pow( (double)histogram->BinToValue( 0 ), 2 ) ) / s2 ) ) - exp( -1 * ( log( pow( (double)histogram->BinToValue( K ), 2 ) ) / s2 ) ) ); std::cout << exp( -1 * ( log( pow( histogram->BinToValue(i-1), 2 ) ) / s2 ) ) << std::endl; std::cout << exp( -1 * ( log( pow( histogram->BinToValue( i ), 2 ) ) / s2 ) ) << std::endl; std::cout << exp( -1 * ( log( pow( histogram->BinToValue( 0 ), 2 ) ) / s2 ) ) << std::endl; std::cout << exp( -1 * ( log( pow( histogram->BinToValue( K ), 2 ) ) / s2 ) ) << std::endl; logLikelihood += curCount * quotient; } return logLikelihood; } double TypedArrayNoiseEstimatorBrummer::SijbersVarHat ( const Histogram::SmartPtr histogram, const double sigmaHat, const int numBinsToUse ) { double h = 0.01; const double fXminusH = SijbersLogLikelihood( histogram, sigmaHat - h, numBinsToUse ); const double fX = SijbersLogLikelihood( histogram, sigmaHat, numBinsToUse ); const double fXplusH = SijbersLogLikelihood( histogram, sigmaHat + h, numBinsToUse ); std::cout << "fXminusH: " << fXminusH << std::endl; std::cout << "fX: " << fX << std::endl; std::cout << "fXplusH: " << fXplusH << std::endl; const double secondDerivOfLogLikelihood = ( fXminusH - 2 * fX + fXplusH ) / ( h * h ); const double varHat = -1.0 * 1 / secondDerivOfLogLikelihood; std::cout << "varHat: " << varHat << std::endl; //return varHat; return 0; } double TypedArrayNoiseEstimatorBrummer::EstimateNumBinsToUse ( const TypedArray* data, const Histogram::SmartPtr histogram, const double sigmaHat ) { UNUSED(data); double bestK = 0; double minKMinimizer = std::numeric_limits::max(); double maxK = histogram->GetNumberOfBins() / 2; for ( int curK = 1; curK < maxK; curK++ ) { const double curKMinimizer = SijbersBiasHat( histogram, sigmaHat, curK )+ SijbersVarHat( histogram, sigmaHat, curK ); //std::cout << "minKMinimizer: " << minKMinimizer << std::endl; //std::cout << "curKMinimizer: " << curKMinimizer << std::endl; if ( curKMinimizer < minKMinimizer ) { minKMinimizer = curKMinimizer; bestK = curK; } } //std::cout << std::endl << "bestK: " << bestK << std::endl; return bestK; } } // namespace cmtk cmtk-3.3.1/libs/Unstable/cmtkTypedArrayNoiseEstimatorBrummer.h000066400000000000000000000054351276303427400245350ustar00rootroot00000000000000/* // // Copyright 2008-2010 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 2398 $ // // $LastChangedDate: 2010-10-05 14:54:37 -0700 (Tue, 05 Oct 2010) $ // // $LastChangedBy: torstenrohlfing $ // */ #include #include #include #include namespace cmtk { /** Estimate noise level in data stored in a TypedArray. * Estimate Rician noise variance using Brummer's method. *\author Mike Hasak */ class TypedArrayNoiseEstimatorBrummer : /// Inherit interface from naive estimator class. protected TypedArrayNoiseEstimatorNaiveGaussian { public: /// This class. typedef TypedArrayNoiseEstimatorBrummer Self; /// Base class. typedef TypedArrayNoiseEstimatorNaiveGaussian Superclass; /// Constructor. TypedArrayNoiseEstimatorBrummer( const TypedArray* data, const size_t histogramBins = 255 ); protected: /// Default constructor; should not be invoked by user code. TypedArrayNoiseEstimatorBrummer() {} /** Compute bias in an ML noise estimate. * Eq. 26 from Sijbers et al, 2007 */ static double SijbersBiasHat ( const Histogram::SmartPtr histogram, const double sigmaHat, const int numBinsToUse ); /** Compute the log-likelihood for the ML noise estimate. * Eq. 26 from Sijbers et al, 2007 */ static double SijbersLogLikelihood ( const Histogram::SmartPtr histogram, const double sigma, const int numBinsToUse ); /** Compute variance of an ML noise estimate. * Eq. 21 from Sijbers et al, 2007 */ static double SijbersVarHat ( const Histogram::SmartPtr histogram, const double sigmaHat, const int numBinsToUse ); /** Figure out how many bins to use in ML noise estimate. * Section 2.3.1 from Sijbers et al, 2007 */ static double EstimateNumBinsToUse ( const TypedArray* data, const Histogram::SmartPtr histogram, const double sigmaHat ); }; } // namespace cmtk cmtk-3.3.1/libs/Unstable/cmtkTypedArrayNoiseEstimatorMaximumLikelihood.cxx000066400000000000000000000106141276303427400271130ustar00rootroot00000000000000/* // // Copyright 2008-2011, 2013 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4876 $ // // $LastChangedDate: 2013-09-24 13:09:58 -0700 (Tue, 24 Sep 2013) $ // // $LastChangedBy: torstenrohlfing $ // */ #include "cmtkTypedArrayNoiseEstimatorMaximumLikelihood.h" namespace cmtk { /* Maximum-likelihood estimation of the noise variance. */ TypedArrayNoiseEstimatorMaximumLikelihood::TypedArrayNoiseEstimatorMaximumLikelihood ( const TypedArray* data, const size_t histogramBins ) { // Histogram::SmartPtr histogram( data->GetHistogram( histogramBins ) ); UNUSED(histogramBins); size_t overriddenHistogramBins = 255; Histogram::SmartPtr histogram( data->GetHistogram( overriddenHistogramBins ) ); // Find first maximum size_t idx = 0; while ( (idx < (overriddenHistogramBins-1)) && ( (*histogram)[idx] <= (*histogram)[idx+1] ) ) { ++idx; } // Now find following minimum while ( (idx < (overriddenHistogramBins-1)) && ( (*histogram)[idx] > (*histogram)[idx+1] ) ) { ++idx; } /* Part of estimating the noise variance by this method is * deciding how many histogram bins to use in the computations * below (i.e. determining the value of K in Sijbers). * K is called numBinsToUse here, and we'll initialize it * to the bin with the minimum following the first maximum. * It will then be refined using the EstimateNumBinsToUse method. */ int numBinsToUse = idx; float sigmaUpperBound = 100; // Step size in search for sigma double stepSize = 0.1; // Threshold for how close two iterations of the below should // be before deciding we've found a good sigma double sigmaThresh = 0.1; // maxLikelySigma corresponds to sigma-hat in the Sijbers paper double maxLikelySigma = 23.0; double prevMaxLikelySigma = 0.0; while ( sqrt( pow( maxLikelySigma - prevMaxLikelySigma, 2 ) ) > sigmaThresh ) { std::cout << "K: " << numBinsToUse << "\tprevMaxLikelySigma: " << prevMaxLikelySigma << "\tmaxLikelySigma: " << maxLikelySigma << std::endl; prevMaxLikelySigma = maxLikelySigma; double minSigmaMinimizer = std::numeric_limits::max(); const double bin0Squared = pow( histogram->BinToValue( 0 ), 2 ); const double binKSquared = pow( histogram->BinToValue(numBinsToUse-1), 2 ); /* This for loop iterates through values of sigma, assigning * the one that produces the least value of minSigmaMinimizer * to maxLikelySigma. */ for ( double sigma = 1; sigma < sigmaUpperBound; sigma += stepSize ) { const double twoSigmaSquared = 2 * pow( sigma, 2 ); double sumForSearch = 0.0; for ( int j = 1; j <= numBinsToUse; j++ ) { const double curAddend = (*histogram)[j] * ( exp( -1 * ( pow( (double)(histogram->BinToValue(j-1)), 2 ) / twoSigmaSquared ) ) - exp( -1 * ( pow( (double)(histogram->BinToValue( j )), 2 ) / twoSigmaSquared ) ) ); sumForSearch += curAddend; } const double curSigmaMinimizer = ( exp( -bin0Squared / twoSigmaSquared ) - exp( -binKSquared / twoSigmaSquared ) ) - sumForSearch; if ( curSigmaMinimizer < minSigmaMinimizer ) { minSigmaMinimizer = curSigmaMinimizer; maxLikelySigma = sigma; } } numBinsToUse = static_cast( Superclass::EstimateNumBinsToUse( data, histogram, maxLikelySigma ) ); } std::cout << maxLikelySigma << std::endl; //return static_cast( maxLikelySigma ); this->m_NoiseLevelSigma = static_cast( 0 ); } } // namespace cmtk cmtk-3.3.1/libs/Unstable/cmtkTypedArrayNoiseEstimatorMaximumLikelihood.h000066400000000000000000000033731276303427400265440ustar00rootroot00000000000000/* // // Copyright 2008-2009 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 2398 $ // // $LastChangedDate: 2010-10-05 14:54:37 -0700 (Tue, 05 Oct 2010) $ // // $LastChangedBy: torstenrohlfing $ // */ #include #include #include namespace cmtk { /** Estimate noise level in data stored in a TypedArray. * Estimate Rician noise variance using Chang's maximum likelihood method. *\author Mike Hasak */ class TypedArrayNoiseEstimatorMaximumLikelihood : /// Inherit basic fields and helper functions protected TypedArrayNoiseEstimatorBrummer { public: /// This class. typedef TypedArrayNoiseEstimatorMaximumLikelihood Self; /// Base class. typedef TypedArrayNoiseEstimatorBrummer Superclass; /// Constructor. TypedArrayNoiseEstimatorMaximumLikelihood( const TypedArray* data, const size_t histogramBins = 255 ); }; } // namespace cmtk cmtk-3.3.1/libs/Unstable/doxygen.h000066400000000000000000000023501276303427400170200ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2011 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 2734 $ // // $LastChangedDate: 2011-01-14 10:23:45 -0800 (Fri, 14 Jan 2011) $ // // $LastChangedBy: torstenrohlfing $ // */ /** \defgroup Unstable cmtkUnstable Library * This library provides classes that are under active development and may have erratic function and quickly varying APIs. */ cmtk-3.3.1/libs/Unstable/dummy.cxx000066400000000000000000000023531276303427400170540ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // Copyright 2004-2009 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 11 $ // // $LastChangedDate: 2009-05-30 11:30:08 -0700 (Sat, 30 May 2009) $ // // $LastChangedBy: torstenrohlfing $ // */ // // This is a dummy cxx file to make sure the "Unstable" library can always be built, even // when there is currently no unstable code, or if all unstable code is in headers. // cmtk-3.3.1/scripts/000077500000000000000000000000001276303427400141535ustar00rootroot00000000000000cmtk-3.3.1/scripts/CMakeLists.txt000066400000000000000000000053601276303427400167170ustar00rootroot00000000000000## ## Copyright 2009-2012, 2014 SRI International ## ## Copyright 2012 Torsten Rohlfing ## ## This file is part of the Computational Morphometry Toolkit. ## ## http://www.nitrc.org/projects/cmtk/ ## ## The Computational Morphometry Toolkit is free software: you can ## redistribute it and/or modify it under the terms of the GNU General Public ## License as published by the Free Software Foundation, either version 3 of ## the License, or (at your option) any later version. ## ## The Computational Morphometry Toolkit is distributed in the hope that it ## will be useful, but WITHOUT ANY WARRANTY; without even the implied ## warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License along ## with the Computational Morphometry Toolkit. If not, see ## . ## ## $Revision: 5336 $ ## ## $LastChangedDate: 2014-04-29 10:29:37 -0700 (Tue, 29 Apr 2014) $ ## ## $LastChangedBy: torstenrohlfing $ ## ## Configure scripts for the build tree SET(CMTK_BINARY_DIR_CONFIG ${CMAKE_BINARY_DIR}/bin) SET(CMTK_LIBRARY_DIR_CONFIG ${CMAKE_BINARY_DIR}/bin) SET(SCRIPTS cmtk_functions.sh cmtk_locking.sh cmtk_locking_procmail.sh) LIST(APPEND SCRIPTS iterative_shape_averaging correct_nex_motion groupwise_reformat) LIST(APPEND SCRIPTS correct_dwi_distortion correct_dwi_distortion_and_motion) FOREACH(s ${SCRIPTS}) CONFIGURE_FILE(${CMAKE_CURRENT_SOURCE_DIR}/${s}.in ${CMAKE_BINARY_DIR}/bin/${s} @ONLY) ENDFOREACH(s ${SCRIPTS}) IF(CMTK_BUILD_WRAPPER) CONFIGURE_FILE(${CMAKE_CURRENT_SOURCE_DIR}/cmtk.in ${CMAKE_BINARY_DIR}/bin/cmtk @ONLY) ENDIF(CMTK_BUILD_WRAPPER) ## Configure SCRIPTS for the install tree # Link directories. IF(CMTK_BUILD_WRAPPER) SET(CMTK_BINARY_DIR_CONFIG ${CMAKE_INSTALL_PREFIX}/${CMTK_INSTALL_LIB_DIR}/bin) ELSE(CMTK_BUILD_WRAPPER) SET(CMTK_BINARY_DIR_CONFIG ${CMAKE_INSTALL_PREFIX}/${CMTK_INSTALL_BIN_DIR}) ENDIF(CMTK_BUILD_WRAPPER) SET(CMTK_LIBRARY_DIR_CONFIG ${CMAKE_INSTALL_PREFIX}/${CMTK_INSTALL_LIB_DIR}) FOREACH(s ${SCRIPTS}) CONFIGURE_FILE(${CMAKE_CURRENT_SOURCE_DIR}/${s}.in ${CMAKE_BINARY_DIR}/Install/${s} @ONLY) IF(CMTK_BUILD_WRAPPER) INSTALL(PROGRAMS ${CMAKE_BINARY_DIR}/Install/${s} DESTINATION ${CMTK_INSTALL_LIB_DIR}/bin COMPONENT tools) ELSE(CMTK_BUILD_WRAPPER) INSTALL(PROGRAMS ${CMAKE_BINARY_DIR}/Install/${s} DESTINATION ${CMTK_INSTALL_BIN_DIR} COMPONENT tools) ENDIF(CMTK_BUILD_WRAPPER) ENDFOREACH(s ${SCRIPTS}) IF(CMTK_BUILD_WRAPPER) CONFIGURE_FILE(${CMAKE_CURRENT_SOURCE_DIR}/cmtk.in ${CMAKE_BINARY_DIR}/Install/cmtk @ONLY) INSTALL(PROGRAMS ${CMAKE_BINARY_DIR}/Install/cmtk DESTINATION ${CMTK_INSTALL_BIN_DIR} COMPONENT tools) ENDIF(CMTK_BUILD_WRAPPER) cmtk-3.3.1/scripts/PackageMacOS000077500000000000000000000034701276303427400163230ustar00rootroot00000000000000#!/bin/sh ## ## Copyright 2010-2012, 2014 SRI International ## ## This file is part of the Computational Morphometry Toolkit. ## ## http://www.nitrc.org/projects/cmtk/ ## ## The Computational Morphometry Toolkit is free software: you can ## redistribute it and/or modify it under the terms of the GNU General Public ## License as published by the Free Software Foundation, either version 3 of ## the License, or (at your option) any later version. ## ## The Computational Morphometry Toolkit is distributed in the hope that it ## will be useful, but WITHOUT ANY WARRANTY; without even the implied ## warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License along ## with the Computational Morphometry Toolkit. If not, see ## . ## ## $Revision: 5210 $ ## ## $LastChangedDate: 2014-02-21 15:35:00 -0800 (Fri, 21 Feb 2014) $ ## ## $LastChangedBy: torstenrohlfing $ ## ## ## SYNOPSIS ## ## This script can be run from anywhere and will create ## all officially distributed MacOS packages, each in ## its own subdirectory of the current working directory. ## SOURCEDIR=`dirname $0` SOURCEDIR=`dirname ${SOURCEDIR}` SOURCEDIR=`cd ${SOURCEDIR}; pwd` SDKs="$*" if [ "${SDKs}" == "" ]; then SDKs="OSX-10.4-i386 OSX-10.5-x86_64 OSX-10.6-x86_64 OSX-10.6-x86_64-CUDA gcc4.7-macports-OSX-10.6 gcc4.8-macports-OSX-10.6" fi /opt/local/bin/svn update source for sdk in ${SDKs}; do if [ ! -d ${sdk} ]; then mkdir ${sdk} pushd ${sdk} /opt/local/bin/cmake -C ${SOURCEDIR}/config/package.cmake -C ${SOURCEDIR}/config/${sdk}.cmake ${SOURCEDIR} else pushd ${sdk} cmake . fi make -j2 package /opt/local/bin/cpack -G PackageMaker popd done cmtk-3.3.1/scripts/cmtk.in000077500000000000000000000031761276303427400154530ustar00rootroot00000000000000#!/bin/bash ## ## Copyright 2011 Yaroslav Halchenko ## ## Copyright 2011-2012, 2014 SRI International ## ## This file is part of the Computational Morphometry Toolkit. ## ## http://www.nitrc.org/projects/cmtk/ ## ## The Computational Morphometry Toolkit is free software: you can ## redistribute it and/or modify it under the terms of the GNU General Public ## License as published by the Free Software Foundation, either version 3 of ## the License, or (at your option) any later version. ## ## The Computational Morphometry Toolkit is distributed in the hope that it ## will be useful, but WITHOUT ANY WARRANTY; without even the implied ## warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License along ## with the Computational Morphometry Toolkit. If not, see ## . ## ## $Revision: 5337 $ ## ## $LastChangedDate: 2014-04-30 13:22:51 -0700 (Wed, 30 Apr 2014) $ ## ## $LastChangedBy: torstenrohlfing $ ## export CMTK_BINARY_DIR=${CMTK_BINARY_DIR:-@CMTK_BINARY_DIR_CONFIG@} export CMTK_LIBRARY_DIR=${CMTK_BINARY_DIR:-@CMTK_LIBRARY_DIR_CONFIG@} CMD=$1 CMDPATH=${CMTK_BINARY_DIR}/$CMD shift if [ "${LD_LIBRARY_PATH}" != "" ]; then export LD_LIBRARY_PATH=${LD_LIBRARY_PATH}:${CMTK_LIBRARY_DIR} else export LD_LIBRARY_PATH=${CMTK_LIBRARY_DIR} fi if echo "$CMD $@" | grep -q -e --help; then [ "$CMD" = "--help" ] && man cmtk || man <( ${CMDPATH} --man ) else ${CMDPATH} "$@" fi # "exit" without argument will forward exit code of last executed command exit cmtk-3.3.1/scripts/cmtk_functions.sh.in000077500000000000000000000035471276303427400201560ustar00rootroot00000000000000#!/bin/sh ## ## Copyright 2007-2011, 2014 SRI International ## ## This file is part of the Computational Morphometry Toolkit. ## ## http://www.nitrc.org/projects/cmtk/ ## ## The Computational Morphometry Toolkit is free software: you can ## redistribute it and/or modify it under the terms of the GNU General Public ## License as published by the Free Software Foundation, either version 3 of ## the License, or (at your option) any later version. ## ## The Computational Morphometry Toolkit is distributed in the hope that it ## will be useful, but WITHOUT ANY WARRANTY; without even the implied ## warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License along ## with the Computational Morphometry Toolkit. If not, see ## . ## ## $Revision: 5378 $ ## ## $LastChangedDate: 2015-01-17 11:31:55 -0800 (Sat, 17 Jan 2015) $ ## ## $LastChangedBy: torstenrohlfing $ ## export CMTK_BINARY_DIR=${CMTK_BINARY_DIR:-@CMTK_BINARY_DIR_CONFIG@} # Check whether we have "lockfile" tool available and include proper script with locking functions if which lockfile > /dev/null; then . ${CMTK_BINARY_DIR}/cmtk_locking_procmail.sh else . ${CMTK_BINARY_DIR}/cmtk_locking.sh fi # For convenience and readability cmtk() { ${CMTK_BINARY_DIR}/$* } # # Given a .hdr file name, compress corresponding .img file. # gzip_hdr_img() { for i in $*; do local img=`echo ${i} | sed 's/\.hdr/\.img/g'` gzip -9f ${img} done } # # Echo a command line, then execute it. # echo_and_exec() { echo $* $* } # # Find file in list of paths # find_file() { local file=$1 shift for d in $*; do test -e ${d}/${file} && echo ${d}/${file} && return done echo "COULD NOT FIND: $file" > /dev/stderr } cmtk-3.3.1/scripts/cmtk_locking.sh.in000077500000000000000000000077641276303427400176010ustar00rootroot00000000000000#!/bin/sh ## ## Copyright 2007-2012 SRI International ## ## This file is part of the Computational Morphometry Toolkit. ## ## http://www.nitrc.org/projects/cmtk/ ## ## The Computational Morphometry Toolkit is free software: you can ## redistribute it and/or modify it under the terms of the GNU General Public ## License as published by the Free Software Foundation, either version 3 of ## the License, or (at your option) any later version. ## ## The Computational Morphometry Toolkit is distributed in the hope that it ## will be useful, but WITHOUT ANY WARRANTY; without even the implied ## warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License along ## with the Computational Morphometry Toolkit. If not, see ## . ## ## $Revision: 4584 $ ## ## $LastChangedDate: 2012-11-12 13:33:41 -0800 (Mon, 12 Nov 2012) $ ## ## $LastChangedBy: torstenrohlfing $ ## ## ## Helper functions for file locking and dependency checking using simple shell tools (this is not 100% NFS-safe) ## CMTK_needs_update() { local target=$1 if test ! -e ${target}; then if test -e ${target}.gz; then target=${target}.gz fi fi shift local sources="$*" for source in ${sources}; do if [ ! -e ${source} ]; then if [ ! -e ${source}.gz ]; then echo "========================================================================" echo "MISSING SOURCE ${source}" echo "========================================================================" return 1 fi fi if [ -e ${source}.lock ]; then echo "========================================================================" echo "SOURCE LOCKED ${source}" echo "========================================================================" return 1 fi done local source if [ ! -e ${target} ]; then echo "========================================================================" echo "CREATE ${target}" echo "========================================================================" return 0 fi for source in ${sources}; do if test ! -e ${source}; then if test -e ${source}.gz; then source=${source}.gz fi fi if test ${target} -ot ${source}; then echo "========================================================================" echo "UPDATE ${target}" echo "DUE TO ${source}" echo "========================================================================" return 0 fi done return 1 } # NFS-safe (hopefully) file locking. CMTK_lockfile_create() { local lockfile=$1.lock local hostpid=`hostname`-$$ if [ -e ${lockfile} ]; then # lockfile already exists, so clearly we were not the first return 1; fi mkdir -p `dirname ${lockfile}` echo ${hostpid} >> ${lockfile} if [ `head -n 1 ${lockfile}` != ${hostpid} ]; then # first one to write PID was not us return 1; fi trap "rm -f ${lockfile}; exit" INT TERM EXIT return 0; } CMTK_lockfile_delete() { local file=${1} local lockfile=${file}.lock local realfile=${file} [ -f ${realfile} ] || realfile=${file}.gz [ -f ${realfile} ] || realfile=${file}.xz if [ -f ${realfile} ]; then # do not update timestamp if file is older than lock (i.e., existed before and did not get updated at all) if test ${lockfile} -ot ${realfile}; then touch --no-create -r ${lockfile} ${realfile} fi fi rm -f ${lockfile} trap - INT TERM EXIT } # # Combine dependency checking with locking of target # CMTK_needs_update_and_lock() { local target=$1 local realtarget=$target [ -e ${realtarget} ] || realtarget=${target}.gz [ -e ${realtarget} ] || realtarget=${target}.xz [ -e ${realtarget} ] || realtarget=${target} shift if CMTK_needs_update ${realtarget} $*; then if CMTK_lockfile_create ${target}; then return 0 fi fi return 1 } cmtk-3.3.1/scripts/cmtk_locking_procmail.sh.in000077500000000000000000000105561276303427400214600ustar00rootroot00000000000000#!/bin/sh ## ## Copyright 2007-2012 SRI International ## ## This file is part of the Computational Morphometry Toolkit. ## ## http://www.nitrc.org/projects/cmtk/ ## ## The Computational Morphometry Toolkit is free software: you can ## redistribute it and/or modify it under the terms of the GNU General Public ## License as published by the Free Software Foundation, either version 3 of ## the License, or (at your option) any later version. ## ## The Computational Morphometry Toolkit is distributed in the hope that it ## will be useful, but WITHOUT ANY WARRANTY; without even the implied ## warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License along ## with the Computational Morphometry Toolkit. If not, see ## . ## ## $Revision: 4584 $ ## ## $LastChangedDate: 2012-11-12 13:33:41 -0800 (Mon, 12 Nov 2012) $ ## ## $LastChangedBy: torstenrohlfing $ ## ## ## Helper functions for file locking and dependency checking using procmail's "lockfile" tool ## # check whether file needs update due to updated dependencies CMTK_needs_update() { local target=$1 if [ -e ${target}.lock ]; then echo "========================================================================" echo "TARGET LOCKED ${target}" echo "========================================================================" return 1 fi shift local sources="$*" for source in ${sources}; do if [ ! -e ${source} ]; then if [ ! -e ${source}.gz ]; then if [ ! -e ${source}.xz ]; then echo "========================================================================" echo "TARGET ${target}" echo "MISSING SOURCE ${source}" echo "========================================================================" return 1 fi fi fi if [ -e ${source}.lock ]; then echo "========================================================================" echo "TARGET ${target}" echo "SOURCE LOCKED ${source}" echo "========================================================================" return 1 fi done if [ ! -e ${target} ]; then echo "========================================================================" echo "CREATE ${target}" echo "========================================================================" return 0 fi local source for source in ${sources}; do if test ! -e ${source}; then if test -e ${source}.gz; then source=${source}.gz else if test -e ${source}.xz; then source=${source}.xz fi fi fi if test ${target} -ot ${source}; then echo "========================================================================" echo "UPDATE ${target}" echo "DUE TO ${source}" echo "========================================================================" return 0 fi done return 1 } # NFS-safe (hopefully) file locking. CMTK_lockfile_create() { local lockfile=$1.lock mkdir -p `dirname ${lockfile}` if lockfile -0 -r0 ${lockfile}; then trap "rm -f ${lockfile}; exit" INT TERM EXIT return 0 fi return 1; } CMTK_lockfile_delete() { local file=${1} local lockfile=${file}.lock local realfile=${file} [ -f ${realfile} ] || realfile=${file}.gz [ -f ${realfile} ] || realfile=${file}.xz if [ -f ${realfile} ]; then # do not update timestamp if file is older than lock (i.e., existed before and did not get updated at all) if test ${lockfile} -ot ${realfile}; then touch --no-create -r ${lockfile} ${realfile} fi fi rm -f ${lockfile} trap - INT TERM EXIT } # # Combine dependency checking with locking of target # CMTK_needs_update_and_lock() { local target=$1 local realtarget=$target [ -e ${realtarget} ] || realtarget=${target}.gz [ -e ${realtarget} ] || realtarget=${target}.xz [ -e ${realtarget} ] || realtarget=${target} shift if CMTK_needs_update ${realtarget} $*; then if CMTK_lockfile_create ${target}; then return 0 else echo "========================================================================" echo "TARGET LOCKED ${target}" echo "========================================================================" fi fi return 1 } cmtk-3.3.1/scripts/correct_dwi_distortion.in000077500000000000000000000066301276303427400212750ustar00rootroot00000000000000#!/bin/sh ## ## Copyright 2012-2014 SRI International ## ## This file is part of the Computational Morphometry Toolkit. ## ## http://www.nitrc.org/projects/cmtk/ ## ## The Computational Morphometry Toolkit is free software: you can ## redistribute it and/or modify it under the terms of the GNU General Public ## License as published by the Free Software Foundation, either version 3 of ## the License, or (at your option) any later version. ## ## The Computational Morphometry Toolkit is distributed in the hope that it ## will be useful, but WITHOUT ANY WARRANTY; without even the implied ## warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License along ## with the Computational Morphometry Toolkit. If not, see ## . ## ## $Revision: 5337 $ ## ## $LastChangedDate: 2014-04-30 13:22:51 -0700 (Wed, 30 Apr 2014) $ ## ## $LastChangedBy: torstenrohlfing $ ## CMTK_BINARY_DIR=${CMTK_BINARY_DIR:-@CMTK_BINARY_DIR_CONFIG@} # Get the cmtk_functions.sh script from the scripts/ directory in the CMTK source tree . ${CMTK_BINARY_DIR}/cmtk_functions.sh # Check for command line arguments, print help if none given if test $# -lt 2; then echo "Correct distortion of diffusion-weighted MR images." echo echo "For this script to be applicable, the b=0 image must have been acquired twice, " echo "once with standard phase encoding direction and once with phase encoding reversed." echo echo "USAGE: $0 outdir b0Reverse b0Forward bX1 [bX2 ...]" exit 2 fi outdir=$1 b0Rev=$2 b0Fwd=$3 if ! cmtk geomatch -v ${b0Rev} ${b0Fwd}; then echo "ERROR: the two b=0 images have mismatched geometries or spatial coordinates:" echo " ${b0Rev}" echo " ${b0Fwd}" exit 1 fi shift 3 bXlist="$*" # # First, do eddy currents by registering all bX images to forward b=0 image # for bX in ${bXlist}; do base=`basename $bX` pref=`echo ${base} | sed 's/\..*//g'` xform=${outdir}/eddy/b0_${pref}.xform if CMTK_needs_update_and_lock ${xform} ${b0Fwd} ${bX}; then echo "Computing eddy current correction for ${base}..." cmtk registrationx --pad-flt -1 --restrict-in-plane xy --cubic --dofs 9,12 --auto-multi-levels 2 --nmi -o ${xform} ${b0Fwd} ${bX} CMTK_lockfile_delete ${xform} fi done b0FwdCorr=${outdir}/`basename ${b0Fwd}` b0RevCorr=${outdir}/`basename ${b0Rev}` if CMTK_needs_update_and_lock ${outdir}/dfield_fwd.nrrd ${b0Fwd} ${b0Rev}; then echo "Computing deformation..." cmtk epiunwarp --no-flip --write-jacobian-fwd ${outdir}/jacobian.nii --smooth-sigma-max 16 --smooth-sigma-min 0 --smooth-sigma-diff 0.25 --iterations 5 --smoothness-constraint-weight 1e4 --phase-encode-ap ${b0Fwd} ${b0Rev} ${b0FwdCorr} ${b0RevCorr} ${outdir}/dfield_fwd.nrrd CMTK_lockfile_delete ${outdir}/dfield_fwd.nrrd fi for bX in ${bXlist}; do base=`basename $bX` pref=`echo ${base} | sed 's/\..*//g'` xform=${outdir}/eddy/b0_${pref}.xform if CMTK_needs_update_and_lock ${outdir}/${base} ${bX} ${outdir}/dfield_fwd.nrrd ${xform}; then echo "Unwarping $bX" cmtk reformatx --sinc-cosine -o ${outdir}/${base} --pad-floating -1 --floating ${bX} ${b0FwdCorr} ${outdir}/dfield_fwd.nrrd ${xform} && cmtk imagemath --in ${outdir}/jacobian.nii ${outdir}/${base} --mul --out ${outdir}/${base} CMTK_lockfile_delete ${outdir}/${base} fi done cmtk-3.3.1/scripts/correct_dwi_distortion_and_motion.in000077500000000000000000000142471276303427400235070ustar00rootroot00000000000000#!/bin/sh ## ## Copyright 2012-2014 SRI International ## ## This file is part of the Computational Morphometry Toolkit. ## ## http://www.nitrc.org/projects/cmtk/ ## ## The Computational Morphometry Toolkit is free software: you can ## redistribute it and/or modify it under the terms of the GNU General Public ## License as published by the Free Software Foundation, either version 3 of ## the License, or (at your option) any later version. ## ## The Computational Morphometry Toolkit is distributed in the hope that it ## will be useful, but WITHOUT ANY WARRANTY; without even the implied ## warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License along ## with the Computational Morphometry Toolkit. If not, see ## . ## ## $Revision: 5378 $ ## ## $LastChangedDate: 2015-01-17 11:31:55 -0800 (Sat, 17 Jan 2015) $ ## ## $LastChangedBy: torstenrohlfing $ ## export CMTK_BINARY_DIR=${CMTK_BINARY_DIR:-@CMTK_BINARY_DIR_CONFIG@} # Get the cmtk_functions.sh script from the scripts/ directory in the CMTK source tree . ${CMTK_BINARY_DIR}/cmtk_functions.sh # Check for command line arguments, print help if none given if test $# -lt 2; then echo "Correct distortion and subject motion in diffusion-weighted MR images." echo echo "For this script to be applicable, the b=0 image must have been acquired twice, " echo "once with standard phase encoding direction and once with phase encoding reversed." echo echo "USAGE: $0 outdir b0Reverse b0Forward bX1 [bX2 ...]" exit 2 fi outdir=$1 b0Rev=$2 b0Fwd=$3 if ! cmtk geomatch -v ${b0Rev} ${b0Fwd}; then echo "ERROR: the two b=0 images have mismatched geometries or spatial coordinated:" echo " ${b0Rev}" echo " ${b0Fwd}" exit 1 fi shift 3 bXlist="$*" # # First, do eddy currents by registering all bX images to forward b=0 image # for bX in ${bXlist}; do base=`basename $bX` pref=`echo ${base} | sed 's/\..*//g'` xform=${outdir}/eddy/b0_${pref}.xform if CMTK_needs_update_and_lock ${xform} ${b0Fwd} ${bX}; then echo "Computing eddy current correction for ${base}..." cmtk registrationx --pad-flt -1 --restrict-in-plane xy --cubic --dofs 9,12 --auto-multi-levels 2 --nmi -o ${xform} ${b0Fwd} ${bX} CMTK_lockfile_delete ${xform} fi done b0FwdCorr=${outdir}/`basename ${b0Fwd}` b0RevCorr=${outdir}/`basename ${b0Rev}` if CMTK_needs_update_and_lock ${outdir}/dfield_fwd.nrrd ${b0Fwd} ${b0Rev}; then echo "Computing deformation..." cmtk epiunwarp --no-flip --write-jacobian-fwd ${outdir}/jacobian.nii --smooth-sigma-max 16 --smooth-sigma-min 0 --smooth-sigma-diff 0.25 --iterations 5 --smoothness-constraint-weight 1e4 --phase-encode-ap ${b0Fwd} ${b0Rev} ${b0FwdCorr} ${b0RevCorr} ${outdir}/dfield_fwd.nrrd CMTK_lockfile_delete ${outdir}/dfield_fwd.nrrd fi unwarpedList="${b0FwdCorr}" for bX in ${bXlist}; do base=`basename $bX` pref=`echo ${base} | sed 's/\..*//g'` xform=${outdir}/eddy/b0_${pref}.xform if CMTK_needs_update_and_lock ${outdir}/${base} ${bX} ${outdir}/dfield_fwd.nrrd ${xform}; then echo "Unwarping $bX" tmpdir=$(mktemp -d) # First, reslice using PEpolar unwarp and eddy current correction cmtk reformatx --sinc-cosine -o ${tmpdir}/reslice.nii --pad-floating -1 --floating ${bX} ${b0FwdCorr} ${outdir}/dfield_fwd.nrrd ${xform} # Second, multiply with Jacobian volume correction field cmtk imagemath --in ${outdir}/jacobian.nii ${tmpdir}/reslice.nii --mul --out ${tmpdir}/reslice_j.nii # Third, make a mask of bad slices (0 - bad slice, 1 - good slice), based on requirement that bad slices be marked as -1 cmtk convertx --float --binarize-thresh -0.5 --byte ${bX} ${tmpdir}/mask.nii # Apply bad-slice mask back to resliced and Jacobian-corrected volume cmtk convertx --thresh-below 0 --set-padding -1 --mask ${tmpdir}/mask.nii ${tmpdir}/reslice_j.nii ${outdir}/${base} rm -rf ${tmpdir} CMTK_lockfile_delete ${outdir}/${base} fi unwarpedList="${unwarpedList} ${outdir}/${base}" done ## ## Motion correction ## average=${outdir}/bAverage.nii if CMTK_needs_update_and_lock ${average} ${b0FwdCorr} ${b0RevCorr} ${unwarpedList}; then cmtk imagemath --in ${b0FwdCorr} ${b0RevCorr} ${unwarpedList} --average --out ${average} CMTK_lockfile_delete ${average} fi for bX in ${b0FwdCorr} ${bXlist}; do base=`basename $bX` bXUnwarp=${outdir}/${base} pref=`echo ${base} | sed 's/\..*//g'` if [ "${bX}" = "${b0FwdCorr}" ]; then if CMTK_needs_update_and_lock ${outdir}/motion/${base} ${b0FwdCorr}; then cmtk convertx ${b0FwdCorr} ${outdir}/motion/${base} CMTK_lockfile_delete ${outdir}/motion/${base} fi else xform=${outdir}/motion/xforms/b0Unwarp_${pref}.xform if CMTK_needs_update_and_lock ${xform} ${average} ${bXUnwarp} ; then cmtk registrationx --pad-flt -1 --linear --dofs 6 --auto-multi-levels 3 --ncc -o ${xform} ${average} ${bXUnwarp} CMTK_lockfile_delete ${xform} fi if CMTK_needs_update_and_lock ${outdir}/motion/${base} ${bXUnwarp} ${xform}; then echo "Reformatting motion-corrected $bX" tmpdir=$(mktemp -d) # First, re-reslice the unwarped image cmtk reformatx --sinc-cosine -o ${tmpdir}/reslice.nii --pad-floating -1 --floating ${bXUnwarp} ${average} ${xform} # Second, make a mask of bad slices (0 - bad slice, 1 - good slice), based on requirement that bad slices be marked as -1 cmtk convertx --float --binarize-thresh -0.5 ${bX} ${tmpdir}/mask.nii # Third, reslice the mask using the same motion xform and interpolation as the image cmtk reformatx --sinc-cosine -o ${tmpdir}/mask_reslice.nii --floating ${tmpdir}/mask.nii ${average} ${xform} # Fourth, threshold resliced mask to include only those pixels that have more then 50% support cmtk convertx --binarize-thresh 0.5 ${tmpdir}/mask_reslice.nii ${tmpdir}/mask_thresh.nii # Fifth, create final image by masking out pixels with under 50% support cmtk convertx --set-padding -1 --mask ${tmpdir}/mask_thresh.nii --thresh-below 0 ${tmpdir}/reslice.nii ${outdir}/motion/${base} rm -rf ${tmpdir} CMTK_lockfile_delete ${outdir}/motion/${base} fi fi done cmtk-3.3.1/scripts/correct_nex_motion.in000077500000000000000000000041761276303427400204160ustar00rootroot00000000000000#!/bin/sh ## ## Copyright 1997-2009 Torsten Rohlfing ## ## Copyright 2004-2012, 2014 SRI International ## ## This file is part of the Computational Morphometry Toolkit. ## ## http://www.nitrc.org/projects/cmtk/ ## ## The Computational Morphometry Toolkit is free software: you can ## redistribute it and/or modify it under the terms of the GNU General Public ## License as published by the Free Software Foundation, either version 3 of ## the License, or (at your option) any later version. ## ## The Computational Morphometry Toolkit is distributed in the hope that it ## will be useful, but WITHOUT ANY WARRANTY; without even the implied ## warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License along ## with the Computational Morphometry Toolkit. If not, see ## . ## ## $Revision: 5337 $ ## ## $LastChangedDate: 2014-04-30 13:22:51 -0700 (Wed, 30 Apr 2014) $ ## ## $LastChangedBy: torstenrohlfing $ ## export CMTK_BINARY_DIR=${CMTK_BINARY_DIR:-@CMTK_BINARY_DIR_CONFIG@} # Get the cmtk_functions.sh script from the scripts/ directory in the CMTK source tree . ${CMTK_BINARY_DIR}/cmtk_functions.sh # Check for command line arguments, print help if none given if test $# -lt 2; then echo "Correct multiple-NEX image motion" echo "USAGE: $0 outImagePath referenceImagePath imageNEX1 [...]" exit 2 fi tmpdir=`mktemp -d` if [ "${tmpdir}" = "" ]; then echo "ERROR: cannot create tmpdir" exit fi outfile=$1 reffile=$2 shift 2 fltfiles="$*" images=${reffile} for f in ${fltfiles}; do name=`basename ${f}` echo "Registration: ${name}" xform=${tmpdir}/${name}.list cmtk registration -i -o ${xform} -a 0.01 --dofs 6 -e 2 ${reffile} ${f} echo "Reformatting: ${name}" nrrd=${tmpdir}/reformat/${name}.nrrd cmtk reformatx --sinc-cosine --sinc-window-width 5 -o ${nrrd} --floating ${f} ${reffile} ${xform} images="${images} ${nrrd}" done echo "Averaging" cmtk average_images -o ${outfile} ${images} echo "Done." rm -rf ${tmpdir} cmtk-3.3.1/scripts/gen_manpages000077500000000000000000000060041276303427400165250ustar00rootroot00000000000000#!/bin/sh #emacs: -*- mode: shell-script; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: t -*- #ex: set sts=4 ts=4 sw=4 noet: #-------------------------- =+- Shell script -+= -------------------------- # # @file gen_manpage.sh # @date Thu Nov 3 21:46:15 2011 # @brief # # # Yaroslav Halchenko Dartmouth # web: http://www.onerussian.com College # e-mail: yoh@onerussian.com ICQ#: 60653192 # # DESCRIPTION (NOTES): # # A little helper to generate all needed manpages for CMTK # # COPYRIGHT: Yaroslav Halchenko 2011 # # LICENSE: MIT # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal # in the Software without restriction, including without limitation the rights # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. # #-----------------\____________________________________/------------------ set -eu MANDIR=$PWD/debian/manpages CMTK_LIBDIR=$PWD/debian/cmtk/usr/lib/cmtk CMTK_BINDIR=$CMTK_LIBDIR/bin export CMTK_LIBDIR CMTK_BINDIR MANDIR # Generate manpages for all tools mkdir -p $MANDIR /bin/ls $CMTK_BINDIR/* \ | grep -v triplanar \ | while read b; do LD_LIBRARY_PATH=$CMTK_LIBDIR \ $b --man >| $MANDIR/cmtk-$(basename $b).1 \ || echo "Manpage for $b -- FAILED"; done # Some didn't generate ;) find $MANDIR -size 0 -delete # Generate the ultimate manpage giving a brief description for all of # the tools rm -f $MANDIR/cmtk.1 { head -1 $MANDIR/cmtk-warp.1 | sed -e 's,warp,CMTK,g' cat < [options] .SH DESCRIPTION This helper script provides a unified access to all command line tools provided by CMTK. Please specify CMTK's command to run and its options. See \fBcmtk-(1)\fR manpage or output of \fBcmtk --help\fR for specific options .SH COMMANDS EOF /bin/ls $MANDIR/*.1 | grep -v -e 'cmtk\.1' | while read f; do descr="$(grep -a -A1 '.SH DESCRIPTION' $f| tail -n 1)" cmd=${f%.*} cmd=${cmd##*cmtk-} cat <| $MANDIR/cmtk.1 cmtk-3.3.1/scripts/groupwise_reformat.in000077500000000000000000000066661276303427400204470ustar00rootroot00000000000000#!/bin/sh ## ## Copyright 2012, 2014 SRI International ## ## Copyright 2012 Torsten Rohlfing ## ## This file is part of the Computational Morphometry Toolkit. ## ## http://www.nitrc.org/projects/cmtk/ ## ## The Computational Morphometry Toolkit is free software: you can ## redistribute it and/or modify it under the terms of the GNU General Public ## License as published by the Free Software Foundation, either version 3 of ## the License, or (at your option) any later version. ## ## The Computational Morphometry Toolkit is distributed in the hope that it ## will be useful, but WITHOUT ANY WARRANTY; without even the implied ## warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License along ## with the Computational Morphometry Toolkit. If not, see ## . ## ## $Revision: 5378 $ ## ## $LastChangedDate: 2015-01-17 11:31:55 -0800 (Sat, 17 Jan 2015) $ ## ## $LastChangedBy: torstenrohlfing $ ## export CMTK_BINARY_DIR=${CMTK_BINARY_DIR:-@CMTK_BINARY_DIR_CONFIG@} # Get the cmtk_functions.sh script from the scripts/ directory in the CMTK source tree . ${CMTK_BINARY_DIR}/cmtk_functions.sh # Check for command line arguments, print help if none given if test $# -lt 3; then echo "Reformat all floating images previously aligned by one of CMTK's groupwise registration tools." echo echo "USAGE: $0 [OPTIONS] groupwiseXform templateGrid outputPathPattern [XFORMS ...]" exit 2 fi # put all arguments starting with "-" into reformat options reformatOptions="" while expr "$1" : ^- >/dev/null; do reformatOptions="${reformatOptions}$1 " shift done # first non-option argument is groupwise xform groupwiseXform=$1 if [ ! -f ${groupwiseXform} ]; then groupwiseXform=${groupwiseXform}.gz if [ ! -f ${groupwiseXform} ]; then echo "ERROR: could not find groupwise transformation $1" exit 1 fi fi # second argument is template grid path templateGrid=$2 if ! ${CMTK_BINARY_DIR}/describe ${templateGrid} > /dev/null; then echo "ERROR: template grid ${templateGrid} is not a valid image." exit 1 fi # third argument is output pattern outPattern=$3 # everything else is optional xforms to be added before the groupwise alignment shift 3 reformatXforms="$*" make_output_path() { local target=$1 local pattern=$2 local base=`basename $target | sed 's/\..*//g'` local dir=`dirname $target` local idx=1 while [ "${base}" != "" ]; do pattern=`eval "echo $pattern | sed 's/%${idx}/${base}/g'"` base=`basename $dir | sed 's/\.,*//g'` dir=`dirname $dir` $((idx=idx+1)) done echo ${pattern} } process_target() { local target=$1 local tmp=`mktemp` echo "! TYPEDSTREAM 1.1" > ${tmp} while IFS="" read line; do if expr "${line}" : ^\} >/dev/null; then break; fi echo "${line}" >> ${tmp} done echo "}" >> ${tmp} outputPath=`make_output_path ${target} ${outPattern}` if [ "${outputPath}" != "" ]; then ${CMTK_BINARY_DIR}/reformatx ${reformatOptions} --floating ${target} --outfile ${outputPath} ${templateGrid} ${tmp} ${reformatXforms} fi rm -rf ${tmp} } parse_xform_file() { while read line; do if expr "${line}" : "target" >/dev/null; then target=`echo ${line} | sed 's/target \"//g; s/\".*//g'` process_target ${target} fi done } gzip -cdf ${groupwiseXform} | parse_xform_file cmtk-3.3.1/scripts/iterative_shape_averaging.in000077500000000000000000000116251276303427400217120ustar00rootroot00000000000000#!/bin/bash ## ## Copyright 2009-2010, 2012, 2014 SRI International ## ## This file is part of the Computational Morphometry Toolkit. ## ## http://www.nitrc.org/projects/cmtk/ ## ## The Computational Morphometry Toolkit is free software: you can ## redistribute it and/or modify it under the terms of the GNU General Public ## License as published by the Free Software Foundation, either version 3 of ## the License, or (at your option) any later version. ## ## The Computational Morphometry Toolkit is distributed in the hope that it ## will be useful, but WITHOUT ANY WARRANTY; without even the implied ## warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License along ## with the Computational Morphometry Toolkit. If not, see ## . ## ## $Revision: 5337 $ ## ## $LastChangedDate: 2014-04-30 13:22:51 -0700 (Wed, 30 Apr 2014) $ ## ## $LastChangedBy: torstenrohlfing $ ## export CMTK_BINARY_DIR=${CMTK_BINARY_DIR:-@CMTK_BINARY_DIR_CONFIG@} # Get the cmtk_functions.sh script from the scripts/ directory in the CMTK source tree . ${CMTK_BINARY_DIR}/cmtk_functions.sh # Check for command line arguments, print help if none given if test $# -lt 2; then echo "Iterative Shape Averging" echo "USAGE: $0 referenceImagePath imagePath1 imagePath2 ..." echo " where the first argument is the path of the initial reference image and" echo " the remaining arguments are the remaining images in the set to be averaged." echo " Please make sure the initial reference image is not also listed in the" echo " list of remaining images." exit 2 fi # We should have received a list of all images via the command line IMAGES="$*" # Warp arguments common to all iterations WARP_ARGS_GLOBAL="--echo -v --refine 1 --delay-refine --energy-weight 1e-1" # This function computed the average of reformatted images. average_images() { local out_img=$1 shift local in_imgs="$*" if CMTK_needs_update_and_lock ${out_img} ${in_imgs}; then cmtk average_images --echo -o ${out_img} ${in_imgs} CMTK_lockfile_delete ${out_img} fi } # This function will compute the affine alignments of one image to the initial reference. pass0() { local images="$*" local in_ref=$1 local out_nii_list="" local ref_name=`basename ${in_ref} | sed 's/\..*//g'` for in_flt in ${images}; do local flt_name=`basename ${in_flt} | sed 's/\..*//g'` local out_xfm=isa/pass0/${flt_name}.xform if CMTK_needs_update_and_lock ${out_xfm} ${in_ref} ${in_flt}; then if [ "${in_ref}" != "${in_flt}" ]; then cmtk registration --echo --initxlate --auto-multi-levels 5 -v --dofs 6 --dofs 9 -o ${out_xfm} ${in_ref} ${in_flt} else cmtk make_initial_affine --identity ${in_ref} ${in_flt} ${out_xfm} fi CMTK_lockfile_delete ${out_xfm} fi local out_nii=isa/pass0/${flt_name}.nii out_nii_list="${out_nii_list} ${out_nii}" if CMTK_needs_update_and_lock ${out_nii} ${out_xfm}; then cmtk reformatx --echo --sinc-cosine -o ${out_nii} --floating ${in_flt} ${in_ref} ${out_xfm} CMTK_lockfile_delete ${out_nii} fi done average_images isa/pass0/average.nii ${out_nii_list} } # Determine warp arguments from iteration number and reference image get_level_warp_args() { local iteration=$1 local initial_ref=$2 local cpspacing=`cmtk describe --machine-readable ${initial_ref} | fgrep FOV | cut -f2 | cmtk sequence | fgrep STATval | cut -f2` for ((i=1;i. ## ## $Revision: 5337 $ ## ## $LastChangedDate: 2014-04-30 13:22:51 -0700 (Wed, 30 Apr 2014) $ ## ## $LastChangedBy: torstenrohlfing $ ## export CMTK_BINARY_DIR=${CMTK_BINARY_DIR:-@CMTK_BINARY_DIR_CONFIG@} # Check for command line arguments, print help if none given if test $# -lt 2; then echo "Locally-weighted multi-atlas label fusion" echo "USAGE: $0 targetImagePath atlasIntensityImage1 atlasLabelImage1 [[atlasIntensityImage2 atlasLabelImage2] ...]" exit 2 fi TARGET=$1 shift ATLAS_I="" ATLAS_L="" while [ "$1" != "" ]; do ATLAS_I="${ATLAS_I} $1" ATLAS_L="${ATLAS_L} $2" shift 2 done echo ${ATLAS_I} echo ${ATLAS_L} tmpdir=`mktemp -d` echo ${tmpdir} idx=0 for atlas in ${ATLAS_I}; do xfm=${tmpdir}/${idx}.xfm ${CMTK_BINARY_DIR}/registrationx --echo --init com --auto-multi-levels 5 --omit-original-data --delta-f-threshold 0.01 --dofs 6,9 --ncc --pad-ref 0 --symmetric -o ${xfm} ${TARGET} ${atlas} idx=`expr ${idx} + 1` done idx=0 for atlas in ${ATLAS_I}; do xfm=${tmpdir}/${idx}.xfm ffd=${tmpdir}/${idx}.ffd ${CMTK_BINARY_DIR}/warpx --echo --grid-spacing 80 --grid-refine 3 --min-stepsize 0.25 --max-stepsize 16 --smoothness-constraint-weight 1e-1 --omit-original-data --delta-f-threshold 0.01 --fast --nmi -o ${ffd} --initial ${xfm} ${TARGET} ${atlas} idx=`expr ${idx} + 1` done idx=0 for atlas in ${ATLAS_I}; do ffd=${tmpdir}/${idx}.ffd out=${tmpdir}/${idx}_i.nii ${CMTK_BINARY_DIR}/reformatx --floating ${atlas} --cubic -o ${out} ${TARGET} ${ffd} idx=`expr ${idx} + 1` done lvote_inputs="" idx=0 for atlas in ${ATLAS_L}; do ffd=${tmpdir}/${idx}.ffd out=${tmpdir}/${idx}_l.nii ${CMTK_BINARY_DIR}/reformatx --floating ${atlas} --pv -o ${out} ${TARGET} ${ffd} lvote_inputs="${lvote_inputs} ${tmpdir}/${idx}_i.nii ${tmpdir}/${idx}_l.nii" idx=`expr ${idx} + 1` done bin/lvote --echo -o output_${idx}_sba.nii --use-sba --patch-radius 5 ${TARGET} ${lvote_inputs} rm -rf ${tmpdir} cmtk-3.3.1/testing/000077500000000000000000000000001276303427400141415ustar00rootroot00000000000000cmtk-3.3.1/testing/CMakeLists.txt000066400000000000000000000022511276303427400167010ustar00rootroot00000000000000## ## Copyright 1997-2009 Torsten Rohlfing ## ## Copyright 2004-2011 SRI International ## ## This file is part of the Computational Morphometry Toolkit. ## ## http://www.nitrc.org/projects/cmtk/ ## ## The Computational Morphometry Toolkit is free software: you can ## redistribute it and/or modify it under the terms of the GNU General Public ## License as published by the Free Software Foundation, either version 3 of ## the License, or (at your option) any later version. ## ## The Computational Morphometry Toolkit is distributed in the hope that it ## will be useful, but WITHOUT ANY WARRANTY; without even the implied ## warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License along ## with the Computational Morphometry Toolkit. If not, see ## . ## ## $Revision: 3071 $ ## ## $LastChangedDate: 2011-03-28 22:23:36 -0700 (Mon, 28 Mar 2011) $ ## ## $LastChangedBy: torsten_at_home $ ## IF(BUILD_APPS) SUBDIRS(apps) ENDIF(BUILD_APPS) IF(BUILD_GUI) SUBDIRS(gui) ENDIF(BUILD_GUI) SUBDIRS(libs) cmtk-3.3.1/testing/apps/000077500000000000000000000000001276303427400151045ustar00rootroot00000000000000cmtk-3.3.1/testing/apps/CMakeLists.txt000066400000000000000000000707311276303427400176540ustar00rootroot00000000000000## ## Copyright 2004-2014 SRI International ## ## Copyright 1997-2011 Torsten Rohlfing ## ## This file is part of the Computational Morphometry Toolkit. ## ## http://www.nitrc.org/projects/cmtk/ ## ## The Computational Morphometry Toolkit is free software: you can ## redistribute it and/or modify it under the terms of the GNU General Public ## License as published by the Free Software Foundation, either version 3 of ## the License, or (at your option) any later version. ## ## The Computational Morphometry Toolkit is distributed in the hope that it ## will be useful, but WITHOUT ANY WARRANTY; without even the implied ## warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License along ## with the Computational Morphometry Toolkit. If not, see ## . ## ## $Revision: 5368 $ ## ## $LastChangedDate: 2014-07-28 17:06:35 -0700 (Mon, 28 Jul 2014) $ ## ## $LastChangedBy: torstenrohlfing $ ## FIND_PROGRAM(BASH_PATH bash PATHS C:/cygwin/bin) SET(testDriver ${BASH_PATH} ${CMAKE_CURRENT_BINARY_DIR}/appsTestDriver.sh) SET(testList "") CONFIGURE_FILE(${CMAKE_CURRENT_SOURCE_DIR}/appsTestDriver.sh.in ${CMAKE_CURRENT_BINARY_DIR}/appsTestDriver.sh @ONLY) # On Windows, remove "\r" (CR) from line ends, which would make Cygwin's sh choke IF(${CMAKE_SYSTEM_NAME} MATCHES "Windows") EXECUTE_PROCESS(COMMAND c:\\cygwin\\bin\\sed -i.bak s/\\r//g ${CMAKE_CURRENT_BINARY_DIR}/appsTestDriver.sh) ENDIF(${CMAKE_SYSTEM_NAME} MATCHES "Windows") # ========================================== # Tests for "avg_adm" tool LIST(APPEND testList avg_admDefault2 avg_admDefault3 avg_admJacobianFloat avg_admLabels avg_admPaddOutZeroNN avg_admNoReferenceModelCubic avg_admNoScaleModelAutoScale avg_admWithAffineNoRefData) # ========================================== # Tests for "dbtool" IF(CMTK_USE_SQLITE) LIST(APPEND testList dbtool_AddImages dbtool_AddImages2 dbtool_ListSpace dbtool_GetXform1 dbtool_GetXform2 dbtool_GetXform3 dbtool_GetXform4 dbtool_GetXform5 dbtool_GetXform6) ENDIF(CMTK_USE_SQLITE) # ========================================== # Tests for "describe" tool LIST(APPEND testList describeMountPoints describeMountPointsMulti describeMountPointsPrefix describeMountPointsPrefixInvalid describeBZip2a describeBZip2b describeLZMAa describeLZMAb describeXZa describeXZb describeEmpty describeDICOM describeMosaicDICOM describeMosaicPACSDICOM describeDICOMZ describeVanderbilt describeMR1 describeMR2 describeMR3 describeMR4 describeHuman describeMRBiorad describeMRBioradGz describeNiftiDetached348 describeEmbedAnalyze describeEmbedNifti describeXform describeXformMachine) IF(CMTK_BUILD_NRRD) LIST(APPEND testList describeMRNrrd1 describeMRNrrd2 describeNrrdNoOrigin describeEmbedNrrd) ENDIF(CMTK_BUILD_NRRD) # ========================================== # Tests for "destripe" tool LIST(APPEND testList destripeDefault destripeKernelFloat) # ========================================== # Tests for "detect_adni_phantom" tool IF(CMTK_USE_FFTW) LIST(APPEND testList detect_adni_phantomDefault detect_adni_phantomRefineXform detect_adni_phantomRefineOutliers detect_adni_phantomExcludeOutliers detect_adni_phantomErodeNonStd detect_adni_phantomBadFOV detect_adni_phantomTolerantBadFOV detect_adni_phantomMissingSphere detect_adni_phantomBrokenSNR) ENDIF(CMTK_USE_FFTW) # ========================================== # Tests for "detect_spheres_matched_filter" tool IF(CMTK_USE_FFTW) LIST(APPEND testList detect_spheres_matched_filter detect_spheres_matched_filterNormalized) ENDIF(CMTK_USE_FFTW) # ========================================== # Tests for "epiunwarp" tool IF(CMTK_BUILD_NRRD) LIST(APPEND testList epiunwarp epiunwarpInitShift) ENDIF(CMTK_BUILD_NRRD) # ========================================== # Tests for "fib2image" tool LIST(APPEND testList fib2image) # ========================================== # Tests for "fibxform" tool LIST(APPEND testList fibxform fibxformSourceTarget) # ========================================== # Tests for "geomatch" tool LIST(APPEND testList geomatchAxSa geomatchAxSaRAS geomatchAxSaNoXforms) # ========================================== # Tests for "groupwise_init" tool LIST(APPEND testList groupwise_initCenterFOV groupwise_initCentersOfMass groupwise_initCentersOfMassScale groupwise_initCentersOfMassTemplate) # ========================================== # Tests for "groupwise_affine" tool LIST(APPEND testList groupwise_affineFromInit groupwise_affineMatchHistograms groupwise_affineFromInitSampling groupwise_affineBackground groupwise_affineUseTemplate groupwise_affineZeroSumSmooth groupwise_affineRMIFromInit groupwise_affineRMIFromInitDeltaF groupwise_affineRMIFromInitSampling groupwise_affineRMIBackground groupwise_affineRMIZeroSumSmooth) # ========================================== # Tests for "groupwise_warp" tool LIST(APPEND testList groupwise_warpFromInit groupwise_warpFromInitProtect groupwise_warpFitFromInit groupwise_warpFromInitZeroSum groupwise_warpUseTemplate groupwise_warpUseTemplateMatchHistograms groupwise_warpMatchHistograms groupwise_warpFromInitNoBG groupwise_warpUseTemplateNoBG groupwise_warpRMIFromInit groupwise_warpRMIFitFromInit groupwise_warpRMIFromInitZeroSum ) # ========================================== # Tests for xml, wiki, man, and help markups IF(CMTK_USE_SQLITE) FOREACH(t film levelset mrbias registration) LIST(APPEND testList xml_${t} wiki_${t} man_${t}) ENDFOREACH(t film levelset mrbias registration) LIST(APPEND xml_gmm) ENDIF(CMTK_USE_SQLITE) FOREACH(t film) LIST(APPEND testList help_${t} help_all_${t}) ENDFOREACH(t film) # ========================================== # Tests for "concat_affine" tool LIST(APPEND testList concat_affineABA concat_affineABAInvert concat_affineAB1A concat_affineAA1 concat_affineA1A) # ========================================== # Tests for "average_images" tool LIST(APPEND testList average_imagesMean average_imagesMeanNormPadd average_imagesMeanAbsLog average_imagesVariance average_imagesStDev average_imagesEntropy average_imagesZScore) # ========================================== # Tests for "convert_warp" tool LIST(APPEND testList convert_warpFractional convert_warpDeformation) # ========================================== # Tests for "convertx" tool LIST(APPEND testList convertxType convertxLabels convertxBinarize convertxBinarizeOtsu convertxBinarizeOtsuNBins convertxPruneHighLow convertxPruneHigh convertxPruneLow convertxBoundaryMap convertxBoundaryMapMultiValue convertxConnectedComponents convertxCropThresholdWriteRegion convertxCropRegion1 convertxCropRegion2 convertxResample convertxResampleExactLabels convertxDownsample convertxDownsampleNiftiSform convertxDownsampleNiftiQform convertxDownsampleNiftiQformSform convertxDownsampleSelect convertxDistanceUnsigned convertxDistanceSigned convertxDistanceSigned2 convertxFlipX convertxFlipYZ convertxErodeDilateErode convertxDilateErodeDilate convertxErodeByDistance convertxErodeByDistanceMultiLabel convertxDilateByDistance convertxGaussianFilterSigma convertxGaussianFilterFWHM convertxHistogramEqualization convertxHistogramEqualizationNBins convertxLaplace convertxMapValues convertxMapValues2 convertxMapValues3 convertxMapValuesOnly convertxMapValuesOnly2 convertxMatchMeanSDev convertxMatchHistograms convertxMask convertxMaskInverse convertxMedianFilter1 convertxMedianFilter2 convertxMedianFilterXYZ convertxMeanFilter convertxFastMean0Filter convertxFastMean1Filter convertxFastMean2Filter convertxFastMean2PadFilter convertxVarianceFilter convertxFastVariance0Filter convertxFastVariance1Filter convertxFastVariance2Filter convertxFastVariance2PadFilter convertxThirdMomentFilter convertxStandardDeviationFilter convertxSmoothnessFilter convertxNiftiToAnalyze convertxNiftiToMetaImage convertxAnalyzeToNifti convertxAnalyzeToNiftiRAS convertxNiftiDetachedToNifti convertxAnalyzeToNiftiDetached convertxUncompressedNIFTI1 convertxUncompressedNIFTI2 convertxUncompressedNIFTI3 convertxUncompressedNIFTI4 convertxThresholdBelow convertxThresholdAbove convertxThresholdBelowToPadding convertxThresholdAboveToPadding convertxScaleToRange convertxRevert convertxReplacePadding convertxReplaceInfNaN) IF(CMTK_BUILD_NRRD) LIST(APPEND testList convertxDownsampleNrrd convertxDownsampleSelectNrrd) ENDIF(CMTK_BUILD_NRRD) # ========================================== # Tests for "dcm2image" tool IF(CMTK_USE_DCMTK) LIST(APPEND testList dcm2image dcm2imageSubs dcm2imageZ dcm2imageEmbedPatientNameAnalyze dcm2imageEmbedPatientNameNifti dcm2imageEmbedSeriesDescriptionNifti dcm2imageEmbedStudyIDDateNifti dcm2imageMosaic dcm2imageMosaicPACS dcm2imageDiffusionGEXML dcm2imageDiffusionSiemensXML dcm2imageMosaicAnon dcm2imageMosaicPACSAnon dcm2imageDiffusionGEXMLAnon dcm2imageDiffusionSiemensXMLAnon dcm2imageExclude dcm2imageInclude dcm2imageSubsDirName) IF(CMTK_BUILD_NRRD) LIST(APPEND testList dcm2imageNrrd dcm2imageEmbedPatientNameNrrd) ENDIF(CMTK_BUILD_NRRD) ENDIF(CMTK_USE_DCMTK) # ========================================== # Tests for "dof2mat" tool LIST(APPEND testList dof2mat dof2matTranspose dof2matLegacy dof2matLegacyFwd dof2matFixed dof2matSuffix dof2mat3x3) # ========================================== # Tests for "filter" tool LIST(APPEND testList filterGaussian filterGaussianSmallKernel filterGaussianNoFilter filterRohlfing) # ========================================== # Tests for "film" tool LIST(APPEND testList filmCubic filmFourthOrder filmMSDLinearNoTrunc filmMISincRefSPGR) # ========================================== # Tests for "fit_affine_xform_landmarks" tool LIST(APPEND testList fit_affine_xform_landmarksRigid fit_affine_xform_landmarksScales fit_affine_xform_landmarksShear fit_affine_xform_landmarksRigidRigid fit_affine_xform_landmarksRigidRigidFlip fit_affine_xform_landmarksRigidScales fit_affine_xform_landmarksRigidShear) # ========================================== # Tests for "fit_affine_xform" tool LIST(APPEND testList fit_affine_xform fit_affine_xformRigid fit_affine_xformReverse) # ========================================== # Tests for "fit_affine_dfield" tool LIST(APPEND testList fit_affine_dfieldFFD) IF(CMTK_BUILD_NRRD) LIST(APPEND testList fit_affine_dfieldDField) ENDIF(CMTK_BUILD_NRRD) # ========================================== # Tests for "fit_spline_dfield" tool IF(CMTK_BUILD_NRRD) LIST(APPEND testList fit_spline_dfieldSpacing fit_spline_dfieldSpacingMultiLevel fit_spline_dfieldDims fit_spline_dfieldDimsRelative fit_spline_dfieldDimsMultiLevel fit_spline_dfieldDimsMultiLevelSafe fit_spline_dfieldDimsWithAffine) ENDIF(CMTK_BUILD_NRRD) # ========================================== # Tests for "fit_spline_xform" tool LIST(APPEND testList fit_spline_xformSpacing fit_spline_xformSpacingReverse fit_spline_xformSpacingMultiLevel fit_spline_xformDims fit_spline_xformDimsMultiLevel) # ========================================== # Tests for "glm" tool LIST(APPEND testList glmDefault glmNormalize glmExp glmNoConstant glmIgnore glmSelect glmCrop) # ========================================== # Tests for "gmm" tool LIST(APPEND testList gmmDefault gmmAlternative) # ========================================== # Tests for "gregxform" tool LIST(APPEND testList gregxformFordwardBackward gregxformAffine gregxformAffineFromWarp gregxformAffineFromWarpFwdBwd) # ========================================== # Tests for "hausdorff" tool LIST(APPEND testList hausdorffBinary12 hausdorffBinary21 hausdorffBinaryForced) # ========================================== # Tests for "histogram" tool LIST(APPEND testList histogram histogramNorm histogramBinsMinMax histogramBinsMinMaxTrunc histogramMask) # ========================================== # Tests for "imagemath" tool LIST(APPEND testList imagemathAbsPop imagemathSqrSqrt imagemathLogExp imagemathDupAddMulDiv imagemathThreshAboveBelow imagemathAverage imagemathVariance imagemathMaskAverage imagemathXor imagemathDupFillMaxValue imagemathAnd imagemathContractLabels imagemathMaxIndex imagemathMaxValue imagemathStackEntropyLabels imagemathVote imagemathProduct imagemathSTAPLE imagemathMultiClassSTAPLE imagemathMultiClassDisputedSTAPLE imagemathCombinePCA imagemathT2 imagemathAtan2 imagemathLogOddsAdd imagemathLogOddsAdd2 imagemathMatchMeanSDev imagemathMatchMeanSDev3 imagemathMatchHistograms imagemathMatchHistogramsPadding imagemathMatchHistogramsPadding2 imagemathMatchHistogramsPaddingUnset) # ========================================== # Tests for "interleaved_bad_slices" tool LIST(APPEND testList interleaved_bad_slicesDefault interleaved_bad_slicesStdDev1 interleaved_bad_slicesRMS interleaved_bad_slicesBulk) # ========================================== # Tests for "jidb" tool LIST(APPEND testList jidbGaussian jidbBoxFourthOrder jidbGaussianScale jidbMIRefSPGR) # ========================================== # Tests for "levelset" tool LIST(APPEND testList levelsetDefault levelsetScaleInitial levelsetBinarizeFastWideBinary ) # ========================================== # Tests for "lmsba" tool LIST(APPEND testList lmsbaRadius3 lmsbaRadius3Outliers lmsbaRadius3OutliersGlobal lmsbaRadius3Search1) # ========================================== # Tests for "lsba" tool LIST(APPEND testList lsbaRadius3 lsbaRadius3Outliers lsbaRadius3OutliersGlobal lsbaRadius3Search1) # ========================================== # Tests for "lvote" tool LIST(APPEND testList lvoteDefault lvoteGlobalWeights lvoteRadius3 lvoteOutliersGlobal) # ========================================== # Tests for "make_initial_affine" tool LIST(APPEND testList make_initial_affineCenterOfMass make_initial_affinePrincipalAxes1 make_initial_affinePrincipalAxes2 make_initial_affinePrincipalAxes3 make_initial_affinePrincipalAxes4 make_initial_affinePrincipalAxes5 make_initial_affinePrincipalAxes6) IF(CMTK_BUILD_NRRD) LIST(APPEND testList make_initial_affineDirectionVectorsNrrdAxSa make_initial_affineDirectionVectorsNrrdAxCo make_initial_affineDirectionVectorsNrrdSaCo make_initial_affineDirectionVectorsNrrdAxSaNative make_initial_affineDirectionVectorsNrrdAxCoNative make_initial_affineDirectionVectorsNrrdSaCoNative) ENDIF(CMTK_BUILD_NRRD) IF(CMTK_USE_SQLITE) LIST(APPEND testList make_initial_affineDB) ENDIF(CMTK_USE_SQLITE) # ========================================== # Tests for "mat2dof" tool LIST(APPEND testList mat2dof1 mat2dof2 mat2dofFile mat2dofToFile mat2dofToFileAppend) # ========================================== # Multi-channel registration requires hash map to build IF(HAVE_STL_HASH_MAP) # ========================================== # Tests for "mcaffine" tool LIST(APPEND testList mcaffine1 mcaffine2 mcaffine3 mcaffine4) # ========================================== # Tests for "mcwarp" tool LIST(APPEND testList mcwarp1 mcwarp2 mcwarp3 mcwarp4 mcwarp5 mcwarp6) ENDIF(HAVE_STL_HASH_MAP) # ========================================== # Tests for "mk_adni_phantom" tool LIST(APPEND testList mk_adni_phantom2mm mk_adni_phantom2mmLabels) # ========================================== # Tests for "mk_analyze_hdr" tool LIST(APPEND testList mk_analyze_hdrDefault mk_analyze_hdrImport mk_analyze_hdrLittleEndian mk_analyze_hdrBigEndian) # ========================================== # Tests for "mk_nifti_hdr" tool LIST(APPEND testList mk_nifti_hdrDefault mk_nifti_hdrDescription mk_nifti_hdrImport mk_nifti_hdrDefaultAttached mk_nifti_hdrDescriptionAttached mk_nifti_hdrImportAttached) # ========================================== # Tests for "mk_phantom_3d" tool LIST(APPEND testList mk_phantom_3dBoxIndexed mk_phantom_3dBoxIndexedRange mk_phantom_3dBoxAbsolute mk_phantom_3dBoxRelative mk_phantom_3dSphereIndexed mk_phantom_3dSphereAbsolute mk_phantom_3dSphereAbsolute2 mk_phantom_3dSphereRelative mk_phantom_3dSphereRelative2 mk_phantom_3dBoxSphere mk_phantom_3dImport mk_phantom_3dImportGrid mk_phantom_3dMRSVoxel) # ========================================== # Tests for "reorient" tool LIST(APPEND testList reorientHdrSaToAx reorientHdrSaToAxNifti reorientHdrCoToAx reorientHdrAxToSa reorientHdrCoToSa reorientHdrAxToCo reorientHdrSaToCo) IF(CMTK_BUILD_NRRD) LIST(APPEND testList reorientNrrdToNrrd reorientNrrdToNrrdRAS reorientNrrdToNrrdSpaceLPS) ENDIF(CMTK_BUILD_NRRD) # ========================================== # Tests for "split" tool LIST(APPEND testList splitAxial splitAxialSlices splitSagittal2 splitCoronal3) IF(CMTK_BUILD_NRRD) LIST(APPEND testList splitAxialNrrd) ENDIF(CMTK_BUILD_NRRD) # ========================================== # Tests for "overlap" tool LIST(APPEND testList overlap overlapNumLabels overlapByLabel overlapFirst overlapFirstByLabel overlapFirstByLabelNumLabels) # ========================================== # Tests for "probe" tool LIST(APPEND testList probeIndexed probeAbsolute probeRelative probePhysical probeIndexedLinear probeIndexedCubic probeIndexedSinc) # ========================================== # Tests for "pxsearch" tool LIST(APPEND testList pxsearchIndexed pxsearchIndexedRadius3 pxsearchIndexedRadius311 pxsearchAbsolute pxsearchRelative pxsearchPhysical) # ========================================== # Tests for "registration" tool LIST(APPEND testList registrationAffineMrMrMSD registrationRigidMrPet registrationRigidMrCt registrationRigidCt registrationRigidPetMr registrationRigidCtMr registrationRigidMrPetNoSwap registrationRigidMrCtNoSwap registrationRigidPetMrDOF9 registrationRigidCtMrDOF7 registrationRigidLabelsDOF69 registrationRigidCrop registrationAutoLevelsRat4 registrationAutoLevelsRat2 registrationAutoLevelsRatToRat registrationAutoLevelsRatToRatDeltaFThreshold registrationAutoLevelsCt3 registrationFromList registrationWithInitial registrationWithInitialInverse) # ========================================== # Tests for "registrationx" tool LIST(APPEND testList registrationxAffineMrMrMSD registrationxShearNoScaleMrMrMSD registrationxFromList registrationxWithInitial registrationxWithInitialInverse registrationxAutoLevelsRat4 registrationxAutoLevelsRat4XY registrationxAutoLevelsRat4YZ registrationxAutoLevelsRat4XZ registrationxAutoLevelsRat4Symmetric registrationxAutoLevelsRat4NONE registrationxAutoLevelsRat4FOV registrationxAutoLevelsRat4COM registrationxAutoLevelsRat4PAX registrationxAutoLevelsRat2 registrationxAutoLevelsRat2Cubic registrationxAutoLevelsRat2Sinc registrationxAutoLevelsRat2NN registrationxAutoLevelsRatToRat registrationxAutoLevelsRatToRatDeltaFThreshold registrationxAutoLevelsRatToRatRMS registrationxAutoLevelsRatToRatNCC registrationxAutoLevelsRatToRatCR registrationxAutoLevelsRatToRatMI registrationxAutoLevelsLabelsNN registrationxAutoLevelsLabelsPV registrationxAutoLevelsCt3) IF(CMTK_USE_SQLITE) LIST(APPEND testList registrationxFromListDB registrationxWithInitialDB registrationxWithInitialInverseDB ) ENDIF(CMTK_USE_SQLITE) # ========================================== # Tests for "mrbias" tool LIST(APPEND testList mrbiasMulIncremental mrbiasMulAutoThresh mrbiasMulOtsuThresh mrbiasMulLogIntensity mrbiasAddMulMask) IF(CMTK_USE_CUDA) LIST(APPEND testList mrbiasMulIncrementalCUDA mrbiasMulLogIntensityCUDA mrbiasAddMulMaskCUDA) ENDIF(CMTK_USE_CUDA) # ========================================== # Tests for "reformatx" tool LIST(APPEND testList reformatxNoXform reformatxLinear ## reformatxLinearFwdBwd reformatxNearestNeighbor reformatxPartialVolume reformatxCubic reformatxCubicInverse reformatxSincCosine reformatxSincCosine5 reformatxMassPreserving reformatxJacobian reformatxInverseJacobian reformatxJacobianDefault reformatxInverseJacobianDefault reformatxSincHamming reformatxTargetGrid reformatxTargetGridAnalyze reformatxTargetGridOffset) IF(CMTK_BUILD_NRRD) LIST(APPEND testList reformatxDfieldNrrd reformatxDfieldNrrdJacobian) ENDIF(CMTK_BUILD_NRRD) # ========================================== # Tests for "sba" tool LIST(APPEND testList sbaDefault sbaOutliers sbaOutliers2) # ========================================== # Tests for "sbai" tool LIST(APPEND testList sbaiDefault) # ========================================== # Tests for "sequence" tool LIST(APPEND testList sequenceDefault sequenceFormat sequenceThresh sequenceAbs sequenceAbsThresh sequenceHistogramDefault sequenceHistogramExplicit) # ========================================== # Tests for "similarity" tool LIST(APPEND testList similarityGrey similarityWithInf similarityLabels similarityGreyMask similarityLabelsMask) # ========================================== # Tests for "streamxform" tool LIST(APPEND testList streamxformFordwardBackward streamxformBackwardForward streamxformFordwardBackwardPoly0 streamxformFordwardBackwardPoly1 streamxformFordwardBackwardPoly2 streamxformFordwardBackwardPoly3 streamxformFordwardBackwardPoly4 streamxformAffinePoly4 streamxformAffineForward streamxformAffineForwardBackward streamxformFordwardBackwardTolerance streamxformAffine streamxformAffineFromTo) # ========================================== # Tests for "sympl" tool LIST(APPEND testList symmetry_plane symmetry_planeThresh) # ========================================== # Tests for "symplx" tool LIST(APPEND testList symplx_Default symplx_Thresh symplx_FixOffset) IF(CMTK_USE_CUDA) LIST(APPEND testList symplx_DefaultCUDA symplx_ThreshCUDA symplx_FixOffsetCUDA) ENDIF(CMTK_USE_CUDA) # ========================================== # Tests for "statistics" tool LIST(APPEND testList statisticsGrey statisticsPercentiles statisticsGreyColumn statisticsGreyExpNotation statisticsGreyMask statisticsGreyMultiMask statisticsMaskMismatch statisticsLabels statisticsLabelsAllUpToHi statisticsLabelsAllUpToLo) # ========================================== # Tests for "stream_pixels" tool LIST(APPEND testList stream_pixels stream_pixelsConvert stream_pixelsReorient stream_pixelsEndian) # ========================================== # Tests for "ttest" tool LIST(APPEND testList ttestDefault ttestLog ttestAbsLog ttestInvert ttestPaired ttestCrossCorrelation ttestZScores ttestMask ttestOneSided) # ========================================== # Tests for "unsplit" tool LIST(APPEND testList unsplitHdrAx unsplitHdrSa unsplitHdrCo unsplitSlices) IF(CMTK_BUILD_NRRD) LIST(APPEND testList unsplitHdrNrrdAx unsplitHdrNrrdSa unsplitHdrNrrdCo unsplitNrrdNrrd) ENDIF(CMTK_BUILD_NRRD) # ========================================== # Tests for "unwarp_image_phantom" tool LIST(APPEND testList unwarp_image_phantomDefault unwarp_image_phantomPoly unwarp_image_phantomPolyDegree2 unwarp_image_phantomSpacing unwarp_image_phantomSpacingInverse unwarp_image_phantomSpacingLevels unwarp_image_phantomSpacingDirect unwarp_image_phantomDims unwarp_image_phantomDimsLevels unwarp_image_phantomDimsLevelsIterations unwarp_image_phantomDimsLevelsThreshold unwarp_image_phantomDimsLevelsIterationsThreshold unwarp_image_phantomDimsDefault) # ========================================== # Tests for "volume_injection" tool LIST(APPEND testList volume_injection volume_injectionReconGrid volume_injectionReconGridOffset volume_injectionIsotropic volume_injectionNoXform volume_injectionNoXformIsotropic) # ========================================== # Tests for "volume_reconstruction" tool LIST(APPEND testList volume_reconstructionFourthOrder volume_reconstructionCubic volume_reconstructionNoXform volume_reconstructionBoxPSF volume_reconstructionGaussianPSF) # ========================================== # Tests for "warp" tool LIST(APPEND testList warpSingleLevel warpSingleLevelExact warpInverseConsistentCC warpMultiLevel warpMultiLevelMatchHistograms warpMultiLevelDeltaFThreshold warpMultiLevelExact warpDelayRefine warpEnergy warpEnergyRelax warpRigidity warpJacobian warpLabels) # ========================================== # Tests for "warpx" tool LIST(APPEND testList warpxSingleLevel warpxSingleLevelExact warpxInverseConsistentCC warpxMultiLevel warpxMultiLevelMatchHistograms warpxMultiLevelDeltaFThreshold warpxMultiLevelExact warpxDelayRefine warpxEnergy warpxEnergyRelax warpxJacobian warpxJacobianUnfold warpxLabels) # ========================================== # Tests for "xform2dfield" tool LIST(APPEND testList xform2dfieldWarpNifti xform2dfieldWarpNiftiFSL xform2dfieldWarpNiftiHdr) IF(CMTK_BUILD_NRRD) LIST(APPEND testList xform2dfieldWarpNrrd xform2dfieldAffineNrrd xform2dfieldDownsampleXYZNrrd xform2dfieldDownsampleXNrrd xform2dfieldConcatNrrd xform2dfieldInverseNrrd) ENDIF(CMTK_BUILD_NRRD) # ========================================== # Tests for "xform2scalar" tool LIST(APPEND testList xform2scalarAffine xform2scalarAffineDoubleY xform2scalarWarp xform2scalarWarpInverseError xform2scalarWarpOnly) # ========================================== # Tests for "vol2csv" tool LIST(APPEND testList vol2csvLabels vol2csvLabelsNoBackground vol2csvDensityLabels vol2csvScale vol2csvScaleGlobal) # ========================================== # Tests for "vtkxform" tool LIST(APPEND testList vtkxform vtkxformInverse) IF(CMTK_BUILD_NRRD) LIST(APPEND testList xform2scalarDfield) ENDIF(CMTK_BUILD_NRRD) # ========================================== # Set up all tests FOREACH(testName ${testList}) IF(CMTK_TESTING_MEMORYCHECK) ADD_TEST(NAME ${testName} COMMAND ${testDriver} ${testName} $ ${MEMORYCHECK_COMMAND}) ELSE(CMTK_TESTING_MEMORYCHECK) ADD_TEST(NAME ${testName} COMMAND ${testDriver} ${testName} $) ENDIF(CMTK_TESTING_MEMORYCHECK) ENDFOREACH(testName ${testList}) SET_TESTS_PROPERTIES(groupwise_warpFromInit PROPERTIES TIMEOUT 4800) SET_TESTS_PROPERTIES(groupwise_warpFitFromInit PROPERTIES TIMEOUT 4800) SET_TESTS_PROPERTIES(groupwise_warpFromInitZeroSum PROPERTIES TIMEOUT 4800) SET_TESTS_PROPERTIES(groupwise_warpUseTemplate PROPERTIES TIMEOUT 6400) SET_TESTS_PROPERTIES(groupwise_warpFromInitNoBG PROPERTIES TIMEOUT 4800) SET_TESTS_PROPERTIES(groupwise_warpUseTemplateNoBG PROPERTIES TIMEOUT 6400) SET_TESTS_PROPERTIES(groupwise_warpRMIFromInit groupwise_warpRMIFitFromInit groupwise_warpRMIFromInitZeroSum PROPERTIES TIMEOUT 4800) # ========================================== # Set up test properties, now that the tests # have been defined. SET_TESTS_PROPERTIES(avg_admLabels filmFourthOrder jidbGaussian jidbGaussianScale volume_reconstructionGaussianPSF volume_reconstructionNoXform PROPERTIES TIMEOUT 1800) SET_TESTS_PROPERTIES(filmCubic PROPERTIES TIMEOUT 1800) SET_TESTS_PROPERTIES(filmMSDLinearNoTrunc PROPERTIES TIMEOUT 3600) SET_TESTS_PROPERTIES(filmMISincRefSPGR PROPERTIES TIMEOUT 3600) SET_TESTS_PROPERTIES(jidbMIRefSPGR PROPERTIES TIMEOUT 3600) SET_TESTS_PROPERTIES(registrationxShearNoScaleMrMrMSD PROPERTIES TIMEOUT 900) IF(HAVE_STL_HASH_MAP) SET_TESTS_PROPERTIES(mcaffine2 PROPERTIES TIMEOUT 1800) SET_TESTS_PROPERTIES(mcwarp1 mcwarp2 mcwarp3 mcwarp4 mcwarp5 mcwarp6 PROPERTIES TIMEOUT 1800) ENDIF(HAVE_STL_HASH_MAP) SET_TESTS_PROPERTIES(mrbiasAddMulMask PROPERTIES TIMEOUT 3600) SET_TESTS_PROPERTIES(mrbiasMulIncremental PROPERTIES TIMEOUT 3600) SET_TESTS_PROPERTIES(mrbiasMulLogIntensity PROPERTIES TIMEOUT 3600) SET_TESTS_PROPERTIES(volume_reconstructionCubic PROPERTIES TIMEOUT 2400) SET_TESTS_PROPERTIES(volume_reconstructionFourthOrder PROPERTIES TIMEOUT 1800) SET_TESTS_PROPERTIES(warpDelayRefine PROPERTIES TIMEOUT 1800) SET_TESTS_PROPERTIES(warpInverseConsistentCC PROPERTIES TIMEOUT 1800) SET_TESTS_PROPERTIES(warpLabels PROPERTIES TIMEOUT 1800) SET_TESTS_PROPERTIES(warpMultiLevel PROPERTIES TIMEOUT 1800) SET_TESTS_PROPERTIES(warpMultiLevelExact PROPERTIES TIMEOUT 1800) SET_TESTS_PROPERTIES(warpSingleLevel PROPERTIES TIMEOUT 1800) SET_TESTS_PROPERTIES(warpSingleLevelExact PROPERTIES TIMEOUT 1800) SET_TESTS_PROPERTIES(xform2scalarWarpInverseError PROPERTIES TIMEOUT 1800) cmtk-3.3.1/testing/apps/appsTestDriver.sh.in000077500000000000000000003754021276303427400210420ustar00rootroot00000000000000#!/bin/bash ## ## Copyright 2004-2014 SRI International ## ## Copyright 1997-2011 Torsten Rohlfing ## ## This file is part of the Computational Morphometry Toolkit. ## ## http://www.nitrc.org/projects/cmtk/ ## ## The Computational Morphometry Toolkit is free software: you can ## redistribute it and/or modify it under the terms of the GNU General Public ## License as published by the Free Software Foundation, either version 3 of ## the License, or (at your option) any later version. ## ## The Computational Morphometry Toolkit is distributed in the hope that it ## will be useful, but WITHOUT ANY WARRANTY; without even the implied ## warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License along ## with the Computational Morphometry Toolkit. If not, see ## . ## ## $Revision: 5370 $ ## ## $LastChangedDate: 2014-07-28 21:26:05 -0700 (Mon, 28 Jul 2014) $ ## ## $LastChangedBy: torsten_at_home $ ## RUNTEST=$1 CONFIG=$2 VALGRIND=$3 BUILDNAME=@BUILDNAME@ DATADIR=@CMTK_DATA_ROOT@/testing/inputs BINDIR=@EXECUTABLE_OUTPUT_PATH@/${CONFIG} if [ ! -d ${BINDIR}/. ]; then BINDIR=@EXECUTABLE_OUTPUT_PATH@ fi BASELINE_DEFAULT=@CMTK_DATA_ROOT@/testing/baseline/${RUNTEST} BASELINE=${BINDIR}/../testing/baseline/${RUNTEST} if [ ! -d ${BASELINE} ]; then BASELINE=${BASELINE_DEFAULT} fi HOSTNAME=`uname -n` NUMDIFF=@NUMDIFF_EXECUTABLE@ tmpdir=${BINDIR}/../testing/temporary/${HOSTNAME}/${RUNTEST} if [ -e ${tmpdir} ]; then rm -rf ${tmpdir} fi mkdir -p ${tmpdir} sqlite=${BINDIR}/sqlite if [ ! -f ${sqlite} ]; then sqlite=sqlite3 fi sed=/opt/local/bin/gsed if [ ! -f ${sed} ]; then sed=sed fi cd ${DATADIR} run() { cmd=$* echo "pushd $PWD; ${BINDIR}/$cmd; popd" 1>&2 if ${VALGRIND} ${BINDIR}/$cmd; then return else exit 1 fi } run_expect_fail() { cmd=$* echo "pushd $PWD; ${BINDIR}/$cmd; popd" if ${BINDIR}/$cmd; then exit 1 else return fi } run_eval() { cmd=$* echo "pushd $PWD; $cmd; popd" if eval "${VALGRIND} $cmd"; then return else exit 1 fi } run_eval_expect_fail() { cmd=$* echo "pushd $PWD; $cmd; popd" if eval "${VALGRIND} $cmd"; then exit 1 else return fi } get_unzipped() { if test -f ${1}.gz; then if [ "`uname`" = "Darwin" ]; then local tmp=`mktemp /tmp/temp.XXXX` else local tmp=`mktemp` fi gzip -cd ${1}.gz > ${tmp} echo ${tmp} else echo ${1} fi } remove_if_tempfile() { for f in $*; do local dir=`dirname $f` if [ "${dir}" == "/tmp" ]; then rm $f fi done } check_result() { baseline=`get_unzipped ${BASELINE}/$1` result=`get_unzipped ${tmpdir}/$1` if test ! -f ${result}; then echo "Results file ${result} does not exist" remove_if_tempfile ${baseline} ${result} exit 1 fi if test ! -f ${baseline}; then echo "Baseline file ${baseline} does not exist" remove_if_tempfile ${baseline} ${result} exit 1 fi local diff="cmp" if file ${result} | fgrep text; then ## diff="diff --strip-trailing-cr" diff="${NUMDIFF} --relative-tolerance=1e-5 --absolute-tolerance=5e-7" fi echo "${diff} ${tmpdir}/$1 ${BASELINE}/$1" if ${diff} $result $baseline; then remove_if_tempfile ${baseline} ${result} return else remove_if_tempfile ${baseline} ${result} exit 1 fi } check_results() { for r in $*; do check_result $r done } check_image() { baseline=${BASELINE}/$1 result=${tmpdir}/$1 abs_threshold=1e-5 ## if this is made much smaller, need to adjust "scale=" on next line: # see if maximum relative difference between images exceeds 1e-6 difference=`${BINDIR}/similarity $baseline $result | fgrep SIMval | cut -f3` compare=`echo "SCALE; $difference > $abs_threshold" | sed 's/e/*10^/g;s/SCALE/scale=10/' | bc` echo difference=$difference compare=$compare if [ "${compare}" = "0" ]; then return else echo "Absolute image difference $difference exceeds threshold $abs_threshold" fi rel_threshold=1e-5 ## if this is made much smaller, need to adjust "scale=" on next line: difference=`${BINDIR}/similarity $baseline $result | fgrep SIMval | cut -f4` compare=`echo "SCALE; $difference > $rel_threshold" | sed 's/e/*10^/g;s/SCALE/scale=10/' | bc` echo difference=$difference compare=$compare if [ "${compare}" = "0" ]; then return else echo "Relative image difference $difference exceeds threshold $rel_threshold" exit 1 fi } check_images() { for r in $*; do check_image $r done } case ${RUNTEST} in average_imagesMean) run average_images -o ${tmpdir}/average.hdr spgr_brain_1.hdr spgr_brain_2.hdr spgr_brain_3.hdr check_results average.img ;; average_imagesMeanNormPadd) run average_images -o ${tmpdir}/average.hdr --set-padding-value 0 --normalize-mean-stdev spgr_brain_1.hdr spgr_brain_2.hdr spgr_brain_3.hdr check_results average.img ;; average_imagesMeanAbsLog) run average_images --set-padding-value 0 -o ${tmpdir}/average.hdr --abs --log spgr_brain_1.hdr spgr_brain_2.hdr spgr_brain_3.hdr check_results average.img ;; average_imagesVariance) run average_images -o ${tmpdir}/variance.hdr --var spgr_brain_1.hdr spgr_brain_2.hdr spgr_brain_3.hdr check_results variance.img ;; average_imagesStDev) run average_images -o ${tmpdir}/stdev.hdr --stdev spgr_brain_1.hdr spgr_brain_2.hdr spgr_brain_3.hdr check_results stdev.img ;; average_imagesEntropy) run average_images -o ${tmpdir}/entropy.hdr --entropy spgr_brain_1.hdr spgr_brain_2.hdr spgr_brain_3.hdr check_results entropy.img ;; average_imagesZScore) run average_images -o ${tmpdir}/zscore.hdr --zscore spgr_brain_1.hdr spgr_brain_2.hdr spgr_brain_3.hdr check_results zscore.img ;; avg_admDefault2) run avg_adm --output ${tmpdir}/avg.hdr --output-warp ${tmpdir}/avg.warp spgr_brain_12_warp.list check_results avg.img avg.warp ;; avg_admDefault3) run avg_adm --output ${tmpdir}/avg.hdr --output-warp ${tmpdir}/avg.warp spgr_brain_12_warp.list spgr_brain_13_warp.list check_results avg.img avg.warp ;; avg_admJacobianFloat) run avg_adm --jacobian --float --output ${tmpdir}/avg.hdr --output-warp ${tmpdir}/avg.warp spgr_brain_12_warp.list spgr_brain_13_warp.list check_results avg.img avg.warp ;; avg_admLabels) run avg_adm --nn --label --replace-from spgr_brain_ --replace-to parc --output ${tmpdir}/avg.hdr --output-warp ${tmpdir}/avg.warp spgr_brain_12_warp.list spgr_brain_13_warp.list check_results avg.img avg.warp ;; avg_admPaddOutZeroNN) run avg_adm --nn --pad-out 0 --output ${tmpdir}/avg.hdr --output-warp ${tmpdir}/avg.warp spgr_brain_12_warp.list spgr_brain_13_warp.list check_results avg.img avg.warp ;; avg_admNoReferenceModelCubic) run avg_adm --byte --cubic --output ${tmpdir}/avg.hdr --output-warp ${tmpdir}/avg.warp --no-ref-model spgr_brain_12_warp.list spgr_brain_13_warp.list check_results avg.img avg.warp ;; avg_admNoScaleModelAutoScale) run avg_adm --byte --output ${tmpdir}/avg.hdr --output-warp ${tmpdir}/avg.warp --no-scale-model --auto-scale spgr_brain_12_warp.list spgr_brain_13_warp.list check_results avg.img avg.warp ;; avg_admWithAffineNoRefData) run avg_adm --byte --output ${tmpdir}/avg.hdr --output-warp ${tmpdir}/avg.warp --no-ref-data --with-affine spgr_brain_12_warp.list spgr_brain_13_warp.list check_results avg.img avg.warp ;; concat_affineABA) run concat_affine -o ${tmpdir}/concat.xform affineA.xform affineB.xform affineA.xform check_results concat.xform ;; concat_affineABAInvert) run concat_affine --invert-output -o ${tmpdir}/concat.xform affineA.xform affineB.xform affineA.xform check_results concat.xform ;; concat_affineAB1A) run concat_affine -o ${tmpdir}/concat.xform affineA.xform --inverse affineB.xform affineA.xform check_results concat.xform ;; concat_affineAA1) run concat_affine -o ${tmpdir}/concat.xform affineA.xform --inverse affineA.xform check_results concat.xform ;; concat_affineA1A) run concat_affine -o ${tmpdir}/concat.xform -- --inverse affineA.xform affineA.xform check_results concat.xform ;; convert_warpFractional) run convert_warp --fractional 0.5 vol001_mr_t0t1_warp.xform ${tmpdir}/fractional.xform check_results fractional.xform ;; convert_warpDeformation) run convert_warp --deformation-only vol001_mr_t0t1_warp.xform ${tmpdir}/deformation.xform check_results deformation.xform ;; convertxType) run convertx --byte --char --float --double --int --uint --short --ushort spgr_brain_1.hdr ${tmpdir}/convert.nii check_results convert.nii ;; convertxLabels) run convertx --labels binary_mask.nii ${tmpdir}/convert.nii check_results convert.nii ;; convertxBinarize) run convertx --binarize-thresh 100 spgr_brain_1.hdr ${tmpdir}/thresh.hdr check_results thresh.img ;; convertxBinarizeOtsu) run convertx --otsu-thresh --byte phantom_ax.nii ${tmpdir}/binary.nii check_results binary.nii ;; convertxBinarizeOtsuNBins) run convertx --otsu-thresh-nbins 128 --byte phantom_ax.nii ${tmpdir}/binary.nii check_results binary.nii ;; convertxPruneHighLow) run convertx --prune-histogram 16 pat001_pet.hdr ${tmpdir}/prune.nii check_results prune.nii ;; convertxPruneHigh) run convertx --prune-histogram-high 16 pat001_pet.hdr ${tmpdir}/prune.nii check_results prune.nii ;; convertxPruneLow) run convertx --prune-histogram-low 16 pat001_pet.hdr ${tmpdir}/prune.nii check_results prune.nii ;; convertxBoundaryMap) run convertx --boundary-map parc1.hdr ${tmpdir}/boundary_map.hdr check_results boundary_map.img ;; convertxConnectedComponents) run convertx --connected-components binary_mask.nii ${tmpdir}/connected.hdr check_results connected.img ;; convertxDistanceUnsigned) run convertx --distance-map parc1_bin.hdr ${tmpdir}/dmap.hdr check_results dmap.img ;; convertxLaplace) run convertx --float --laplace-filter spgr_brain_1.hdr ${tmpdir}/laplace.hdr check_results laplace.img ;; convertxMapValues) run convertx --map-values 82,86:254 --map-values 16:255 --set-padding 128 --map-values 0 parc3.hdr ${tmpdir}/mapped.hdr check_results mapped.img ;; convertxMapValues2) run convertx --set-padding 128 --map-values 82,86:254+16:255+0 parc3.hdr ${tmpdir}/mapped.hdr check_results mapped.img ;; convertxMapValues3) run convertx --set-padding 128 --map-values 16:255+82,86:254+0 parc3.hdr ${tmpdir}/mapped.hdr check_results mapped.img ;; convertxMapValuesOnly) run convertx --set-padding 255 --map-values-only 82,86:1 parc3.hdr ${tmpdir}/mapped.hdr check_results mapped.img ;; convertxMapValuesOnly2) run convertx --set-padding 255 --map-values-only 82:1+86:1 parc3.hdr ${tmpdir}/mapped.hdr check_results mapped.img ;; convertxRevert) run convertx --revert parc1_bin.hdr ${tmpdir}/revert.hdr check_results revert.img run convertx --revert parc1.hdr ${tmpdir}/revert.hdr check_results revert.img ;; convertxDistanceSigned) run convertx --signed-distance-map parc1_bin.hdr ${tmpdir}/dmap.hdr check_results dmap.img ;; convertxDistanceSigned2) run convertx --signed-distance-map parc1.hdr ${tmpdir}/dmap.hdr check_results dmap.img ;; convertxBoundaryMapMultiValue) run convertx --multi-boundary-map parc1.hdr ${tmpdir}/boundary_map_multi.hdr check_results boundary_map_multi.img ;; convertxCropThresholdWriteRegion) run_eval "${BINDIR}/convertx --crop-by-threshold-write-region 1 spgr_brain_1.hdr ${tmpdir}/cropped.nii > ${tmpdir}/crop.txt" check_results cropped.nii crop.txt ;; convertxCropRegion1) run_eval "${BINDIR}/convertx --crop-by-index 2,2,2,12,12,12 spgr_brain_1.hdr ${tmpdir}/cropped.nii" check_results cropped.nii ;; convertxCropRegion2) run_eval "${BINDIR}/convertx --crop-by-index 10,10,10,-10,-10,-10 spgr_brain_1.hdr ${tmpdir}/cropped.nii" check_results cropped.nii ;; convertxResample) run convertx --resample 1.0 phantom_ax.nii ${tmpdir}/resample.nii check_results resample.nii ;; convertxResampleExactLabels) run convertx --resample-exact 10 lvote_atlas_l0.nii ${tmpdir}/resample.nii check_results resample.nii ;; convertxDownsample) run convertx --downsample-average 8,4,1 phantom_ax.hdr ${tmpdir}/downsample.hdr check_results downsample.hdr downsample.img ;; convertxDownsampleNiftiSform) run convertx --downsample-average 8,4,1 phantom_ax_sform.nii ${tmpdir}/downsample_ax.nii run convertx --downsample-average 1,8,4 phantom_sa_sform.nii ${tmpdir}/downsample_sa.nii run convertx --downsample-average 4,1,8 phantom_co_sform.nii ${tmpdir}/downsample_co.nii check_results downsample_ax.nii downsample_sa.nii downsample_co.nii ;; convertxDownsampleNiftiQform) run convertx --downsample-average 8,4,1 phantom_ax_qform.nii ${tmpdir}/downsample_ax.nii run convertx --downsample-average 1,8,4 phantom_sa_qform.nii ${tmpdir}/downsample_sa.nii run convertx --downsample-average 4,1,8 phantom_co_qform.nii ${tmpdir}/downsample_co.nii check_results downsample_ax.nii downsample_sa.nii downsample_co.nii ;; convertxDownsampleNiftiQformSform) run convertx --downsample-average 8,4,1 phantom_ax_qform_sform.nii ${tmpdir}/downsample_ax.nii run convertx --downsample-average 1,8,4 phantom_sa_qform_sform.nii ${tmpdir}/downsample_sa.nii run convertx --downsample-average 4,1,8 phantom_co_qform_sform.nii ${tmpdir}/downsample_co.nii check_results downsample_ax.nii downsample_sa.nii downsample_co.nii ;; convertxDownsampleNrrd) run convertx --downsample-average 8,4,1 phantom_ax.nhdr ${tmpdir}/downsample.nhdr check_results downsample.nhdr downsample.raw ;; convertxDownsampleSelect) run convertx --downsample-select 8,4,1 phantom_ax.hdr ${tmpdir}/downsample.hdr check_results downsample.hdr downsample.img ;; convertxDownsampleSelectNrrd) run convertx --downsample-select 8,4,1 phantom_ax.nhdr ${tmpdir}/downsample.nhdr check_results downsample.nhdr downsample.raw ;; convertxFlipX) run convertx --flip-x parc1_bin.hdr ${tmpdir}/flip.hdr check_results flip.img ;; convertxFlipYZ) run convertx --flip-y --flip-z parc1_bin.hdr ${tmpdir}/flip.hdr check_results flip.img ;; convertxErodeDilateErode) run convertx --erode 1 --dilate 2 --erode 1 parc1_bin.hdr ${tmpdir}/parc1_ede.img check_results parc1_ede.img ;; convertxDilateErodeDilate) run convertx --dilate 1 --erode 2 --dilate 1 parc1_bin.hdr ${tmpdir}/parc1_ded.img check_results parc1_ded.img ;; convertxErodeByDistance) run convertx --erode-distance 10 parc1_bin.hdr ${tmpdir}/parc1_erode.nii check_results parc1_erode.nii ;; convertxErodeByDistanceMultiLabel) run convertx --erode-distance-multilabel 10 lvote_atlas_l0.nii ${tmpdir}/erode.nii check_results erode.nii ;; convertxDilateByDistance) run convertx --dilate-distance 10 parc1_bin.hdr ${tmpdir}/parc1_dilate.nii check_results parc1_dilate.nii ;; convertxGaussianFilterSigma) run convertx --gaussian-filter-sigma 5.0 spgr_brain_1.hdr ${tmpdir}/spgr_brain_1.hdr check_results spgr_brain_1.img ;; convertxGaussianFilterFWHM) run convertx --gaussian-filter-fwhm 10.0 spgr_brain_1.hdr ${tmpdir}/spgr_brain_1.hdr check_results spgr_brain_1.img ;; convertxHistogramEqualization) run convertx --histogram-equalization spgr_brain_1.hdr ${tmpdir}/spgr_brain_1.hdr check_results spgr_brain_1.img ;; convertxHistogramEqualizationNBins) run convertx --histogram-equalization-nbins 32 spgr_brain_1.hdr ${tmpdir}/spgr_brain_1.hdr check_results spgr_brain_1.img ;; convertxMatchMeanSDev) run convertx --match-mean-sdev spgr_brain_2.hdr spgr_brain_1.hdr ${tmpdir}/match.nii check_results match.nii ;; convertxMatchHistograms) run convertx --match-histograms spgr_brain_2.hdr spgr_brain_1.hdr ${tmpdir}/match.nii check_results match.nii ;; convertxMask) run convertx --mask spgr_3t_mask.hdr spgr_3t.hdr ${tmpdir}/masked.hdr check_results masked.img ;; convertxMaskInverse) run convertx --mask-inverse spgr_3t_mask.hdr spgr_3t.hdr ${tmpdir}/masked.hdr check_results masked.img ;; convertxMedianFilter1) run convertx --median-filter 1 spgr_brain_1.hdr ${tmpdir}/spgr_brain_1.hdr check_results spgr_brain_1.img ;; convertxMedianFilter2) run convertx --median-filter 2 spgr_brain_1.hdr ${tmpdir}/spgr_brain_1.hdr check_results spgr_brain_1.img ;; convertxMedianFilterXYZ) run convertx --median-filter 2,2,1 spgr_brain_1.hdr ${tmpdir}/spgr_brain_1.hdr check_results spgr_brain_1.img ;; convertxMeanFilter) run convertx --float --mean-filter 1 spgr_brain_1.hdr ${tmpdir}/spgr_brain_1.hdr check_results spgr_brain_1.img ;; convertxFastMean0Filter) run convertx --float --fast-mean-filter 0 spgr_brain_1.hdr ${tmpdir}/spgr_brain_1.hdr check_results spgr_brain_1.img ;; convertxFastMean1Filter) run convertx --float --fast-mean-filter 1 spgr_brain_1.hdr ${tmpdir}/spgr_brain_1.hdr check_results spgr_brain_1.img ;; convertxFastMean2Filter) run convertx --float --fast-mean-filter 2 spgr_brain_1.hdr ${tmpdir}/spgr_brain_1.hdr check_results spgr_brain_1.img ;; convertxFastMean2PadFilter) run convertx --set-padding 0 --float --fast-mean-filter 2 spgr_brain_1.hdr ${tmpdir}/spgr_brain_1.hdr check_results spgr_brain_1.img ;; convertxVarianceFilter) run convertx --float --variance-filter 1 spgr_brain_1.hdr ${tmpdir}/spgr_brain_1.hdr check_results spgr_brain_1.img ;; convertxFastVariance0Filter) run convertx --float --fast-variance-filter 0 spgr_brain_1.hdr ${tmpdir}/spgr_brain_1.hdr check_results spgr_brain_1.img ;; convertxFastVariance1Filter) run convertx --float --fast-variance-filter 1 spgr_brain_1.hdr ${tmpdir}/spgr_brain_1.hdr check_results spgr_brain_1.img ;; convertxFastVariance2Filter) run convertx --float --fast-variance-filter 2 spgr_brain_1.hdr ${tmpdir}/spgr_brain_1.hdr check_results spgr_brain_1.img ;; convertxFastVariance2PadFilter) run convertx --set-padding 0 --float --fast-variance-filter 2 spgr_brain_1.hdr ${tmpdir}/spgr_brain_1.hdr check_results spgr_brain_1.img ;; convertxThirdMomentFilter) run convertx --float --third-moment-filter 1 spgr_brain_1.hdr ${tmpdir}/spgr_brain_1.hdr check_results spgr_brain_1.img ;; convertxStandardDeviationFilter) run convertx --float --standard-deviation-filter 1 spgr_brain_1.hdr ${tmpdir}/spgr_brain_1.hdr check_results spgr_brain_1.img ;; convertxSmoothnessFilter) run convertx --float --smoothness-filter 1 spgr_brain_1.hdr ${tmpdir}/spgr_brain_1.hdr check_results spgr_brain_1.img ;; convertxNiftiToAnalyze) run convertx spgr_brain_1.nii ${tmpdir}/spgr_brain_1.hdr check_results spgr_brain_1.hdr spgr_brain_1.img ;; convertxNiftiToMetaImage) run convertx spgr_brain_1.nii ${tmpdir}/spgr_brain_1.mha check_results spgr_brain_1.mha ;; convertxAnalyzeToNifti) run convertx spgr_brain_1.hdr ${tmpdir}/spgr_brain_1.nii check_results spgr_brain_1.nii ;; convertxAnalyzeToNiftiRAS) export CMTK_LEGACY_WRITE_IMAGES_RAS=1 run convertx spgr_brain_1.hdr ${tmpdir}/spgr_brain_1.nii unset CMTK_LEGACY_WRITE_IMAGES_RAS check_results spgr_brain_1.nii ;; convertxNiftiDetachedToNifti) run convertx spgr_brain_nifti.hdr ${tmpdir}/spgr_brain_1.nii check_results spgr_brain_1.nii ;; convertxAnalyzeToNiftiDetached) run convertx spgr_brain_1.hdr ${tmpdir}/spgr_brain_1.img check_results spgr_brain_1.hdr spgr_brain_1.img ;; convertxUncompressedNIFTI1) export CMTK_WRITE_UNCOMPRESSED=1 run convertx parc1_bin.hdr ${tmpdir}/out.nii unset CMTK_WRITE_UNCOMPRESSED if [ ! -e ${tmpdir}/out.nii ]; then exit 1; fi ;; convertxUncompressedNIFTI2) export CMTK_WRITE_UNCOMPRESSED=1 run convertx parc1_bin.hdr ${tmpdir}/out.nii.gz unset CMTK_WRITE_UNCOMPRESSED if [ ! -e ${tmpdir}/out.nii.gz ]; then exit 1; fi ;; convertxUncompressedNIFTI3) unset CMTK_WRITE_UNCOMPRESSED run convertx parc1_bin.hdr ${tmpdir}/out.nii if [ ! -e ${tmpdir}/out.nii.gz ]; then exit 1; fi ;; convertxUncompressedNIFTI4) unset CMTK_WRITE_UNCOMPRESSED run convertx parc1_bin.hdr ${tmpdir}/out.nii.gz if [ ! -e ${tmpdir}/out.nii.gz ]; then exit 1; fi ;; convertxThresholdBelow) run convertx --set-padding 0 --thresh-below 100 spgr_brain_1.hdr ${tmpdir}/thresh.hdr check_results thresh.img ;; convertxThresholdAbove) run convertx --set-padding 0 --thresh-above 100 spgr_brain_1.hdr ${tmpdir}/thresh.hdr check_results thresh.img ;; convertxThresholdBelowToPadding) run convertx --set-padding 0 --thresh-below-to-padding 100 spgr_brain_1.hdr ${tmpdir}/thresh.hdr check_results thresh.img ;; convertxThresholdAboveToPadding) run convertx --set-padding 0 --thresh-above-to-padding 100 spgr_brain_1.hdr ${tmpdir}/thresh.hdr check_results thresh.img ;; convertxScaleToRange) run convertx --scale-to-range 128:192 spgr_brain_1.hdr ${tmpdir}/scaled.hdr check_results scaled.img ;; convertxReplacePadding) run convertx --byte --set-padding 0 --replace-padding 255 spgr_brain_1.hdr ${tmpdir}/replaced.hdr check_results replaced.img ;; convertxReplaceInfNaN) run convertx --replace-inf-nan 255 infinite.nii ${tmpdir}/replaced.hdr check_results replaced.img ;; dbtool_AddImages) run dbtool add_images ${tmpdir}/db.sqlite image1 image2 ${sqlite} ${tmpdir}/db.sqlite "SELECT * FROM images ORDER BY path ASC" > ${tmpdir}/images check_results images ;; dbtool_AddImages2) run dbtool add_images ${tmpdir}/db.sqlite image1 image2 image3 image3 ${sqlite} ${tmpdir}/db.sqlite "SELECT * FROM images ORDER BY path ASC" > ${tmpdir}/images check_results images ;; dbtool_ListSpace) run_eval "${BINDIR}/dbtool list_space images.sqlite image1c > ${tmpdir}/list.txt" check_results list.txt ;; dbtool_GetXform1) run_eval "${BINDIR}/dbtool get_xform imagesSingleXform.sqlite image1.nii image2.nii > ${tmpdir}/xform.txt" check_results xform.txt ;; dbtool_GetXform2) run_eval "${BINDIR}/dbtool get_xform imagesSingleXform.sqlite image2.nii image1.nii > ${tmpdir}/xform.txt" check_results xform.txt ;; dbtool_GetXform3) run_eval "${BINDIR}/dbtool get_xform imagesMultiXforms.sqlite image1.nii image2.nii > ${tmpdir}/xform.txt" check_results xform.txt ;; dbtool_GetXform4) run_eval "${BINDIR}/dbtool get_xform imagesMultiXforms.sqlite image2.nii image1.nii > ${tmpdir}/xform.txt" check_results xform.txt ;; dbtool_GetXform5) run_eval "${BINDIR}/dbtool get_xform --all imagesMultiXforms.sqlite image1.nii image2.nii > ${tmpdir}/xform.txt" check_results xform.txt ;; dbtool_GetXform6) run_eval "${BINDIR}/dbtool get_xform --all imagesMultiXforms.sqlite image2.nii image1.nii > ${tmpdir}/xform.txt" check_results xform.txt ;; dcm2image) run dcm2image -O ${tmpdir}/00%n.hdr dcm/ check_results 001.hdr 001.img 002.hdr 002.img 003.hdr 003.img ;; dcm2imageSubs) run dcm2image -O ${tmpdir}/%D_%R_%E%N.nii dcm/ check_results 3-plane-localizer_5.104_1.52-1.nii 3-plane-localizer_5.104_1.52-2.nii 3-plane-localizer_5.104_1.52-3.nii ;; dcm2imageZ) run dcm2image -O ${tmpdir}/00%n.nii dcmz/ check_results 001.nii 002.nii 003.nii ;; dcm2imageNrrd) run dcm2image -O ${tmpdir}/00%n.nhdr dcm/ check_results 001.nhdr 001.raw 002.nhdr 002.raw 003.nhdr 003.raw ;; dcm2imageEmbedPatientNameAnalyze) run dcm2image --embed PatientName -O ${tmpdir}/00%n.hdr dcm/ check_results 001.hdr 002.hdr 003.hdr ;; dcm2imageEmbedPatientNameNifti) run dcm2image --embed PatientName -O ${tmpdir}/00%n.nii dcm/ check_results 001.nii 002.nii 003.nii ;; dcm2imageEmbedSeriesDescriptionNifti) run dcm2image --embed SeriesDescription -O ${tmpdir}/00%n.nii dcm/ check_results 001.nii 002.nii 003.nii ;; dcm2imageEmbedStudyIDDateNifti) run dcm2image --embed StudyID_StudyDate -O ${tmpdir}/00%n.nii dcm/ check_results 001.nii ;; dcm2imageEmbedPatientNameNrrd) run dcm2image --embed PatientName -O ${tmpdir}/00%n.nhdr dcm/ check_results 001.nhdr 002.nhdr 003.nhdr ;; dcm2imageMosaic) run dcm2image -x --include-identifiers -O ${tmpdir}/00.nii mr-mosaic/ check_results 00.nii 00.nii.xml ;; dcm2imageMosaicPACS) run dcm2image -x --include-identifiers -O ${tmpdir}/00.nii mr-mosaic-pacs/ check_results 00.nii 00.nii.xml ;; dcm2imageDiffusionGEXML) run dcm2image -x --tolerance 1e-4 --include-identifiers -O ${tmpdir}/%n.nii mr-dwi-ge/ check_results 1.nii 1.nii.xml 2.nii 2.nii.xml ;; dcm2imageDiffusionSiemensXML) run dcm2image -x --include-identifiers -O ${tmpdir}/%n.nii mr-dwi-siemens/ check_results 1.nii 1.nii.xml 2.nii 2.nii.xml 3.nii 3.nii.xml 4.nii 4.nii.xml ;; dcm2imageMosaicAnon) run dcm2image -x -O ${tmpdir}/00.nii mr-mosaic/ check_results 00.nii 00.nii.xml ;; dcm2imageMosaicPACSAnon) run dcm2image -x -O ${tmpdir}/00.nii mr-mosaic-pacs/ check_results 00.nii 00.nii.xml ;; dcm2imageDiffusionGEXMLAnon) run dcm2image -x --tolerance 1e-4 -O ${tmpdir}/%n.nii mr-dwi-ge/ check_results 1.nii 1.nii.xml 2.nii 2.nii.xml ;; dcm2imageDiffusionSiemensXMLAnon) run dcm2image -x -O ${tmpdir}/%n.nii mr-dwi-siemens/ check_results 1.nii 1.nii.xml 2.nii 2.nii.xml 3.nii 3.nii.xml 4.nii 4.nii.xml ;; dcm2imageExclude) run dcm2image --exclude ImageOrientationPatient=1\\-0\\0\\-0\\1\\0 -O ${tmpdir}/image%n.nii dcm/ check_results image1.nii image2.nii ;; dcm2imageInclude) run dcm2image --filter ImageOrientationPatient=1\\-0\\0\\-0\\1\\0 -O ${tmpdir}/image%n.nii dcm/ check_results image.nii ;; dcm2imageSubsDirName) run dcm2image -x --tolerance 1e-4 --include-identifiers -O ${tmpdir}/%1/%0.nii mr-dwi-ge/ mr-dwi-siemens/ check_results mr-dwi-ge/I0150.dcm.nii mr-dwi-ge/I0150.dcm.nii.xml mr-dwi-ge/I0280.dcm.nii mr-dwi-ge/I0280.dcm.nii.xml check_results mr-dwi-siemens/I0001.dcm.nii mr-dwi-siemens/I0001.dcm.nii.xml mr-dwi-siemens/I0002.dcm.nii mr-dwi-siemens/I0002.dcm.nii.xml check_results mr-dwi-siemens/I0003.dcm.nii mr-dwi-siemens/I0003.dcm.nii.xml mr-dwi-siemens/I0004.dcm.nii mr-dwi-siemens/I0004.dcm.nii.xml ;; describeEmpty) run_eval "${BINDIR}/describe -m empty.nii > ${tmpdir}/describe.txt" check_results describe.txt ;; describeMountPoints) run_eval "CMTK_MOUNTPOINTS=INPUTS=${DATADIR} ${BINDIR}/describe -m INPUTS/spgr_brain_1.hdr > ${tmpdir}/describe.txt" check_results describe.txt ;; describeMountPointsMulti) run_eval "CMTK_MOUNTPOINTS=DUMMY=/fs/dummy,INPUTS=${DATADIR} ${BINDIR}/describe -m INPUTS/spgr_brain_1.hdr > ${tmpdir}/describe.txt" check_results describe.txt ;; describeMountPointsPrefix) run_eval "CMTK_MOUNTPOINTS=^INPUTS=${DATADIR} ${BINDIR}/describe -m INPUTS/spgr_brain_1.hdr > ${tmpdir}/describe.txt" check_results describe.txt ;; describeMountPointsPrefixInvalid) run_eval "CMTK_MOUNTPOINTS=^NPUTS=INVALID,^INPUTS=${DATADIR} ${BINDIR}/describe -m INPUTS/spgr_brain_1.hdr > ${tmpdir}/describe.txt" check_results describe.txt ;; describeDICOM) run_eval "${BINDIR}/describe -m dcm/I0007.dcm > ${tmpdir}/describe.txt" check_results describe.txt ;; describeDICOMZ) run_eval "${BINDIR}/describe -m dcmz/I0007.dcm > ${tmpdir}/describe.txt" check_results describe.txt ;; describeMosaicDICOM) run_eval "${BINDIR}/describe -m mr-mosaic/fmri.dcm > ${tmpdir}/describe.txt" check_results describe.txt ;; describeMosaicPACSDICOM) run_eval "${BINDIR}/describe -m mr-mosaic-pacs/image.dcm > ${tmpdir}/describe.txt" check_results describe.txt ;; describeVanderbilt) run_eval "${BINDIR}/describe -m vanderbilt/header.ascii > ${tmpdir}/describe.txt" check_results describe.txt ;; describeBZip2a) run_eval "${BINDIR}/describe -m bzip_test.nii > ${tmpdir}/describe.txt" check_results describe.txt ;; describeBZip2b) run_eval "${BINDIR}/describe -m bzip_test.nii.bz2 > ${tmpdir}/describe.txt" check_results describe.txt ;; describeLZMAa) run_eval "${BINDIR}/describe -m lzma_test.nii > ${tmpdir}/describe.txt" check_results describe.txt ;; describeLZMAb) run_eval "${BINDIR}/describe -m lzma_test.nii.lzma > ${tmpdir}/describe.txt" check_results describe.txt ;; describeXZa) run_eval "${BINDIR}/describe -m xz_test.nii > ${tmpdir}/describe.txt" check_results describe.txt ;; describeXZb) run_eval "${BINDIR}/describe -m xz_test.nii.xz > ${tmpdir}/describe.txt" check_results describe.txt ;; describeMR1) run_eval "${BINDIR}/describe -m parc1_bin.hdr > ${tmpdir}/describe.txt" check_results describe.txt ;; describeMR2) run_eval "${BINDIR}/describe -m phantom_ax.hdr phantom_co.hdr phantom_sa.hdr > ${tmpdir}/describe.txt" check_results describe.txt ;; describeMR3) run_eval "${BINDIR}/describe -m header_only.hdr > ${tmpdir}/describe.txt" check_results describe.txt ;; describeMR4) run_eval "${BINDIR}/describe -m phantom_ax.nii phantom_co.nii phantom_sa.nii > ${tmpdir}/describe.txt" check_results describe.txt ;; describeHuman) run_eval "${BINDIR}/describe --read-ras phantom_ax.nii phantom_co.nii phantom_sa.nii > ${tmpdir}/describe.txt" check_results describe.txt ;; describeMRBiorad) run_eval "${BINDIR}/describe -m bioradvol.PIC > ${tmpdir}/describe.txt" check_results describe.txt ;; describeMRBioradGz) run_eval "${BINDIR}/describe -m gz_bioradvol.PIC.gz > ${tmpdir}/describe.txt" check_results describe.txt ;; describeMRNrrd1) run_eval "${BINDIR}/describe -m phantom_ax.nhdr phantom_co.nhdr phantom_sa.nhdr > ${tmpdir}/describe.txt" check_results describe.txt ;; describeMRNrrd2) run_eval "${BINDIR}/describe -m split_ax_0.nhdr split_ax_1.nhdr split_ax_2.nhdr > ${tmpdir}/describe.txt" check_results describe.txt ;; describeNrrdNoOrigin) run_eval "${BINDIR}/describe -m phantom_ax_noOrigin.nhdr > ${tmpdir}/describe.txt" check_results describe.txt ;; describeNiftiDetached348) run_eval "${BINDIR}/describe -m spgr_brain_nifti_hdr348.hdr > ${tmpdir}/describe.txt" check_results describe.txt ;; describeEmbedAnalyze) run_eval "${BINDIR}/describe -m embed_patient.hdr > ${tmpdir}/describe.txt" check_results describe.txt ;; describeEmbedNifti) run_eval "${BINDIR}/describe -m embed_patient.nii > ${tmpdir}/describe.txt" check_results describe.txt ;; describeEmbedNrrd) run_eval "${BINDIR}/describe -m embed_patient.nrrd > ${tmpdir}/describe.txt" check_results describe.txt ;; describeXform) run_eval "${BINDIR}/describe spgr_brain_12_warp.list vol001_mr_t0t1.list > ${tmpdir}/describe.txt" check_results describe.txt ;; describeXformMachine) run_eval "${BINDIR}/describe -m spgr_brain_12_warp.list vol001_mr_t0t1.list > ${tmpdir}/describe.txt" check_results describe.txt ;; destripeDefault) run destripe -y stripes_crop_byte.nii ${tmpdir}/destripe.nii check_results destripe.nii ;; destripeKernelFloat) run destripe -y --kernel-fwhm 5 --kernel-radius 2 --write-float stripes_crop_byte.nii ${tmpdir}/destripe.nii check_results destripe.nii ;; detect_adni_phantomDefault) run detect_adni_phantom --write-labels ${tmpdir}/labels.nii --write-rigid ${tmpdir}/rigid.xform --write-affine ${tmpdir}/affine.xform spgr_magphan.nii ${tmpdir}/magphan.xml check_results labels.nii rigid.xform affine.xform magphan.xml ;; detect_adni_phantomRefineXform) run detect_adni_phantom --tolerant --refine-xform --write-rigid ${tmpdir}/rigid.xform --write-affine ${tmpdir}/affine.xform spgr_magphan_lowcontrast.nii ${tmpdir}/magphan.xml check_results rigid.xform affine.xform magphan.xml ;; detect_adni_phantomRefineOutliers) run detect_adni_phantom --tolerant --refine-outliers --write-rigid ${tmpdir}/rigid.xform --write-affine ${tmpdir}/affine.xform spgr_magphan_lowcontrast.nii ${tmpdir}/magphan.xml check_results rigid.xform affine.xform magphan.xml ;; detect_adni_phantomExcludeOutliers) run detect_adni_phantom --tolerant --exclude-outliers --write-rigid ${tmpdir}/rigid.xform --write-affine ${tmpdir}/affine.xform spgr_magphan_lowcontrast.nii ${tmpdir}/magphan.xml check_results rigid.xform affine.xform magphan.xml ;; detect_adni_phantomErodeNonStd) run detect_adni_phantom --erode-snr 20 --erode-cnr 2 spgr_magphan.nii ${tmpdir}/magphan.xml check_results magphan.xml ;; detect_adni_phantomBadFOV) run_expect_fail detect_adni_phantom spgr_magphan_badFOV.nii ${tmpdir}/magphan.xml ;; detect_adni_phantomTolerantBadFOV) run detect_adni_phantom --tolerant --write-labels ${tmpdir}/labels.nii spgr_magphan_badFOV.nii ${tmpdir}/magphan.xml check_results magphan.xml labels.nii ;; detect_adni_phantomMissingSphere) run detect_adni_phantom --write-labels ${tmpdir}/labels.nii --write-rigid ${tmpdir}/rigid.xform --write-affine ${tmpdir}/affine.xform spgr_magphan_missingSphere.nii ${tmpdir}/magphan.xml check_results labels.nii rigid.xform affine.xform magphan.xml ;; detect_adni_phantomBrokenSNR) run detect_adni_phantom --write-labels ${tmpdir}/labels.nii --write-rigid ${tmpdir}/rigid.xform --write-affine ${tmpdir}/affine.xform spgr_magphan_brokenSNR.nii ${tmpdir}/magphan.xml check_results labels.nii rigid.xform affine.xform magphan.xml ;; detect_spheres_matched_filter) run detect_spheres_matched_filter --radius 15 --filter-margin 2 adni_phantom2mm.nii ${tmpdir}/filtered.nii run convertx --float --downsample-select 4,4,4 ${tmpdir}/filtered.nii ${tmpdir}/filtered.nii check_images filtered.nii ;; detect_spheres_matched_filterNormalized) run detect_spheres_matched_filter --normalized --radius 15 --filter-margin 1 adni_phantom2mm.nii ${tmpdir}/filtered.nii run convertx --float --downsample-select 4,4,4 ${tmpdir}/filtered.nii ${tmpdir}/filtered.nii check_images filtered.nii ;; dof2mat) run_eval "${BINDIR}/dof2mat parc1_parc2_9dof.xform > ${tmpdir}/matrix" check_results matrix ;; dof2matTranspose) run_eval "${BINDIR}/dof2mat --transpose parc1_parc2_9dof.xform > ${tmpdir}/matrix" check_results matrix ;; dof2matLegacy) run_eval "${BINDIR}/dof2mat vol001_mr_t0t1.list > ${tmpdir}/matrix" check_results matrix ;; dof2matLegacyFwd) run_eval "${BINDIR}/dof2mat vol001_mr_t0t1_fwd.list > ${tmpdir}/matrix" check_results matrix ;; dof2matFixed) run_eval "${BINDIR}/dof2mat vol001_mr_t0t1_fwd24.list > ${tmpdir}/matrix" check_results matrix ;; dof2matSuffix) run_eval "${BINDIR}/dof2mat xform.ZYX > ${tmpdir}/matrix" check_results matrix ;; dof2mat3x3) run_eval "${BINDIR}/dof2mat --matrix3x3 vol001_mr_t0t1_fwd24.list > ${tmpdir}/matrix" check_results matrix ;; stream_pixels) run_eval "${BINDIR}/stream_pixels jacobian-01.nii jacobian-02.nii > ${tmpdir}/stream.raw" check_results stream.raw ;; stream_pixelsConvert) run_eval "${BINDIR}/stream_pixels --convert int jacobian-01.nii jacobian-02.nii > ${tmpdir}/stream.raw" check_results stream.raw ;; stream_pixelsReorient) run_eval "${BINDIR}/stream_pixels --reorient RIP jacobian-01.nii jacobian-02.nii > ${tmpdir}/stream.raw" check_results stream.raw ;; stream_pixelsEndian) run_eval "${BINDIR}/stream_pixels --change-endian jacobian-01.nii jacobian-02.nii > ${tmpdir}/stream.raw" check_results stream.raw ;; epiunwarp) export CMTK_NUM_THREADS=1 run epiunwarp --no-init-shift-com --write-jacobian-fwd ${tmpdir}/jacobian.nii --folding-constraint-weight 0 --smooth-sigma-max 16 --smooth-sigma-min 0 --smooth-sigma-diff 1 --iterations 5 --smoothness-constraint-weight 1e4 --phase-encode-ap dwi_fwd.nii dwi_rev.nii ${tmpdir}/c1.nii ${tmpdir}/c2.nii ${tmpdir}/df.nrrd ${tmpdir}/df2.nrrd unset CMTK_NUM_THREADS check_images c1.nii c2.nii jacobian.nii check_results df.nrrd df2.nrrd ;; epiunwarpInitShift) export CMTK_NUM_THREADS=1 run epiunwarp --write-jacobian-fwd ${tmpdir}/jacobian.nii --folding-constraint-weight 0 --smooth-sigma-max 16 --smooth-sigma-min 0 --smooth-sigma-diff 1 --iterations 5 --smoothness-constraint-weight 1e4 --phase-encode-ap dwi_fwd.nii dwi_rev.nii ${tmpdir}/c1.nii ${tmpdir}/c2.nii ${tmpdir}/df.nrrd unset CMTK_NUM_THREADS check_images c1.nii c2.nii jacobian.nii check_results df.nrrd ;; fib2image) run_eval "${BINDIR}/fib2image -o ${tmpdir}/fibers.nii fiber_grid.hdr < fibers.fib" check_images fibers.nii ;; fibxform) run_eval "${BINDIR}/fibxform crop-x30.xform < fibers.fib > ${tmpdir}/output.fib" check_results output.fib ;; fibxformSourceTarget) run_eval "${BINDIR}/fibxform --source-image fiber_grid.hdr --target-image fiber_grid.hdr crop-x30.xform < fibers.fib > ${tmpdir}/output.fib" check_results output.fib ;; filterGaussian) run filter --gaussian 1 --radius 2 rat_fse_erly.hdr ${tmpdir}/filter.hdr check_results filter.img ;; filterGaussianSmallKernel) run filter --gaussian 1 --radius 1.1 rat_fse_erly.hdr ${tmpdir}/filter.hdr check_results filter.img ;; filterGaussianNoFilter) run filter --gaussian 1 --radius 0.5 rat_fse_erly.hdr ${tmpdir}/filter.hdr check_results filter.img ;; filterRohlfing) run filter --rohlfing --intensity-gaussian 1 --gaussian 10 spgr_3t.hdr ${tmpdir}/filter.hdr spgr_3t_mask.hdr check_results filter.img ;; filmFourthOrder) run film --coronal --nmi --fourth-order-error --passes 3 --num-iterations 3 --injection-kernel-radius 2 --injection-kernel-sigma 1 --write-injected-image ${tmpdir}/injected.hdr interleave_thnfse.hdr ${tmpdir}/corrected.hdr check_results corrected.img ;; filmCubic) run film --coronal --nmi --cubic --passes 3 --num-iterations 3 --injection-kernel-radius 2 --injection-kernel-sigma 1 --write-injected-image ${tmpdir}/injected.hdr interleave_thnfse.hdr ${tmpdir}/corrected.hdr check_results corrected.img ;; filmMSDLinearNoTrunc) run film --msd --coronal --linear --no-truncation --passes 3 --num-iterations 3 --injection-kernel-radius 2 --injection-kernel-sigma 1 interleave_thnfse.hdr ${tmpdir}/corrected.hdr check_results corrected.img ;; filmMISincRefSPGR) run film --mi --coronal --cubic --passes 3 --num-iterations 3 --injection-kernel-radius 2 --injection-kernel-sigma 1 --reference-image interleave_3dspgr.hdr interleave_thnfse.hdr ${tmpdir}/corrected.hdr check_results corrected.img ;; fit_affine_xform) run fit_affine_xform -o ${tmpdir}/affine.xform vol001_mr_t0.hdr vol001_mr_t0t1_warp.xform --inverse vol001_mr_t0t1.list check_results affine.xform ;; fit_affine_xformRigid) run fit_affine_xform --rigid -o ${tmpdir}/rigid.xform vol001_mr_t0.hdr vol001_mr_t0t1_warp.xform --inverse vol001_mr_t0t1.list check_results rigid.xform ;; fit_affine_xformReverse) run fit_affine_xform -o ${tmpdir}/affine.xform vol001_mr_t1.hdr --inverse vol001_mr_t0t1.list vol001_mr_t0t1_warp.xform check_results affine.xform ;; fit_affine_xform_landmarksRigid) run fit_affine_xform_landmarks landmarksSource landmarksTargetRigid ${tmpdir}/affine.xform check_results affine.xform ;; fit_affine_xform_landmarksScales) run fit_affine_xform_landmarks landmarksSource landmarksTargetScales ${tmpdir}/affine.xform check_results affine.xform ;; fit_affine_xform_landmarksShear) run fit_affine_xform_landmarks landmarksSource landmarksTargetShear ${tmpdir}/affine.xform check_results affine.xform ;; fit_affine_xform_landmarksRigidRigid) run fit_affine_xform_landmarks --rigid landmarksSource landmarksTargetRigid ${tmpdir}/affine.xform check_results affine.xform ;; fit_affine_xform_landmarksRigidRigidFlip) run fit_affine_xform_landmarks --rigid landmarksSource2 landmarksTarget2 ${tmpdir}/rigid.xform check_results rigid.xform ;; fit_affine_xform_landmarksRigidScales) run fit_affine_xform_landmarks --rigid landmarksSource landmarksTargetScales ${tmpdir}/affine.xform check_results affine.xform ;; fit_affine_xform_landmarksRigidShear) run fit_affine_xform_landmarks --rigid landmarksSource landmarksTargetShear ${tmpdir}/affine.xform check_results affine.xform ;; fit_affine_dfieldDField) run fit_affine_dfield vol001_mr_t0t1_dfield.nrrd ${tmpdir}/affine_fit.xform check_results affine_fit.xform ;; fit_affine_dfieldFFD) run fit_affine_dfield vol001_mr_t0t1_warp.xform ${tmpdir}/affine_fit.xform check_results affine_fit.xform ;; fit_spline_dfieldSpacing) run fit_spline_dfield --final-cp-spacing 40 vol001_mr_t0t1_dfield.nrrd ${tmpdir}/warp.xform check_results warp.xform ;; fit_spline_dfieldSpacingMultiLevel) run fit_spline_dfield --final-cp-spacing 40 --levels 2 vol001_mr_t0t1_dfield.nrrd ${tmpdir}/warp.xform check_results warp.xform ;; fit_spline_dfieldDims) run fit_spline_dfield --final-cp-dims 10,10,5 vol001_mr_t0t1_dfield.nrrd ${tmpdir}/warp.xform check_results warp.xform ;; fit_spline_dfieldDimsRelative) run fit_spline_dfield --relative --final-cp-dims 10,10,5 vol001_mr_t0t1_dfield.nrrd ${tmpdir}/warp.xform check_results warp.xform ;; fit_spline_dfieldDimsMultiLevel) run fit_spline_dfield --final-cp-dims 10,10,5 --levels 2 vol001_mr_t0t1_dfield.nrrd ${tmpdir}/warp.xform check_results warp.xform ;; fit_spline_dfieldDimsMultiLevelSafe) run fit_spline_dfield --final-cp-dims 10,10,5 --levels 400 vol001_mr_t0t1_dfield.nrrd ${tmpdir}/warp.xform check_results warp.xform ;; fit_spline_dfieldDimsWithAffine) run fit_spline_dfield --fit-affine-first --final-cp-dims 10,10,5 vol001_mr_t0t1_dfield.nrrd ${tmpdir}/warp.xform check_results warp.xform ;; fit_spline_xformSpacing) run fit_spline_xform --final-cp-spacing 40 -o ${tmpdir}/warp.xform vol001_mr_t0.hdr vol001_mr_t0t1_warp.xform --inverse vol001_mr_t0t1.list check_results warp.xform ;; fit_spline_xformSpacingReverse) run fit_spline_xform --final-cp-spacing 40 -o ${tmpdir}/warp.xform vol001_mr_t1.hdr --inverse vol001_mr_t0t1.list vol001_mr_t0t1_warp.xform check_results warp.xform ;; fit_spline_xformSpacingMultiLevel) run fit_spline_xform --final-cp-spacing 40 --levels 2 -o ${tmpdir}/warp.xform vol001_mr_t0.hdr vol001_mr_t0t1_warp.xform --inverse vol001_mr_t0t1.list check_results warp.xform ;; fit_spline_xformDims) run fit_spline_xform --final-cp-dims 10,10,5 -o ${tmpdir}/warp.xform vol001_mr_t0.hdr vol001_mr_t0t1_warp.xform --inverse vol001_mr_t0t1.list check_results warp.xform ;; fit_spline_xformDimsMultiLevel) run fit_spline_xform --final-cp-dims 10,10,5 --levels 2 -o ${tmpdir}/warp.xform vol001_mr_t0.hdr vol001_mr_t0t1_warp.xform --inverse vol001_mr_t0t1.list check_results warp.xform ;; geomatchAxSa) run_eval_expect_fail "${BINDIR}/geomatch -v phantom_ax.nii.gz phantom_sa.nii.gz >& ${tmpdir}/stderr" check_results stderr ;; geomatchAxSaRAS) run_eval_expect_fail "${BINDIR}/geomatch -v --no-check-xforms --read-ras phantom_ax.nii.gz phantom_sa.nii.gz >& ${tmpdir}/stderr" check_results stderr ;; geomatchAxSaNoXforms) run_eval "${BINDIR}/geomatch -v --no-check-xforms phantom_ax.nii.gz phantom_sa.nii.gz >& ${tmpdir}/stderr" check_results stderr ;; glmDefault) run_eval "${BINDIR}/glm -v -O ${tmpdir}/model_%s_%02d_%s.hdr jacobian_glm.txt jacobian-%s.nii | fgrep -v ${tmpdir} > ${tmpdir}/model_stdout.txt" check_results model_stdout.txt model_fstat_00_model.img model_tstat_00_age.img model_param_00_age.img check_results model_tstat_01_sex.img model_param_01_sex.img model_tstat_02_CONST.img model_param_02_CONST.img ;; glmNormalize) run_eval "${BINDIR}/glm --normalize -v -O ${tmpdir}/model_%s_%02d_%s.hdr jacobian_glm.txt jacobian-%s.nii | fgrep -v ${tmpdir} > ${tmpdir}/model_stdout.txt" check_results model_stdout.txt model_fstat_00_model.img model_tstat_00_age.img model_param_00_age.img check_results model_tstat_01_sex.img model_param_01_sex.img model_tstat_02_CONST.img model_param_02_CONST.img ;; glmExp) run_eval "${BINDIR}/glm --exp -v -O ${tmpdir}/model_%s_%02d_%s.hdr jacobian_glm.txt jacobian-%s.nii | fgrep -v ${tmpdir} > ${tmpdir}/model_stdout.txt" check_results model_stdout.txt model_fstat_00_model.img model_tstat_00_age.img model_param_00_age.img check_results model_tstat_01_sex.img model_param_01_sex.img model_tstat_02_CONST.img model_param_02_CONST.img ;; glmNoConstant) run_eval "${BINDIR}/glm --exclude-constant -v -O ${tmpdir}/model_%s_%02d_%s.hdr jacobian_glm.txt jacobian-%s.nii | fgrep -v ${tmpdir} > ${tmpdir}/model_stdout.txt" check_results model_stdout.txt model_fstat_00_model.img model_tstat_00_age.img model_param_00_age.img check_results model_tstat_01_sex.img model_param_01_sex.img ;; glmIgnore) run_eval "${BINDIR}/glm --ignore-parameter 1 -v -O ${tmpdir}/model_%s_%02d_%s.hdr jacobian_glm.txt jacobian-%s.nii | fgrep -v ${tmpdir} > ${tmpdir}/model_stdout.txt" check_results model_stdout.txt model_fstat_00_model.img model_tstat_00_age.img model_param_00_age.img check_results model_tstat_02_CONST.img model_param_02_CONST.img ;; glmSelect) run_eval "${BINDIR}/glm --select-parameter age -v -O ${tmpdir}/model_%s_%02d_%s.hdr jacobian_glm.txt jacobian-%s.nii | fgrep -v ${tmpdir} > ${tmpdir}/model_stdout.txt" check_results model_stdout.txt model_fstat_00_model.img check_results model_tstat_00_age.img model_param_00_age.img model_tstat_02_CONST.img model_param_02_CONST.img ;; glmCrop) run_eval "${BINDIR}/glm --crop 10,10,0,20,20,2 -v -O ${tmpdir}/model_%s_%02d_%s.hdr jacobian_glm.txt jacobian-%s.nii | fgrep -v ${tmpdir} > ${tmpdir}/model_stdout.txt" check_results model_stdout.txt model_fstat_00_model.img model_tstat_00_age.img model_param_00_age.img check_results model_tstat_01_sex.img model_param_01_sex.img model_tstat_02_CONST.img model_param_02_CONST.img ;; gmmDefault) run gmm --mask gmm/mask.nii gmm/spgr.nii ${tmpdir}/gmm.nii gmm/prior1.nii gmm/prior2.nii gmm/prior3.nii check_results gmm.nii ;; gmmAlternative) run gmm --classes 2 --iterations 5 --priors-init-only --prior-epsilon 0.05 --probability-maps gmm/spgr.nii ${tmpdir}/gmm.nii gmm/prior1.nii gmm/prior2.nii check_results gmm.nii check_images gmm_prob1.nii gmm_prob2.nii ;; gregxformFordwardBackward) run_eval "cat vol001_t0_points.xyz | ${BINDIR}/gregxform -f vol001_mr_t0t1_warp.xform | ${BINDIR}/gregxform vol001_mr_t0t1_warp.xform > ${tmpdir}/vol001_t0_points.xyz" check_results vol001_t0_points.xyz ;; gregxformAffine) run_eval "cat vol001_t0_points.xyz | ${BINDIR}/gregxform -f vol001_mr_t0t1.list > ${tmpdir}/vol001_t0_points.xyz" check_results vol001_t0_points.xyz ;; gregxformAffineFromWarp) run_eval "cat vol001_t0_points.xyz | ${BINDIR}/gregxform --affine -f vol001_mr_t0t1_warp.xform > ${tmpdir}/vol001_t0_points.xyz" check_results vol001_t0_points.xyz ;; gregxformAffineFromWarpFwdBwd) run_eval "cat vol001_t0_points.xyz | ${BINDIR}/gregxform --affine -f vol001_mr_t0t1_warp.xform | ${BINDIR}/gregxform --affine vol001_mr_t0t1_warp.xform > ${tmpdir}/vol001_t0_points.xyz" check_results vol001_t0_points.xyz ;; groupwise_affineFromInit) run groupwise_affine -v --force-background 0 -O ${tmpdir} --dofs 6 --dofs 9 -e 4 -a 0.25 --downsample-from 2 --downsample-to 1 --sampling-density 1.0 --zero-sum groupwise_init_brain123.xforms check_results groupwise.xforms average.nii ;; groupwise_affineMatchHistograms) export CMTK_NUM_THREADS=1 run groupwise_affine -v --match-histograms --force-background 0 -O ${tmpdir} --dofs 6 --dofs 9 -e 4 -a 0.25 --downsample-from 2 --downsample-to 1 --sampling-density 1.0 --zero-sum groupwise_init_brain123.xforms unset CMTK_NUM_THREADS check_results groupwise.xforms average.nii ;; groupwise_affineFromInitSampling) run groupwise_affine -v --force-background 0 -O ${tmpdir} --dofs 6 --dofs 9 -e 4 -a 0.25 --downsample-to 1 --sampling-density 0.5 --zero-sum groupwise_init_brain123.xforms ## no baseline; this mode uses probabilistic sampling ;; groupwise_affineBackground) run groupwise_affine -v --force-background 0 -O ${tmpdir} --template spgr_brain_1.hdr --dofs 6 --dofs 9 -e 2 -a 0.25 --downsample-from 2 --downsample-to 1 --sampling-density 1.0 --zero-sum spgr_brain_1.hdr spgr_brain_2.hdr spgr_brain_3.hdr check_results groupwise.xforms average.nii ;; groupwise_affineUseTemplate) export CMTK_NUM_THREADS=1 run groupwise_affine -v --force-background 0 -O ${tmpdir} --template spgr_brain_1.hdr --dofs 6 --dofs 9 -e 2 -a 0.25 --downsample-from 2 --downsample-to 1 --sampling-density 1.0 --template-with-data spgr_brain_1.hdr spgr_brain_2.hdr spgr_brain_3.hdr unset CMTK_NUM_THREADS check_results groupwise.xforms average.nii ;; groupwise_affineZeroSumSmooth) export CMTK_NUM_THREADS=1 run groupwise_affine -v --smooth 0.5 --downsample-to 0 -O ${tmpdir} --template spgr_brain_1.hdr --dofs 6 --dofs 9 -e 2 -a 0.25 --downsample-from 2 --sampling-density 1.0 --zero-sum spgr_brain_1.hdr spgr_brain_2.hdr spgr_brain_3.hdr unset CMTK_NUM_THREADS check_results groupwise.xforms average.nii ;; groupwise_affineRMIFromInit) run groupwise_affine --rmi --force-background 0 -O ${tmpdir} --dofs 6 --dofs 9 -e 4 -a 0.25 --downsample-from 2 --downsample-to 1 --sampling-density 1.0 --zero-sum groupwise_init_brain123.xforms check_results groupwise.xforms average.nii ;; groupwise_affineRMIFromInitDeltaF) run groupwise_affine --rmi --force-background 0 -O ${tmpdir} --dofs 6,9 -e 4 --delta-f-threshold 0.1 -a 0.25 --downsample-from 2 --downsample-to 1 --sampling-density 1.0 --zero-sum groupwise_init_brain123.xforms check_results groupwise.xforms average.nii ;; groupwise_affineRMIFromInitSampling) run groupwise_affine --rmi --force-background 0 -O ${tmpdir} --dofs 6 --dofs 9 -e 4 -a 0.25 --downsample-to 1 --sampling-density 0.5 --zero-sum groupwise_init_brain123.xforms ## no baseline; this mode uses probabilistic sampling ;; groupwise_affineRMIBackground) run groupwise_affine --rmi --force-background 0 -O ${tmpdir} --template spgr_brain_1.hdr --dofs 6 --dofs 9 -e 2 -a 0.25 --downsample-from 2 --downsample-to 1 --sampling-density 1.0 --zero-sum spgr_brain_1.hdr spgr_brain_2.hdr spgr_brain_3.hdr check_results groupwise.xforms average.nii ;; groupwise_affineRMIZeroSumSmooth) run groupwise_affine --rmi --smooth 0.5 --downsample-to 0 -O ${tmpdir} --template spgr_brain_1.hdr --dofs 6 --dofs 9 -e 2 -a 0.25 --downsample-from 2 --sampling-density 1.0 --zero-sum spgr_brain_1.hdr spgr_brain_2.hdr spgr_brain_3.hdr check_results groupwise.xforms average.nii ;; groupwise_warpFromInit) run groupwise_warp -v --force-background 0 -O ${tmpdir} --grid-spacing 200 --refine-grid 1 -e 2 -a 1 --downsample-from 2 --downsample-to 1 groupwise_init_brain123.xforms check_results groupwise.xforms average.nii ;; groupwise_warpFromInitProtect) run groupwise_warp -v --force-background 0 -vv --disable-cp-mask box_corner.nii -O ${tmpdir} --grid-spacing 200 --refine-grid 1 -e 2 -a 1 --downsample-from 2 --downsample-to 1 groupwise_init_brain123.xforms check_results groupwise.xforms ;; groupwise_warpFitFromInit) run groupwise_warp -v --force-background 0 -O ${tmpdir} --grid-spacing 200 --grid-spacing-fit --refine-grid 1 -e 4 -a 1 --downsample-from 2 --downsample-to 1 groupwise_init_brain123.xforms check_results groupwise.xforms average.nii ;; groupwise_warpUseTemplate) run groupwise_warp -v --force-background 0 -O ${tmpdir} --grid-spacing 200 --refine-grid 1 -e 2 -a 1 --downsample-from 2 --downsample-to 2 --template-with-data spgr_brain_1.hdr groupwise_init_brain123.xforms check_results groupwise.xforms average.nii ;; groupwise_warpUseTemplateMatchHistograms) export CMTK_NUM_THREADS=1 run groupwise_warp -v --match-histograms --force-background 0 -O ${tmpdir} --grid-spacing 200 --refine-grid 1 -e 2 -a 1 --downsample-from 2 --downsample-to 2 --template-with-data spgr_brain_1.hdr groupwise_init_brain123.xforms unset CMTK_NUM_THREADS check_results groupwise.xforms average.nii ;; groupwise_warpMatchHistograms) export CMTK_NUM_THREADS=1 run groupwise_warp -v --match-histograms --force-background 0 -O ${tmpdir} --grid-spacing 200 --refine-grid 1 -e 2 -a 1 --downsample-from 2 --downsample-to 2 --template spgr_brain_1.hdr groupwise_init_brain123.xforms unset CMTK_NUM_THREADS check_results groupwise.xforms average.nii ;; groupwise_warpFromInitZeroSum) run groupwise_warp -v --zero-sum --force-background 0 -O ${tmpdir} --grid-spacing 200 --refine-grid 1 -e 2 -a 1 --downsample-from 2 --downsample-to 1 groupwise_init_brain123.xforms check_results groupwise.xforms average.nii ;; groupwise_warpFromInitNoBG) run groupwise_warp -v -O ${tmpdir} --grid-spacing 200 --refine-grid 1 -e 2 -a 1 --downsample-from 2 --downsample-to 1 groupwise_init_brain123.xforms check_results groupwise.xforms average.nii ;; groupwise_warpUseTemplateNoBG) run groupwise_warp -v -O ${tmpdir} --grid-spacing 200 --refine-grid 1 -e 2 -a 1 --downsample-from 2 --downsample-to 2 --template-with-data spgr_brain_1.hdr groupwise_init_brain123.xforms check_results groupwise.xforms average.nii ;; groupwise_warpRMIFromInit) run groupwise_warp --rmi -v --force-background 0 -O ${tmpdir} --grid-spacing 200 --refine-grid 1 -e 2 -a 1 --downsample-from 2 --downsample-to 1 groupwise_init_brain123.xforms check_results groupwise.xforms average.nii ;; groupwise_warpRMIFitFromInit) run groupwise_warp --rmi -v --force-background 0 -O ${tmpdir} --grid-spacing 200 --grid-spacing-fit --refine-grid 1 -e 4 -a 1 --downsample-from 2 --downsample-to 1 groupwise_init_brain123.xforms check_results groupwise.xforms average.nii ;; groupwise_warpRMIFromInitZeroSum) run groupwise_warp --rmi -v --zero-sum --force-background 0 -O ${tmpdir} --grid-spacing 200 --refine-grid 1 -e 2 -a 1 --downsample-from 2 --downsample-to 1 groupwise_init_brain123.xforms check_results groupwise.xforms average.nii ;; groupwise_initCentersOfMass) run groupwise_init -O ${tmpdir} --align-centers-of-mass spgr_brain_1.hdr spgr_brain_2.hdr spgr_brain_3.hdr check_results groupwise.xforms # cannot compare separate xforms due to local file system path in the file check_results average.nii ;; groupwise_initCenterFOV) run groupwise_init -O ${tmpdir} --center-template spgr_brain_1.hdr spgr_brain_2.hdr spgr_brain_3.hdr check_results groupwise.xforms # cannot compare separate xforms due to local file system path in the file check_results average.nii ;; groupwise_initCentersOfMassScale) run groupwise_init -O ${tmpdir} --align-centers-of-mass --init-scales spgr_brain_1.hdr spgr_brain_2.hdr spgr_brain_3.hdr check_results groupwise.xforms # cannot compare separate xforms due to local file system path in the file check_results average.nii ;; groupwise_initCentersOfMassTemplate) run groupwise_init -O ${tmpdir} --template spgr_brain_1.hdr --align-centers-of-mass spgr_brain_1.hdr spgr_brain_2.hdr spgr_brain_3.hdr check_results groupwise.xforms check_results pairs/target-000.list/registration pairs/target-001.list/registration pairs/target-002.list/registration check_results average.nii ;; hausdorffBinary12) run_eval "${BINDIR}/hausdorff parc1_bin.hdr parc2_bin.hdr > ${tmpdir}/hd.txt" check_results hd.txt ;; hausdorffBinary21) run_eval "${BINDIR}/hausdorff parc2_bin.hdr parc1_bin.hdr > ${tmpdir}/hd.txt" check_results hd.txt ;; hausdorffBinaryForced) run_eval "${BINDIR}/hausdorff parc1.hdr parc2.hdr > ${tmpdir}/hd.txt" check_results hd.txt ;; histogram) run histogram -o ${tmpdir}/histogram spgr_3t.hdr check_results histogram ;; histogramNorm) run histogram --normalize -o ${tmpdir}/histogram spgr_3t.hdr check_results histogram ;; histogramBinsMinMax) run histogram --min 300 --max 3000 --nbins 16 -o ${tmpdir}/histogram spgr_3t.hdr check_results histogram ;; histogramBinsMinMaxTrunc) run histogram --truncate --min 300 --max 3000 --nbins 16 -o ${tmpdir}/histogram spgr_3t.hdr check_results histogram ;; histogramMask) run histogram --mask spgr_3t_mask.hdr -o ${tmpdir}/histogram spgr_3t.hdr check_results histogram ;; imagemathAbsPop) run imagemath --in pat001_pet.hdr pat001_mr_T1.hdr --pop --abs --out ${tmpdir}/abs.nii check_results abs.nii ;; imagemathSqrSqrt) run imagemath --in pat001_pet.hdr --sqr --sqrt --out ${tmpdir}/abs.nii check_results abs.nii ;; imagemathLogExp) run imagemath --in spgr_brain_1.hdr --log --exp --out ${tmpdir}/logexp.nii check_results logexp.nii ;; imagemathDupAddMulDiv) run imagemath --in spgr_brain_1.hdr --dup --in spgr_brain_2.hdr spgr_brain_3.hdr --add --mul --div --out ${tmpdir}/damd.nii check_results damd.nii ;; imagemathThreshAboveBelow) run imagemath --in pat001_pet.hdr --thresh-above 1000 --thresh-below 0 --out ${tmpdir}/thresh.nii check_results thresh.nii ;; imagemathAverage) run imagemath --in parc1_bin.hdr parc2_bin.hdr parc3_bin.hdr --average --out ${tmpdir}/average.hdr check_results average.img ;; imagemathVariance) run imagemath --in spgr_brain_1.hdr spgr_brain_2.hdr spgr_brain_3.hdr --variance --out ${tmpdir}/variance.nii check_results variance.nii ;; imagemathMaskAverage) run imagemath --in spgr_3t.hdr spgr_3t_mask.hdr --mask-average --out ${tmpdir}/average.hdr check_results average.img ;; imagemathXor) run imagemath --in parc1.hdr --scalar-xor 1 --out ${tmpdir}/xor.hdr check_results xor.img ;; imagemathDupFillMaxValue) run imagemath --in parc1.hdr --dup --fill 1 --max-value --out ${tmpdir}/result.hdr check_results result.img ;; imagemathAnd) run imagemath --in parc1.hdr --scalar-and 1 --out ${tmpdir}/and.hdr check_results and.img ;; imagemathContractLabels) run imagemath --in parc1.hdr parc2.hdr parc3.hdr --contract-labels --out ${tmpdir}/contract.hdr check_results contract.img ;; imagemathMaxIndex) run imagemath --in parc1.hdr parc2.hdr parc3.hdr --max-index --out ${tmpdir}/maxidx.nii check_results maxidx.nii ;; imagemathMaxValue) run imagemath --in parc1.hdr parc2.hdr parc3.hdr --max-value --out ${tmpdir}/maxval.nii check_results maxval.nii ;; imagemathVote) run imagemath --in parc1_bin.hdr parc2_bin.hdr parc3_bin.hdr --vote --out ${tmpdir}/vote.hdr check_results vote.img ;; imagemathProduct) run imagemath --in parc1_bin.hdr parc2_bin.hdr parc3_bin.hdr --product --out ${tmpdir}/prod.nii check_results prod.nii ;; imagemathStackEntropyLabels) run imagemath --in parc1_bin.hdr parc2_bin.hdr parc3_bin.hdr --stack-entropy-labels --out ${tmpdir}/entropy.hdr check_results entropy.img ;; imagemathSTAPLE) run imagemath --in parc1_bin.hdr parc2_bin.hdr parc3_bin.hdr --staple 10 --out ${tmpdir}/staple.hdr check_results staple.img ;; imagemathMultiClassSTAPLE) run imagemath --in parc1.hdr parc2.hdr parc3.hdr --mstaple 2 --out ${tmpdir}/mstaple.hdr check_results mstaple.img ;; imagemathMultiClassDisputedSTAPLE) run imagemath --in parc1.hdr parc2.hdr parc3.hdr --mstaple-disputed 2 --out ${tmpdir}/mstaple.hdr check_results mstaple.img ;; imagemathCombinePCA) run imagemath --in rat_fse_erly.hdr rat_fse_late.hdr --combine-pca --trunc --out ${tmpdir}/combine_pca.hdr check_results combine_pca.img ;; imagemathT2) run imagemath --float --in rat_fse_erly.hdr --log --in rat_fse_late.hdr --log --scalar-mul -1 --add --one-over --out ${tmpdir}/t2.hdr check_results t2.img ;; imagemathAtan2) run imagemath --float --in rat_fse_erly.hdr rat_fse_late.hdr --atan2 --out ${tmpdir}/atan.nii check_results atan.nii ;; imagemathLogOddsAdd) run imagemath --float --in pbmap_wm_2.nii --logit --in pbmap_wm_1.nii --logit --average --logistic --out ${tmpdir}/logodds_add.hdr check_results logodds_add.img ;; imagemathLogOddsAdd2) run imagemath --float --in pbmap_wm_2.nii pbmap_wm_1.nii --all --logit --average --all --logistic --out ${tmpdir}/logodds_add.hdr check_results logodds_add.img ;; imagemathMatchMeanSDev) run imagemath --in spgr_brain_1.hdr spgr_brain_2.hdr --match-mean-sdev --out ${tmpdir}/match.nii check_results match.nii ;; imagemathMatchMeanSDev3) run imagemath --in spgr_brain_1.hdr spgr_brain_2.hdr spgr_brain_3.hdr --match-mean-sdev3 --out ${tmpdir}/match.nii check_results match.nii ;; imagemathMatchHistograms) run imagemath --in spgr_brain_1.hdr spgr_brain_2.hdr --match-histograms --out ${tmpdir}/match_histograms.hdr check_results match_histograms.img ;; imagemathMatchHistogramsPadding) run imagemath --set-padding-value 0 --in spgr_brain_1.hdr spgr_brain_2.hdr --match-histograms --out ${tmpdir}/match_histograms.hdr check_results match_histograms.img ;; imagemathMatchHistogramsPadding2) run imagemath --in spgr_brain_1.hdr spgr_brain_2.hdr --set-padding-value 0 --match-histograms --out ${tmpdir}/match_histograms.hdr check_results match_histograms.img ;; imagemathMatchHistogramsPaddingUnset) run imagemath --set-padding-value 0 --in spgr_brain_1.hdr --unset-padding --in spgr_brain_2.hdr --match-histograms --out ${tmpdir}/match_histograms.hdr check_results match_histograms.img ;; interleaved_bad_slicesDefault) run interleaved_bad_slices rsfMRI-motion/*.nii* > ${tmpdir}/badSlices.txt check_results badSlices.txt ;; interleaved_bad_slicesStdDev1) run interleaved_bad_slices --stdev-thresh 1 rsfMRI-motion/*.nii* > ${tmpdir}/badSlices.txt check_results badSlices.txt ;; interleaved_bad_slicesRMS) run interleaved_bad_slices --rms rsfMRI-motion/*.nii* > ${tmpdir}/badSlices.txt check_results badSlices.txt ;; interleaved_bad_slicesBulk) run interleaved_bad_slices --bad-slices-thresh 1 rsfMRI-motion/*.nii* > ${tmpdir}/badSlices.txt check_results badSlices.txt ;; jidbBoxFourthOrder) run jidb --coronal --nmi --psf box --fourth-order-error --passes 3 --num-iterations 3 --injection-kernel-radius 2 --injection-kernel-sigma 1 --write-injected-image ${tmpdir}/injected.hdr interleave_thnfse.hdr ${tmpdir}/corrected.hdr check_results corrected.img ;; jidbGaussian) run jidb --coronal --nmi --psf gaussian --passes 3 --num-iterations 3 --injection-kernel-radius 2 --injection-kernel-sigma 1 --write-injected-image ${tmpdir}/injected.hdr interleave_thnfse.hdr ${tmpdir}/corrected.hdr check_results corrected.img ;; jidbGaussianScale) run jidb --psf gaussian --nmi --psf-scale 0.5 --no-truncation --passes 3 --num-iterations 3 --injection-kernel-radius 2 --injection-kernel-sigma 1 interleave_thnfse.hdr ${tmpdir}/corrected.hdr check_results corrected.img ;; jidbMIRefSPGR) run jidb --mi --passes 3 --num-iterations 3 --injection-kernel-radius 2 --injection-kernel-sigma 1 --reference-image interleave_3dspgr.hdr interleave_thnfse.hdr ${tmpdir}/corrected.hdr check_results corrected.img ;; levelsetDefault) run levelset -v vol001_mr_t1.hdr ${tmpdir}/levelset.hdr check_results levelset.img ;; levelsetScaleInitial) run levelset -v --scale-initial-sphere 0.5 vol001_mr_t1.hdr ${tmpdir}/levelset.hdr check_results levelset.img ;; levelsetBinarizeFastWideBinary) run levelset -v --binarize --delta 1.0 --filter-sigma 4.0 vol001_mr_t1.hdr ${tmpdir}/levelset.hdr check_results levelset.img ;; lvoteDefault) run lvote -v -o ${tmpdir}/lvote.nii lvote_target.nii lvote_atlas_i0.nii lvote_atlas_l0.nii lvote_atlas_i1.nii lvote_atlas_l1.nii lvote_atlas_i2.nii lvote_atlas_l2.nii check_results lvote.nii ;; lvoteGlobalWeights) run lvote -v -o ${tmpdir}/lvote.nii --use-global-weights lvote_target.nii lvote_atlas_i0.nii lvote_atlas_l0.nii lvote_atlas_i1.nii lvote_atlas_l1.nii lvote_atlas_i2.nii lvote_atlas_l2.nii check_results lvote.nii ;; lvoteRadius3) run lvote -v -o ${tmpdir}/lvote.nii --patch-radius 3 lvote_target.nii lvote_atlas_i0.nii lvote_atlas_l0.nii lvote_atlas_i1.nii lvote_atlas_l1.nii lvote_atlas_i2.nii lvote_atlas_l2.nii check_results lvote.nii ;; lvoteOutliersGlobal) run lvote -v -o ${tmpdir}/lvote.nii --patch-radius 3 --no-global-outliers lvote_target.nii lvote_atlas_i0.nii lvote_atlas_l0.nii lvote_atlas_i1.nii lvote_atlas_l1.nii lvote_atlas_i2.nii lvote_atlas_l2.nii check_results lvote.nii ;; lsbaRadius3) run lsba -v -o ${tmpdir}/lsba.nii --patch-radius 3 lvote_target.nii lvote_atlas_i0.nii lvote_atlas_l0.nii lvote_atlas_i1.nii lvote_atlas_l1.nii lvote_atlas_i2.nii lvote_atlas_l2.nii check_results lsba.nii ;; lsbaRadius3Outliers) run lsba -v -o ${tmpdir}/lsba.nii --patch-radius 3 --no-local-outliers lvote_target.nii lvote_atlas_i0.nii lvote_atlas_l0.nii lvote_atlas_i1.nii lvote_atlas_l1.nii lvote_atlas_i2.nii lvote_atlas_l2.nii check_results lsba.nii ;; lsbaRadius3OutliersGlobal) run lsba -v -o ${tmpdir}/lsba.nii --patch-radius 3 --no-global-outliers lvote_target.nii lvote_atlas_i0.nii lvote_atlas_l0.nii lvote_atlas_i1.nii lvote_atlas_l1.nii lvote_atlas_i2.nii lvote_atlas_l2.nii check_results lsba.nii ;; lsbaRadius3Search1) run lsba -v -o ${tmpdir}/lsba.nii --search-radius 1 --patch-radius 3 --no-local-outliers lvote_target.nii lvote_atlas_i0.nii lvote_atlas_l0.nii lvote_atlas_i1.nii lvote_atlas_l1.nii lvote_atlas_i2.nii lvote_atlas_l2.nii check_results lsba.nii ;; lmsbaRadius3) run lmsba -v -o ${tmpdir}/lmsba.nii --patch-radius 3 lvote_target.nii lvote_atlas_i0.nii lvote_atlas_l0.nii lvote_atlas_i1.nii lvote_atlas_l1.nii lvote_atlas_i2.nii lvote_atlas_l2.nii check_results lmsba.nii ;; lmsbaRadius3Outliers) run lmsba -v -o ${tmpdir}/lmsba.nii --patch-radius 3 --no-local-outliers lvote_target.nii lvote_atlas_i0.nii lvote_atlas_l0.nii lvote_atlas_i1.nii lvote_atlas_l1.nii lvote_atlas_i2.nii lvote_atlas_l2.nii check_results lmsba.nii ;; lmsbaRadius3OutliersGlobal) run lmsba -v -o ${tmpdir}/lmsba.nii --patch-radius 3 --no-global-outliers lvote_target.nii lvote_atlas_i0.nii lvote_atlas_l0.nii lvote_atlas_i1.nii lvote_atlas_l1.nii lvote_atlas_i2.nii lvote_atlas_l2.nii check_results lmsba.nii ;; lmsbaRadius3Search1) run lmsba -v -o ${tmpdir}/lmsba.nii --search-radius 1 --patch-radius 3 --no-local-outliers lvote_target.nii lvote_atlas_i0.nii lvote_atlas_l0.nii lvote_atlas_i1.nii lvote_atlas_l1.nii lvote_atlas_i2.nii lvote_atlas_l2.nii check_results lmsba.nii ;; make_initial_affineCenterOfMass) run make_initial_affine --mode centers-of-mass box1.hdr box3.hdr ${tmpdir}/xform check_results xform ;; make_initial_affinePrincipalAxes1) run make_initial_affine --mode principal-axes box1.hdr box2.hdr ${tmpdir}/xform check_results xform ;; make_initial_affinePrincipalAxes2) run make_initial_affine --mode principal-axes box1.hdr box3.hdr ${tmpdir}/xform check_results xform ;; make_initial_affinePrincipalAxes3) run make_initial_affine --mode principal-axes box2.hdr box3.hdr ${tmpdir}/xform check_results xform ;; make_initial_affinePrincipalAxes4) run make_initial_affine --mode principal-axes box1.hdr box4.hdr ${tmpdir}/xform check_results xform ;; make_initial_affinePrincipalAxes5) run make_initial_affine --mode principal-axes box2.hdr box4.hdr ${tmpdir}/xform check_results xform ;; make_initial_affinePrincipalAxes6) run make_initial_affine --mode principal-axes box3.hdr box4.hdr ${tmpdir}/xform check_results xform ;; make_initial_affineDirectionVectorsNrrdAxSa) run make_initial_affine phantom_ax.nhdr phantom_sa.nhdr ${tmpdir}/xform check_results xform ;; make_initial_affineDirectionVectorsNrrdAxCo) run make_initial_affine phantom_ax.nhdr phantom_co.nhdr ${tmpdir}/xform check_results xform ;; make_initial_affineDirectionVectorsNrrdSaCo) run make_initial_affine phantom_sa.nhdr phantom_co.nhdr ${tmpdir}/xform check_results xform ;; make_initial_affineDirectionVectorsNrrdAxSaNative) run make_initial_affine --native-space phantom_ax.nhdr phantom_sa.nhdr ${tmpdir}/xform check_results xform ;; make_initial_affineDirectionVectorsNrrdAxCoNative) run make_initial_affine --native-space phantom_ax.nhdr phantom_co.nhdr ${tmpdir}/xform check_results xform ;; make_initial_affineDirectionVectorsNrrdSaCoNative) run make_initial_affine --native-space phantom_sa.nhdr phantom_co.nhdr ${tmpdir}/xform check_results xform ;; make_initial_affineDB) run make_initial_affine --db ${tmpdir}/db.sqlite box1.hdr box3.hdr ${tmpdir}/xform # only test for crash; local xform path in database is not portable for comparison ;; mat2dof1) run_eval "${BINDIR}/mat2dof --transpose --center -28,-48,-28 --offset 28,48,28 --pixel-size 1.06667,0.5,1.06667 --list ${tmpdir} < rigid-air.mat" check_results registration ;; mat2dof2) run_eval "${BINDIR}/mat2dof --transpose --center -28,-48,-28 --offset 28,48,28 --pixel-size 1.06667,0.5,1.06667 --inverse --list ${tmpdir} < rigid-air.mat" check_results registration ;; mat2dofFile) run_eval "${BINDIR}/mat2dof --transpose --center -28,-48,-28 --offset 28,48,28 --pixel-size 1.06667,0.5,1.06667 --list ${tmpdir} rigid-air.mat" check_results registration ;; mat2dofToFile) run_eval "${BINDIR}/mat2dof -o ${tmpdir}/xform rigid-air.mat" check_results xform ;; mat2dofToFileAppend) echo "WILL THIS STILL BE HERE?" > ${tmpdir}/xform run_eval "${BINDIR}/mat2dof --append -o ${tmpdir}/xform rigid-air.mat" check_results xform ;; mcaffine1) export CMTK_NUM_THREADS=1 run mcaffine --downsample-from 4 --downsample-to 1 --initial-step-size 1 --final-step-size 0.5 --dofs 6 --covariance -o ${tmpdir}/xform pat001_mr_T1.hdr -- pat001_pet.hdr unset CMTK_NUM_THREADS check_results xform ;; mcaffine2) run mcaffine --initial-xform mcaffine2_initial.xform --downsample-from 4 --downsample-to 1 --initial-step-size 1 --final-step-size 0.5 --dofs 6 --histograms -o ${tmpdir}/xform pat001_mr_T1.hdr -- pat001_pet.hdr check_results xform ;; mcaffine3) run mcaffine --downsample-from 4 --downsample-to 1 --initial-step-size 1 --final-step-size 0.5 --dofs 6 --dofs 9 --covariance -o ${tmpdir}/xform rat_fse_erly.hdr rat_fse_late.hdr -- rat2_fse_erly.hdr rat2_fse_late.hdr check_results xform ;; mcaffine4) run mcaffine --downsample-from 4 --downsample-to 1 --delta-f-threshold 0.1 --initial-step-size 1 --final-step-size 0.5 --dofs 6,9 --covariance -o ${tmpdir}/xform rat_fse_erly.hdr rat_fse_late.hdr -- rat2_fse_erly.hdr rat2_fse_late.hdr check_results xform ;; mcwarp1) # Test breaks when using multiple threads due to floating point effects export CMTK_NUM_THREADS=1 run mcwarp --downsample-from 2 --downsample-to 1 --initial-step-size 1 --final-step-size 0.5 --grid-spacing 14 --refine-grid 2 --covariance -o ${tmpdir}/xform mcaffine_rat_rat2.xform unset CMTK_NUM_THREADS check_results xform ;; mcwarp2) # Test breaks when using multiple threads due to floating point effects export CMTK_NUM_THREADS=1 run mcwarp --downsample-from 2 --downsample-to 1 --initial-step-size 1 --final-step-size 0.5 --grid-spacing 14 --refine-grid 2 --histograms -o ${tmpdir}/xform mcaffine_rat_rat2.xform unset CMTK_NUM_THREADS check_results xform ;; mcwarp3) # Test breaks when using multiple threads due to floating point effects export CMTK_NUM_THREADS=1 run mcwarp --downsample-from 2 --downsample-to 1 --delta-f-threshold 0.1 --initial-step-size 1 --final-step-size 0.5 --grid-spacing 14 --refine-grid 2 --covariance -o ${tmpdir}/xform mcaffine_rat_rat2.xform unset CMTK_NUM_THREADS check_results xform ;; mcwarp4) # Test breaks when using multiple threads due to floating point effects export CMTK_NUM_THREADS=1 run mcwarp --downsample-from 2 --downsample-to 1 --delta-f-threshold 0.1 --initial-step-size 1 --final-step-size 0.5 --grid-spacing 14 --refine-grid 2 --covariance --intensity-correction -o ${tmpdir}/xform mcaffine_rat_rat2.xform unset CMTK_NUM_THREADS check_results xform ;; mcwarp5) # Test breaks when using multiple threads due to floating point effects export CMTK_NUM_THREADS=1 run mcwarp --downsample-from 2 --downsample-to 1 --delta-f-threshold 0.1 --initial-step-size 1 --final-step-size 0.5 --grid-spacing 14 --adaptive-fix-thresh-factor 0.5 -o ${tmpdir}/xform mcaffine_rat_rat2.xform unset CMTK_NUM_THREADS check_results xform ;; mcwarp6) # Test breaks when using multiple threads due to floating point effects export CMTK_NUM_THREADS=1 run mcwarp --downsample-from 2 --downsample-to 1 --delta-f-threshold 0.1 --initial-step-size 1 --final-step-size 0.5 --grid-spacing 14 --adaptive-fix-thresh-factor-entropy 0.5 -o ${tmpdir}/xform mcaffine_rat_rat2.xform unset CMTK_NUM_THREADS check_results xform ;; mk_adni_phantom2mm) run mk_adni_phantom --resolution 2 ${tmpdir}/phantom.nii check_results phantom.nii ;; mk_adni_phantom2mmLabels) run mk_adni_phantom --resolution 2 --write-labels ${tmpdir}/phantom.lbl --write-landmarks ${tmpdir}/phantom.xyz --labels ${tmpdir}/phantom.nii check_results phantom.nii phantom.lbl phantom.xyz ;; mk_analyze_hdrDefault) run mk_analyze_hdr ${tmpdir}/analyze.hdr run_eval "${BINDIR}/describe -m ${tmpdir}/analyze.hdr | fgrep --invert-match FNAME > ${tmpdir}/analyze.txt" check_results analyze.hdr analyze.txt ;; mk_analyze_hdrImport) run mk_analyze_hdr --import spgr_3t.hdr ${tmpdir}/analyze.hdr run_eval "${BINDIR}/describe -m ${tmpdir}/analyze.hdr | fgrep --invert-match FNAME > ${tmpdir}/analyze.txt" check_results analyze.hdr analyze.txt ;; mk_analyze_hdrLittleEndian) run mk_analyze_hdr --description LittleEndian --little-endian --float --dims 100,200,300 --voxel 0.3,0.2,0.1 --offset 1024 ${tmpdir}/analyze.hdr run_eval "${BINDIR}/describe -m ${tmpdir}/analyze.hdr | fgrep --invert-match FNAME > ${tmpdir}/analyze.txt" check_results analyze.hdr analyze.txt ;; mk_analyze_hdrBigEndian) run mk_analyze_hdr --description BigEndian --big-endian --ushort --dims 100,200,300 --voxel 0.3,0.2,0.1 --offset 1024 ${tmpdir}/analyze.hdr run_eval "${BINDIR}/describe -m ${tmpdir}/analyze.hdr | fgrep --invert-match FNAME > ${tmpdir}/analyze.txt" check_results analyze.hdr analyze.txt ;; mk_nifti_hdrDefault) run mk_nifti_hdr ${tmpdir}/nifti.hdr run_eval "${BINDIR}/describe -m ${tmpdir}/nifti.hdr | fgrep --invert-match FNAME > ${tmpdir}/nifti.txt" check_results nifti.hdr nifti.txt ;; mk_nifti_hdrDescription) run_eval "${BINDIR}/mk_nifti_hdr --description \"This is a description text\" ${tmpdir}/nifti.hdr" check_results nifti.hdr ;; mk_nifti_hdrImport) run mk_nifti_hdr --import phantom_ax.nii ${tmpdir}/nifti.hdr run_eval "${BINDIR}/describe -m ${tmpdir}/nifti.hdr | fgrep --invert-match FNAME > ${tmpdir}/nifti.txt" check_results nifti.hdr nifti.txt ;; mk_nifti_hdrDefaultAttached) run mk_nifti_hdr --attached ${tmpdir}/nifti.nii run_eval "${BINDIR}/describe -m ${tmpdir}/nifti.nii | fgrep --invert-match FNAME > ${tmpdir}/nifti.txt" check_results nifti.nii nifti.txt ;; mk_nifti_hdrDescriptionAttached) run_eval "${BINDIR}/mk_nifti_hdr --attached --description \"This is a description text\" ${tmpdir}/nifti.nii" check_results nifti.nii ;; mk_nifti_hdrImportAttached) run mk_nifti_hdr --attached --import phantom_ax.nii ${tmpdir}/nifti.nii run_eval "${BINDIR}/describe -m ${tmpdir}/nifti.nii | fgrep --invert-match FNAME > ${tmpdir}/nifti.txt" check_results nifti.nii nifti.txt ;; mk_phantom_3dBoxIndexed) run mk_phantom_3d -o ${tmpdir}/phantom.nii --dims 10,10,10 --voxel 2,2,2 box 2,2,2 5,5,5 10 check_results phantom.nii ;; mk_phantom_3dBoxIndexedRange) run mk_phantom_3d -o ${tmpdir}/phantom.nii --dims 10,10,10 --voxel 2,2,2 box 11,11,11 5,5,5 10 check_results phantom.nii ;; mk_phantom_3dBoxAbsolute) run mk_phantom_3d --absolute -o ${tmpdir}/phantom.nii --dims 10,10,10 --voxel 1,1,1 box 2,2,2 5,5,5 10 check_results phantom.nii ;; mk_phantom_3dBoxRelative) run mk_phantom_3d --relative -o ${tmpdir}/phantom.nii --dims 10,10,10 --voxel 1,1,1 box 0.1,0.2,0.3 0.5,0.5,0.5 10 check_results phantom.nii ;; mk_phantom_3dSphereIndexed) run mk_phantom_3d --coordinates indexed -o ${tmpdir}/phantom.nii --dims 10,10,10 --voxel 1,1,1 sphere 7,7,7 5 20 check_results phantom.nii ;; mk_phantom_3dSphereAbsolute) run mk_phantom_3d --coordinates absolute -o ${tmpdir}/phantom.nii --dims 10,10,10 --voxel 1,1,1 sphere 7,7,7 5 20 check_results phantom.nii ;; mk_phantom_3dSphereAbsolute2) run mk_phantom_3d --coordinates absolute -o ${tmpdir}/phantom.nii --dims 10,10,10 --voxel 1,1,2 sphere 7,7,7 5 20 check_results phantom.nii ;; mk_phantom_3dSphereRelative) run mk_phantom_3d --coordinates relative -o ${tmpdir}/phantom.nii --dims 10,10,10 --voxel 1,1,1 sphere 0.5,0.4,0.3 0.4 20 check_results phantom.nii ;; mk_phantom_3dSphereRelative2) run mk_phantom_3d --coordinates relative -o ${tmpdir}/phantom.nii --dims 10,10,10 --voxel 1,1,2 sphere 0.5,0.4,0.3 0.4 20 check_results phantom.nii ;; mk_phantom_3dBoxSphere) run mk_phantom_3d -o ${tmpdir}/phantom.nii --char --bg 50 --dims 10,10,10 --voxel 1,1,1 sphere 7,7,7 5 20 box 2,2,2 5,5,5 10 check_results phantom.nii ;; mk_phantom_3dImport) run mk_phantom_3d -o ${tmpdir}/phantom.nii --import spgr_3t_mask.hdr sphere 30,40,30 20 0 check_results phantom.nii ;; mk_phantom_3dImportGrid) run mk_phantom_3d -o ${tmpdir}/phantom.nii --import-grid spgr_3t_mask.hdr sphere 30,40,30 20 10 check_results phantom.nii ;; mk_phantom_3dMRSVoxel) run mk_phantom_3d --echo -v --bg 0 -o ${tmpdir}/voxel.nii --import-grid ge-mrs/lfse.nii mrs-voxel ge-mrs/spectro.dcm 100 check_results voxel.nii ;; mrbiasMulIncremental) run mrbias --incremental -M 2 --write-bias-mul ${tmpdir}/bias_mul.hdr --thresh-min 100 spgr_3t.hdr ${tmpdir}/corrected.hdr check_results corrected.img bias_mul.img ;; mrbiasMulAutoThresh) run mrbias -v -M 2 --set-padding-value 0 --thresh-auto spgr_3t.hdr ${tmpdir}/corrected.hdr check_results corrected.img ;; mrbiasMulOtsuThresh) run mrbias -v -M 2 --set-padding-value 0 --thresh-otsu-nbins 256 spgr_3t.hdr ${tmpdir}/corrected.hdr check_results corrected.img ;; mrbiasMulLogIntensity) run mrbias --log-intensities -M 2 --write-bias-mul ${tmpdir}/bias_mul.hdr --thresh-min 100 spgr_3t.hdr ${tmpdir}/corrected.hdr check_results corrected.img bias_mul.img ;; mrbiasAddMulMask) export CMTK_NUM_THREADS=1 run mrbias -A 1 -M 2 --mask spgr_3t_mask.hdr --write-bias-add ${tmpdir}/bias_add.hdr --write-bias-mul ${tmpdir}/bias_mul.hdr spgr_3t.hdr ${tmpdir}/corrected.hdr unset CMTK_NUM_THREADS check_results corrected.img bias_mul.img bias_add.img ;; mrbiasMulIncrementalCUDA) run mrbias_cuda --incremental -M 2 --write-bias-mul ${tmpdir}/bias_mul.hdr --thresh-min 100 spgr_3t.hdr ${tmpdir}/corrected.hdr check_results corrected.img bias_mul.img ;; mrbiasMulLogIntensityCUDA) run mrbias_cuda --log-intensities -M 2 --write-bias-mul ${tmpdir}/bias_mul.hdr --thresh-min 100 spgr_3t.hdr ${tmpdir}/corrected.hdr check_results corrected.img bias_mul.img ;; mrbiasAddMulMaskCUDA) run mrbias_cuda -A 1 -M 2 --mask spgr_3t_mask.hdr --write-bias-add ${tmpdir}/bias_add.hdr --write-bias-mul ${tmpdir}/bias_mul.hdr spgr_3t.hdr ${tmpdir}/corrected.hdr check_results corrected.img bias_mul.img bias_add.img ;; overlap) run_eval "${BINDIR}/overlap parc1.hdr parc2.hdr parc3.hdr > ${tmpdir}/overlap.txt" check_results overlap.txt ;; overlapNumLabels) run_eval "${BINDIR}/overlap -N 2 parc1.hdr parc2.hdr parc3.hdr > ${tmpdir}/overlap.txt" check_results overlap.txt ;; overlapByLabel) run_eval "${BINDIR}/overlap --by-label parc1.hdr parc2.hdr parc3.hdr > ${tmpdir}/overlap.txt" check_results overlap.txt ;; overlapFirst) run_eval "${BINDIR}/overlap --first-label 10 parc1.hdr parc2.hdr parc3.hdr > ${tmpdir}/overlap.txt" check_results overlap.txt ;; overlapFirstByLabel) run_eval "${BINDIR}/overlap --by-label --first-label 10 parc1.hdr parc2.hdr parc3.hdr > ${tmpdir}/overlap.txt" check_results overlap.txt ;; overlapFirstByLabelNumLabels) run_eval "${BINDIR}/overlap --by-label --first-label 10 --num-labels 10 parc1.hdr parc2.hdr parc3.hdr > ${tmpdir}/overlap.txt" check_results overlap.txt ;; probeIndexed) run_eval "echo 98 154 1 |${BINDIR}/probe --indexed phantom_ax.nii.gz > ${tmpdir}/probe.txt" check_results probe.txt ;; probeAbsolute) run_eval "echo 91.875 144.375 3 |${BINDIR}/probe --absolute phantom_ax.nii.gz > ${tmpdir}/probe.txt" check_results probe.txt ;; probeRelative) run_eval "echo 0.384313725 0.603921569 0.5 |${BINDIR}/probe --relative phantom_ax.nii.gz > ${tmpdir}/probe.txt" check_results probe.txt ;; probePhysical) run_eval "echo -27.656 24.844 0 |${BINDIR}/probe --physical phantom_ax.nii.gz > ${tmpdir}/probe.txt" check_results probe.txt ;; probeIndexedLinear) run_eval "echo 98.5 154.5 1 |${BINDIR}/probe --linear --indexed phantom_ax.nii.gz > ${tmpdir}/probe.txt" check_results probe.txt ;; probeIndexedCubic) run_eval "echo 98.5 154.5 1 |${BINDIR}/probe --cubic --indexed phantom_ax.nii.gz > ${tmpdir}/probe.txt" check_results probe.txt ;; probeIndexedSinc) run_eval "echo 98.5 154.5 1 |${BINDIR}/probe --sinc-cosine --indexed phantom_ax.nii.gz > ${tmpdir}/probe.txt" check_results probe.txt ;; pxsearchIndexed) run_eval "echo 98 154 1 |${BINDIR}/pxsearch --input-coordinates indexed --output-coordinates indexed phantom_ax.nii.gz > ${tmpdir}/pxsearch.txt" check_results pxsearch.txt ;; pxsearchIndexedRadius3) run_eval "echo 98 154 1 |${BINDIR}/pxsearch --radius 3 --input-coordinates indexed --output-coordinates indexed phantom_ax.nii.gz > ${tmpdir}/pxsearch.txt" check_results pxsearch.txt ;; pxsearchIndexedRadius311) run_eval "echo 98 154 1 |${BINDIR}/pxsearch --radius 3,1,1 --input-coordinates indexed --output-coordinates indexed phantom_ax.nii.gz > ${tmpdir}/pxsearch.txt" check_results pxsearch.txt ;; pxsearchAbsolute) run_eval "echo 91.875 144.375 3 |${BINDIR}/pxsearch --input-coordinates absolute --output-coordinates absolute phantom_ax.nii.gz > ${tmpdir}/pxsearch.txt" check_results pxsearch.txt ;; pxsearchRelative) run_eval "echo 0.384313725 0.603921569 0.5 |${BINDIR}/pxsearch --input-coordinates relative --output-coordinates relative phantom_ax.nii.gz > ${tmpdir}/pxsearch.txt" check_results pxsearch.txt ;; pxsearchPhysical) run_eval "echo -27.656 24.844 0 |${BINDIR}/pxsearch --input-coordinates physical --output-coordinates physical phantom_ax.nii.gz > ${tmpdir}/pxsearch.txt" check_results pxsearch.txt ;; reformatxNoXform) run reformatx --linear -o ${tmpdir}/reformat.hdr --floating vol001_mr_t1.hdr vol001_mr_t0_crop.hdr check_results reformat.img ;; reformatxLinear) run reformatx --linear -o ${tmpdir}/reformat.hdr --floating vol001_mr_t1.hdr vol001_mr_t0_crop.hdr vol001_mr_t0t1.list check_results reformat.img ;; reformatxNearestNeighbor) run reformatx --nn -o ${tmpdir}/reformat.hdr --floating vol001_mr_t1.hdr vol001_mr_t0_crop.hdr vol001_mr_t0t1.list check_results reformat.img ;; reformatxPartialVolume) run reformatx --pv -o ${tmpdir}/reformat.hdr --floating vol001_mr_t1.hdr vol001_mr_t0_crop.hdr vol001_mr_t0t1.list check_results reformat.img ;; reformatxLinearFwdBwd) run reformatx --linear --short -o ${tmpdir}/reformat.hdr --floating vol001_mr_t0.hdr vol001_mr_t0.hdr vol001_mr_t0t1.list --inverse vol001_mr_t0t1.list check_results vol001_mr_t0.img ;; reformatxCubic) run reformatx --cubic -o ${tmpdir}/reformat.hdr --floating vol001_mr_t1.hdr vol001_mr_t0_crop.hdr vol001_mr_t0t1.list check_results reformat.img ;; reformatxCubicInverse) run reformatx --cubic -o ${tmpdir}/reformat.hdr --floating vol001_mr_t1.hdr vol001_mr_t0_crop.hdr --inverse vol001_mr_t0t1.list check_results reformat.img ;; reformatxSincCosine) run reformatx --sinc-cosine -o ${tmpdir}/reformat.hdr --floating vol001_mr_t1.hdr vol001_mr_t0_crop.hdr vol001_mr_t0t1.list check_results reformat.img ;; reformatxSincHamming) run reformatx --sinc-hamming -o ${tmpdir}/reformat.hdr --floating vol001_mr_t1.hdr vol001_mr_t0_crop.hdr vol001_mr_t0t1.list check_results reformat.img ;; reformatxSincCosine5) run reformatx --sinc-cosine --sinc-window-radius 5 -o ${tmpdir}/reformat.hdr --floating vol001_mr_t1.hdr vol001_mr_t0_crop.hdr vol001_mr_t0t1.list check_results reformat.img ;; reformatxMassPreserving) run reformatx --preserve-mass -o ${tmpdir}/reformat.hdr --floating vol001_mr_t1.hdr vol001_mr_t0.hdr vol001_mr_t0t1_warp.xform check_results reformat.img ;; reformatxJacobian) run reformatx --jacobian-correct-global -o ${tmpdir}/jacobian.hdr vol001_mr_t0_crop.hdr vol001_mr_t0_crop.xform --jacobian vol001_mr_t0t1_warp.xform check_results jacobian.img ;; reformatxJacobianDefault) run reformatx -o ${tmpdir}/jacobian.hdr vol001_mr_t0_crop.hdr vol001_mr_t0_crop.xform --jacobian vol001_mr_t0t1_warp.xform check_results jacobian.img ;; reformatxInverseJacobian) run reformatx --jacobian-correct-global -o ${tmpdir}/jacobian.hdr vol001_mr_t0_crop.hdr vol001_mr_t0_crop.xform --jacobian --inverse vol001_mr_t0t1_warp.xform check_results jacobian.img ;; reformatxInverseJacobianDefault) run reformatx -o ${tmpdir}/jacobian.hdr vol001_mr_t0_crop.hdr vol001_mr_t0_crop.xform --jacobian --inverse vol001_mr_t0t1_warp.xform check_results jacobian.img ;; reformatxDfieldNrrd) run reformatx -o ${tmpdir}/reformat.hdr --floating parc2.hdr parc1.hdr parc1_parc2_dfield.nrrd check_results reformat.img ;; reformatxDfieldNrrdJacobian) run reformatx -o ${tmpdir}/jacobian.hdr vol001_mr_t0.hdr --jacobian vol001_mr_t0t1_dfield.nrrd check_results jacobian.img ;; reformatxTargetGrid) run reformatx -o ${tmpdir}/reformat.nii --target-grid 32,43,21:5.625,5.625,7.5 --floating spgr_brain_1.hdr check_results reformat.nii ;; reformatxTargetGridAnalyze) run reformatx -o ${tmpdir}/reformat.hdr --target-grid 32,43,21:5.625,5.625,7.5 --floating spgr_brain_1.hdr check_results reformat.hdr reformat.img ;; reformatxTargetGridOffset) run reformatx -o ${tmpdir}/reformat.nii --target-grid 32,43,21:5.625,5.625,7.5:5.625,0,0 --floating spgr_brain_1.hdr check_results reformat.nii ;; registrationAffineMrMrMSD) run registration -i --dofs 6,9 --msd --match-histograms -o ${tmpdir} pat001_mr_T1.hdr pat002_mr_T2.hdr check_results registration ;; registrationFromList) run registration -v --dofs 0 -o ${tmpdir} vol001_mr_t0t1.list check_results registration ;; registrationWithInitial) run registration -v --dofs 0 -o ${tmpdir} --initial vol001_mr_t0t1.list vol001_mr_t0.hdr vol001_mr_t1.hdr check_results registration ;; registrationWithInitialInverse) run registration -v --dofs 0 -o ${tmpdir} --initial vol001_mr_t0t1.list --initial-is-inverse vol001_mr_t0t1.list check_results registration ;; registrationAutoLevelsRat4) run registration -v -i --auto-multi-levels 4 --dofs 6 -o ${tmpdir} rat_fse_erly.hdr rat_fse_late.hdr check_results registration ;; registrationAutoLevelsRat2) run registration -v -i --auto-multi-levels 2 --dofs 6 -o ${tmpdir} rat_fse_erly.hdr rat_fse_late.hdr check_results registration ;; registrationAutoLevelsRatToRat) run registration -v -i --auto-multi-levels 2 --dofs 6,9 --msd --match-histograms -o ${tmpdir} rat_fse_erly.hdr rat2_fse_erly.hdr check_results registration ;; registrationAutoLevelsRatToRatDeltaFThreshold) run registration -v -i --auto-multi-levels 2 --dofs 6,9 --msd --match-histograms --delta-f-threshold 0.01 -o ${tmpdir} rat_fse_erly.hdr rat2_fse_erly.hdr check_results registration ;; registrationAutoLevelsCt3) run registration --msd --auto-multi-levels 3 --dofs 6 -o ${tmpdir} pat002_ct.hdr pat002_ct.hdr check_results registration ;; registrationxAffineMrMrMSD) run registrationx --init fov --dofs 6,9 --msd --match-histograms -o ${tmpdir} pat001_mr_T1.hdr pat002_mr_T2.hdr check_results registration ;; registrationxShearNoScaleMrMrMSD) run registrationx --auto-multi-levels 4 --init fov --dofs 603 --msd --match-histograms -o ${tmpdir} pat001_mr_T1.hdr pat002_mr_T2.hdr check_results registration ;; registrationxFromList) run registrationx -v --dofs 0 -o ${tmpdir} vol001_mr_t0t1.list check_results registration ;; registrationxWithInitial) run registrationx -v --dofs 0 -o ${tmpdir} --initial vol001_mr_t0t1.list vol001_mr_t0.hdr vol001_mr_t1.hdr check_results registration ;; registrationxWithInitialInverse) run registrationx -v --dofs 0 -o ${tmpdir} --initial vol001_mr_t0t1.list --initial-is-inverse vol001_mr_t0t1.list check_results registration ;; registrationxFromListDB) run registrationx --db ${tmpdir}/db.sqlite -v --dofs 0 -o ${tmpdir} vol001_mr_t0t1.list ;; registrationxWithInitialDB) run registrationx --db ${tmpdir}/db.sqlite -v --dofs 0 -o ${tmpdir} --initial vol001_mr_t0t1.list vol001_mr_t0.hdr vol001_mr_t1.hdr ;; registrationxWithInitialInverseDB) run registrationx --db ${tmpdir}/db.sqlite -v --dofs 0 -o ${tmpdir} --initial vol001_mr_t0t1.list --initial-is-inverse vol001_mr_t0t1.list ;; registrationxAutoLevelsRat4) run registrationx -v --auto-multi-levels 4 --dofs 6 -o ${tmpdir} --write-itk ${tmpdir}/xform.tfm rat_fse_erly.hdr rat_fse_late.hdr check_results registration xform.tfm ;; registrationxAutoLevelsRat4XY) run registrationx -v --restrict-in-plane xy --auto-multi-levels 4 --dofs 6,9,12 -o ${tmpdir} rat_fse_erly.hdr rat2_fse_late.hdr check_results registration ;; registrationxAutoLevelsRat4XZ) run registrationx -v --restrict-in-plane xz --auto-multi-levels 4 --dofs 6,9,12 -o ${tmpdir} rat_fse_erly.hdr rat2_fse_late.hdr check_results registration ;; registrationxAutoLevelsRat4YZ) run registrationx -v --restrict-in-plane yz --auto-multi-levels 4 --dofs 6,9,12 -o ${tmpdir} rat_fse_erly.hdr rat2_fse_late.hdr check_results registration ;; registrationxAutoLevelsRat4Symmetric) run registrationx -v --symmetric --auto-multi-levels 4 --dofs 6 -o ${tmpdir} rat_fse_erly.hdr rat_fse_late.hdr check_results registration ;; registrationxAutoLevelsRat4NONE) run registrationx -v --init none --auto-multi-levels 4 --dofs 6 -o ${tmpdir} rat_fse_erly.hdr rat_fse_late.hdr check_results registration ;; registrationxAutoLevelsRat4FOV) run registrationx -v --init fov --auto-multi-levels 4 --dofs 6 -o ${tmpdir} rat_fse_erly.hdr rat_fse_late.hdr check_results registration ;; registrationxAutoLevelsRat4COM) run registrationx -v --init com --auto-multi-levels 4 --dofs 6 -o ${tmpdir} rat_fse_erly.hdr rat_fse_late.hdr check_results registration ;; registrationxAutoLevelsRat4PAX) run registrationx -v --init pax --auto-multi-levels 4 --dofs 6 -o ${tmpdir} rat_fse_erly.hdr rat_fse_late.hdr check_results registration ;; registrationxAutoLevelsRat2) run registrationx -v --init fov --auto-multi-levels 2 --dofs 6 -o ${tmpdir} rat_fse_erly.hdr rat_fse_late.hdr check_results registration ;; registrationxAutoLevelsRat2Cubic) run registrationx -v --cubic --init fov --auto-multi-levels 2 --dofs 6 -o ${tmpdir} rat_fse_erly.hdr rat_fse_late.hdr check_results registration ;; registrationxAutoLevelsRat2Sinc) run registrationx -v --cosine-sinc --init fov --auto-multi-levels 2 --dofs 6 -o ${tmpdir} rat_fse_erly.hdr rat_fse_late.hdr check_results registration ;; registrationxAutoLevelsRat2NN) run registrationx -v --nearest-neighbor --init fov --auto-multi-levels 2 --dofs 6 -o ${tmpdir} rat_fse_erly.hdr rat_fse_late.hdr check_results registration ;; registrationxAutoLevelsRatToRat) export CMTK_NUM_THREADS=1 run registrationx -v --init fov --auto-multi-levels 2 --dofs 6,9 --msd --match-histograms -o ${tmpdir} rat_fse_erly.hdr rat2_fse_erly.hdr unset CMTK_NUM_THREADS check_results registration ;; registrationxAutoLevelsRatToRatNCC) run registrationx -v --init fov --auto-multi-levels 2 --ncc -o ${tmpdir} rat_fse_erly.hdr rat2_fse_erly.hdr check_results registration ;; registrationxAutoLevelsRatToRatRMS) run registrationx -v --init fov --auto-multi-levels 2 --rms -o ${tmpdir} rat_fse_erly.hdr rat2_fse_erly.hdr check_results registration ;; registrationxAutoLevelsRatToRatCR) run registrationx -v --init fov --auto-multi-levels 2 --cr -o ${tmpdir} rat_fse_erly.hdr rat2_fse_erly.hdr check_results registration ;; registrationxAutoLevelsRatToRatMI) run registrationx -v --init fov --auto-multi-levels 2 --mi -o ${tmpdir} rat_fse_erly.hdr rat2_fse_erly.hdr check_results registration ;; registrationxAutoLevelsRatToRatDeltaFThreshold) export CMTK_NUM_THREADS=1 run registrationx -v --init fov --auto-multi-levels 2 --dofs 6,9 --msd --match-histograms --delta-f-threshold 0.01 -o ${tmpdir} rat_fse_erly.hdr rat2_fse_erly.hdr unset CMTK_NUM_THREADS check_results registration ;; registrationxAutoLevelsCt3) run registrationx --pad-flt -10000 --write-reformatted ${tmpdir}/reformat.nii --msd --auto-multi-levels 3 --dofs 6 -o ${tmpdir} pat002_ct.hdr pat002_ct.hdr check_results registration reformat.nii ;; registrationxAutoLevelsLabelsNN) run registrationx -v --interpolation nearest-neighbor --class-ref label --class-flt label --auto-multi-levels 4 --dofs 6,9 --nmi -o ${tmpdir} parc1.hdr parc2.hdr check_results registration ;; registrationxAutoLevelsLabelsPV) run registrationx -v --interpolation partial-volume --class-ref label --class-flt label --auto-multi-levels 4 --dofs 6,9 --nmi -o ${tmpdir} parc1.hdr parc2.hdr check_results registration ;; reorientHdrSaToAx) run reorient -o RAS phantom_sa.hdr ${tmpdir}/reorient.hdr check_results reorient.hdr reorient.img ;; reorientHdrSaToAxNifti) run reorient -i RAS -o RAS phantom_sa.hdr ${tmpdir}/reorient.nii check_results reorient.nii ;; reorientHdrCoToAx) run reorient -o RAS phantom_co.hdr ${tmpdir}/reorient.hdr check_results reorient.hdr reorient.img ;; reorientHdrAxToSa) run reorient -o ASL phantom_ax.hdr ${tmpdir}/reorient.hdr check_results reorient.hdr reorient.img ;; reorientHdrCoToSa) run reorient -o ASL phantom_co.hdr ${tmpdir}/reorient.hdr check_results reorient.hdr reorient.img ;; reorientHdrAxToCo) run reorient -o LSA phantom_ax.hdr ${tmpdir}/reorient.hdr check_results reorient.hdr reorient.img ;; reorientHdrSaToCo) run reorient -o LSA phantom_sa.hdr ${tmpdir}/reorient.hdr check_results reorient.hdr reorient.img ;; reorientNrrdToNrrd) run reorient vol001_mr_t0_crop.nrrd ${tmpdir}/vol001_mr_t0_crop.nhdr check_results vol001_mr_t0_crop.nhdr vol001_mr_t0_crop.raw ;; reorientNrrdToNrrdRAS) run reorient --output-orientation RAS vol001_mr_t0_crop.nrrd ${tmpdir}/vol001_mr_t0_crop.nhdr check_results vol001_mr_t0_crop.nhdr vol001_mr_t0_crop.raw ;; reorientNrrdToNrrdSpaceLPS) run reorient --output-space LPS vol001_mr_t0_crop.nrrd ${tmpdir}/vol001_mr_t0_crop.nhdr check_results vol001_mr_t0_crop.nhdr vol001_mr_t0_crop.raw ;; registrationRigidMrPet) run registration -i --dofs 6 -o ${tmpdir} pat001_mr_T1.hdr pat001_pet.hdr check_results registration ;; registrationRigidMrCt) run registration -i --dofs 6 -o ${tmpdir} pat002_mr_T2.hdr pat002_ct.hdr check_results registration ;; registrationRigidCt) run registration --msd -i --dofs 6 -o ${tmpdir} pat002_ct.hdr pat002_ct.hdr check_results registration ;; registrationRigidPetMr) run registration -i --dofs 6 -o ${tmpdir} pat001_pet.hdr pat001_mr_T1.hdr check_results registration ;; registrationRigidCtMr) run registration -i --dofs 6 -o ${tmpdir} pat002_ct.hdr pat002_mr_T2.hdr check_results registration ;; registrationRigidMrPetNoSwap) run registration --no-switch -i --dofs 6 -o ${tmpdir} pat001_mr_T1.hdr pat001_pet.hdr check_results registration ;; registrationRigidMrCtNoSwap) run registration --no-switch -i --dofs 6 -o ${tmpdir} pat002_mr_T2.hdr pat002_ct.hdr check_results registration ;; registrationRigidPetMrDOF9) run registration -i --dofs 9 -o ${tmpdir} pat001_pet.hdr pat001_mr_T1.hdr check_results registration ;; registrationRigidCtMrDOF7) run registration -i --dofs 7 -o ${tmpdir} pat002_ct.hdr pat002_mr_T2.hdr check_results registration ;; registrationRigidLabelsDOF69) run registration --dofs 6 --dofs 9 --class-ref label --class-flt label -o ${tmpdir} parc1.hdr parc2.hdr check_results registration ;; registrationRigidCrop) run registration -v -i -e 2.0 -a 0.125 --sampling 0.25 --crop-index-ref 17,20,0,47,49,12 --crop-index-flt 12,15,0,52,54,12 --dofs 6 -o ${tmpdir} rat_fse_erly.hdr rat_fse_late.hdr check_results registration ;; sbaDefault) run sba -o ${tmpdir}/sba.nii -n 255 parc1.hdr parc2.hdr parc3.hdr check_results sba.nii ;; sbaOutliers) run sba --exclude-outliers -o ${tmpdir}/sba.nii -n 255 parc1.hdr parc1.hdr parc1.hdr parc1.hdr parc1.hdr parc2.hdr check_results sba.nii ;; sbaOutliers2) run sba --exclude-outliers -o ${tmpdir}/sba.nii -n 255 parc1.hdr parc2.hdr parc3.hdr check_results sba.nii ;; sbaiDefault) run sbai -o ${tmpdir}/sbai.nii -n 255 parc12_warp.xform parc13_warp.xform check_results sbai.nii ;; sequenceDefault) run_eval "cat numbers.txt | ${BINDIR}/sequence > ${tmpdir}/sequence.txt" check_results sequence.txt ;; sequenceFormat) run_eval "cat numbers.txt | ${BINDIR}/sequence --format %g > ${tmpdir}/sequence.txt" check_results sequence.txt ;; sequenceThresh) run_eval "cat numbers.txt | ${BINDIR}/sequence --thresh 1e4 > ${tmpdir}/sequence.txt" check_results sequence.txt ;; sequenceAbs) run_eval "cat numbers.txt | ${BINDIR}/sequence --abs > ${tmpdir}/sequence.txt" check_results sequence.txt ;; sequenceAbsThresh) run_eval "cat numbers.txt | ${BINDIR}/sequence --thresh 1000 --abs > ${tmpdir}/sequence.txt" check_results sequence.txt ;; sequenceHistogramDefault) run_eval "cat numbers.txt | ${BINDIR}/sequence --write-histogram ${tmpdir}/histogram.csv > ${tmpdir}/sequence.txt" check_results sequence.txt histogram.csv ;; sequenceHistogramExplicit) run_eval "cat numbers.txt | ${BINDIR}/sequence --histogram-bins 10 --histogram-min 0 --histogram-max 1000 --write-histogram ${tmpdir}/histogram.csv > ${tmpdir}/sequence.txt" check_results sequence.txt histogram.csv ;; similarityGrey) run_eval "${BINDIR}/similarity --histogram-text-file ${tmpdir}/histogram.txt rat_fse_erly.hdr rat_fse_late.hdr > ${tmpdir}/similarity.txt" check_results histogram.txt similarity.txt ;; similarityWithInf) run_eval "${BINDIR}/similarity float_with_inf.nii.gz float_with_inf.nii.gz > ${tmpdir}/similarity.txt" check_results similarity.txt ;; similarityLabels) run_eval "${BINDIR}/similarity --labels --histogram-text-file ${tmpdir}/histogram.txt parc1.hdr parc2.hdr > ${tmpdir}/similarity.txt" check_results histogram.txt similarity.txt ;; similarityGreyMask) run_eval "${BINDIR}/similarity --mask rat_fse_erly.hdr --histogram-text-file ${tmpdir}/histogram.txt rat_fse_erly.hdr rat_fse_late.hdr > ${tmpdir}/similarity.txt" check_results histogram.txt similarity.txt ;; similarityLabelsMask) run_eval "${BINDIR}/similarity --mask parc3_bin.hdr --labels --histogram-text-file ${tmpdir}/histogram.txt parc1.hdr parc2.hdr > ${tmpdir}/similarity.txt" check_results histogram.txt similarity.txt ;; splitAxial) run split --output-xform-path ${tmpdir}/split_ax_%1d.xform --axial spgr_3t.hdr ${tmpdir}/split_ax_%1d.hdr check_results split_ax_0.img split_ax_1.img split_ax_0.xform split_ax_1.xform ;; splitAxialSlices) run split --factor 0 --axial phantom_ax_downsampled.nii ${tmpdir}/slice_%1d.nii check_results slice_0.nii slice_1.nii slice_2.nii ;; splitAxialNrrd) run split --axial --factor 3 spgr_3t.hdr ${tmpdir}/split_ax_%1d.nhdr check_results split_ax_0.nhdr split_ax_0.raw split_ax_1.nhdr split_ax_1.raw split_ax_2.nhdr split_ax_2.raw ;; splitSagittal2) run split --output-xform-path ${tmpdir}/split_sa_%1d.xform --factor 2 --sagittal spgr_3t.hdr ${tmpdir}/split_sa_%1d.hdr check_results split_sa_0.img split_sa_1.img split_sa_0.xform split_sa_1.xform ;; splitCoronal3) run split --output-xform-path ${tmpdir}/split_co_%1d.xform --factor 3 --coronal spgr_3t.hdr ${tmpdir}/split_co_%1d.hdr check_results split_co_0.img split_co_1.img split_co_2.img split_co_0.xform split_co_1.xform split_co_2.xform ;; statisticsGrey) run_eval "${BINDIR}/statistics spgr_3t.hdr > ${tmpdir}/statistics.txt" check_results statistics.txt ;; statisticsPercentiles) run_eval "${BINDIR}/statistics -p 0.1 --percentile 0.5 -p 0.75 spgr_3t.hdr > ${tmpdir}/statistics.txt" check_results statistics.txt ;; statisticsGreyColumn) run_eval "${BINDIR}/statistics -C spgr_3t.hdr > ${tmpdir}/statistics.txt" check_results statistics.txt ;; statisticsGreyExpNotation) run_eval "${BINDIR}/statistics -E spgr_3t.hdr > ${tmpdir}/statistics.txt" check_results statistics.txt ;; statisticsGreyMask) run_eval "${BINDIR}/statistics -m spgr_3t_mask.hdr spgr_3t.hdr > ${tmpdir}/statistics.txt" check_results statistics.txt ;; statisticsGreyMultiMask) run_eval "${BINDIR}/statistics -M spgr_3t_mask.hdr spgr_3t.hdr > ${tmpdir}/statistics.txt" check_results statistics.txt ;; statisticsMaskMismatch) run_eval "${BINDIR}/statistics -M parc1.hdr spgr_3t.hdr > ${tmpdir}/statistics.txt" check_results statistics.txt ;; statisticsLabels) run_eval "${BINDIR}/statistics -l parc1.hdr > ${tmpdir}/statistics.txt" check_results statistics.txt ;; statisticsLabelsAllUpToHi) run_eval "${BINDIR}/statistics --mask-output-all-up-to 256 -l parc1.hdr > ${tmpdir}/statistics.txt" check_results statistics.txt ;; statisticsLabelsAllUpToLo) run_eval "${BINDIR}/statistics --mask-output-all-up-to 2 -l parc1.hdr > ${tmpdir}/statistics.txt" check_results statistics.txt ;; streamxformFordwardBackward) run_eval "cat vol001_t0_points.xyzl | ${BINDIR}/streamxform vol001_mr_t0t1_warp.xform --inverse vol001_mr_t0t1_warp.xform > ${tmpdir}/vol001_t0_points.xyzl" check_results vol001_t0_points.xyzl ;; streamxformBackwardForward) run_eval "cat vol001_t0_points.xyzl | ${BINDIR}/streamxform -- --inverse vol001_mr_t0t1_warp.xform vol001_mr_t0t1_warp.xform > ${tmpdir}/vol001_t0_points.xyzl" check_results vol001_t0_points.xyzl ;; streamxformFordwardBackwardPoly0) run_eval "cat vol001_t0_points.xyzl | ${BINDIR}/streamxform poly0.xform --inverse poly0.xform > ${tmpdir}/vol001_t0_points.xyzl" check_results vol001_t0_points.xyzl ;; streamxformFordwardBackwardPoly1) run_eval "cat vol001_t0_points.xyzl | ${BINDIR}/streamxform poly1.xform --inverse poly1.xform > ${tmpdir}/vol001_t0_points.xyzl" check_results vol001_t0_points.xyzl ;; streamxformFordwardBackwardPoly2) run_eval "cat vol001_t0_points.xyzl | ${BINDIR}/streamxform poly2.xform --inverse poly2.xform > ${tmpdir}/vol001_t0_points.xyzl" check_results vol001_t0_points.xyzl ;; streamxformFordwardBackwardPoly3) run_eval "cat vol001_t0_points.xyzl | ${BINDIR}/streamxform poly3.xform --inverse poly3.xform > ${tmpdir}/vol001_t0_points.xyzl" check_results vol001_t0_points.xyzl ;; streamxformFordwardBackwardPoly4) run_eval "cat vol001_t0_points.xyzl | ${BINDIR}/streamxform poly4.xform --inverse poly4.xform > ${tmpdir}/vol001_t0_points.xyzl" check_results vol001_t0_points.xyzl ;; streamxformAffinePoly4) run_eval "cat vol001_t0_points.xyzl | ${BINDIR}/streamxform --affine-only poly4.xform > ${tmpdir}/vol001_t0_points.xyzl" check_results vol001_t0_points.xyzl ;; streamxformAffineForward) run_eval "cat vol001_t0_points.xyzl | ${BINDIR}/streamxform --affine-only vol001_mr_t0t1_warp.xform > ${tmpdir}/vol001_t1_points.xyzl" check_results vol001_t1_points.xyzl ;; streamxformAffineForwardBackward) run_eval "cat vol001_t0_points.xyzl | ${BINDIR}/streamxform --affine-only vol001_mr_t0t1_warp.xform --inverse vol001_mr_t0t1_warp.xform > ${tmpdir}/vol001_t0_points.xyzl" check_results vol001_t0_points.xyzl ;; streamxformFordwardBackwardTolerance) run_eval "cat vol001_t0_points.xyzl | ${BINDIR}/streamxform --inversion-tolerance 0.1 vol001_mr_t0t1_warp.xform --inverse vol001_mr_t0t1_warp.xform > ${tmpdir}/vol001_t0_points.xyzl" check_results vol001_t0_points.xyzl ;; streamxformAffine) run_eval "cat vol001_t0_points.xyzl | ${BINDIR}/streamxform vol001_mr_t0t1.list > ${tmpdir}/vol001_t0_points.xyzl" check_results vol001_t0_points.xyzl ;; streamxformAffineFromTo) run_eval "cat vol001_t0_points.xyzl | ${BINDIR}/streamxform --source-image vol001_mr_t0.hdr --target-image vol001_mr_t1.hdr vol001_mr_t0t1.list > ${tmpdir}/vol001_t0_points.xyzl" check_results vol001_t0_points.xyzl ;; symmetry_plane) run sympl --sampling 1 --levels 4 --accuracy 0.1 --write-xform ${tmpdir}/xform --sinc --write-subtract ${tmpdir}/subtract.hdr --write-marked ${tmpdir}/marked.nii --write-mirror ${tmpdir}/mirror.nii -o ${tmpdir}/parameters cad001_ct.hdr check_results parameters xform subtract.img marked.nii mirror.nii ;; symmetry_planeThresh) run sympl --sampling 1 --levels 4 --accuracy 0.1 --min-value -224 --max-value 176 --cubic --write-aligned ${tmpdir}/aligned.hdr -o ${tmpdir}/parameters cad001_ct.hdr check_results parameters aligned.img ;; symplx_Default) run symplx --sampling 1 --levels 4 --accuracy 0.1 --write-xform ${tmpdir}/xform --sinc --write-subtract ${tmpdir}/subtract.hdr --write-marked ${tmpdir}/marked.nii --write-mirror ${tmpdir}/mirror.nii -o ${tmpdir}/parameters cad001_ct.hdr check_results parameters xform subtract.img marked.nii mirror.nii ;; symplx_Thresh) run symplx --sampling 1 --levels 4 --accuracy 0.1 --min-value -224 --max-value 176 --cubic --write-aligned ${tmpdir}/aligned.hdr -o ${tmpdir}/parameters cad001_ct.hdr check_results parameters aligned.img ;; symplx_FixOffset) run symplx --sampling 1 --levels 4 --accuracy 0.1 --fix-offset --cubic --write-aligned ${tmpdir}/aligned.hdr -o ${tmpdir}/parameters cad001_ct.hdr check_results parameters aligned.img ;; symplx_DefaultCUDA) run symplx_cuda --sampling 1 --levels 4 --accuracy 0.1 --write-xform ${tmpdir}/xform --sinc --write-subtract ${tmpdir}/subtract.hdr -o ${tmpdir}/parameters cad001_ct.hdr check_results parameters xform subtract.img ;; symplx_ThreshCUDA) run symplx_cuda --sampling 1 --levels 4 --accuracy 0.1 --min-value -224 --max-value 176 --cubic --write-aligned ${tmpdir}/aligned.hdr -o ${tmpdir}/parameters cad001_ct.hdr check_results parameters aligned.img ;; symplx_FixOffsetCUDA) run symplx_cuda --sampling 1 --levels 4 --accuracy 0.1 --fix-offset --cubic --write-aligned ${tmpdir}/aligned.hdr -o ${tmpdir}/parameters cad001_ct.hdr check_results parameters aligned.img ;; ttestDefault) run ttest -o ${tmpdir}/ttest.hdr --tstats-file ${tmpdir}/tstats.hdr jacobian-01.nii jacobian-02.nii -- jacobian-03.nii jacobian-04.nii check_results ttest.img tstats.img ;; ttestOneSided) run ttest -o ${tmpdir}/ttest.hdr --tstats-file ${tmpdir}/tstats.hdr jacobian-01.nii jacobian-02.nii jacobian-03.nii jacobian-04.nii check_results ttest.img tstats.img ;; ttestSymmetric) run ttest -o ${tmpdir}/ttest.hdr --tstats-file ${tmpdir}/tstats.hdr --symmetric jacobian-01.nii jacobian-02.nii jacobian-03.nii jacobian-04.nii check_results ttest.img tstats.img ;; ttestLog) run ttest --log -o ${tmpdir}/ttest.hdr --tstats-file ${tmpdir}/tstats.hdr jacobian-01.nii jacobian-02.nii -- jacobian-03.nii jacobian-04.nii check_results ttest.img tstats.img ;; ttestAbsLog) run ttest --abs --log -o ${tmpdir}/ttest.hdr --tstats-file ${tmpdir}/tstats.hdr jacobian-01.nii jacobian-02.nii -- jacobian-03.nii jacobian-04.nii check_results ttest.img tstats.img ;; ttestInvert) run ttest --invert -o ${tmpdir}/ttest.hdr --tstats-file ${tmpdir}/tstats.hdr jacobian-01.nii jacobian-02.nii -- jacobian-03.nii jacobian-04.nii check_results ttest.img tstats.img ;; ttestPaired) run ttest --paired -o ${tmpdir}/ttest.hdr --tstats-file ${tmpdir}/tstats.hdr jacobian-01.nii jacobian-02.nii -- jacobian-03.nii jacobian-04.nii check_results ttest.img tstats.img ;; ttestCrossCorrelation) run ttest --cross-correlation -o ${tmpdir}/ttest.hdr --tstats-file ${tmpdir}/pvals.hdr jacobian-01.nii jacobian-02.nii jacobian-03.nii -- jacobian-04.nii jacobian-03.nii jacobian-02.nii check_results ttest.img pvals.img ;; ttestZScores) run ttest --zscores -o ${tmpdir}/zscores.hdr jacobian-01.nii jacobian-02.nii -- jacobian-03.nii jacobian-04.nii check_results zscores.img ;; ttestMask) run ttest --mask jacobian-mask.nii -o ${tmpdir}/ttest.hdr --tstats-file ${tmpdir}/tstats.hdr jacobian-01.nii jacobian-02.nii -- jacobian-03.nii jacobian-04.nii check_results ttest.img tstats.img ;; unsplitHdrAx) run unsplit --axial -o ${tmpdir}/unsplit.hdr split_ax_0.hdr split_ax_1.hdr check_results unsplit.hdr unsplit.img ;; unsplitHdrSa) run unsplit --sagittal -o ${tmpdir}/unsplit.hdr split_sa_0.hdr split_sa_1.hdr check_results unsplit.hdr unsplit.img ;; unsplitHdrCo) run unsplit --coronal -o ${tmpdir}/unsplit.hdr split_co_0.hdr split_co_1.hdr split_co_2.hdr check_results unsplit.hdr unsplit.img ;; unsplitHdrNrrdAx) run unsplit --axial -o ${tmpdir}/unsplit.nhdr split_ax_0.hdr split_ax_1.hdr check_results unsplit.nhdr unsplit.raw ;; unsplitHdrNrrdSa) run unsplit --sagittal -o ${tmpdir}/unsplit.nhdr split_sa_0.hdr split_sa_1.hdr check_results unsplit.nhdr unsplit.raw ;; unsplitHdrNrrdCo) run unsplit --coronal -o ${tmpdir}/unsplit.nhdr split_co_0.hdr split_co_1.hdr split_co_2.hdr check_results unsplit.nhdr unsplit.raw ;; unsplitNrrdNrrd) run unsplit --axial -o ${tmpdir}/unsplit.nhdr split_ax_0.nhdr split_ax_1.nhdr split_ax_2.nhdr check_results unsplit.nhdr unsplit.raw ;; unsplitSlices) run unsplit --axial -o ${tmpdir}/unsplit.nii phantom_ax_downsampled_slice_0.nii phantom_ax_downsampled_slice_1.nii phantom_ax_downsampled_slice_2.nii check_results unsplit.nii ;; unwarp_image_phantomDefault) run unwarp_image_phantom magphan.xml spgr_magphan.nii ${tmpdir}/poly.xform check_results poly.xform ;; unwarp_image_phantomPoly) run unwarp_image_phantom --poly magphan.xml spgr_magphan.nii ${tmpdir}/poly.xform check_results poly.xform ;; unwarp_image_phantomPolyDegree2) run unwarp_image_phantom --poly --degree 2 magphan.xml spgr_magphan.nii ${tmpdir}/ffd.xform check_results ffd.xform ;; unwarp_image_phantomSpacing) run unwarp_image_phantom --spline --iterations-per-level 1 --final-cp-spacing 80 magphan.xml spgr_magphan.nii ${tmpdir}/ffd.xform check_results ffd.xform ;; unwarp_image_phantomSpacingInverse) run unwarp_image_phantom --spline --iterations-per-level 1 --final-cp-spacing 80 --fit-inverse magphan.xml spgr_magphan.nii ${tmpdir}/ffd.xform check_results ffd.xform ;; unwarp_image_phantomSpacingLevels) run unwarp_image_phantom --spline --iterations-per-level 1 --final-cp-spacing 80 --levels 3 magphan.xml spgr_magphan.nii ${tmpdir}/ffd.xform check_results ffd.xform ;; unwarp_image_phantomSpacingDirect) run unwarp_image_phantom --spline --iterations-per-level 1 --no-fit-affine --final-cp-spacing 80 magphan.xml spgr_magphan.nii ${tmpdir}/ffd.xform check_results ffd.xform ;; unwarp_image_phantomDims) run unwarp_image_phantom --spline --iterations-per-level 1 --final-cp-dims 5,5,5 magphan.xml spgr_magphan.nii ${tmpdir}/ffd.xform check_results ffd.xform ;; unwarp_image_phantomDimsLevels) run unwarp_image_phantom --spline --iterations-per-level 1 --final-cp-dims 5,5,5 --levels 3 magphan.xml spgr_magphan.nii ${tmpdir}/ffd.xform check_results ffd.xform ;; unwarp_image_phantomDimsLevelsIterations) run unwarp_image_phantom --spline --iterations-per-level 10 --final-cp-dims 5,5,5 --levels 3 magphan.xml spgr_magphan.nii ${tmpdir}/ffd.xform check_results ffd.xform ;; unwarp_image_phantomDimsLevelsThreshold) run unwarp_image_phantom --spline --iterations-per-level 1 --rms-threshold 0.1 --final-cp-dims 5,5,5 --levels 3 magphan.xml spgr_magphan.nii ${tmpdir}/ffd.xform check_results ffd.xform ;; unwarp_image_phantomDimsLevelsIterationsThreshold) run unwarp_image_phantom --spline --iterations-per-level 10 --rms-threshold 0.1 --final-cp-dims 5,5,5 --levels 3 magphan.xml spgr_magphan.nii ${tmpdir}/ffd.xform check_results ffd.xform ;; unwarp_image_phantomDimsDefault) run unwarp_image_phantom --spline --final-cp-dims 5,5,5 magphan.xml spgr_magphan.nii ${tmpdir}/ffd.xform check_results ffd.xform ;; volume_injection) run volume_injection --recon-grid-path spgr_3t.hdr -o ${tmpdir}/injection.hdr --injection-kernel-sigma 0.5 --injection-kernel-radius 2 split_ax_0.hdr split_ax_01.xform split_ax_1.hdr check_results injection.img ;; volume_injectionReconGrid) run volume_injection --recon-grid 49,65,35:3.75,3.75,3.75 -o ${tmpdir}/injection.nii --injection-kernel-sigma 0.5 --injection-kernel-radius 2 split_ax_0.hdr split_ax_01.xform split_ax_1.hdr check_results injection.nii ;; volume_injectionReconGridOffset) run volume_injection --recon-grid 48,64,34:3.75,3.75,3.75:1.875,1.875,1.875 -o ${tmpdir}/injection.nii --injection-kernel-sigma 0.5 --injection-kernel-radius 2 split_ax_0.hdr split_ax_01.xform split_ax_1.hdr check_results injection.nii ;; volume_injectionIsotropic) run volume_injection --recon-grid-path spgr_3t.hdr -o ${tmpdir}/injection.hdr --injection-kernel-sigma 0.5 --injection-kernel-radius 2 --isotropic-injection split_ax_0.hdr split_ax_01.xform split_ax_1.hdr check_results injection.img ;; volume_injectionNoXform) run volume_injection -o ${tmpdir}/injection.hdr --injection-kernel-sigma 0.5 --injection-kernel-radius 2 --exclude-first-image spgr_3t.hdr -- split_ax_0.hdr split_ax_01.xform split_ax_1.hdr check_results injection.img ;; volume_injectionNoXformIsotropic) run volume_injection -o ${tmpdir}/injection.hdr --injection-kernel-sigma 0.5 --injection-kernel-radius 2 --isotropic-injection --exclude-first-image spgr_3t.hdr -- split_ax_0.hdr split_ax_01.xform split_ax_1.hdr check_results injection.img ;; volume_reconstructionFourthOrder) run volume_reconstruction --recon-grid-path spgr_3t.hdr -o ${tmpdir}/reconstruction.hdr --linear --fourth-order-error --num-iterations 2 --injection-kernel-sigma 0.5 --injection-kernel-radius 2 --isotropic-injection --write-injected-image ${tmpdir}/injection.hdr split_ax_0.hdr split_ax_01.xform split_ax_1.hdr check_results injection.img reconstruction.img ;; volume_reconstructionCubic) run volume_reconstruction --recon-grid-path spgr_3t.hdr -o ${tmpdir}/reconstruction.hdr --cubic --num-iterations 2 --injection-kernel-sigma 0.5 --injection-kernel-radius 2 --isotropic-injection --write-injected-image ${tmpdir}/injection.hdr split_ax_0.hdr split_ax_01.xform split_ax_1.hdr check_results injection.img reconstruction.img ;; volume_reconstructionNoXform) run volume_reconstruction -o ${tmpdir}/reconstruction.hdr --linear --num-iterations 2 --injection-kernel-sigma 0.5 --injection-kernel-radius 2 --isotropic-injection --write-injected-image ${tmpdir}/injection.hdr --exclude-first-image spgr_3t.hdr -- split_ax_0.hdr split_ax_01.xform split_ax_1.hdr check_results injection.img reconstruction.img ;; volume_reconstructionBoxPSF) run volume_reconstruction --recon-grid-path spgr_3t.hdr -o ${tmpdir}/reconstruction.hdr --deblurring box --psf 0.9375,0.9375,1.25 --num-iterations 2 --injection-kernel-sigma 0.5 --injection-kernel-radius 2 split_ax_0.hdr split_ax_01.xform split_ax_1.hdr check_results reconstruction.img ;; volume_reconstructionGaussianPSF) run volume_reconstruction --recon-grid-path spgr_3t.hdr -o ${tmpdir}/reconstruction.hdr --deblurring gaussian --psf 0.9375,0.9375,1.25 --num-iterations 2 --injection-kernel-sigma 0.5 --injection-kernel-radius 2 split_ax_0.hdr split_ax_01.xform split_ax_1.hdr check_results reconstruction.img ;; warpSingleLevel) run warp --fast --exploration 8 --grid-spacing 160 --accuracy 1 --no-adaptive-fix -o ${tmpdir} vol001_mr_t0t1.list check_results registration ;; warpSingleLevelExact) run warp --fast --exploration 8 --grid-spacing 180 --exact-spacing --accuracy 1 --sampling 3 --no-adaptive-fix -o ${tmpdir} vol001_mr_t0t1.list check_results registration ;; warpInverseConsistentCC) run warp --fast --exploration 8 --grid-spacing 80 --accuracy 1 --ncc --ic-weight 1e-2 -o ${tmpdir} vol001_mr_t0t1.list check_results registration ;; warpMultiLevel) run warp --fast --exploration 8 --grid-spacing 160 --accuracy 1 --refine 1 -o ${tmpdir} vol001_mr_t0t1.list check_results registration ;; warpMultiLevelMatchHistograms) run warp --fast --exploration 8 --match-histograms --msd --grid-spacing 160 --accuracy 1 --refine 1 -o ${tmpdir} vol001_mr_t0t1.list check_results registration ;; warpMultiLevelDeltaFThreshold) run warp --fast --exploration 8 --delta-f-threshold 0.01 --msd --grid-spacing 160 --accuracy 1 --refine 1 -o ${tmpdir} vol001_mr_t0t1.list check_results registration ;; warpMultiLevelExact) run warp --fast --exploration 8 --grid-spacing 160 --exact-spacing --accuracy 1 --refine 1 -o ${tmpdir} vol001_mr_t0t1.list check_results registration ;; warpDelayRefine) run warp --fast --exploration 12 --grid-spacing 160 --accuracy 2 --refine 1 --delay-refine --sampling 6 -o ${tmpdir} vol001_mr_t0t1.list check_results registration ;; warpEnergy) run warp --fast --exploration 8 --grid-spacing 160 --accuracy 1 --refine 1 --energy-weight 1e-1 -o ${tmpdir} vol001_mr_t0t1.list check_results registration ;; warpEnergyRelax) run warp --fast --exploration 8 --grid-spacing 160 --accuracy 1 --refine 1 --energy-weight 1e-1 --relax 1e-2 -o ${tmpdir} vol001_mr_t0t1.list check_results registration ;; warpJacobian) export CMTK_NUM_THREADS=1 run warp --fast --exploration 12 --grid-spacing 160 --accuracy 2 --refine 1 --jacobian-weight 1e-1 --sampling 12 --omit-original-data -o ${tmpdir} vol001_mr_t0t1.list unset CMTK_NUM_THREADS check_results registration ;; warpRigidity) run warp --fast --exploration 12 --grid-spacing 160 --accuracy 2 --refine 1 --rigidity-weight 1e-1 --sampling 12 --omit-original-data -o ${tmpdir} vol001_mr_t0t1.list check_results registration ;; warpLabels) run warp --fast --exploration 8 --grid-spacing 90 --accuracy 1 --refine 1 --class-ref label --class-flt label -o ${tmpdir} --initial parc1_parc2_9dof.xform parc1.hdr parc2.hdr check_results registration ;; warpxSingleLevel) run warpx --fast --max-stepsize 8 --grid-spacing 160 --min-stepsize 1 --no-adaptive-fix -o ${tmpdir} --write-itk-xform ${tmpdir}/xform.tfm vol001_mr_t0t1.list check_results registration xform.tfm ;; warpxSingleLevelExact) run warpx --fast --max-stepsize 8 --grid-spacing 180 --exact-spacing --min-stepsize 1 --sampling 3 --no-adaptive-fix -o ${tmpdir} vol001_mr_t0t1.list check_results registration ;; warpxInverseConsistentCC) export CMTK_NUM_THREADS=1 run warpx --fast --max-stepsize 8 --grid-spacing 80 --min-stepsize 1 --ncc --inverse-consistency-weight 1e-2 -o ${tmpdir} vol001_mr_t0t1.list unset CMTK_NUM_THREADS check_results registration ;; warpxMultiLevel) run warpx --pad-flt -10000 --write-reformatted ${tmpdir}/reformat.nii --fast --max-stepsize 8 --grid-spacing 160 --min-stepsize 1 --grid-refine 1 -o ${tmpdir} vol001_mr_t0t1.list check_results registration reformat.nii ;; warpxMultiLevelMatchHistograms) export CMTK_NUM_THREADS=1 run warpx --fast --max-stepsize 8 --match-histograms --msd --grid-spacing 160 --min-stepsize 1 --grid-refine 1 -o ${tmpdir} vol001_mr_t0t1.list unset CMTK_NUM_THREADS check_results registration ;; warpxMultiLevelDeltaFThreshold) export CMTK_NUM_THREADS=1 run warpx --fast --max-stepsize 8 --delta-f-threshold 0.01 --msd --grid-spacing 160 --min-stepsize 1 --grid-refine 1 -o ${tmpdir} vol001_mr_t0t1.list unset CMTK_NUM_THREADS check_results registration ;; warpxMultiLevelExact) run warpx --fast --max-stepsize 8 --grid-spacing 160 --exact-spacing --min-stepsize 1 --grid-refine 1 -o ${tmpdir} vol001_mr_t0t1.list check_results registration ;; warpxDelayRefine) run warpx --fast --max-stepsize 12 --grid-spacing 160 --min-stepsize 2 --grid-refine 1 --delay-refine --sampling 6 -o ${tmpdir} vol001_mr_t0t1.list check_results registration ;; warpxEnergy) run warpx --fast --max-stepsize 8 --grid-spacing 160 --min-stepsize 1 --grid-refine 1 --smoothness-constraint-weight 1e-1 -o ${tmpdir} vol001_mr_t0t1.list check_results registration ;; warpxEnergyRelax) run warpx --fast --max-stepsize 8 --grid-spacing 160 --min-stepsize 1 --grid-refine 1 --smoothness-constraint-weight 1e-1 --constraint-relaxation-factor 1e-2 -o ${tmpdir} vol001_mr_t0t1.list check_results registration ;; warpxJacobian) export CMTK_NUM_THREADS=1 run warpx --fast --max-stepsize 12 --grid-spacing 160 --min-stepsize 2 --grid-refine 1 --jacobian-constraint-weight 1e-1 --sampling 12 --omit-original-data -o ${tmpdir} vol001_mr_t0t1.list unset CMTK_NUM_THREADS check_results registration ;; warpxJacobianUnfold) export CMTK_NUM_THREADS=1 run warpx --fast --relax-to-unfold --max-stepsize 12 --grid-spacing 160 --min-stepsize 2 --grid-refine 1 --jacobian-constraint-weight 1e-1 --sampling 12 --omit-original-data -o ${tmpdir} vol001_mr_t0t1.list unset CMTK_NUM_THREADS check_results registration ;; warpxLabels) run warpx --fast --max-stepsize 8 --grid-spacing 90 --min-stepsize 1 --grid-refine 1 --class-ref label --class-flt label -o ${tmpdir} --initial parc1_parc2_9dof.xform parc1.hdr parc2.hdr check_results registration ;; vtkxform) run_eval "${BINDIR}/vtkxform vol001_mr_t0t1.list < polydata_ascii.vtk > ${tmpdir}/output.vtk" check_results output.vtk ;; vtkxformInverse) run_eval "${BINDIR}/vtkxform -- --inverse vol001_mr_t0t1.list < polydata_ascii.vtk > ${tmpdir}/output.vtk" check_results output.vtk ;; xform2dfieldWarpNrrd) run xform2dfield -v ${tmpdir}/dfield.nhdr vol001_mr_t0_crop.hdr vol001_mr_t0t1_warp.xform check_results dfield.nhdr dfield.raw ;; xform2dfieldWarpNifti) run xform2dfield -v ${tmpdir}/dfield.nii vol001_mr_t0_crop.hdr vol001_mr_t0t1_warp.xform check_results dfield.nii ;; xform2dfieldWarpNiftiFSL) run xform2dfield --target-image-fsl ncanda_t1_jacobian.nii.gz --output-absolute ${tmpdir}/dfield.nii vol001_mr_t0_crop.hdr vol001_mr_t0t1_warp.xform check_results dfield.nii ;; xform2dfieldWarpNiftiHdr) run xform2dfield -v ${tmpdir}/dfield.img vol001_mr_t0_crop.hdr vol001_mr_t0t1_warp.xform check_results dfield.hdr dfield.img ;; xform2dfieldAffineNrrd) run xform2dfield -v ${tmpdir}/dfield.nhdr vol001_mr_t0_crop.hdr vol001_mr_t0_crop.xform check_results dfield.nhdr dfield.raw ;; xform2dfieldDownsampleXYZNrrd) run xform2dfield -v --downsample 4,4,2 ${tmpdir}/dfield.nhdr vol001_mr_t0_crop.hdr vol001_mr_t0_crop.xform check_results dfield.nhdr dfield.raw ;; xform2dfieldDownsampleXNrrd) run xform2dfield -v --downsample 4 ${tmpdir}/dfield.nhdr vol001_mr_t0_crop.hdr vol001_mr_t0_crop.xform check_results dfield.nhdr dfield.raw ;; xform2dfieldConcatNrrd) run xform2dfield -v ${tmpdir}/dfield.nhdr vol001_mr_t0_crop.hdr vol001_mr_t0_crop.xform vol001_mr_t0t1_warp.xform check_results dfield.nhdr dfield.raw ;; xform2dfieldInverseNrrd) run xform2dfield -v ${tmpdir}/dfield.nhdr vol001_mr_t0_crop.hdr vol001_mr_t0_crop.xform --inverse vol001_mr_t0t1_warp.xform check_results dfield.nhdr dfield.raw ;; xform2scalarAffine) run xform2scalar --float --output ${tmpdir}/magnitude.nii vol001_mr_t0.hdr vol001_mr_t0t1.list check_results magnitude.nii ;; xform2scalarAffineDoubleY) run xform2scalar --mode x-component --output ${tmpdir}/xcomponent.nii vol001_mr_t0.hdr vol001_mr_t0t1.list check_results xcomponent.nii ;; xform2scalarWarp) run xform2scalar --float --output ${tmpdir}/magnitude.nii vol001_mr_t0.hdr vol001_mr_t0t1_warp.xform check_results magnitude.nii ;; xform2scalarWarpInverseError) run xform2scalar --float --output ${tmpdir}/magnitude.nii vol001_mr_t0.hdr vol001_mr_t0t1_warp.xform --inverse vol001_mr_t0t1_warp.xform check_results magnitude.nii ;; xform2scalarWarpOnly) run xform2scalar --warp-only --float --output ${tmpdir}/magnitude.nii vol001_mr_t0.hdr vol001_mr_t0t1_warp.xform check_results magnitude.nii ;; xform2scalarDfield) run xform2scalar --float --output ${tmpdir}/magnitude.nii parc1.hdr parc1_parc2_dfield.nrrd check_results magnitude.nii ;; vol2csvLabels) run vol2csv -o ${tmpdir}/volumes.csv --labels-file sri24_ventricles.txt ncanda_t1_ventricles.nii ncanda_t1_pve_0.nii ncanda_t1_pve_1.nii ncanda_t1_pve_2.nii check_results volumes.csv ;; vol2csvLabelsNoBackground) run vol2csv -o ${tmpdir}/volumes.csv --labels-file sri24_ventricles_noBG.txt ncanda_t1_ventricles.nii ncanda_t1_pve_0.nii ncanda_t1_pve_1.nii ncanda_t1_pve_2.nii check_results volumes.csv ;; vol2csvDensityLabels) run vol2csv -o ${tmpdir}/volumes.csv --density-labels CSF,GM,WM ncanda_t1_ventricles.nii ncanda_t1_pve_0.nii ncanda_t1_pve_1.nii ncanda_t1_pve_2.nii check_results volumes.csv ;; vol2csvScale) run vol2csv -o ${tmpdir}/volumes.csv --pixel-scale-image ncanda_t1_jacobian.nii ncanda_t1_ventricles.nii ncanda_t1_pve_0.nii ncanda_t1_pve_1.nii ncanda_t1_pve_2.nii check_results volumes.csv ;; vol2csvScaleGlobal) run vol2csv -o ${tmpdir}/volumes.csv --pixel-scale-factor 2 ncanda_t1_ventricles.nii ncanda_t1_pve_0.nii ncanda_t1_pve_1.nii ncanda_t1_pve_2.nii check_results volumes.csv ;; help_film) run_eval "CMTK_CONSOLE_LINE_WIDTH=100 ${BINDIR}/film --help > ${tmpdir}/film.help" check_results film.help ;; help_all_film) run_eval "CMTK_CONSOLE_LINE_WIDTH=100 ${BINDIR}/film --help-all > ${tmpdir}/film.help" check_results film.help ;; xml_film) run_eval "CMTK_CONSOLE_LINE_WIDTH=100 ${BINDIR}/film --xml | ${sed} '//{ N; s/^.*$// }' > ${tmpdir}/film.xml" check_results film.xml ;; xml_gmm) run_eval "CMTK_CONSOLE_LINE_WIDTH=100 ${BINDIR}/gmm --xml | ${sed} '//{ N; s/^.*$// }' > ${tmpdir}/gmm.xml" check_results gmm.xml ;; xml_levelset) run_eval "CMTK_CONSOLE_LINE_WIDTH=100 ${BINDIR}/levelset --xml | ${sed} '//{ N; s/^.*$// }' > ${tmpdir}/levelset.xml" check_results levelset.xml ;; xml_mrbias) run_eval "CMTK_CONSOLE_LINE_WIDTH=100 ${BINDIR}/mrbias --xml | ${sed} '//{ N; s/^.*$// }' > ${tmpdir}/mrbias.xml" check_results mrbias.xml ;; xml_registration) run_eval "CMTK_CONSOLE_LINE_WIDTH=100 ${BINDIR}/registration --xml | ${sed} '//{ N; s/^.*$// }' > ${tmpdir}/registration.xml" check_results registration.xml ;; wiki_film) run_eval "CMTK_CONSOLE_LINE_WIDTH=100 ${BINDIR}/film --wiki > ${tmpdir}/film.wiki" check_results film.wiki ;; wiki_levelset) run_eval "CMTK_CONSOLE_LINE_WIDTH=100 ${BINDIR}/levelset --wiki > ${tmpdir}/levelset.wiki" check_results levelset.wiki ;; wiki_mrbias) run_eval "CMTK_CONSOLE_LINE_WIDTH=100 ${BINDIR}/mrbias --wiki > ${tmpdir}/mrbias.wiki" check_results mrbias.wiki ;; wiki_registration) run_eval "CMTK_CONSOLE_LINE_WIDTH=100 ${BINDIR}/registration --wiki > ${tmpdir}/registration.wiki" check_results registration.wiki ;; man_film) run_eval "CMTK_CONSOLE_LINE_WIDTH=100 ${BINDIR}/film --man | fgrep -v .TH > ${tmpdir}/film.man" check_results film.man ;; man_levelset) run_eval "CMTK_CONSOLE_LINE_WIDTH=100 ${BINDIR}/levelset --man | fgrep -v .TH > ${tmpdir}/levelset.man" check_results levelset.man ;; man_mrbias) run_eval "CMTK_CONSOLE_LINE_WIDTH=100 ${BINDIR}/mrbias --man | fgrep -v .TH > ${tmpdir}/mrbias.man" check_results mrbias.man ;; man_registration) run_eval "CMTK_CONSOLE_LINE_WIDTH=100 ${BINDIR}/registration --man | fgrep -v .TH > ${tmpdir}/registration.man" check_results registration.man ;; *) exit 2 ;; esac if [ "${tmpdir}" != "" ]; then rm -rf ${tmpdir} fi cmtk-3.3.1/testing/gui/000077500000000000000000000000001276303427400147255ustar00rootroot00000000000000cmtk-3.3.1/testing/gui/CMakeLists.txt000066400000000000000000000041661276303427400174740ustar00rootroot00000000000000## ## Copyright 1997-2009 Torsten Rohlfing ## ## Copyright 2004-2009, 2014 SRI International ## ## This file is part of the Computational Morphometry Toolkit. ## ## http://www.nitrc.org/projects/cmtk/ ## ## The Computational Morphometry Toolkit is free software: you can ## redistribute it and/or modify it under the terms of the GNU General Public ## License as published by the Free Software Foundation, either version 3 of ## the License, or (at your option) any later version. ## ## The Computational Morphometry Toolkit is distributed in the hope that it ## will be useful, but WITHOUT ANY WARRANTY; without even the implied ## warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License along ## with the Computational Morphometry Toolkit. If not, see ## . ## ## $Revision: 5261 $ ## ## $LastChangedDate: 2014-03-28 09:19:19 -0700 (Fri, 28 Mar 2014) $ ## ## $LastChangedBy: torstenrohlfing $ ## # ========================================== # Tests for "triplanar" gui application # (batch mode) SET(testDriver ${CMAKE_CURRENT_BINARY_DIR}/triplanarTestDriver.sh) CONFIGURE_FILE(${CMAKE_CURRENT_SOURCE_DIR}/triplanarTestDriver.sh.in ${CMAKE_CURRENT_BINARY_DIR}/triplanarTestDriver.sh @ONLY) SET(testList triplanarDefault triplanarZoom300 triplanarZoom25 triplanarSetPixelWindowLevel triplanarColormaps triplanarPhantomAx triplanarPhantomSa triplanarPhantomCo) IF(IGS_BUILD_NRRD OR IGS_USE_TEEM) SET(testList ${testList} triplanarPhantomAxNrrd triplanarPhantomSaNrrd triplanarPhantomCoNrrd) ENDIF(IGS_BUILD_NRRD OR IGS_USE_TEEM) # ========================================== # Set up all tests FOREACH(testName ${testList}) IF(CMTK_TESTING_MEMORYCHECK) ADD_TEST(${testName} /bin/bash ${testDriver} ${testName} ${MEMORYCHECK_COMMAND}) ELSE(CMTK_TESTING_MEMORYCHECK) ADD_TEST(${testName} /bin/bash ${testDriver} ${testName}) ENDIF(CMTK_TESTING_MEMORYCHECK) ENDFOREACH(testName ${testList}) cmtk-3.3.1/testing/gui/testDriverFunctions.sh000066400000000000000000000034141276303427400213070ustar00rootroot00000000000000#!/bin/sh ## ## Copyright 1997-2009 Torsten Rohlfing ## Copyright 2004-2009 SRI International ## ## This file is part of the Computational Morphometry Toolkit. ## ## http://www.nitrc.org/projects/cmtk/ ## ## The Computational Morphometry Toolkit is free software: you can ## redistribute it and/or modify it under the terms of the GNU General Public ## License as published by the Free Software Foundation, either version 3 of ## the License, or (at your option) any later version. ## ## The Computational Morphometry Toolkit is distributed in the hope that it ## will be useful, but WITHOUT ANY WARRANTY; without even the implied ## warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License along ## with the Computational Morphometry Toolkit. If not, see ## . ## ## $Revision: 41 $ ## ## $LastChangedDate: 2009-06-08 14:25:53 -0700 (Mon, 08 Jun 2009) $ ## ## $LastChangedBy: torstenrohlfing $ ## BUILDNAME=$1 BINDIR=$2 DATADIR=$3 RUNTEST=$4 HOSTNAME=`uname -n` tmpdir=${BINDIR}/../testing/temporary/${HOSTNAME}/${RUNTEST} mkdir -p ${tmpdir} BASELINE=${DATADIR}/testing/baseline/${RUNTEST} if [ -d ${DATADIR}/testing/baseline/${BUILDNAME}/${RUNTEST} ]; then BASELINE=${DATADIR}/testing/${BUILDNAME}/${RUNTEST} fi cd ${DATADIR}/testing/inputs run() { local cmd=$* if ! $cmd; then exit 1 fi } get_unzipped() { if [ -e ${1}.gz ]; then local tmp=`mktemp` gzip -cd ${1}.gz > ${tmp} echo ${tmp} else echo ${1} fi } check_result() { local result=`get_unzipped $1` local baseline=`get_unzipped $2` echo "diff $1 $2" if ! diff $result $baseline; then exit 1 fi } cmtk-3.3.1/testing/gui/triplanarTestDriver.sh000066400000000000000000000105661276303427400213010ustar00rootroot00000000000000#!/bin/sh ## ## Copyright 1997-2009 Torsten Rohlfing ## Copyright 2004-2009 SRI International ## ## This file is part of the Computational Morphometry Toolkit. ## ## http://www.nitrc.org/projects/cmtk/ ## ## The Computational Morphometry Toolkit is free software: you can ## redistribute it and/or modify it under the terms of the GNU General Public ## License as published by the Free Software Foundation, either version 3 of ## the License, or (at your option) any later version. ## ## The Computational Morphometry Toolkit is distributed in the hope that it ## will be useful, but WITHOUT ANY WARRANTY; without even the implied ## warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License along ## with the Computational Morphometry Toolkit. If not, see ## . ## ## $Revision: 918 $ ## ## $LastChangedDate: 2009-11-30 13:18:53 -0800 (Mon, 30 Nov 2009) $ ## ## $LastChangedBy: torstenrohlfing $ ## BUILDNAME=$1 BINDIR=$2 DATADIR=$3 RUNTEST=$4 HOSTNAME=`uname -n` tmpdir=${BINDIR}/../testing/temporary/${HOSTNAME}/${RUNTEST} mkdir -p ${tmpdir} BASELINE=${DATADIR}/testing/baseline/${RUNTEST} if [ -d ${DATADIR}/testing/baseline/${BUILDNAME}/${RUNTEST} ]; then BASELINE=${DATADIR}/testing/${BUILDNAME}/${RUNTEST} fi cd ${DATADIR}/testing/inputs run() { cmd=$* echo "cd $PWD; $cmd" if $cmd; then return else exit 1 fi } run_eval() { cmd=$* echo "cd $PWD; $cmd" if eval "$cmd"; then return else exit 1 fi } get_unzipped() { if test -f ${1}.gz; then tmp=`mktemp` gzip -cd ${1}.gz > ${tmp} echo ${tmp} else echo ${1} fi } check_result() { baseline=`get_unzipped ${BASELINE}/$1` result=`get_unzipped ${tmpdir}/$1` if test ! -f ${result}; then echo "Results file ${result} does not exist" exit 1 fi if test ! -f ${baseline}; then echo "Baseline file ${baseline} does not exist" exit 1 fi echo "diff ${BASELINE}/$1 ${tmpdir}/$1" if diff $result $baseline; then return else exit 1 fi } check_results() { for r in $*; do check_result $r done } case ${RUNTEST} in triplanarDefault) run ${BINDIR}/triplanar --exec load pat001_pet.hdr export-panel ${tmpdir}/panel.ppm export-axial ${tmpdir}/axial.ppm export-coronal ${tmpdir}/coronal.ppm export-sagittal ${tmpdir}/sagittal.ppm for img in panel axial sagittal coronal; do if check_result ${img}.ppm; then echo $img ok. else exit $? fi done ;; triplanarZoom300) run ${BINDIR}/triplanar --exec load pat001_pet.hdr zoom 300 export-panel ${tmpdir}/panel.ppm check_result panel.ppm ;; triplanarZoom25) run ${BINDIR}/triplanar --exec load pat001_pet.hdr zoom 25 export-panel ${tmpdir}/panel.ppm check_result panel.ppm ;; triplanarSetPixelWindowLevel) run ${BINDIR}/triplanar --exec load pat002_ct.hdr crosshair off goto-pixel 32,20,0 window-level 500:0 export-panel ${tmpdir}/panel.ppm check_result panel.ppm ;; triplanarColormaps) run ${BINDIR}/triplanar --exec load parc1.hdr crosshair off window-level 116:58 colormap Labels export-axial ${tmpdir}/labels.ppm colormap Red export-axial ${tmpdir}/red.ppm colormap Rainbow export-axial ${tmpdir}/rainbow.ppm for img in labels red rainbow; do if check_result ${img}.ppm; then echo $img ok. else exit $? fi done ;; triplanarPhantomAx) run ${BINDIR}/triplanar --exec load phantom_ax.hdr crosshair off export-panel ${tmpdir}/panel.ppm check_result panel.ppm ;; triplanarPhantomSa) run ${BINDIR}/triplanar --exec load phantom_sa.hdr crosshair off export-panel ${tmpdir}/panel.ppm check_result panel.ppm ;; triplanarPhantomCo) run ${BINDIR}/triplanar --exec load phantom_co.hdr crosshair off export-panel ${tmpdir}/panel.ppm check_result panel.ppm ;; triplanarPhantomAxNrrd) run ${BINDIR}/triplanar --exec load phantom_ax.nhdr crosshair off export-panel ${tmpdir}/panel.ppm check_result panel.ppm ;; triplanarPhantomSaNrrd) run ${BINDIR}/triplanar --exec load phantom_sa.nhdr crosshair off export-panel ${tmpdir}/panel.ppm check_result panel.ppm ;; triplanarPhantomCoNrrd) run ${BINDIR}/triplanar --exec load phantom_co.nhdr crosshair off export-panel ${tmpdir}/panel.ppm check_result panel.ppm ;; esac if [ "${tmpdir}" != "" ]; then rm -rf ${tmpdir} fi cmtk-3.3.1/testing/gui/triplanarTestDriver.sh.in000066400000000000000000000110131276303427400216720ustar00rootroot00000000000000#!/bin/sh ## ## Copyright 1997-2009 Torsten Rohlfing ## Copyright 2004-2009 SRI International ## ## This file is part of the Computational Morphometry Toolkit. ## ## http://www.nitrc.org/projects/cmtk/ ## ## The Computational Morphometry Toolkit is free software: you can ## redistribute it and/or modify it under the terms of the GNU General Public ## License as published by the Free Software Foundation, either version 3 of ## the License, or (at your option) any later version. ## ## The Computational Morphometry Toolkit is distributed in the hope that it ## will be useful, but WITHOUT ANY WARRANTY; without even the implied ## warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License along ## with the Computational Morphometry Toolkit. If not, see ## . ## ## $Revision: 1092 $ ## ## $LastChangedDate: 2009-12-22 12:52:08 -0800 (Tue, 22 Dec 2009) $ ## ## $LastChangedBy: torstenrohlfing $ ## RUNTEST=$1 VALGRIND=$2 BUILDNAME=@BUILDNAME@ DATADIR=@CMTK_DATA_ROOT@/testing/inputs BINDIR=@EXECUTABLE_OUTPUT_PATH@/@CMAKE_BUILD_TYPE@ if [ ! -d ${BINDIR} ]; then BINDIR=@EXECUTABLE_OUTPUT_PATH@ fi BASELINE_DEFAULT=@CMTK_DATA_ROOT@/testing/baseline/${RUNTEST} BASELINE=${BINDIR}/../testing/baseline/${RUNTEST} if [ ! -d ${BASELINE} ]; then BASELINE=${BASELINE_DEFAULT} fi HOSTNAME=`uname -n` tmpdir=${BINDIR}/../testing/temporary/${HOSTNAME}/${RUNTEST} mkdir -p ${tmpdir} cd ${DATADIR} run() { cmd=$* echo "cd $PWD; $cmd" if $cmd; then return else exit 1 fi } run_eval() { cmd=$* echo "cd $PWD; $cmd" if eval "$cmd"; then return else exit 1 fi } get_unzipped() { if test -f ${1}.gz; then tmp=`mktemp` gzip -cd ${1}.gz > ${tmp} echo ${tmp} else echo ${1} fi } check_result() { baseline=`get_unzipped ${BASELINE}/$1` result=`get_unzipped ${tmpdir}/$1` if test ! -f ${result}; then echo "Results file ${result} does not exist" exit 1 fi if test ! -f ${baseline}; then echo "Baseline file ${baseline} does not exist" exit 1 fi echo "diff ${BASELINE}/$1 ${tmpdir}/$1" if diff $result $baseline; then return else exit 1 fi } check_results() { for r in $*; do check_result $r done } case ${RUNTEST} in triplanarDefault) run ${BINDIR}/triplanar --exec load pat001_pet.hdr export-panel ${tmpdir}/panel.ppm export-axial ${tmpdir}/axial.ppm export-coronal ${tmpdir}/coronal.ppm export-sagittal ${tmpdir}/sagittal.ppm for img in panel axial sagittal coronal; do if check_result ${img}.ppm; then echo $img ok. else exit $? fi done ;; triplanarZoom300) run ${BINDIR}/triplanar --exec load pat001_pet.hdr zoom 300 export-panel ${tmpdir}/panel.ppm check_result panel.ppm ;; triplanarZoom25) run ${BINDIR}/triplanar --exec load pat001_pet.hdr zoom 25 export-panel ${tmpdir}/panel.ppm check_result panel.ppm ;; triplanarSetPixelWindowLevel) run ${BINDIR}/triplanar --exec load pat002_ct.hdr crosshair off goto-pixel 32,20,0 window-level 500:0 export-panel ${tmpdir}/panel.ppm check_result panel.ppm ;; triplanarColormaps) run ${BINDIR}/triplanar --exec load parc1.hdr crosshair off window-level 116:58 colormap Labels export-axial ${tmpdir}/labels.ppm colormap Red export-axial ${tmpdir}/red.ppm colormap Rainbow export-axial ${tmpdir}/rainbow.ppm for img in labels red rainbow; do if check_result ${img}.ppm; then echo $img ok. else exit $? fi done ;; triplanarPhantomAx) run ${BINDIR}/triplanar --exec load phantom_ax.hdr crosshair off export-panel ${tmpdir}/panel.ppm check_result panel.ppm ;; triplanarPhantomSa) run ${BINDIR}/triplanar --exec load phantom_sa.hdr crosshair off export-panel ${tmpdir}/panel.ppm check_result panel.ppm ;; triplanarPhantomCo) run ${BINDIR}/triplanar --exec load phantom_co.hdr crosshair off export-panel ${tmpdir}/panel.ppm check_result panel.ppm ;; triplanarPhantomAxNrrd) run ${BINDIR}/triplanar --exec load phantom_ax.nhdr crosshair off export-panel ${tmpdir}/panel.ppm check_result panel.ppm ;; triplanarPhantomSaNrrd) run ${BINDIR}/triplanar --exec load phantom_sa.nhdr crosshair off export-panel ${tmpdir}/panel.ppm check_result panel.ppm ;; triplanarPhantomCoNrrd) run ${BINDIR}/triplanar --exec load phantom_co.nhdr crosshair off export-panel ${tmpdir}/panel.ppm check_result panel.ppm ;; esac if [ "${tmpdir}" != "" ]; then rm -rf ${tmpdir} fi cmtk-3.3.1/testing/libs/000077500000000000000000000000001276303427400150725ustar00rootroot00000000000000cmtk-3.3.1/testing/libs/Base/000077500000000000000000000000001276303427400157445ustar00rootroot00000000000000cmtk-3.3.1/testing/libs/Base/CMakeLists.txt000066400000000000000000000054401276303427400205070ustar00rootroot00000000000000## ## Copyright 1997-2009 Torsten Rohlfing ## ## Copyright 2004-2012 SRI International ## ## This file is part of the Computational Morphometry Toolkit. ## ## http://www.nitrc.org/projects/cmtk/ ## ## The Computational Morphometry Toolkit is free software: you can ## redistribute it and/or modify it under the terms of the GNU General Public ## License as published by the Free Software Foundation, either version 3 of ## the License, or (at your option) any later version. ## ## The Computational Morphometry Toolkit is distributed in the hope that it ## will be useful, but WITHOUT ANY WARRANTY; without even the implied ## warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License along ## with the Computational Morphometry Toolkit. If not, see ## . ## ## $Revision: 4510 $ ## ## $LastChangedDate: 2012-09-14 14:34:59 -0700 (Fri, 14 Sep 2012) $ ## ## $LastChangedBy: torstenrohlfing $ ## # ========================================== # Setup binary test drivers SET(CMTK_LIBRARIES "cmtkBase;cmtkNumerics;cmtkSystem") SET(DRIVERS libBaseTests) FOREACH(D ${DRIVERS}) ADD_EXECUTABLE(${D} ${D}.cxx) TARGET_LINK_LIBRARIES(${D} ${CMTK_LIBRARIES} ${CMTK_SYSTEM_LIBRARIES}) ENDFOREACH(D ${DRIVERS}) # ========================================== # Tests for "cmtk::DataGrid" class SET(Tests ${Tests} DataGridMatches) # ========================================== # Tests for "cmtk::Region" class SET(Tests ${Tests} RegionSizeInt RegionSizeFloat) # ========================================== # Tests for "cmtk::ParametricPlane" class SET(Tests ${Tests} ParametricPlaneMirror ParametricPlaneMirrorOffset) # ========================================== # Tests for "cmtk::ScalarImage" class SET(Tests ${Tests} ScalarImage) # ========================================== # Tests for "cmtk::SplineWarpXform" class SET(Tests ${Tests} SplineWarpXform SplineWarpXformInverse) # ========================================== # Tests for "cmtk::SymmetricMatrix" class SET(Tests ${Tests} SymmetricMatrix SymmetricMatrixResize SymmetricMatrixEqual) # ========================================== # Tests for "cmtk::TypedArray" class SET(Tests ${Tests} TypedArrayMatchHistogram1 TypedArrayMatchHistogram2 TypedArrayMatchHistogram3 TypedArrayMatchHistogram4) # ========================================== # Tests for "cmtk::UniformVolume" class SET(Tests ${Tests} UniformVolumeMatches) # ========================================== # Tests for "cmtk::MathUtil" functions SET(Tests ${Tests} EigenSystemSymmetricMatrix3x3 MathUtilUniformRandom) FOREACH(T ${Tests}) ADD_TEST(${T} ${EXECUTABLE_OUTPUT_PATH}/libBaseTests ${T}) ENDFOREACH(T Tests) cmtk-3.3.1/testing/libs/Base/cmtkDataGridTests.txx000066400000000000000000000032771276303427400221030ustar00rootroot00000000000000/* // // Copyright 2004-2010 SRI International // Copyright 1997-2009 Torsten Rohlfing // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4427 $ // // $LastChangedDate: 2012-06-12 11:23:22 -0700 (Tue, 12 Jun 2012) $ // // $LastChangedBy: torstenrohlfing $ // */ #include // test "GridMatches" function int testDataGridMatches() { const int dims1_array[3] = { 10, 11, 12 }; cmtk::DataGrid::IndexType dims1 = cmtk::DataGrid::IndexType::FromPointer( dims1_array ); const int dims2_array[3] = { 11, 12, 10 }; cmtk::DataGrid::IndexType dims2 = cmtk::DataGrid::IndexType::FromPointer( dims2_array ); cmtk::DataGrid grid1a( dims1 ); cmtk::DataGrid grid1b( dims1 ); cmtk::DataGrid grid2( dims2 ); if ( !grid1a.GridMatches( grid1b ) ) return 1; if ( !grid1b.GridMatches( grid1a ) ) return 1; if ( grid1a.GridMatches( grid2 ) ) return 1; return 0; } cmtk-3.3.1/testing/libs/Base/cmtkEigenSystemSymmetricMatrix3x3Tests.txx000066400000000000000000000055151276303427400262750ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2010 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 2398 $ // // $LastChangedDate: 2010-10-05 14:54:37 -0700 (Tue, 05 Oct 2010) $ // // $LastChangedBy: torstenrohlfing $ // */ #include #include #include #include template bool testMatrixEigensystem ( const T m[3][3], const T evals[3], const T evecs[3][3] ) { const T tolerance = 1e-6; cmtk::Matrix3x3 matrix( &m[0][0] ); cmtk::EigenSystemSymmetricMatrix3x3 es( matrix ); // compare eigenvalues for ( size_t i = 0; i<3; ++i ) { if ( fabs( evals[i] - es.GetNthEigenvalue(i) ) > tolerance ) { std::cerr << "Eigenvalues do not match." << std::endl << " ACTUAL: " << es.GetNthEigenvalue(0) << " " << es.GetNthEigenvalue(1) << " " << es.GetNthEigenvalue(2) << std::endl << " BASELN: " << evals[0] << " " << evals[1] << " " << evals[2] << std::endl; return false; } } // compare eigenvectors for ( size_t i = 0; i<3; ++i ) { const cmtk::FixedVector<3,T> actual = es.GetNthEigenvector( i ); for ( size_t j = 0; j<3; ++j ) { if ( fabs( evecs[i][j] - actual[j] ) > tolerance ) { std::cerr << "Eigenvectors do not match." << std::endl; for ( size_t ii = 0; ii<3; ++ii ) { const cmtk::FixedVector<3,T> ev = es.GetNthEigenvector( ii ); std::cerr << " ACTUAL: " << ev[0] << " " << ev[1] << " " << ev[2] << std::endl; std::cerr << " BASELN: " << evecs[ii][0] << " " << evecs[ii][1] << " " << evecs[ii][2] << " " << std::endl; } return false; } } } return true; } // test eigenvalues computation int testEigenSystemSymmetricMatrix3x3() { const double dm1[3][3] = { { 1, 0, 0 }, { 0, -2, 0 }, { 0, 0, 3 } }; const double evals1[3] = { 1, -2, 3 }; const double evecs1[3][3] = { { 1, 0, 0 }, { 0, 1, 0 }, { 0, 0, 1 } }; if ( ! testMatrixEigensystem( dm1, evals1, evecs1 ) ) { return 1; } return 0; } cmtk-3.3.1/testing/libs/Base/cmtkMathUtilTests.txx000066400000000000000000000043211276303427400221420ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // Copyright 2004-2009 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 2398 $ // // $LastChangedDate: 2010-10-05 14:54:37 -0700 (Tue, 05 Oct 2010) $ // // $LastChangedBy: torstenrohlfing $ // */ #include #include // test whether uniform random numbers are reasonably uniform; does NOT test whether they are random! int testMathUtilUniformRandom() { const size_t nSamples = 100000; const size_t nBins = 50; unsigned int histogram[nBins]; memset( histogram, 0, sizeof( histogram ) ); for ( size_t n = 0; n < nSamples; ++n ) { ++histogram[static_cast( nBins * cmtk::MathUtil::UniformRandom() )]; } // allow +/- 5% deviation in each bin. This seems to be what Numerical Recipes can do. const unsigned int lower = static_cast( 0.95 * nSamples / nBins ); const unsigned int upper = static_cast( 1.05 * nSamples / nBins ); unsigned int countOutside = 0; for ( size_t n = 0; n < nBins; ++n ) { if ( (histogram[n] < lower ) || (histogram[n] > upper ) ) ++countOutside; } const unsigned int threshold = static_cast( 0.05 * nBins ); if ( countOutside > threshold ) { std::cerr << "Too many bins outside +/- 5% range. Actual: " << countOutside << ", threshold: " << threshold << std::endl; return 1; } return 0; } cmtk-3.3.1/testing/libs/Base/cmtkParametricPlaneTests.txx000066400000000000000000000075231276303427400234710ustar00rootroot00000000000000/* // // Copyright 2010 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 2401 $ // // $LastChangedDate: 2010-10-05 16:09:13 -0700 (Tue, 05 Oct 2010) $ // // $LastChangedBy: torstenrohlfing $ // */ #include #include #include #include // test parametric plane reflection with zero offset int testParametricPlaneMirror() { typedef cmtk::ParametricPlane PlaneType; typedef PlaneType::CoordinateVectorType VectorType; typedef cmtk::AffineXform::MatrixType MatrixType; VectorType origin; std::fill( origin.begin(), origin.end(), 0 ); PlaneType plane; plane.SetOrigin( origin ); plane.SetTheta( cmtk::Units::Degrees( 30 ) ); plane.SetPhi( cmtk::Units::Degrees( 45 ) ); for ( double rho = 0; rho < 21; rho += 20 ) { plane.SetRho( rho ); VectorType v; v[0] = v[1] = v[2] = 1; VectorType pv = v; plane.MirrorInPlace( pv ); MatrixType pm = plane.GetMirrorXformMatrix(); VectorType mv = v * pm; if ( sqrt( (mv-pv).SumOfSquares() ) > 1e-5 ) { std::cerr << "ParametricPlane::MirrorInPlace and mirror matrix produce different results." << std::endl; return 1; } mv *= pm; if ( sqrt( (mv-v).SumOfSquares() ) > 1e-5 ) { std::cerr << "ParametricPlane mirror matrix applied twice does not return to original point." << std::endl; return 1; } plane.MirrorInPlace( pv ); if ( sqrt( (pv-v).SumOfSquares() ) > 1e-5 ) { std::cerr << "ParametricPlane::MirrorInPlane applied twice does not return to original point." << std::endl; return 1; } } return 0; } // test parametric plane reflection with non-zero offset int testParametricPlaneMirrorOffset() { typedef cmtk::ParametricPlane PlaneType; typedef PlaneType::CoordinateVectorType VectorType; typedef cmtk::AffineXform::MatrixType MatrixType; VectorType origin; origin[0] = 10; origin[1] = 20; origin[2] = 30; PlaneType plane; plane.SetOrigin( origin ); plane.SetTheta( cmtk::Units::Degrees( 30 ) ); plane.SetPhi( cmtk::Units::Degrees( 45 ) ); for ( double rho = 0; rho < 21; rho += 20 ) { plane.SetRho( rho ); VectorType v; v[0] = v[1] = v[2] = 1; VectorType pv = v; plane.MirrorInPlace( pv ); MatrixType pm = plane.GetMirrorXformMatrix(); VectorType mv = v * pm; if ( sqrt( (mv-pv).SumOfSquares() ) > 1e-5 ) { std::cerr << "ParametricPlane::MirrorInPlace and mirror matrix produce different results." << std::endl; return 1; } mv *= pm; if ( sqrt( (mv-v).SumOfSquares() ) > 1e-5 ) { std::cerr << "ParametricPlane mirror matrix applied twice does not return to original point." << std::endl; return 1; } plane.MirrorInPlace( pv ); if ( sqrt( (pv-v).SumOfSquares() ) > 1e-5 ) { std::cerr << "ParametricPlane::MirrorInPlane applied twice does not return to original point." << std::endl; return 1; } } return 0; } cmtk-3.3.1/testing/libs/Base/cmtkRegionTests.txx000066400000000000000000000050771276303427400216470ustar00rootroot00000000000000/* // // Copyright 2010, 2012 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4427 $ // // $LastChangedDate: 2012-06-12 11:23:22 -0700 (Tue, 12 Jun 2012) $ // // $LastChangedBy: torstenrohlfing $ // */ #include #include // test "Size" member function for int regions int testRegionSizeInt() { const int regionFromArray[] = { 1, 2, 3 }; const int regionToArray[] = { 2, 4, 7 }; const cmtk::Region<3,int> r3( (cmtk::FixedVector<3,int>::FromPointer( regionFromArray )), cmtk::FixedVector<3,int>::FromPointer( regionToArray ) ); if ( r3.Size() != 8 ) return 1; const cmtk::Region<2,int> r2( (cmtk::FixedVector<2,int>::FromPointer( regionFromArray )), cmtk::FixedVector<2,int>::FromPointer( regionToArray ) ); if ( r2.Size() != 2 ) return 1; const cmtk::Region<1,int> r1( (cmtk::FixedVector<1,int>::FromPointer( regionFromArray )), cmtk::FixedVector<1,int>::FromPointer( regionToArray ) ); if ( r1.Size() != 1 ) return 1; return 0; } // test "Size" member function for float regions int testRegionSizeFloat() { const float regionFromArray[] = { 1, 2, 3 }; const float regionToArray[] = { 2, 4, 7 }; const cmtk::Region<3,float> r3( (cmtk::FixedVector<3,float>::FromPointer( regionFromArray )), cmtk::FixedVector<3,float>::FromPointer( regionToArray ) ); if ( r3.Size() != 8 ) return 1; const cmtk::Region<2,float> r2( (cmtk::FixedVector<2,float>::FromPointer( regionFromArray )), cmtk::FixedVector<2,float>::FromPointer( regionToArray ) ); if ( r2.Size() != 2 ) return 1; const cmtk::Region<1,float> r1( (cmtk::FixedVector<1,float>::FromPointer( regionFromArray )), cmtk::FixedVector<1,float>::FromPointer( regionToArray ) ); if ( r1.Size() != 1 ) return 1; return 0; } cmtk-3.3.1/testing/libs/Base/cmtkScalarImageTests.txx000066400000000000000000000023431276303427400225650ustar00rootroot00000000000000/* // // Copyright 2004-2010 SRI International // Copyright 1997-2009 Torsten Rohlfing // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 2398 $ // // $LastChangedDate: 2010-10-05 14:54:37 -0700 (Tue, 05 Oct 2010) $ // // $LastChangedBy: torstenrohlfing $ // */ #include // test "GridMatches" function int testScalarImage() { cmtk::ScalarImage image; cmtk::ScalarImage image2( image ); return 0; } cmtk-3.3.1/testing/libs/Base/cmtkSplineWarpXformTests.txx000066400000000000000000000333751276303427400235260ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2012 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4427 $ // // $LastChangedDate: 2012-06-12 11:23:22 -0700 (Tue, 12 Jun 2012) $ // // $LastChangedBy: torstenrohlfing $ // */ #include #include #include #include int testSplineWarpXform() { return 0; } int testSplineWarpXformInverse() { // parameters for the current WarpSingleLevel baseline transformation const cmtk::Types::Coordinate domain[3] = { 357.1875, 357.1875, 125 }; const int dims[3] = { 7, 7, 4 }; const size_t nparameters = 3 * 7 * 7 * 4; cmtk::Types::Coordinate parameters[nparameters] = { -88.706249, -84.998951480000002, -133.41214640000001, 0.58282801900000003, -85.552844629999996, -132.6599301, 89.312814939999996, -86.301460410000004, -132.5876548, 177.77193539999999, -85.646700699999997, -133.84530789999999, 266.44951250000003, -79.496837150000005, -133.9639134, 355.83432440000001, -82.938156820000003, -133.6333545, 444.48369489999999, -85.145062800000005, -134.2765378, -87.318911839999998, 3.4110965480000002, -132.63593660000001, 2.617442912, -0.38841385519999999, -127.8795088, 90.052308600000003, -4.7583292430000004, -131.88906729999999, 179.2010363, 0.1205040694, -133.54224679999999, 264.80709460000003, 12.066754080000001, -130.28005279999999, 357.41610070000002, 7.3845064359999997, -127.76716690000001, 445.47999440000001, 3.1919038909999999, -132.9684795, -83.704470520000001, 92.192599630000004, -132.28900619999999, 4.9322056060000001, 90.245003139999994, -128.26304400000001, 87.099999409999995, 74.254342940000001, -135.77073709999999, 170.00687600000001, 88.896090749999999, -155.3283208, 273.92401669999998, 93.242624550000002, -140.88084140000001, 361.17963400000002, 89.683891509999995, -130.97434670000001, 445.75771220000001, 91.876312139999996, -133.26873420000001, -81.061337280000004, 181.6535792, -133.0931166, 1.0531317360000001, 188.02919120000001, -137.56335189999999, 89.184044760000006, 178.8696482, -159.36757170000001, 174.015658, 183.60500909999999, -151.0189499, 267.2078841, 179.12156770000001, -144.6624151, 359.74886040000001, 176.8779567, -137.3049566, 444.22113439999998, 180.80086940000001, -133.6307272, -83.727375629999997, 267.29410710000002, -133.48054730000001, 1.7494822249999999, 266.0169631, -132.43946500000001, 99.806269929999999, 267.21390309999998, -150.58490649999999, 177.13017690000001, 262.75285589999999, -147.6204467, 260.4250389, 252.11927119999999, -145.32091500000001, 359.6639907, 257.1055576, -136.3139481, 445.63570859999999, 268.85830870000001, -133.20231140000001, -87.196138289999993, 356.09219039999999, -131.7662282, 5.5288343080000004, 348.41581830000001, -127.61307909999999, 93.661780930000006, 350.04663319999997, -134.64285319999999, 179.84697249999999, 356.04154419999998, -138.72237849999999, 263.79976570000002, 351.84695269999997, -134.74277140000001, 354.08110850000003, 354.72597280000002, -127.8249725, 446.35394769999999, 356.2593943, -132.68096320000001, -88.868390099999999, 445.57343600000002, -132.74548110000001, 0.13575482220000001, 444.76912679999998, -131.80384480000001, 88.728966940000007, 444.62619749999999, -132.15681269999999, 177.92944069999999, 446.30323390000001, -133.03929260000001, 266.35346240000001, 444.6431498, -132.9607939, 355.45975850000002, 445.66228130000002, -133.26875509999999, 444.53735660000001, 445.56110649999999, -133.65817060000001, -87.595741469999993, -85.615120719999993, -5.958190739, 0.44238839149999998, -98.505404619999993, -2.2983611289999999, 91.516483010000002, -100.0147607, -5.1136814890000002, 181.5319556, -104.2308865, -5.8053919389999997, 268.33913130000002, -88.234155250000001, -6.6261121190000001, 357.53476169999999, -84.906273350000006, -3.6736957480000001, 445.84451159999998, -85.767281420000003, -6.19765224, -84.004465359999998, 1.1194690920000001, -2.3237842579999999, 7.0343907919999999, 2.887315584, 9.3485539650000007, 85.201014270000002, 3.2166232570000002, -2.638737415, 185.81284930000001, 3.2168928389999998, -5.0315402049999998, 263.85491830000001, -5.885358095, -1.5863716800000001, 355.65862499999997, -0.22509619959999999, 9.7554431780000002, 446.56552870000002, 0.91509386459999997, -1.77461844, -76.155025309999999, 91.465387050000004, -4.5014921750000001, -0.025622953949999999, 91.082948139999999, 36.132634940000003, 88.895439049999993, 91.715001909999998, 5.5567657759999998, 175.59212350000001, 91.715271490000006, -19.827600650000001, 267.31410360000001, 91.71554107, -18.57972865, 355.65862499999997, 92.067915659999997, 15.83988965, 447.8203795, 94.976408219999996, -1.8117929740000001, -89.183814949999999, 182.31435640000001, -0.18152812469999999, -0.025622953949999999, 180.213111, 29.576301829999998, 88.895439049999993, 180.21338059999999, -50.387582109999997, 177.81650099999999, 187.63108, -6.3127768949999998, 266.73756300000002, 188.21391969999999, -39.724707619999997, 355.65862499999997, 194.76894530000001, 12.719229840000001, 448.49040489999999, 180.30507940000001, 6.4192943600000003, -86.124877290000001, 266.36517220000002, 0.81955630999999995, -0.025622953949999999, 268.71148959999999, 6.6378779159999999, 88.895439049999993, 268.71175920000002, -8.5170915610000009, 177.81650099999999, 268.71202879999998, -40.255468520000001, 266.73756300000002, 268.71229840000001, -11.29851287, 355.65862499999997, 268.71256799999998, 12.631458200000001, 455.28980799999999, 262.68077749999998, 3.5113257330000001, -83.442058020000005, 352.86904729999998, -1.6556360590000001, -0.025622953949999999, 357.20986829999998, 18.933949649999999, 92.201104720000004, 357.21013790000001, -5.9082610569999998, 185.02496339999999, 357.21040749999997, -38.860956639999998, 265.44028609999998, 357.21067699999998, -14.704373049999999, 355.65862499999997, 357.2109466, 9.0930626809999993, 444.68610519999999, 356.86132309999999, -1.78315417, -87.485908379999998, 444.2545389, -4.8936582450000001, 3.2569376710000002, 444.43092489999998, 0.096468254239999995, 91.317244149999993, 442.34335479999999, -6.8668513320000004, 174.63876999999999, 450.46435989999998, -5.3950795060000001, 265.36603500000001, 445.70905570000002, -3.9132990560000001, 354.38166330000001, 445.87286769999997, -4.1942974719999997, 445.27059109999999, 444.60602310000002, -6.1777497380000002, -87.057384740000003, 11 -86.151767390000003, 121.0374534, 1.7057175179999999, -96.19556394, 122.554754, 89.579146320000007, -104.5236975, 118.37620750000001, 179.89012020000001, -104.9898585, 117.0390142, 267.55622369999998, -91.202323030000002, 116.7009912, 355.50636229999998, -91.482698450000001, 121.1113952, 445.60660159999998, -86.026599090000005, 120.60045580000001, -84.171902759999995, -0.01482944089, 121.7216818, 7.2181945880000002, 3.0001385580000002, 107.25279829999999, 80.852307519999997, 3.0004081390000001, 107.9820965, 176.8311132, 3.0006777210000002, 120.5239799, 263.74011230000002, 3.0009473029999998, 120.63115879999999, 355.37072819999997, 3.0012168840000002, 104.91928830000001, 446.56621439999998, -2.4291254979999999, 119.3823773, -81.5035065, 90.071761339999995, 120.84644969999999, 0.20142214319999999, 90.574182809999996, 113.35627599999999, 89.122484139999997, 91.498786789999997, 128.82387059999999, 175.58544979999999, 101.4990564, 122.9002965, 268.38340210000001, 91.499325959999993, 120.5156942, 355.88567010000003, 91.499595540000001, 131.39708440000001, 444.7709438, 96.842661910000004, 119.9692496, -89.055928379999997, 182.5025977, 120.1122323, 0.20142214319999999, 179.9968959, 122.4469038, 89.122484139999997, 179.9971654, 122.601725, 178.04354609999999, 179.997435, 126.95405100000001, 266.96460810000002, 179.99770459999999, 109.6421946, 355.88567010000003, 180.75639219999999, 121.4688462, 447.20440389999999, 185.2565166, 129.8153983, -89.080491530000003, 271.57375159999998, 118.9826401, 0.20142214319999999, 268.49527449999999, 133.51210159999999, 89.122484139999997, 268.49554410000002, 123.2495995, 178.04354609999999, 268.49581369999999, 156.55418539999999, 266.96460810000002, 268.49608330000001, 126.9632696, 355.88567010000003, 268.49635280000001, 125.8377127, 456.11810259999999, 263.93876160000002, 124.1798611, -78.757516609999996, 354.1474331, 118.3106315, 0.20142214319999999, 356.99365319999998, 106.9986686, 80.916068390000007, 356.99392280000001, 122.5179942, 183.90640450000001, 356.99419230000001, 139.70791149999999, 274.36699199999998, 356.99446189999998, 128.8031953, 355.88567010000003, 356.9947315, 113.7361619, 431.15941249999997, 357.78121429999999, 120.79886980000001, -87.137463229999994, 444.211366, 121.661636, 3.032267085, 442.4181438, 122.02193889999999, 90.122988419999999, 447.11456579999998, 119.2880096, 171.88924359999999, 443.27612269999997, 126.6859994, 266.08858839999999, 445.49284060000002, 123.3604062, 354.6575727, 447.03908790000003, 121.0512234, 444.93700189999998, 444.38999630000001, 120.7991143, -88.437080820000006, -85.679078360000005, 248.01994110000001, 0.90716082620000005, -87.579235199999999, 247.72265619999999, 90.422524789999997, -89.779116000000002, 247.09381070000001, 177.9337969, -99.358937710000006, 246.43002150000001, 268.200943, -91.2776566, 246.78031390000001, 356.61125870000001, -87.247415489999995, 247.33492340000001, 445.13719680000003, -85.752559039999994, 247.14701299999999, -88.667440450000001, 2.378526865, 248.15868649999999, 2.8663826349999999, -1.1642714359999999, 247.05720909999999, 89.347263699999999, -9.1457145759999996, 242.8314393, 174.87415490000001, -9.2338554550000005, 240.81855210000001, 271.94218269999999, -10.368706850000001, 243.2065522, 357.89138079999998, -12.21101354, 243.7249606, 445.92261459999997, 1.966463385, 247.059834, -88.912398969999998, 91.495463610000002, 248.26963259999999, 2.7270593399999998, 91.117068270000004, 244.86417639999999, 82.594282609999993, 92.770709539999999, 245.50448940000001, 178.86315379999999, 126.0907351, 229.5875126, 276.54213440000001, 100.338989, 241.38238050000001, 357.5755231, 92.732282940000005, 243.25368760000001, 448.57513119999999, 91.815159890000004, 244.83041610000001, -90.320214309999997, 180.10600199999999, 246.54689540000001, 3.6745516139999999, 186.6898391, 241.52917769999999, 89.349529239999995, 179.11642739999999, 239.24101239999999, 173.81978810000001, 162.44730430000001, 250.41458320000001, 265.41958579999999, 180.45522170000001, 247.98236069999999, 353.23377349999998, 192.21066049999999, 251.7287273, 445.66010940000001, 181.26521289999999, 246.71072000000001, -86.986800479999999, 269.28480500000001, 244.58283109999999, 0.51393186719999995, 268.80303909999998, 236.98863900000001, 89.621769709999995, 266.26399020000002, 238.93264049999999, 179.266944, 238.34859410000001, 265.1629236, 269.35367380000002, 256.8740277, 257.8996176, 361.80705540000002, 263.43252030000002, 246.61417800000001, 436.91912619999999, 269.42811289999997, 247.08582329999999, -87.15915038, 359.0044408, 247.66274319999999, -0.82839372629999997, 360.31312320000001, 241.9673334, 92.841495690000002, 355.37873960000002, 250.24562259999999, 175.9934432, 348.99376549999999, 257.76013139999998, 266.66541599999999, 356.44961849999999, 252.0638975, 353.65442949999999, 358.05145049999999, 245.86233609999999, 442.59605629999999, 357.38704439999998, 247.363855, -88.558712749999998, 445.28969480000001, 248.99420019999999, 0.055054464290000001, 448.6656423, 248.6825187, 89.390803320000003, 450.3772573, 249.60217069999999, 177.06173150000001, 446.93556389999998, 250.2954866, 266.63590900000003, 444.83473020000002, 249.33391330000001, 355.91280690000002, 445.81271129999999, 248.44589859999999, 444.89687249999997, 445.23544709999999 }; cmtk::CoordinateVector::SmartPtr vParameters( new cmtk::CoordinateVector( sizeof( nparameters ), parameters, false /*free*/ ) ); cmtk::SplineWarpXform splineWarp( cmtk::FixedVector<3,cmtk::Types::Coordinate>::FromPointer( domain ), cmtk::SplineWarpXform::ControlPointIndexType::FromPointer( dims ), vParameters ); int failed = 0, total = 0; #pragma omp parallel for reduction(+:failed) reduction(+:total) for ( int k = 0; k < (int)domain[2]; k += 4 ) { cmtk::Xform::SpaceVectorType v, vX, u; v[2] = k; for ( int j = 0; j < (int)domain[1]; j += 8 ) { v[1] = j; for ( int i = 0; i < (int)domain[0]; i += 8 ) { v[0] = i; ++total; vX = splineWarp.Apply( v ); const bool success = splineWarp.ApplyInverse( vX, u, 0.1 /*accuracy*/ ); if ( ! success ) { ++failed; } } } } std::cerr << "Inversion failed for " << failed << " out of " << total << " points." << std::endl; return failed != 0; } cmtk-3.3.1/testing/libs/Base/cmtkSymmetricMatrixTests.txx000066400000000000000000000061241276303427400235570ustar00rootroot00000000000000/* // // Copyright 2010-2011 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 3142 $ // // $LastChangedDate: 2011-04-14 13:32:28 -0700 (Thu, 14 Apr 2011) $ // // $LastChangedBy: torstenrohlfing $ // */ #include #include // test symmetric behaviour int testSymmetricMatrix() { cmtk::SymmetricMatrix m( 2 ); m(0,0) = -1; m(1,1) = -2; if ( m(0,0) != -1 ) { cmtk::StdErr << "testSymmetricMatrix test failed at label A\n"; return 1; } if ( m(1,1) != -2 ) { cmtk::StdErr << "testSymmetricMatrix test failed at label B\n"; return 1; } m(0,1) = 0; m(1,0) = 1; if ( m(0,1) != 1 ) { cmtk::StdErr << "testSymmetricMatrix test failed at label C\n"; return 1; } m(0,1) = 2; if ( m(1,0) != 2 ) { cmtk::StdErr << "testSymmetricMatrix test failed at label D\n"; return 1; } if ( m(0,0) != -1 ) { cmtk::StdErr << "testSymmetricMatrix test failed at label E\n"; return 1; } if ( m(1,1) != -2 ) { cmtk::StdErr << "testSymmetricMatrix test failed at label F\n"; return 1; } return 0; } // test resize functionality int testSymmetricMatrixResize() { cmtk::SymmetricMatrix m( 1 ); m(0,0) = 1; m.Resize( 2 ); m(0,1) = 2; m(1,1) = 3; if ( m(0,0) != 1 ) { cmtk::StdErr << "testSymmetricMatrixResize failed at label A\n"; return 1; } if ( m(1,0) != 2 ) { cmtk::StdErr << "testSymmetricMatrixResize failed at label B\n"; return 1; } if ( m(1,1) != 3 ) { cmtk::StdErr << "testSymmetricMatrixResize failed at label C\n"; return 1; } m.Resize( 1 ); if ( m(0,0) != 1 ) { cmtk::StdErr << "testSymmetricMatrixResize failed at label D\n"; return 1; } return 0; } // test equality/inequality operators int testSymmetricMatrixEqual() { cmtk::SymmetricMatrix m1( 1 ); cmtk::SymmetricMatrix m2( 2 ); if ( m1 == m2 ) { cmtk::StdErr << "testSymmetricMatrixEqual failed at label A\n"; return 1; } m1.Resize( 2 ); m1(0,0) = m2(0,0) = 1; m1(1,0) = m2(1,0) = 2; m1(1,1) = m2(1,1) = 3; if ( m1 != m2 ) { cmtk::StdErr << "testSymmetricMatrixEqual failed at label B\n"; return 1; } return 0; } cmtk-3.3.1/testing/libs/Base/cmtkTypedArrayFunctionHistogramMatchingTests.txx000066400000000000000000000146601276303427400275450ustar00rootroot00000000000000/* // // Copyright 2004-2010 SRI International // Copyright 1997-2009 Torsten Rohlfing // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 2398 $ // // $LastChangedDate: 2010-10-05 14:54:37 -0700 (Tue, 05 Oct 2010) $ // // $LastChangedBy: torstenrohlfing $ // */ #include #include #include #include // test "TypedArray::MatchHistogramToReference" function int testTypedArrayMatchHistogram1() { float data1[] = { 0, 0, 0, 1, 1, 1, 2, 2, 2, 3, 3, 3 }; cmtk::TypedArray::SmartPtr array1( cmtk::TypedArray::Create( cmtk::TYPE_FLOAT, data1, sizeof( data1 ) / sizeof( *data1 ), false /*freeArray*/ ) ); float data2[] = { 0, 0, 0, 1, 1, 1, 2, 2, 2, 3, 3, 3 }; cmtk::TypedArray::SmartPtr array2( cmtk::TypedArray::Create( cmtk::TYPE_FLOAT, data2, sizeof( data2 ) / sizeof( *data2 ), false /*freeArray*/ ) ); array2->ApplyFunctionObject( cmtk::TypedArrayFunctionHistogramMatching( *array2, *array1 ) ); float maxDeviation = 0; for ( size_t i = 0; iGetDataSize(); ++i ) { cmtk::Types::DataItem value; array2->Get( value, i ); maxDeviation = std::max( maxDeviation, fabs(value-data1[i]) ); } if ( maxDeviation > 0.01 ) return 1; else return 0; } // test "TypedArray::MatchHistogramToReference" function int testTypedArrayMatchHistogram2() { float data1[] = { 0, 0, 0, 1, 1, 1, 2, 2, 2, 3, 3, 3 }; cmtk::TypedArray::SmartPtr array1( cmtk::TypedArray::Create( cmtk::TYPE_FLOAT, data1, sizeof( data1 ) / sizeof( *data1 ), false /*freeArray*/ ) ); float data2[] = { 0.1, 0.1, 0.5, 0.5, 2.5, 2.5, 3.0, 3.0 }; float base2[] = { 0.0, 0.0, 1.0, 1.0, 2.0, 2.0, 3.0, 3.0 }; cmtk::TypedArray::SmartPtr array2( cmtk::TypedArray::Create( cmtk::TYPE_FLOAT, data2, sizeof( data2 ) / sizeof( *data2 ), false /*freeArray*/ ) ); array2->ApplyFunctionObject( cmtk::TypedArrayFunctionHistogramMatching( *array2, *array1 ) ); for ( size_t i = 0; iGetDataSize(); ++i ) { cmtk::Types::DataItem value; array2->Get( value, i ); } float maxDeviation = 0; for ( size_t i = 0; iGetDataSize(); ++i ) { cmtk::Types::DataItem value; array2->Get( value, i ); maxDeviation = std::max( maxDeviation, fabs(value-base2[i]) ); } if ( maxDeviation > 0.01 ) return 1; else return 0; } // test histogram matching from existing histograms int testTypedArrayMatchHistogram3() { float data1[] = { 0, 0, 0, 1, 1, 1, 2, 2, 2, 3, 3, 3 }; cmtk::TypedArray::SmartPtr array1( cmtk::TypedArray::Create( cmtk::TYPE_FLOAT, data1, sizeof( data1 ) / sizeof( *data1 ), false /*freeArray*/ ) ); float data2[] = { 0.1, 0.1, 0.5, 0.5, 2.5, 2.5, 3.0, 3.0 }; cmtk::TypedArray::SmartPtr array2a( cmtk::TypedArray::Create( cmtk::TYPE_FLOAT, data2, sizeof( data2 ) / sizeof( *data2 ), false /*freeArray*/ ) ); cmtk::TypedArray::SmartPtr array2b( cmtk::TypedArray::Create( cmtk::TYPE_FLOAT, data2, sizeof( data2 ) / sizeof( *data2 ), false /*freeArray*/ ) ); array2a->ApplyFunctionObject( cmtk::TypedArrayFunctionHistogramMatching( *array2a, *array1 ) ); cmtk::TypedArrayFunctionHistogramMatching::HistogramType::SmartPtr hist1( array1->GetHistogram( cmtk::TypedArrayFunctionHistogramMatching::DefaultNumberOfHistogramBins ) ); cmtk::TypedArrayFunctionHistogramMatching::HistogramType::SmartPtr hist2b( array2b->GetHistogram( cmtk::TypedArrayFunctionHistogramMatching::DefaultNumberOfHistogramBins ) ); array2b->ApplyFunctionObject( cmtk::TypedArrayFunctionHistogramMatching( *hist2b, *hist1 ) ); float maxDeviation = 0; for ( size_t i = 0; iGetDataSize(); ++i ) { cmtk::Types::DataItem a, b; array2a->Get( a, i ); array2b->Get( b, i ); maxDeviation = std::max( maxDeviation, fabs(a-b) ); } if ( maxDeviation > 1e-8 ) return 1; else return 0; } // test whether histogram matching breaks with unequal numbers of bins for both histograms. int testTypedArrayMatchHistogram4() { float data1[] = { 0, 0, 0, 1, 1, 1, 2, 2, 2, 3, 3, 3 }; cmtk::TypedArray::SmartPtr array1a( cmtk::TypedArray::Create( cmtk::TYPE_FLOAT, data1, sizeof( data1 ) / sizeof( *data1 ), false /*freeArray*/ ) ); cmtk::TypedArray::SmartPtr array1b( cmtk::TypedArray::Create( cmtk::TYPE_FLOAT, data1, sizeof( data1 ) / sizeof( *data1 ), false /*freeArray*/ ) ); float data2[] = { 0.1, 0.1, 0.5, 0.5, 2.5, 2.5, 3.0, 3.0 }; cmtk::TypedArray::SmartPtr array2a( cmtk::TypedArray::Create( cmtk::TYPE_FLOAT, data2, sizeof( data2 ) / sizeof( *data2 ), false /*freeArray*/ ) ); cmtk::TypedArray::SmartPtr array2b( cmtk::TypedArray::Create( cmtk::TYPE_FLOAT, data2, sizeof( data2 ) / sizeof( *data2 ), false /*freeArray*/ ) ); cmtk::TypedArrayFunctionHistogramMatching::HistogramType::SmartPtr hist1a( array1a->GetHistogram( cmtk::TypedArrayFunctionHistogramMatching::DefaultNumberOfHistogramBins ) ); cmtk::TypedArrayFunctionHistogramMatching::HistogramType::SmartPtr hist2a( array2a->GetHistogram( cmtk::TypedArrayFunctionHistogramMatching::DefaultNumberOfHistogramBins / 2 ) ); array2a->ApplyFunctionObject( cmtk::TypedArrayFunctionHistogramMatching( *hist2a, *hist1a ) ); cmtk::TypedArrayFunctionHistogramMatching::HistogramType::SmartPtr hist1b( array1b->GetHistogram( cmtk::TypedArrayFunctionHistogramMatching::DefaultNumberOfHistogramBins / 2 ) ); cmtk::TypedArrayFunctionHistogramMatching::HistogramType::SmartPtr hist2b( array2b->GetHistogram( cmtk::TypedArrayFunctionHistogramMatching::DefaultNumberOfHistogramBins ) ); array2b->ApplyFunctionObject( cmtk::TypedArrayFunctionHistogramMatching( *hist2b, *hist1b ) ); // here, we're only testing for crashes, leaks, etc. return 0; } cmtk-3.3.1/testing/libs/Base/cmtkTypedArrayTests.txx000066400000000000000000000022031276303427400224740ustar00rootroot00000000000000/* // // Copyright 2004-2010 SRI International // Copyright 1997-2009 Torsten Rohlfing // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 2398 $ // // $LastChangedDate: 2010-10-05 14:54:37 -0700 (Tue, 05 Oct 2010) $ // // $LastChangedBy: torstenrohlfing $ // */ #include #include #include cmtk-3.3.1/testing/libs/Base/cmtkUniformVolumeTests.txx000066400000000000000000000043371276303427400232310ustar00rootroot00000000000000/* // // Copyright 2004-2012 SRI International // // Copyright 1997-2010 Torsten Rohlfing // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4427 $ // // $LastChangedDate: 2012-06-12 11:23:22 -0700 (Tue, 12 Jun 2012) $ // // $LastChangedBy: torstenrohlfing $ // */ #include // test "GridMatches" function int testUniformVolumeMatches() { const int dims1[3] = { 10, 11, 12 }; const cmtk::Types::Coordinate size1a[3] = { 9, 10, 11 }; const cmtk::Types::Coordinate size1b[3] = { 10, 11, 12 }; const int dims2[3] = { 11, 12, 10 }; const cmtk::Types::Coordinate size2[3] = { 11, 12, 10 }; cmtk::UniformVolume volume1a( (cmtk::UniformVolume::IndexType::FromPointer( dims1 )), cmtk::FixedVector<3,cmtk::Types::Coordinate>::FromPointer( size1a ) ); cmtk::UniformVolume volume1b( (cmtk::UniformVolume::IndexType::FromPointer( dims1 )), cmtk::FixedVector<3,cmtk::Types::Coordinate>::FromPointer( size1b ) ); cmtk::UniformVolume volume1c( (cmtk::UniformVolume::IndexType::FromPointer( dims1 )), cmtk::FixedVector<3,cmtk::Types::Coordinate>::FromPointer( size1b ) ); cmtk::UniformVolume volume2( (cmtk::UniformVolume::IndexType::FromPointer( dims2 )), cmtk::FixedVector<3,cmtk::Types::Coordinate>::FromPointer( size2 ) ); if ( volume1a.GridMatches( volume1b ) ) return 1; if ( !volume1b.GridMatches( volume1c ) ) return 1; if ( volume1a.GridMatches( volume2 ) ) return 1; return 0; } cmtk-3.3.1/testing/libs/Base/libBaseTests.cxx000066400000000000000000000060771276303427400210660ustar00rootroot00000000000000/* // // Copyright 2004-2012 SRI International // // Copyright 1997-2009 Torsten Rohlfing // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4510 $ // // $LastChangedDate: 2012-09-14 14:34:59 -0700 (Fri, 14 Sep 2012) $ // // $LastChangedBy: torstenrohlfing $ // */ #include #include "cmtkDataGridTests.txx" #include "cmtkEigenSystemSymmetricMatrix3x3Tests.txx" #include "cmtkMathUtilTests.txx" #include "cmtkParametricPlaneTests.txx" #include "cmtkRegionTests.txx" #include "cmtkScalarImageTests.txx" #include "cmtkSplineWarpXformTests.txx" #include "cmtkSymmetricMatrixTests.txx" #include "cmtkTypedArrayFunctionHistogramMatchingTests.txx" #include "cmtkUniformVolumeTests.txx" int main( const int argc, const char* argv[] ) { cmtk::TestFunctionMap map; map.AddTest( "DataGridMatches", &testDataGridMatches ); map.AddTest( "EigenSystemSymmetricMatrix3x3", &testEigenSystemSymmetricMatrix3x3 ); map.AddTest( "MathUtilUniformRandom", &testMathUtilUniformRandom ); map.AddTest( "ParametricPlaneMirror", &testParametricPlaneMirror ); map.AddTest( "ParametricPlaneMirrorOffset", &testParametricPlaneMirrorOffset ); map.AddTest( "RegionSizeInt", &testRegionSizeInt ); map.AddTest( "RegionSizeFloat", &testRegionSizeFloat ); map.AddTest( "ScalarImage", &testScalarImage ); map.AddTest( "SplineWarpXform", &testSplineWarpXform ); map.AddTest( "SplineWarpXformInverse", &testSplineWarpXformInverse ); map.AddTest( "SymmetricMatrix", &testSymmetricMatrix ); map.AddTest( "SymmetricMatrixResize", &testSymmetricMatrixResize ); map.AddTest( "SymmetricMatrixEqual", &testSymmetricMatrixEqual ); map.AddTest( "TypedArrayMatchHistogram1", &testTypedArrayMatchHistogram1 ); map.AddTest( "TypedArrayMatchHistogram2", &testTypedArrayMatchHistogram2 ); map.AddTest( "TypedArrayMatchHistogram3", &testTypedArrayMatchHistogram3 ); map.AddTest( "TypedArrayMatchHistogram4", &testTypedArrayMatchHistogram4 ); map.AddTest( "UniformVolumeMatches", &testUniformVolumeMatches ); // is test name given on command line? if ( argc < 2 ) { } else { return map.RunTestByName( argv[1] ); } } cmtk-3.3.1/testing/libs/CMakeLists.txt000066400000000000000000000024331276303427400176340ustar00rootroot00000000000000## ## Copyright 1997-2009 Torsten Rohlfing ## ## Copyright 2004-2010, 2013 SRI International ## ## This file is part of the Computational Morphometry Toolkit. ## ## http://www.nitrc.org/projects/cmtk/ ## ## The Computational Morphometry Toolkit is free software: you can ## redistribute it and/or modify it under the terms of the GNU General Public ## License as published by the Free Software Foundation, either version 3 of ## the License, or (at your option) any later version. ## ## The Computational Morphometry Toolkit is distributed in the hope that it ## will be useful, but WITHOUT ANY WARRANTY; without even the implied ## warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License along ## with the Computational Morphometry Toolkit. If not, see ## . ## ## $Revision: 4802 $ ## ## $LastChangedDate: 2013-08-29 13:45:41 -0700 (Thu, 29 Aug 2013) $ ## ## $LastChangedBy: torstenrohlfing $ ## # ========================================== # Delegate testing to separate subdirs for each library SUBDIRS(System) SUBDIRS(Registration) SUBDIRS(IO) SUBDIRS(Base) IF(CMTK_USE_CUDA) SUBDIRS(GPU) ENDIF(CMTK_USE_CUDA) cmtk-3.3.1/testing/libs/GPU/000077500000000000000000000000001276303427400155255ustar00rootroot00000000000000cmtk-3.3.1/testing/libs/GPU/CMakeLists.txt000066400000000000000000000037361276303427400202760ustar00rootroot00000000000000## ## Copyright 1997-2009 Torsten Rohlfing ## ## Copyright 2004-2010 SRI International ## ## This file is part of the Computational Morphometry Toolkit. ## ## http://www.nitrc.org/projects/cmtk/ ## ## The Computational Morphometry Toolkit is free software: you can ## redistribute it and/or modify it under the terms of the GNU General Public ## License as published by the Free Software Foundation, either version 3 of ## the License, or (at your option) any later version. ## ## The Computational Morphometry Toolkit is distributed in the hope that it ## will be useful, but WITHOUT ANY WARRANTY; without even the implied ## warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License along ## with the Computational Morphometry Toolkit. If not, see ## . ## ## $Revision: 1960 $ ## ## $LastChangedDate: 2010-07-06 15:38:46 -0700 (Tue, 06 Jul 2010) $ ## ## $LastChangedBy: torstenrohlfing $ ## # ========================================== # Setup binary test drivers SET(CMTK_LIBRARIES "cmtkGPU;cmtkBase;cmtkNumerics;cmtkSystem") SET(DRIVERS libGPUTests) FOREACH(D ${DRIVERS}) CUDA_ADD_EXECUTABLE(${D} ${D}.cxx) TARGET_LINK_LIBRARIES(${D} ${CMTK_LIBRARIES} ${CMTK_SYSTEM_LIBRARIES}) ENDFOREACH(D ${DRIVERS}) IF(CMTK_USE_CUDA) # ========================================== # Tests for "cmtk::DeviceHistogram" class SET(Tests ${Tests} DeviceHistogramEntropy DeviceHistogramPopulate DeviceHistogramPopulateLog) # ========================================== # Tests for "cmtk::DeviceMemory" class SET(Tests ${Tests} DeviceMemory) # ========================================== # Tests for "cmtk::UniformVolume" class SET(Tests ${Tests} DeviceUniformVolume) ENDIF(CMTK_USE_CUDA) FOREACH(T ${Tests}) ADD_TEST(${T} ${EXECUTABLE_OUTPUT_PATH}/libGPUTests ${T}) ENDFOREACH(T Tests) cmtk-3.3.1/testing/libs/GPU/cmtkDeviceHistogramTests.txx000066400000000000000000000155651276303427400232650ustar00rootroot00000000000000/* // // Copyright 2010 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 2398 $ // // $LastChangedDate: 2010-10-05 14:54:37 -0700 (Tue, 05 Oct 2010) $ // // $LastChangedBy: torstenrohlfing $ // */ #include #include #include #include int checkEntropy( const std::string& testName, const float hData[100], cmtk::DeviceHistogram& dHist, const float baseline ) { dHist.GetDataOnDevice().CopyToDevice( hData, 100 ); const float entropy = dHist.GetEntropy(); if ( fabs( entropy - baseline ) > 1e-5 ) { std::cerr << "Test " << testName << " entropy " << entropy << " deviates from baseline " << baseline << std::endl; return 1; } return 0; } // test "DeviceHistogram" class int testDeviceHistogramEntropy() { try { cmtk::DeviceHistogram::SmartPtr histogram100 = cmtk::DeviceHistogram::Create( 100 ); cmtk::DeviceHistogram::SmartPtr histogram200 = cmtk::DeviceHistogram::Create( 200 ); float floatHost[100]; // compute entropy for all-zeros for ( size_t i = 0; i < 100; ++i ) floatHost[i] = 0; if ( checkEntropy( "AllZeros100", floatHost, *histogram100, 0 ) || checkEntropy( "AllZeros200", floatHost, *histogram200, 0 ) ) return 1; // compute entropy for single non-zero bin floatHost[0] = 1; if ( checkEntropy( "SingleBin100", floatHost, *histogram100, 0 ) || checkEntropy( "SingleBin200", floatHost, *histogram200, 0 ) ) return 1; // compute entropy for all-ones for ( size_t i = 0; i < 100; ++i ) floatHost[i] = 1; if ( checkEntropy( "AllOnes100", floatHost, *histogram100, 4.60517 ) || checkEntropy( "AllOnes200", floatHost, *histogram200, 4.60517 ) ) return 1; // compute entropy for 50x 0, 50x 1 for ( size_t i = 0; i < 50; ++i ) floatHost[i] = 0; if ( checkEntropy( "50One50Zero100", floatHost, *histogram100, 3.91202 ) || checkEntropy( "50One50Zero200", floatHost, *histogram200, 3.91202 ) ) return 1; // compute entropy for 50x "0 1" alternating for ( size_t i = 0; i < 50; ++i ) { floatHost[i<<1] = 1; floatHost[1+(i<<1)] = 0; } if ( checkEntropy( "50OneZeroPairs100", floatHost, *histogram100, 3.91202 ) || checkEntropy( "50OneZeroPairs200", floatHost, *histogram200, 3.91202 ) ) return 1; } catch ( std::bad_alloc ) { std::cerr << "Caught bad_alloc()" << std::endl; return 1; } return 0; } template int compareHistogramToBaseline( const cmtk::DeviceHistogram& histD, const float* base ) { cmtk::FixedVector histogram; histD.GetDataOnDevice().CopyToHost( &histogram[0], NBINS ); for ( size_t j = 0; j < NBINS; ++j ) { if ( histogram[j] != base[j] ) { std::cerr << "actual\tbaseline" << std::endl; for ( size_t i = 0; i < NBINS; ++i ) { std::cerr << histogram[i] << "\t" << base[i] << std::endl; } std::cerr << std::endl; return 1; } } return 0; } int testDeviceHistogramPopulate() { try { cmtk::DeviceHistogram::SmartPtr histogramD = cmtk::DeviceHistogram::Create( 4 ); const float data[10] = { 0, 6, 3, 7, 2, 2, 8, 8, 1, 10 }; cmtk::DeviceMemory::SmartPtr dataD = cmtk::DeviceMemory::Create( 10, data ); // reset histogram histogramD->Reset(); const float baseline0[4] = { 0, 0, 0, 0 }; if ( compareHistogramToBaseline<4>( *histogramD, baseline0 ) ) return 1; // populate histogram from data histogramD->Populate( *dataD, 0.0 /*rangeFrom*/, 10.0 /*rangeTo*/ ); const float baseline1[4] = { 5, 1, 3, 1 }; if ( compareHistogramToBaseline<4>( *histogramD, baseline1 ) ) return 1; // add same data to histogram second time without reset histogramD->Populate( *dataD, 0.0 /*rangeFrom*/, 10.0 /*rangeTo*/ ); const float baseline2[4] = { 10, 2, 6, 2 }; if ( compareHistogramToBaseline<4>( *histogramD, baseline2 ) ) return 1; // reset and populate histogram using mask const int mask[10] = { 0, 0, 1, 1, 0, 0, 0, 1, 1, 1 }; cmtk::DeviceMemory::SmartPtr maskD = cmtk::DeviceMemory::Create( 10, mask ); histogramD->Reset(); histogramD->Populate( *dataD, *maskD, 0.0 /*rangeFrom*/, 10.0 /*rangeTo*/ ); const float baseline3[4] = { 2, 0, 2, 1 }; if ( compareHistogramToBaseline<4>( *histogramD, baseline3 ) ) return 1; } catch ( std::bad_alloc ) { std::cerr << "Caught bad_alloc()" << std::endl; return 1; } return 0; } int testDeviceHistogramPopulateLog() { try { cmtk::DeviceHistogram::SmartPtr histogramD = cmtk::DeviceHistogram::Create( 4 ); const float data[10] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; cmtk::DeviceMemory::SmartPtr dataD = cmtk::DeviceMemory::Create( 10, data ); // reset histogram histogramD->Reset(); const float baseline0[4] = { 0, 0, 0, 0 }; if ( compareHistogramToBaseline<4>( *histogramD, baseline0 ) ) return 1; // populate histogram from data histogramD->Populate( *dataD, 0.0 /*rangeFrom*/, 9.0 /*rangeTo*/, true /*logScale*/ ); const float baseline1[4] = { 4, 3, 3, 0 }; if ( compareHistogramToBaseline<4>( *histogramD, baseline1 ) ) return 1; // add same data to histogram second time without reset histogramD->Populate( *dataD, 0.0 /*rangeFrom*/, 9.0 /*rangeTo*/, true /*logScale*/ ); const float baseline2[4] = { 8, 6, 6, 0 }; if ( compareHistogramToBaseline<4>( *histogramD, baseline2 ) ) return 1; // reset and populate histogram using mask const int mask[10] = { 0, 0, 1, 1, 0, 0, 0, 1, 1, 1 }; cmtk::DeviceMemory::SmartPtr maskD = cmtk::DeviceMemory::Create( 10, mask ); histogramD->Reset(); histogramD->Populate( *dataD, *maskD, 0.0 /*rangeFrom*/, 9.0 /*rangeTo*/, true /*logScale*/ ); const float baseline3[4] = { 2, 0, 3, 0 }; if ( compareHistogramToBaseline<4>( *histogramD, baseline3 ) ) return 1; } catch ( std::bad_alloc ) { std::cerr << "Caught bad_alloc()" << std::endl; return 1; } return 0; } cmtk-3.3.1/testing/libs/GPU/cmtkDeviceMemoryTests.txx000066400000000000000000000063451276303427400225740ustar00rootroot00000000000000/* // // Copyright 2010 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 2398 $ // // $LastChangedDate: 2010-10-05 14:54:37 -0700 (Tue, 05 Oct 2010) $ // // $LastChangedBy: torstenrohlfing $ // */ #include #include // test "DeviceMemory" class int testDeviceMemory() { size_t memFreeBefore, memTotalBefore; cudaError_t status = cudaMemGetInfo( &memFreeBefore, &memTotalBefore ); if ( status != cudaSuccess ) { std::cerr << "Call to cudaMemGetInfo() failed with error " << cudaGetErrorString( status ) << std::endl; return 1; } try { float floatHost[100]; cmtk::DeviceMemory::SmartPtr floatDevice = cmtk::DeviceMemory::Create( 100 ); floatDevice->CopyToDevice( floatHost, 100 ); floatDevice->CopyToHost( floatHost, 100 ); int intHost[100]; cmtk::DeviceMemory::SmartPtr intDevice = cmtk::DeviceMemory::Create( 100 ); intDevice->CopyToDevice( intHost, 100 ); intDevice->CopyToHost( intHost, 100 ); char charHost[100]; cmtk::DeviceMemory::SmartPtr charDevice = cmtk::DeviceMemory::Create( 100 ); charDevice->CopyToDevice( charHost, 100 ); charDevice->CopyToHost( charHost, 100 ); cmtk::DeviceMemory::SmartPtr float2Device = cmtk::DeviceMemory::Create( 100 ); float2Device->CopyOnDevice( *floatDevice, 100 ); size_t memFreeAfter, memTotalAfter; if ( cudaMemGetInfo( &memFreeAfter, &memTotalAfter ) != cudaSuccess ) { std::cerr << "Second call to cudaMemGetInfo() failed." << std::endl; return 1; } if ( memFreeBefore == memFreeAfter ) { std::cerr << "Free device memory constant despite allocation at" << memFreeBefore << std::endl; return 1; } } catch ( std::bad_alloc ) { std::cerr << "Caught bad_alloc()" << std::endl; return 1; } size_t memFreeAfter, memTotalAfter; if ( cudaMemGetInfo( &memFreeAfter, &memTotalAfter ) != cudaSuccess ) { std::cerr << "Third call to cudaMemGetInfo() failed." << std::endl; return 1; } if ( memTotalBefore != memTotalAfter ) { std::cerr << "Total device memory changed by " << (memTotalBefore - memTotalAfter) << std::endl; return 1; } if ( memFreeBefore != memFreeAfter ) { std::cerr << "Free device memory changed by" << (memFreeBefore - memFreeAfter) << std::endl; return 1; } return 0; } cmtk-3.3.1/testing/libs/GPU/cmtkDeviceUniformVolumeTests.txx000066400000000000000000000041511276303427400241240ustar00rootroot00000000000000/* // // Copyright 2010, 2012, 2013 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4876 $ // // $LastChangedDate: 2013-09-24 13:09:58 -0700 (Tue, 24 Sep 2013) $ // // $LastChangedBy: torstenrohlfing $ // */ #include // test "DeviceUniformVolume" class int testDeviceUniformVolume() { try { const int dims[3] = { 10, 10, 10 }; const float size[3] = { 9, 9, 9 }; cmtk::UniformVolume volume( (cmtk::FixedVector<3,int>::FromPointer( dims )), cmtk::FixedVector<3,float>::FromPointer( size ) ); // first, try to create representation of actually empty volume. cmtk::DeviceUniformVolume::SmartPtr volumeDevice = cmtk::DeviceUniformVolume::Create( volume ); // second, allocate pixel data and create another device instance. volume.CreateDataArray( cmtk::TYPE_INT ); cmtk::DeviceUniformVolume::SmartPtr volumeDevice2 = cmtk::DeviceUniformVolume::Create( volume ); // third, change pixel data to double precision float and create another device instance. volume.CreateDataArray( cmtk::TYPE_DOUBLE ); cmtk::DeviceUniformVolume::SmartPtr volumeDevice3 = cmtk::DeviceUniformVolume::Create( volume ); } catch ( std::bad_alloc ) { std::cerr << "Caught bad_alloc()" << std::endl; return 1; } return 0; } cmtk-3.3.1/testing/libs/GPU/libGPUTests.cxx000066400000000000000000000034651276303427400204260ustar00rootroot00000000000000/* // // Copyright 2004-2010 SRI International // // Copyright 1997-2009 Torsten Rohlfing // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 2398 $ // // $LastChangedDate: 2010-10-05 14:54:37 -0700 (Tue, 05 Oct 2010) $ // // $LastChangedBy: torstenrohlfing $ // */ #include #include #include #include "cmtkDeviceHistogramTests.txx" #include "cmtkDeviceMemoryTests.txx" #include "cmtkDeviceUniformVolumeTests.txx" int main( const int argc, const char* argv[] ) { cmtk::TestFunctionMap map; map.AddTest( "DeviceHistogramEntropy", &testDeviceHistogramEntropy ); map.AddTest( "DeviceHistogramPopulate", &testDeviceHistogramPopulate ); map.AddTest( "DeviceHistogramPopulateLog", &testDeviceHistogramPopulateLog ); map.AddTest( "DeviceMemory", &testDeviceMemory ); map.AddTest( "DeviceUniformVolume", &testDeviceUniformVolume ); // is test name given on command line? if ( argc < 2 ) { } else { return map.RunTestByName( argv[1] ); } } cmtk-3.3.1/testing/libs/IO/000077500000000000000000000000001276303427400154015ustar00rootroot00000000000000cmtk-3.3.1/testing/libs/IO/CMakeLists.txt000066400000000000000000000034021276303427400201400ustar00rootroot00000000000000## ## Copyright 1997-2010 Torsten Rohlfing ## ## Copyright 2004-2012 SRI International ## ## This file is part of the Computational Morphometry Toolkit. ## ## http://www.nitrc.org/projects/cmtk/ ## ## The Computational Morphometry Toolkit is free software: you can ## redistribute it and/or modify it under the terms of the GNU General Public ## License as published by the Free Software Foundation, either version 3 of ## the License, or (at your option) any later version. ## ## The Computational Morphometry Toolkit is distributed in the hope that it ## will be useful, but WITHOUT ANY WARRANTY; without even the implied ## warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License along ## with the Computational Morphometry Toolkit. If not, see ## . ## ## $Revision: 4412 $ ## ## $LastChangedDate: 2012-06-04 12:58:46 -0700 (Mon, 04 Jun 2012) $ ## ## $LastChangedBy: torstenrohlfing $ ## # ========================================== # Setup binary test drivers SET(CMTK_LIBRARIES "cmtkIO;cmtkBase;cmtkNumerics;cmtkSystem") SET(DRIVERS "") IF(CMTK_USE_SQLITE) LIST(APPEND DRIVERS libIOTestsSQLite) ENDIF(CMTK_USE_SQLITE) FOREACH(D ${DRIVERS}) ADD_EXECUTABLE(${D} ${D}.cxx) TARGET_LINK_LIBRARIES(${D} ${CMTK_LIBRARIES} ${CMTK_SYSTEM_LIBRARIES}) ENDFOREACH(D ${DRIVERS}) # ========================================== # Tests for "cmtk::SQLite" class IF(CMTK_USE_SQLITE) SET(Tests SQLiteNew SQLiteOpen SQLiteCreateAndInsert SQLiteQuery ) FOREACH(T ${Tests}) ADD_TEST(${T} ${EXECUTABLE_OUTPUT_PATH}/libIOTestsSQLite ${T}) ENDFOREACH(T Tests) ENDIF(CMTK_USE_SQLITE) cmtk-3.3.1/testing/libs/IO/cmtkSQLiteTests.txx000066400000000000000000000036541276303427400212210ustar00rootroot00000000000000/* // // Copyright 2004-2010 SRI International // // Copyright 1997-2009 Torsten Rohlfing // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 2398 $ // // $LastChangedDate: 2010-10-05 14:54:37 -0700 (Tue, 05 Oct 2010) $ // // $LastChangedBy: torstenrohlfing $ // */ #include // test SQLite database creation int testSQLiteNew() { cmtk::SQLite db( ":memory:" ); return 0; } // test SQLite open of existing file int testSQLiteOpen() { cmtk::SQLite db( CMTK_DATADIR "/empty.sqlite", true /*readOnly*/ ); return 0; } // test SQLite table creation and data insertion int testSQLiteCreateAndInsert() { cmtk::SQLite db( ":memory:" ); db.Exec( "create table testing ( id integer primary key, data text )" ); db.Exec( "insert into testing values ( NULL, 'test1')" ); db.Exec( "insert into testing values ( 2, 'test2')" ); db.Exec( "insert into testing values ( NULL, 'test3')" ); return 0; } // test SQLite database query int testSQLiteQuery() { cmtk::SQLite db( CMTK_DATADIR "/testDB.sqlite", true /*readOnly*/ ); cmtk::SQLite::TableType table; db.Query( "select * from testing", table ); return 0; } cmtk-3.3.1/testing/libs/IO/libIOTestsSQLite.cxx000066400000000000000000000046071276303427400212370ustar00rootroot00000000000000/* // // Copyright 2004-2010 SRI International // // Copyright 1997-2009 Torsten Rohlfing // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 2280 $ // // $LastChangedDate: 2010-08-23 14:47:56 -0700 (Mon, 23 Aug 2010) $ // // $LastChangedBy: torstenrohlfing $ // */ #include #include #include "cmtkSQLiteTests.txx" /** Set up table of test names and function pointers */ typedef int (*testFuncPtr)(); typedef struct __testNameAndFunctionPointer { const char* name; const testFuncPtr func; } testNameAndFunctionPointer; const testNameAndFunctionPointer testTable[] = { { "SQLiteNew", &testSQLiteNew }, { "SQLiteOpen", &testSQLiteOpen }, { "SQLiteCreateAndInsert", &testSQLiteCreateAndInsert }, { "SQLiteQuery", &testSQLiteQuery }, { NULL, NULL } }; int main( const int argc, const char* argv[] ) { int testNumber = -1; // is test name given on command line? if ( argc < 2 ) { // no: ask user in dialog mode for ( size_t i = 0; testTable[i].name; ++i ) { std::cout << i << ". " << testTable[i].name << std::endl; } std::cout << "Run test number: "; std::cin >> testNumber; } else { // batch mode: find test by name given on command line for ( size_t i = 0; testTable[i].name; ++i ) { if ( !strcmp( argv[1], testTable[i].name ) ) testNumber = i; } } // run test, or return error if none found if ( testNumber < 0 ) { std::cerr << "Test " << argv[1] << " not found!" << std::endl; return 2; } else return testTable[testNumber].func(); } cmtk-3.3.1/testing/libs/Registration/000077500000000000000000000000001276303427400175445ustar00rootroot00000000000000cmtk-3.3.1/testing/libs/Registration/CMakeLists.txt000066400000000000000000000047151276303427400223130ustar00rootroot00000000000000## ## Copyright 2004-2012 SRI International ## ## Copyright 1997-2009 Torsten Rohlfing ## ## This file is part of the Computational Morphometry Toolkit. ## ## http://www.nitrc.org/projects/cmtk/ ## ## The Computational Morphometry Toolkit is free software: you can ## redistribute it and/or modify it under the terms of the GNU General Public ## License as published by the Free Software Foundation, either version 3 of ## the License, or (at your option) any later version. ## ## The Computational Morphometry Toolkit is distributed in the hope that it ## will be useful, but WITHOUT ANY WARRANTY; without even the implied ## warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License along ## with the Computational Morphometry Toolkit. If not, see ## . ## ## $Revision: 4367 $ ## ## $LastChangedDate: 2012-05-29 16:54:19 -0700 (Tue, 29 May 2012) $ ## ## $LastChangedBy: torstenrohlfing $ ## # ========================================== # Setup binary test drivers SET(CMTK_LIBRARIES "cmtkRegistration;cmtkIO;cmtkBase;cmtkNumerics;cmtkSystem") SET(DRIVERS libRegistrationTests) IF(CMTK_USE_SQLITE) SET(DRIVERS ${DRIVERS} libRegistrationTestsSQLite) ENDIF(CMTK_USE_SQLITE) FOREACH(D ${DRIVERS}) ADD_EXECUTABLE(${D} ${D}.cxx) TARGET_LINK_LIBRARIES(${D} ${CMTK_LIBRARIES} ${CMTK_SYSTEM_LIBRARIES}) ENDFOREACH(D ${DRIVERS}) # ========================================== # Tests for "cmtk::TypedArraySimilarity" class SET(Tests ${Tests} TypedArraySimilarity) FOREACH(T ${Tests}) ADD_TEST(${T} ${EXECUTABLE_OUTPUT_PATH}/libRegistrationTests ${T}) ENDFOREACH(T ${Tests}) # ========================================== # Tests for "cmtk::XformImageDB" class IF(CMTK_USE_SQLITE) SET(Tests ImageXformDBCreate ImageXformDBAddImage ImageXformDBAddImageRepeat ImageXformDBAddImagePair ImageXformDBAddImageThenXform ImageXformDBAddImageWithXform ImageXformDBAddXformSameSpace ImageXformDBAddXformRefined ImageXformDBFindXform ImageXformDBFindXformSameSpace ImageXformDBFindXformInverse ImageXformDBFindXformNone ImageXformDBFindXformMultiple ImageXformDBFindXformLevels ImageXformDBFindXformLevelsInverse ) FOREACH(T ${Tests}) ADD_TEST(${T} ${EXECUTABLE_OUTPUT_PATH}/libRegistrationTestsSQLite ${T}) ENDFOREACH(T ${Tests}) ENDIF(CMTK_USE_SQLITE) cmtk-3.3.1/testing/libs/Registration/cmtkImageXformDBTests.txx000066400000000000000000000327531276303427400244710ustar00rootroot00000000000000/* // // Copyright 2010, 2013 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4859 $ // // $LastChangedDate: 2013-09-22 12:06:02 -0700 (Sun, 22 Sep 2013) $ // // $LastChangedBy: torsten_at_home $ // */ #include // test database creation int testImageXformDBCreate() { cmtk::ImageXformDB db( "imagexform.sqlite" ); return 0; } // test open of existing file int testImageXformDBOpen() { cmtk::ImageXformDB db( CMTK_DATADIR "/empty.sqlite", true /*readOnly*/ ); return 0; } // test adding an image to a database int testImageXformDBAddImage() { cmtk::ImageXformDB db( ":memory:" ); db.DebugModeOn(); db.AddImage( "image1.nii" ); const cmtk::ImageXformDB::PrimaryKeyType key1 = db.FindImageSpaceID( "image1.nii" ); if ( key1 == cmtk::ImageXformDB::NOTFOUND ) { std::cerr << "No space key for image1." << std::endl; return 1; } db.AddImage( "image2.nii", "image1.nii" ); const cmtk::ImageXformDB::PrimaryKeyType key2 = db.FindImageSpaceID( "image2.nii" ); if ( key2 == cmtk::ImageXformDB::NOTFOUND ) { std::cerr << "No space key for image2." << std::endl; return 1; } if ( key1 != key2 ) { std::cerr << "Keys for image1 and image2 do not match." << std::endl; return 1; } db.AddImage( "image3.nii", "image2.nii" ); const cmtk::ImageXformDB::PrimaryKeyType key3 = db.FindImageSpaceID( "image3.nii" ); if ( key3 == cmtk::ImageXformDB::NOTFOUND ) { std::cerr << "No space key for image3." << std::endl; return 1; } if ( key1 != key3 ) { std::cerr << "Keys for image1 and image3 do not match." << std::endl; return 1; } db.AddImage( "image4.nii" ); const cmtk::ImageXformDB::PrimaryKeyType key4 = db.FindImageSpaceID( "image4.nii" ); if ( key4 == cmtk::ImageXformDB::NOTFOUND ) { std::cerr << "No space key for image4." << std::endl; return 1; } if ( key4 == key1 ) { std::cerr << "Keys for image1 and image4 match when they should not." << std::endl; return 1; } return 0; } // test adding an image to a database repeatedly without creating multiple entries. int testImageXformDBAddImageRepeat() { cmtk::ImageXformDB db( ":memory:" ); db.DebugModeOn(); db.AddImage( "image1.nii" ); const cmtk::ImageXformDB::PrimaryKeyType key1 = db.FindImageSpaceID( "image1.nii" ); if ( key1 == cmtk::ImageXformDB::NOTFOUND ) { std::cerr << "No space key for image1 (first add)" << std::endl; return 1; } db.AddImage( "image1.nii" ); const cmtk::ImageXformDB::PrimaryKeyType key2 = db.FindImageSpaceID( "image1.nii" ); if ( key2 == cmtk::ImageXformDB::NOTFOUND ) { std::cerr << "No space key for image1 (second add)" << std::endl; return 1; } if ( key1 != key2 ) { std::cerr << "Two different keys." << std::endl; return 1; } const std::vector list = db.GetSpaceImageList( db.FindImageSpaceID( "image1.nii" ) ); if ( list.size() != 1 ) { std::cerr << "Number of entries is not equal to 1." << std::endl; return 1; } return 0; } // test adding an image to a database int testImageXformDBAddImagePair() { cmtk::ImageXformDB db( ":memory:" ); db.DebugModeOn(); db.AddImage( "image2.nii", "image1.nii" ); const cmtk::ImageXformDB::PrimaryKeyType key1 = db.FindImageSpaceID( "image1.nii" ); if ( key1 == cmtk::ImageXformDB::NOTFOUND ) { std::cerr << "No space key for image1." << std::endl; return 1; } const cmtk::ImageXformDB::PrimaryKeyType key2 = db.FindImageSpaceID( "image2.nii" ); if ( key2 == cmtk::ImageXformDB::NOTFOUND ) { std::cerr << "No space key for image2." << std::endl; return 1; } if ( key1 != key2 ) { std::cerr << "Keys for image1 and image2 do not match." << std::endl; return 1; } return 0; } // test adding images, then a transformation to a database int testImageXformDBAddImageThenXform() { cmtk::ImageXformDB db( ":memory:" ); db.DebugModeOn(); db.AddImage( "image1.nii" ); db.AddImage( "image2.nii" ); db.AddImage( "image4.nii" ); if ( ! db.AddImagePairXform( "xform12", true /*invertible*/, "image1.nii", "image2.nii" ) || ! db.AddImagePairXform( "xform21", false /*invertible*/, "image2.nii", "image1.nii" ) || ! db.AddImagePairXform( "xform42", false /*invertible*/, "image4.nii", "image2.nii" ) ) return 1; return 0; } // test adding images and transformations to a database int testImageXformDBAddImageWithXform() { cmtk::ImageXformDB db( ":memory:" ); db.DebugModeOn(); if ( ! db.AddImagePairXform( "xform12", true /*invertible*/, "image1.nii", "image2.nii" ) || ! db.AddImagePairXform( "xform21", false /*invertible*/, "image2.nii", "image1.nii" ) || ! db.AddImagePairXform( "xform42", false /*invertible*/, "image4.nii", "image2.nii" ) ) return 1; return 0; } // test: try adding a transformation between images in the same space int testImageXformDBAddXformSameSpace() { cmtk::ImageXformDB db( ":memory:" ); db.DebugModeOn(); db.AddImage( "image1.nii" ); db.AddImage( "image2.nii", "image1.nii" ); if ( db.AddImagePairXform( "xform12", true /*invertible*/, "image1.nii", "image2.nii" ) ) return 1; return 0; } // test: add transformation, then add its refinement. int testImageXformDBAddXformRefined() { cmtk::ImageXformDB db( ":memory:" ); db.DebugModeOn(); db.AddImage( "image1.nii" ); db.AddImage( "image2.nii" ); if ( ! db.AddImagePairXform( "xform12", true /*invertible*/, "image1.nii", "image2.nii" ) || ! db.AddRefinedXform( "xform12refined", true /*invertible*/, "xform12" ) ) { std::cerr << "DB add transformation." << std::endl; return 1; } if ( (db.FindXformLevel( "xform12" ) != 0) || (db.FindXformLevel( "xform12refined" ) != 1) ) { std::cerr << "DB transformation levels are incorrect." << std::endl; return 1; } return 0; } // test getting simple transformations int testImageXformDBFindXform() { cmtk::ImageXformDB db( ":memory:" ); db.DebugModeOn(); if ( ! db.AddImagePairXform( "xform12", true /*invertible*/, "image1.nii", "image2.nii" ) ) return 1; std::string xform; bool inverse; if ( ! db.FindXform( "image1.nii", "image2.nii", xform, inverse ) ) { std::cerr << "DB transformation lookup failed." << std::endl; return 1; } if ( xform != "xform12" ) { std::cerr << "DB transformation returned wrong xform." << std::endl; return 1; } if ( inverse ) { std::cerr << "DB transformation returned wrong inversion flag." << std::endl; return 1; } return 0; } // test getting transformation between two images in the same space. int testImageXformDBFindXformSameSpace() { cmtk::ImageXformDB db( ":memory:" ); db.DebugModeOn(); db.AddImage( "image1.nii" ); db.AddImage( "image2.nii", "image1.nii" ); std::string xform; bool inverse; if ( ! db.FindXform( "image1.nii", "image2.nii", xform, inverse ) ) { std::cerr << "DB transformation lookup failed." << std::endl; return 1; } if ( xform != "" ) { std::cerr << "DB transformation returned wrong xform." << std::endl; return 1; } if ( inverse ) { std::cerr << "DB transformation returned wrong inversion flag." << std::endl; return 1; } return 0; } // test getting inverse transformations int testImageXformDBFindXformInverse() { cmtk::ImageXformDB db( ":memory:" ); db.DebugModeOn(); if ( ! db.AddImagePairXform( "xform12", true /*invertible*/, "image1.nii", "image2.nii" ) ) return 1; std::string xform; bool inverse; if ( ! db.FindXform( "image2.nii", "image1.nii", xform, inverse ) ) { std::cerr << "DB transformation lookup failed." << std::endl; return 1; } if ( xform != "xform12" ) { std::cerr << "DB transformation returned wrong xform." << std::endl; return 1; } if ( !inverse ) { std::cerr << "DB transformation returned wrong inversion flag." << std::endl; return 1; } return 0; } // test getting transformations when none actually exists int testImageXformDBFindXformNone() { cmtk::ImageXformDB db( ":memory:" ); db.DebugModeOn(); if ( ! db.AddImagePairXform( "xform12", true /*invertible*/, "image1.nii", "image2.nii" ) ) return 1; std::string xform; bool inverse; if ( db.FindXform( "image1.nii", "image3.nii", xform, inverse ) ) { std::cerr << "DB transformation lookup succeeded when it should have failed." << std::endl; return 1; } if ( db.FindXform( "image3.nii", "image2.nii", xform, inverse ) ) { std::cerr << "DB transformation lookup succeeded when it should have failed." << std::endl; return 1; } if ( db.FindXform( "image3.nii", "image3.nii", xform, inverse ) ) { std::cerr << "DB transformation lookup succeeded when it should have failed." << std::endl; return 1; } return 0; } // test getting transformations when multiple different ones exist int testImageXformDBFindXformMultiple() { cmtk::ImageXformDB db( ":memory:" ); db.DebugModeOn(); if ( ! db.AddImagePairXform( "affine12", true /*invertible*/, "image1.nii", "image2.nii" ) || ! db.AddImagePairXform( "nonrigid12", false /*invertible*/, "image1.nii", "image2.nii" ) ) return 1; std::string xform; bool inverse; if ( ! db.FindXform( "image1.nii", "image2.nii", xform, inverse ) ) { std::cerr << "DB transformation lookup failed." << std::endl; return 1; } if ( xform != "nonrigid12" ) { std::cerr << "DB transformation returned wrong xform." << std::endl; return 1; } if ( inverse ) { std::cerr << "DB transformation returned wrong inversion flag." << std::endl; return 1; } if ( ! db.FindXform( "image2.nii", "image1.nii", xform, inverse ) ) { std::cerr << "DB transformation lookup failed." << std::endl; return 1; } if ( xform != "nonrigid12" ) { std::cerr << "DB transformation returned wrong xform." << std::endl; return 1; } if ( !inverse ) { std::cerr << "DB transformation returned wrong inversion flag." << std::endl; return 1; } return 0; } // test getting transformations when multiple refinement levels exist. int testImageXformDBFindXformLevels() { cmtk::ImageXformDB db( ":memory:" ); db.DebugModeOn(); if ( ! db.AddImagePairXform( "affine12", true /*invertible*/, "image1.nii", "image2.nii" ) || ! db.AddRefinedXform( "nonrigid12", false /*invertible*/, "affine12" ) ) return 1; std::string xform; bool inverse; if ( ! db.FindXform( "image1.nii", "image2.nii", xform, inverse ) ) { std::cerr << "DB transformation lookup failed." << std::endl; return 1; } if ( xform != "nonrigid12" ) { std::cerr << "DB transformation returned wrong xform." << std::endl; return 1; } if ( inverse ) { std::cerr << "DB transformation returned wrong inversion flag." << std::endl; return 1; } if ( ! db.FindXform( "image2.nii", "image1.nii", xform, inverse ) ) { std::cerr << "DB transformation lookup failed." << std::endl; return 1; } if ( xform != "nonrigid12" ) { std::cerr << "DB transformation returned wrong xform." << std::endl; return 1; } if ( !inverse ) { std::cerr << "DB transformation returned wrong inversion flag." << std::endl; return 1; } return 0; } // test getting transformations when multiple refinement levels exist and the refined transformation is based on an inverted initial transformation int testImageXformDBFindXformLevelsInverse() { cmtk::ImageXformDB db( ":memory:" ); db.DebugModeOn(); if ( ! db.AddImagePairXform( "affine12", true /*invertible*/, "image1.nii", "image2.nii" ) || ! db.AddRefinedXform( "nonrigid21", false /*invertible*/, "affine12", true /*initInverse*/ ) ) return 1; std::string xform; bool inverse; if ( ! db.FindXform( "image1.nii", "image2.nii", xform, inverse ) ) { std::cerr << "DB transformation lookup failed." << std::endl; return 1; } if ( xform != "affine12" ) { std::cerr << "DB transformation returned wrong xform." << std::endl; return 1; } if ( inverse ) { std::cerr << "DB transformation returned wrong inversion flag." << std::endl; return 1; } if ( ! db.FindXform( "image2.nii", "image1.nii", xform, inverse ) ) { std::cerr << "DB transformation lookup failed." << std::endl; return 1; } if ( xform != "nonrigid21" ) { std::cerr << "DB transformation returned wrong xform." << std::endl; return 1; } if ( inverse ) { std::cerr << "DB transformation returned wrong inversion flag." << std::endl; return 1; } return 0; } cmtk-3.3.1/testing/libs/Registration/cmtkTypedArraySimilarityTests.txx000066400000000000000000000067741276303427400263640ustar00rootroot00000000000000/* // // Copyright 2004-2010 SRI International // // Copyright 1997-2009 Torsten Rohlfing // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 2398 $ // // $LastChangedDate: 2010-10-05 14:54:37 -0700 (Tue, 05 Oct 2010) $ // // $LastChangedBy: torstenrohlfing $ // */ #include #include #ifdef HAVE_IEEEFP_H # include #endif #include #include // Check TypedArraySimilarity result against baseline. int testTypedArraySimilarityCheck( const char* name, const cmtk::Types::DataItem result, const cmtk::Types::DataItem baseline ) { const cmtk::Types::DataItem tolerance = 1e-4; const cmtk::Types::DataItem error = 2 * fabs(result-baseline) / (fabs(result)+fabs(baseline)); if ( !finite( result ) || (error > tolerance) ) { std::cerr << name << " returned " << result << ", which exceeds tolerance for baseline " << baseline << std::endl; return 0; } return 1; } // test "TypedArraySimilarity" class int testTypedArraySimilarity() { cmtk::UniformVolume::SmartPtr testVolume( cmtk::VolumeIO::Read( CMTK_DATADIR "/spgr_3t.hdr" ) ); if ( ! testVolume ) { std::cerr << "SETUP ERROR: could not read test image 'spgr_3t.hdr'" << std::endl; return 1; } cmtk::TypedArray::SmartPtr data0( testVolume->GetOrthoSlice( 2, 34 )->GetPixelData() ); cmtk::TypedArray::SmartPtr data1( testVolume->GetOrthoSlice( 2, 35 )->GetPixelData() ); int success = 0; success += testTypedArraySimilarityCheck( "GetMutualInformation", cmtk::TypedArraySimilarity::GetMutualInformation( data0, data1 ), 1.55075 ); success += testTypedArraySimilarityCheck( "GetNormalizedMutualInformation", cmtk::TypedArraySimilarity::GetNormalizedMutualInformation( data0, data1 ), 1.29928 ); success += testTypedArraySimilarityCheck( "GetPeakSignalToNoiseRatio", cmtk::TypedArraySimilarity::GetPeakSignalToNoiseRatio( data0, data1 ), -4.98786 ); success += testTypedArraySimilarityCheck( "GetMinusMeanSquaredDifference", cmtk::TypedArraySimilarity::GetMinusMeanSquaredDifference( data0, data1 ), -9545.03 ); success += testTypedArraySimilarityCheck( "GetCrossCorrelation", cmtk::TypedArraySimilarity::GetCrossCorrelation( data0, data1 ), 0.964253 ); success += testTypedArraySimilarityCheck( "GetCorrelationRatio", cmtk::TypedArraySimilarity::GetCorrelationRatio( data0, data1 ), 0.933251 ); cmtk::Types::DataItem scaleFactor = 0; success += testTypedArraySimilarityCheck( "GetDifferenceImageEntropy", cmtk::TypedArraySimilarity::GetDifferenceArrayEntropy( data0, data1, scaleFactor ), 2.89753 ); success += testTypedArraySimilarityCheck( "GetDifferenceImageEntropy::scaleFactor", scaleFactor, 1.01683 ); return (success == 8) ? 0 : 1; } cmtk-3.3.1/testing/libs/Registration/libRegistrationTests.cxx000066400000000000000000000042571276303427400244640ustar00rootroot00000000000000/* // // Copyright 2004-2012 SRI International // // Copyright 1997-2009 Torsten Rohlfing // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4367 $ // // $LastChangedDate: 2012-05-29 16:54:19 -0700 (Tue, 29 May 2012) $ // // $LastChangedBy: torstenrohlfing $ // */ #include #include "cmtkTypedArraySimilarityTests.txx" /** Set up table of test names and function pointers */ typedef int (*testFuncPtr)(); typedef struct __testNameAndFunctionPointer { const char* name; const testFuncPtr func; } testNameAndFunctionPointer; const testNameAndFunctionPointer testTable[] = { { "TypedArraySimilarity", &testTypedArraySimilarity }, { NULL, NULL } }; int main( const int argc, const char* argv[] ) { int testNumber = -1; // is test name given on command line? if ( argc < 2 ) { // no: ask user in dialog mode for ( size_t i = 0; testTable[i].name; ++i ) { std::cout << i << ". " << testTable[i].name << std::endl; } std::cout << "Run test number: "; std::cin >> testNumber; } else { // batch mode: find test by name given on command line for ( size_t i = 0; testTable[i].name; ++i ) { if ( !strcmp( argv[1], testTable[i].name ) ) testNumber = i; } } // run test, or return error if none found if ( testNumber < 0 ) return 2; else return testTable[testNumber].func(); } cmtk-3.3.1/testing/libs/Registration/libRegistrationTestsSQLite.cxx000066400000000000000000000066221276303427400255440ustar00rootroot00000000000000/* // // Copyright 2004-2010 SRI International // // Copyright 1997-2009 Torsten Rohlfing // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 2192 $ // // $LastChangedDate: 2010-08-11 13:38:05 -0700 (Wed, 11 Aug 2010) $ // // $LastChangedBy: torstenrohlfing $ // */ #include #include #include "cmtkImageXformDBTests.txx" /** Set up table of test names and function pointers */ typedef int (*testFuncPtr)(); typedef struct __testNameAndFunctionPointer { const char* name; const testFuncPtr func; } testNameAndFunctionPointer; const testNameAndFunctionPointer testTable[] = { { "ImageXformDBCreate", &testImageXformDBCreate }, { "ImageXformDBOpen", &testImageXformDBOpen }, { "ImageXformDBAddImage", &testImageXformDBAddImage }, { "ImageXformDBAddImageRepeat", &testImageXformDBAddImageRepeat }, { "ImageXformDBAddImagePair", &testImageXformDBAddImagePair }, { "ImageXformDBAddImageThenXform", &testImageXformDBAddImageThenXform }, { "ImageXformDBAddImageWithXform", &testImageXformDBAddImageWithXform }, { "ImageXformDBAddXformSameSpace", &testImageXformDBAddXformSameSpace }, { "ImageXformDBAddXformRefined", &testImageXformDBAddXformRefined }, { "ImageXformDBFindXform", &testImageXformDBFindXform }, { "ImageXformDBFindXformSameSpace", &testImageXformDBFindXformSameSpace }, { "ImageXformDBFindXformInverse", &testImageXformDBFindXformInverse }, { "ImageXformDBFindXformNone", &testImageXformDBFindXformNone }, { "ImageXformDBFindXformMultiple", &testImageXformDBFindXformMultiple }, { "ImageXformDBFindXformLevels", &testImageXformDBFindXformLevels }, { "ImageXformDBFindXformLevelsInverse", &testImageXformDBFindXformLevelsInverse }, { NULL, NULL } }; int main( const int argc, const char* argv[] ) { int testNumber = -1; // is test name given on command line? if ( argc < 2 ) { // no: ask user in dialog mode for ( size_t i = 0; testTable[i].name; ++i ) { std::cout << i << ". " << testTable[i].name << std::endl; } std::cout << "Run test number: "; std::cin >> testNumber; } else { // batch mode: find test by name given on command line for ( size_t i = 0; testTable[i].name; ++i ) { if ( !strcmp( argv[1], testTable[i].name ) ) testNumber = i; } } // run test, or return error if none found if ( testNumber < 0 ) { std::cerr << "Test " << argv[1] << " not found!" << std::endl; return 2; } else return testTable[testNumber].func(); } cmtk-3.3.1/testing/libs/System/000077500000000000000000000000001276303427400163565ustar00rootroot00000000000000cmtk-3.3.1/testing/libs/System/CMakeLists.txt000066400000000000000000000031751276303427400211240ustar00rootroot00000000000000## ## Copyright 1997-2009 Torsten Rohlfing ## ## Copyright 2004-2012 SRI International ## ## This file is part of the Computational Morphometry Toolkit. ## ## http://www.nitrc.org/projects/cmtk/ ## ## The Computational Morphometry Toolkit is free software: you can ## redistribute it and/or modify it under the terms of the GNU General Public ## License as published by the Free Software Foundation, either version 3 of ## the License, or (at your option) any later version. ## ## The Computational Morphometry Toolkit is distributed in the hope that it ## will be useful, but WITHOUT ANY WARRANTY; without even the implied ## warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License along ## with the Computational Morphometry Toolkit. If not, see ## . ## ## $Revision: 4039 $ ## ## $LastChangedDate: 2012-03-16 13:36:14 -0700 (Fri, 16 Mar 2012) $ ## ## $LastChangedBy: torstenrohlfing $ ## # ========================================== # Setup binary test drivers SET(CMTK_LIBRARIES "cmtkSystem") SET(DRIVERS libSystemTests) FOREACH(D ${DRIVERS}) ADD_EXECUTABLE(${D} ${D}.cxx) TARGET_LINK_LIBRARIES(${D} ${CMTK_LIBRARIES}) ENDFOREACH(D ${DRIVERS}) # ========================================== # Tests for "StackBacktrace" class SET(Tests StackBacktrace) # ========================================== # Tests for "StrUtility" functions LIST(APPEND Tests StrNStr) FOREACH(T ${Tests}) ADD_TEST(${T} ${EXECUTABLE_OUTPUT_PATH}/libSystemTests ${T}) ENDFOREACH(T Tests) cmtk-3.3.1/testing/libs/System/libSystemTests.cxx000066400000000000000000000043251276303427400221040ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2011 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4039 $ // // $LastChangedDate: 2012-03-16 13:36:14 -0700 (Fri, 16 Mar 2012) $ // // $LastChangedBy: torstenrohlfing $ // */ #include #include #include #include "testStackBacktrace.txx" #include "testStrNStr.txx" /** Set up table of test names and function pointers */ typedef int (*testFuncPtr)(); typedef struct { const char* name; const testFuncPtr func; } testNameAndFunctionPointer; const testNameAndFunctionPointer testTable[] = { { "StackBacktrace", &testStackBacktrace }, { "StrNStr", &testStrNStr }, { NULL, NULL } }; int main( const int argc, const char* argv[] ) { int testNumber = -1; // is test name given on command line? if ( argc < 2 ) { // no: ask user in dialog mode for ( size_t i = 0; testTable[i].name; ++i ) { std::cout << i << ". " << testTable[i].name << std::endl; } std::cout << "Run test number: "; std::cin >> testNumber; } else { // batch mode: find test by name given on command line for ( size_t i = 0; testTable[i].name; ++i ) { if ( !strcmp( argv[1], testTable[i].name ) ) testNumber = i; } } // run test, or return error if none found if ( testNumber < 0 ) return 2; else return testTable[testNumber].func(); } cmtk-3.3.1/testing/libs/System/testStackBacktrace.txx000066400000000000000000000026121276303427400226710ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2011 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4039 $ // // $LastChangedDate: 2012-03-16 13:36:14 -0700 (Fri, 16 Mar 2012) $ // // $LastChangedBy: torstenrohlfing $ // */ #include namespace cmtk { static StackBacktrace StackBacktraceInstance; } // deliberately crash to test stack trace output int testStackBacktrace() { cmtk::StackBacktrace::SetExitCode( 0 ); char* nullPtr = NULL; (*nullPtr) = 0; // if we didn't catch a SEGFAULT, the test failed return 1; } cmtk-3.3.1/testing/libs/System/testStrNStr.txx000066400000000000000000000050511276303427400214030ustar00rootroot00000000000000/* // // Copyright 1997-2009 Torsten Rohlfing // // Copyright 2004-2011 SRI International // // This file is part of the Computational Morphometry Toolkit. // // http://www.nitrc.org/projects/cmtk/ // // The Computational Morphometry Toolkit is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // The Computational Morphometry Toolkit is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with the Computational Morphometry Toolkit. If not, see // . // // $Revision: 4039 $ // // $LastChangedDate: 2012-03-16 13:36:14 -0700 (Fri, 16 Mar 2012) $ // // $LastChangedBy: torstenrohlfing $ // */ #include #include int testStrNStr() { const char* needle = "needle"; const char* haystack1 = "this is the needle"; // make sure we find needle if ( cmtk::StrNStr( haystack1, strlen( haystack1 ), needle ) == NULL ) { cmtk::StdErr << "StrNStr test #1 failed\n"; return 1; } // make sure we stop looking after nBytes needle if ( cmtk::StrNStr( haystack1, strlen( haystack1 ) - 1, needle ) != NULL ) { cmtk::StdErr << "StrNStr test #2 failed\n"; return 1; } // make sure we find only needle const char* haystack2 = "this is not the Needle"; if ( cmtk::StrNStr( haystack2, strlen( haystack2 ), needle ) != NULL ) { cmtk::StdErr << "StrNStr test #3 failed\n"; return 1; } // make sure we find only needle const char* haystack3 = "this is not the needl either"; if ( cmtk::StrNStr( haystack3, strlen( haystack3 ), needle ) != NULL ) { cmtk::StdErr << "StrNStr test #4 failed\n"; return 1; } // make sure we find only needle const char haystack4[] = "first put \x00 then put needle"; if ( cmtk::StrNStr( haystack4, sizeof( haystack4 ), needle ) == NULL ) { cmtk::StdErr << "StrNStr test #5 failed\n"; return 1; } // make sure we can find prefixes const char* haystack5 = "needle first"; if ( cmtk::StrNStr( haystack5, strlen( haystack5 ), needle ) == NULL ) { cmtk::StdErr << "StrNStr test #6 failed\n"; return 1; } return 0; }