/* concorde microPET to minc */
#include "config.h"
#define _XOPEN_SOURCE 1
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <float.h>
#include <time.h>
#include <minc.h>
#include <time_stamp.h>
#include <ParseArgv.h>
#define VERSIONSTR VERSION " built " __DATE__ " " __TIME__
/*************************************************************************
* Concorde microPET definitions
*/
/* file_type field */
#define UPET_FT_UNKNOWN 0
#define UPET_FT_LIST_MODE 1
#define UPET_FT_SINOGRAM 2
#define UPET_FT_NORMALIZATION 3
#define UPET_FT_ATTENUATION_CORRECTION 4
#define UPET_FT_IMAGE 5 /* Standard image data file (typical) */
#define UPET_FT_BLANK 6
#define UPET_FT_MU_MAP 8 /* Mu map data file */
#define UPET_FT_SCATTER_CORRECTION 9
/* acquisition_mode field */
#define UPET_AM_UNKNOWN 0
#define UPET_AM_BLANK 1
#define UPET_AM_EMISSION 2
#define UPET_AM_DYNAMIC 3
#define UPET_AM_GATED 4
#define UPET_AM_CONTINUOUS_BED_MOTION 5
#define UPET_AM_SINGLES_TRANSMISSION 6
#define UPET_AM_WINDOWED_COINCIDENCE_TRANSMISSION 7
#define UPET_AM_NONWINDOWED_COINCIDENCE_TRANSMISSION 8
#define UPET_DT_UNKNOWN 0
#define UPET_DT_BYTE 1
#define UPET_DT_II16 2 /* Intel 16-bit signed integer */
#define UPET_DT_II32 3 /* Intel 32-bit signed integer */
#define UPET_DT_IF32 4 /* Intel 32-bit float */
#define UPET_DT_MF32 5 /* Sun 32-bit float */
#define UPET_DT_MI16 6 /* Sun 16-bit signed integer */
#define UPET_DT_MI32 7 /* Sun 32-bit signed integer */
#define DECLARE_FUNC(x) \
static int x(struct conversion_info *ci_ptr, char *val_str, char *new_var, char *new_att)
struct conversion_info {
FILE *hdr_fp;
FILE *img_fp;
int mnc_fd;
int frame_index;
int frame_zero;
int data_type;
nc_type minc_type;
int dim_count;
int dim_lengths[5];
int dim_ids[5];
double dim_steps[5];
int frame_nbytes;
int frame_nvoxels;
void *frame_buffer;
double scale_factor;
double deadtime_correction;
double decay_correction;
double calibration_factor;
double isotope_branching_fraction;
int swap_size; /* 0, 2, 4 */
};
DECLARE_FUNC(upet_file_type);
DECLARE_FUNC(upet_acq_mode);
DECLARE_FUNC(upet_bed_motion);
DECLARE_FUNC(upet_data_type);
DECLARE_FUNC(upet_data_order);
DECLARE_FUNC(upet_ndims);
DECLARE_FUNC(upet_total_frames);
DECLARE_FUNC(upet_x_dim);
DECLARE_FUNC(upet_y_dim);
DECLARE_FUNC(upet_z_dim);
DECLARE_FUNC(upet_vector_dim);
DECLARE_FUNC(upet_injection_time);
DECLARE_FUNC(upet_scan_time);
DECLARE_FUNC(upet_axial_crystal_pitch);
DECLARE_FUNC(upet_pixel_size);
DECLARE_FUNC(upet_dose_units);
DECLARE_FUNC(upet_calibration_factor);
DECLARE_FUNC(upet_rotation);
DECLARE_FUNC(upet_isotope_branching_fraction);
DECLARE_FUNC(upet_frame_no);
DECLARE_FUNC(upet_frame_start);
DECLARE_FUNC(upet_frame_duration);
DECLARE_FUNC(upet_frame_min);
DECLARE_FUNC(upet_frame_max);
DECLARE_FUNC(upet_frame_file_ptr);
DECLARE_FUNC(upet_frame_scale_factor);
DECLARE_FUNC(upet_frame_decay_correction);
DECLARE_FUNC(upet_frame_deadtime_correction);
static void copy_init(struct conversion_info *ci_ptr);
static void copy_frame(struct conversion_info *ci_ptr);
/* These values are used to represent the field types in the microPET
* header file.
*/
#define UPET_TYPE_STR 1 /* String */
#define UPET_TYPE_INT 2 /* Integer */
#define UPET_TYPE_REAL 3 /* Floating-point */
#define UPET_TYPE_TIME 4 /* Timestamp */
#define UPET_TYPE_FILTER 5 /* Integer type followed by a float cutoff */
#define UPET_TYPE_3X64 6 /* 3 64-bit integers */
#define UPET_TYPE_FPTR 7 /* File pointer (2 32 bit integers) */
#define UPET_TYPE_SINGLE 8 /* Block #, singles/sec, raw singles/sec */
/* concorde keywords */
struct keywd_entry {
char *upet_kwd;
short upet_type;
char *mnc_var;
char *mnc_att;
int (*func)(struct conversion_info *, char *, char *, char *);
};
/* Per-frame attributes in the concorde microPET header */
struct keywd_entry frm_atts[] = {
{ "frame",
UPET_TYPE_INT, NULL, NULL, upet_frame_no },
{ "event_type",
UPET_TYPE_INT, NULL, NULL, NULL },
{ "gate",
UPET_TYPE_INT, NULL, NULL, NULL },
{ "bed",
UPET_TYPE_INT, NULL, NULL, NULL },
{ "bed_offset",
UPET_TYPE_REAL, NULL, NULL, NULL },
{ "ending_bed_offset",
UPET_TYPE_REAL, NULL, NULL, NULL },
{ "vertical_bed_offset",
UPET_TYPE_REAL, NULL, NULL, NULL },
{ "data_file_pointer",
UPET_TYPE_FPTR, NULL, NULL, upet_frame_file_ptr },
{ "frame_start",
UPET_TYPE_REAL, NULL, NULL, upet_frame_start },
{ "frame_duration",
UPET_TYPE_REAL, NULL, NULL, upet_frame_duration },
{ "scale_factor",
UPET_TYPE_REAL, NULL, NULL, upet_frame_scale_factor },
{ "minimum",
UPET_TYPE_REAL, NULL, NULL, upet_frame_min },
{ "maximum",
UPET_TYPE_REAL, NULL, NULL, upet_frame_max },
{ "deadtime_correction",
UPET_TYPE_REAL, NULL, NULL, upet_frame_deadtime_correction },
{ "decay_correction",
UPET_TYPE_REAL, NULL, NULL, upet_frame_decay_correction },
{ "prompts",
UPET_TYPE_3X64, NULL, NULL, NULL },
{ "delays",
UPET_TYPE_3X64, NULL, NULL, NULL },
{ "trues",
UPET_TYPE_3X64, NULL, NULL, NULL },
{ "prompts_rate",
UPET_TYPE_INT, NULL, NULL, NULL },
{ "delays_rate",
UPET_TYPE_INT, NULL, NULL, NULL },
{ "singles",
UPET_TYPE_SINGLE, NULL, NULL, NULL },
{ NULL, 0, NULL, NULL, NULL }
};
/* Per-volume attributes in the concorde microPET header */
struct keywd_entry vol_atts[] = {
{ "ROI_file:",
UPET_TYPE_STR, NULL, NULL, NULL },
{ "version",
UPET_TYPE_STR, NULL, NULL, NULL },
{ "model",
UPET_TYPE_INT, MIstudy, MIdevice_model, NULL },
{ "institution",
UPET_TYPE_STR, MIstudy, MIinstitution, NULL },
{ "study",
UPET_TYPE_STR, NULL, NULL, NULL },
{ "file_name",
UPET_TYPE_STR, NULL, NULL, NULL }, /* actual file data */
{ "file_type",
UPET_TYPE_INT, NULL, NULL, upet_file_type },
{ "acquisition_mode",
UPET_TYPE_INT, NULL, NULL, upet_acq_mode},
{ "bed_motion",
UPET_TYPE_INT, NULL, NULL, upet_bed_motion},
{ "total_frames",
UPET_TYPE_INT, NULL, NULL, upet_total_frames },
{ "isotope",
UPET_TYPE_STR, MIacquisition, MIradionuclide, NULL },
{ "isotope_half_life",
UPET_TYPE_REAL, MIacquisition, MIradionuclide_halflife, NULL },
{ "isotope_branching_fraction",
UPET_TYPE_REAL, NULL, NULL, upet_isotope_branching_fraction },
{ "transaxial_crystals_per_block",
UPET_TYPE_INT, NULL, NULL, NULL },
{ "axial_crystals_per_block",
UPET_TYPE_INT, NULL, NULL, NULL },
{ "intrinsic_crystal_offset",
UPET_TYPE_INT, NULL, NULL, NULL },
{ "transaxial_blocks",
UPET_TYPE_INT, NULL, NULL, NULL },
{ "axial_blocks",
UPET_TYPE_INT, NULL, NULL, NULL },
{ "transaxial_crystal_pitch",
UPET_TYPE_REAL, NULL, NULL, NULL },
{ "axial_crystal_pitch",
UPET_TYPE_REAL, NULL, NULL, upet_axial_crystal_pitch },
{ "radius",
UPET_TYPE_REAL, NULL, NULL, NULL },
{ "radial_fov",
UPET_TYPE_REAL, NULL, NULL, NULL },
{ "src_radius",
UPET_TYPE_REAL, NULL, NULL, NULL },
{"src_cm_per_rev",
UPET_TYPE_REAL, NULL, NULL, NULL },
{"src_steps_per_rev",
UPET_TYPE_INT, NULL, NULL, NULL },
{"tx_src_type",
UPET_TYPE_INT, NULL, NULL, NULL },
{"default_projections",
UPET_TYPE_INT, NULL, NULL, NULL },
{"default_transaxial_angles",
UPET_TYPE_INT, NULL, NULL, NULL },
{"crystal_thickness",
UPET_TYPE_REAL, NULL, NULL, NULL },
{"depth_of_interaction",
UPET_TYPE_REAL, NULL, NULL, NULL },
{"transaxial_bin_size",
UPET_TYPE_REAL, NULL, NULL, NULL },
{"axial_plane_size",
UPET_TYPE_REAL, NULL, NULL, NULL },
{"lld",
UPET_TYPE_REAL, NULL, NULL, NULL },
{"uld",
UPET_TYPE_REAL, NULL, NULL, NULL },
{"timing_window",
UPET_TYPE_INT, NULL, NULL, NULL },
{"data_type",
UPET_TYPE_INT, NULL, NULL, upet_data_type },
{"data_order",
UPET_TYPE_INT, NULL, NULL, upet_data_order },
{"span",
UPET_TYPE_INT, NULL, NULL, NULL },
{"ring_difference",
UPET_TYPE_INT, NULL, NULL, NULL },
{"number_of_dimensions",
UPET_TYPE_INT, NULL, NULL, upet_ndims },
{"x_dimension",
UPET_TYPE_INT, NULL, NULL, upet_x_dim },
{"y_dimension",
UPET_TYPE_INT, NULL, NULL, upet_y_dim },
{"z_dimension",
UPET_TYPE_INT, NULL, NULL, upet_z_dim },
{"w_dimension",
UPET_TYPE_INT, NULL, NULL, upet_vector_dim },
{"x_filter",
UPET_TYPE_FILTER, NULL, NULL, NULL },
{"y_filter",
UPET_TYPE_FILTER, NULL, NULL, NULL },
{"z_filter",
UPET_TYPE_FILTER, NULL, NULL, NULL },
{"histogram_version",
UPET_TYPE_STR, NULL, NULL, NULL },
{"rebinning_type",
UPET_TYPE_INT, NULL, NULL, NULL },
{"rebinning_version",
UPET_TYPE_STR, NULL, NULL, NULL },
{"recon_algorithm",
UPET_TYPE_INT, NULL, NULL, NULL },
{"recon_version",
UPET_TYPE_STR, NULL, NULL, NULL },
{"map_subsets",
UPET_TYPE_INT, NULL, NULL, NULL },
{"map_osem3d_iterations",
UPET_TYPE_INT, NULL, NULL, NULL },
{"map_iterations",
UPET_TYPE_INT, NULL, NULL, NULL },
{"map_beta",
UPET_TYPE_REAL, NULL, NULL, NULL },
{"map_blur_type",
UPET_TYPE_INT, NULL, NULL, NULL },
{"map_prior_type",
UPET_TYPE_INT, NULL, NULL, NULL },
{"map_blur_file",
UPET_TYPE_STR, NULL, NULL, NULL },
{"map_pmatrix_file",
UPET_TYPE_STR, NULL, NULL, NULL },
{"deadtime_correction_applied",
UPET_TYPE_INT, NULL, NULL, NULL },
{"decay_correction_applied",
UPET_TYPE_INT, NULL, NULL, NULL },
{"normalization_applied",
UPET_TYPE_INT, NULL, NULL, NULL },
{"normalization_filename",
UPET_TYPE_STR, NULL, NULL, NULL },
{"attenuation_applied",
UPET_TYPE_INT, NULL, NULL, NULL },
{"attenuation_filename",
UPET_TYPE_STR, NULL, NULL, NULL },
{"scatter_correction",
UPET_TYPE_INT, NULL, NULL, NULL },
{"scatter_version",
UPET_TYPE_STR, NULL, NULL, NULL },
{"arc_correction_applied",
UPET_TYPE_INT, NULL, NULL, NULL },
{"rotation",
UPET_TYPE_REAL, NULL, NULL, upet_rotation },
{"x_offset",
UPET_TYPE_REAL, NULL, NULL, NULL },
{"y_offset",
UPET_TYPE_REAL, NULL, NULL, NULL },
{"z_offset",
UPET_TYPE_REAL, NULL, NULL, NULL },
{"zoom",
UPET_TYPE_REAL, NULL, NULL, NULL },
{"pixel_size",
UPET_TYPE_REAL, NULL, NULL, upet_pixel_size },
{"calibration_units",
UPET_TYPE_INT, NULL, NULL, NULL },
{"calibration_factor",
UPET_TYPE_REAL, NULL, NULL, upet_calibration_factor },
{"calibration_branching_fraction",
UPET_TYPE_REAL, NULL, NULL, NULL },
{"number_of_singles_rates",
UPET_TYPE_INT, NULL, NULL, NULL },
{"investigator",
UPET_TYPE_STR, MIstudy, "investigator", NULL },
{"operator",
UPET_TYPE_STR, MIstudy, MIoperator, NULL },
{"study_identifier",
UPET_TYPE_STR, MIstudy, MIstudy_id, NULL },
{"scan_time",
UPET_TYPE_TIME, NULL, NULL, upet_scan_time },
{"injected_compound",
UPET_TYPE_STR, NULL, NULL, NULL },
{"dose_units",
UPET_TYPE_INT, MIacquisition, MIdose_units, upet_dose_units },
{"dose",
UPET_TYPE_REAL, MIacquisition, MIinjection_dose, NULL },
{"injection_time",
UPET_TYPE_TIME, NULL, NULL, upet_injection_time },
{"injection_decay_correction",
UPET_TYPE_REAL, NULL, NULL, NULL },
{"subject_identifier",
UPET_TYPE_STR, NULL, NULL, NULL },
{"subject_genus",
UPET_TYPE_STR, NULL, NULL, NULL },
{"subject_orientation",
UPET_TYPE_INT, NULL, NULL, NULL },
{"subject_length_units",
UPET_TYPE_INT, NULL, NULL, NULL },
{"subject_length",
UPET_TYPE_REAL, NULL, NULL, NULL },
{"subject_weight_units",
UPET_TYPE_INT, NULL, NULL, NULL },
{"subject_weight",
UPET_TYPE_REAL, NULL, NULL, NULL },
{"subject_phenotype",
UPET_TYPE_STR, NULL, NULL, NULL },
{"study_model",
UPET_TYPE_STR, NULL, NULL, NULL },
{"anesthesia",
UPET_TYPE_STR, NULL, NULL, NULL },
{"analgesia",
UPET_TYPE_STR, NULL, NULL, NULL },
{"other_drugs",
UPET_TYPE_STR, NULL, NULL, NULL },
{"food_access",
UPET_TYPE_STR, NULL, NULL, NULL },
{"water_access",
UPET_TYPE_STR, NULL, NULL, NULL },
{NULL, 0, NULL, NULL, NULL }
};
/* Reflects "normal" image data order */
#define DIM_T 0
#define DIM_Z 1
#define DIM_Y 2
#define DIM_X 3
#define DIM_W 4
static char *_dimnames[5];
/* Calculate the overall scaling factor for the image data from the
* conversion information structure.
*/
#define COMBINED_SCALE_FACTOR(ci_ptr) \
((ci_ptr->scale_factor * ci_ptr->calibration_factor) / \
(ci_ptr->isotope_branching_fraction))
#define ORIENT_BODY 1
#define ORIENT_HEAD 2
int _orient_flag = ORIENT_HEAD;
int _verbose_flag = 1;
ArgvInfo argTable[] = {
{"-head", ARGV_CONSTANT, (char *) ORIENT_HEAD, (char *) &_orient_flag,
"Orient image for cerebral viewing (as with human brain)"},
{"-body", ARGV_CONSTANT, (char *) ORIENT_BODY, (char *) &_orient_flag,
"Orient image for whole-body viewing (Z along long axis)"},
{"-quiet", ARGV_CONSTANT, (char *) 0, (char *) &_verbose_flag,
"Turn off the various progress reporting messages."},
{NULL, ARGV_VERINFO, (char *) VERSIONSTR, (char *) NULL, NULL},
{NULL, ARGV_END, NULL, NULL, NULL}
};
typedef enum {
MSG_INFO,
MSG_WARNING,
MSG_ERROR,
MSG_FATAL
} msg_level_t;
static void
message(msg_level_t level, char *fmt, ...)
{
va_list ap;
const char *prefix_str;
switch (level) {
case MSG_WARNING:
prefix_str = "WARNING: ";
break;
case MSG_ERROR:
prefix_str = "ERROR: ";
break;
case MSG_FATAL:
prefix_str = "FATAL: ";
break;
default:
prefix_str = NULL;
break;
}
va_start(ap, fmt);
if (_verbose_flag || level != MSG_INFO) {
if (level != MSG_INFO) {
if (prefix_str != NULL) {
fprintf(stderr, prefix_str);
}
vfprintf(stderr, fmt, ap);
}
if (prefix_str != NULL) {
fprintf(stdout, prefix_str);
}
vfprintf(stdout, fmt, ap);
}
va_end(ap);
}
static int
is_host_big_endian()
{
long ltmp = 0x04030201;
char *ctmp = (char *) <mp;
if (ctmp[0] == 0x01) {
return (0);
}
if (ctmp[0] == 0x04) {
return (1);
}
return (-1);
}
static void
usage(const char *progname)
{
fprintf(stderr, "\nUsage: %s [<options>] input.img[.hdr] [output.mnc]\n",
progname);
fprintf(stderr, " %s [-help]\n\n", progname);
exit(-1);
}
int
upet_to_minc(char *hdr_fname, char *img_fname, char *out_fname,
char *prog_name)
{
char *line_ptr;
char line_buf[1024];
char *val_ptr;
int in_header;
double dbl_tmp;
int int_tmp;
struct conversion_info ci;
struct keywd_entry *ke_ptr;
int is_known;
char *argv_tmp[5];
char *out_history;
ci.hdr_fp = fopen(hdr_fname, "r");
if (ci.hdr_fp == NULL) {
perror(hdr_fname);
return (-1);
}
ci.img_fp = fopen(img_fname, "r");
if (ci.img_fp == NULL) {
perror(img_fname);
return (-1);
}
ci.mnc_fd = micreate(out_fname, NC_NOCLOBBER);
if (ci.mnc_fd < 0) {
perror(out_fname);
return (-1);
}
ci.frame_zero = -1; /* Initial frame is -1 until set. */
/* Define the basic MINC group variables.
*/
micreate_group_variable(ci.mnc_fd, MIstudy);
micreate_group_variable(ci.mnc_fd, MIacquisition);
micreate_group_variable(ci.mnc_fd, MIpatient);
ncvardef(ci.mnc_fd, "micropet", NC_SHORT, 0, NULL);
/* Fake the history here */
argv_tmp[0] = prog_name;
argv_tmp[1] = VERSIONSTR;
argv_tmp[2] = hdr_fname;
argv_tmp[3] = img_fname;
argv_tmp[4] = out_fname;
out_history = time_stamp(5, argv_tmp);
miattputstr(ci.mnc_fd, NC_GLOBAL, MIhistory, out_history);
free(out_history);
in_header = 1;
ci.frame_nbytes = 1;
ci.frame_nvoxels = 1;
/* Collect the headers */
while (fgets(line_buf, sizeof(line_buf), ci.hdr_fp) != NULL) {
if (line_buf[0] == '#') /* */
continue;
line_ptr = line_buf;
while (!isspace(*line_ptr)) {
line_ptr++;
}
*line_ptr++ = '\0';
val_ptr = line_ptr;
while (*line_ptr != '\n' && *line_ptr != '\r' && *line_ptr != '\0') {
line_ptr++;
}
*line_ptr = '\0';
is_known = 0;
if (in_header) {
if (*val_ptr != '\0') {
/* Save the raw attribute into the file */
ncattput(ci.mnc_fd, ncvarid(ci.mnc_fd, "micropet"),
line_buf, NC_CHAR, strlen(val_ptr), val_ptr);
}
for (ke_ptr = vol_atts; ke_ptr->upet_kwd != NULL; ke_ptr++) {
if (!strcmp(ke_ptr->upet_kwd, line_buf)) {
is_known = 1;
if (ke_ptr->func != NULL) {
(*ke_ptr->func)(&ci, val_ptr,
ke_ptr->mnc_var,
ke_ptr->mnc_att);
}
else if (ke_ptr->mnc_var != NULL &&
ke_ptr->mnc_att != NULL) {
/* Interpret based upon type */
switch (ke_ptr->upet_type) {
case UPET_TYPE_INT:
int_tmp = atoi(val_ptr);
miattputint(ci.mnc_fd,
ncvarid(ci.mnc_fd, ke_ptr->mnc_var),
ke_ptr->mnc_att,
int_tmp);
break;
case UPET_TYPE_REAL:
dbl_tmp = atof(val_ptr);
miattputdbl(ci.mnc_fd,
ncvarid(ci.mnc_fd, ke_ptr->mnc_var),
ke_ptr->mnc_att,
dbl_tmp);
break;
case UPET_TYPE_STR:
miattputstr(ci.mnc_fd,
ncvarid(ci.mnc_fd, ke_ptr->mnc_var),
ke_ptr->mnc_att,
val_ptr);
break;
}
}
break;
}
}
}
else {
/* Not in the header any longer
*/
for (ke_ptr = frm_atts; ke_ptr->upet_kwd != NULL; ke_ptr++) {
if (!strcmp(ke_ptr->upet_kwd, line_buf)) {
is_known = 1;
if (ke_ptr->func != NULL) {
(*ke_ptr->func)(&ci, val_ptr,
ke_ptr->mnc_var,
ke_ptr->mnc_att);
}
break;
}
}
}
if (!is_known) {
if (!strcmp(line_buf, "end_of_header")) {
if (in_header) {
in_header = 0;
copy_init(&ci);
}
else {
copy_frame(&ci);
}
}
else {
message(MSG_WARNING, "Unrecognized keyword %s\n", line_buf);
}
}
}
fclose(ci.hdr_fp);
fclose(ci.img_fp);
miclose(ci.mnc_fd);
return (0);
}
int
main(int argc, char **argv)
{
char *line_ptr;
int i;
char img_fname[1024];
char hdr_fname[1024];
char out_fname[1024];
int result;
if (ParseArgv(&argc, argv, argTable, 0) || argc < 2) {
usage(argv[0]);
return (-1);
}
/* Set the dimension names. This is done here since the correct
* arrangement depends on the value of _orient_flag
*/
_dimnames[DIM_T] = MItime;
_dimnames[DIM_X] = MIxspace;
_dimnames[DIM_Y] = MIyspace;
_dimnames[DIM_Z] = MIzspace;
_dimnames[DIM_W] = MIvector_dimension;
if (_orient_flag == ORIENT_HEAD) {
/* If using head orientation, exchange Y and Z.
*/
_dimnames[DIM_Y] = MIzspace;
_dimnames[DIM_Z] = MIyspace;
}
/* Open the header and the associated binary file. */
for (i = 1; i < argc; i++) {
/* Here we try to be flexible about allowing the user to specify
* either the name of the .hdr file or the name of the .img file,
* or just the base name of the two files. All three options
* should work.
*/
strcpy(img_fname, argv[i]);
strcpy(hdr_fname, argv[i]);
/* Find the last extension.
*/
line_ptr = strrchr(argv[i], '.');
/* Did the user specify the .hdr file??
*/
if (line_ptr != NULL && !strcmp(line_ptr, ".hdr")) {
line_ptr = strrchr(img_fname, '.');
if (line_ptr != NULL) {
*line_ptr = '\0';
}
}
/* Did the user specify the .img file??
*/
else if (line_ptr != NULL && !strcmp(line_ptr, ".img")) {
strcat(hdr_fname, ".hdr");
}
/* Or perhaps just the base name??
*/
else {
strcat(img_fname, ".img");
strcat(hdr_fname, ".img.hdr");
}
/* See if there is a filename following this one, and if so, does it
* end with the ".mnc" extension. If so, take that names as the
* output for this conversions.
*/
if (i < argc - 1 &&
(line_ptr = strrchr(argv[i+1], '.')) != NULL &&
!strcmp(line_ptr, ".mnc")) {
strcpy(out_fname, argv[i+1]);
i++;
}
else {
strcpy(out_fname, img_fname);
line_ptr = strrchr(out_fname, '.');
if (line_ptr != NULL) {
strcpy(line_ptr, ".mnc");
}
}
/* Perform the conversion.
*/
message(MSG_INFO, "Starting conversion\n");
message(MSG_INFO, "- Input header: %s\n", hdr_fname);
message(MSG_INFO, "- Input image: %s\n", img_fname);
message(MSG_INFO, "- Output file: %s\n", out_fname);
result = upet_to_minc(hdr_fname, img_fname, out_fname, argv[0]);
if (result < 0) {
message(MSG_ERROR, "Error creating %s\n", out_fname);
}
else {
message(MSG_INFO, "Finished creating %s\n", out_fname);
}
}
}
DECLARE_FUNC(upet_file_type)
{
int file_type = atoi(val_str);
switch (file_type) {
case UPET_FT_IMAGE: /* Image file */
case UPET_FT_MU_MAP: /* Mu map file */
return (0);
default:
message(MSG_WARNING,
"File type %d is not handled. Conversion results may be problematic...\n", file_type);
break;
}
return (1);
}
DECLARE_FUNC(upet_acq_mode)
{
int mode_int = atoi(val_str);
char *mode_str;
switch (mode_int) {
case UPET_AM_UNKNOWN:
mode_str = "unknown";
break;
case UPET_AM_BLANK:
mode_str = "blank";
break;
case UPET_AM_EMISSION:
mode_str = "emission";
break;
case UPET_AM_DYNAMIC:
mode_str = "dynamic";
break;
case UPET_AM_GATED:
mode_str = "gated";
break;
case UPET_AM_CONTINUOUS_BED_MOTION:
mode_str = "continuous_bed_motion";
break;
case UPET_AM_SINGLES_TRANSMISSION:
mode_str = "singles_transmission";
break;
case UPET_AM_WINDOWED_COINCIDENCE_TRANSMISSION:
mode_str = "windowed_coincidence_transmission";
break;
case UPET_AM_NONWINDOWED_COINCIDENCE_TRANSMISSION:
mode_str = "non-windowed_coincidence_transmission";
break;
default:
message(MSG_WARNING, "Unknown acquisition mode %d\n", mode_int);
mode_str = NULL;
break;
}
if (mode_str != NULL) {
miattputstr(ci_ptr->mnc_fd, ncvarid(ci_ptr->mnc_fd, MIacquisition),
"micropet_mode", mode_str);
}
return (0);
}
DECLARE_FUNC(upet_bed_motion)
{
return (0);
}
DECLARE_FUNC(upet_data_type)
{
ci_ptr->data_type = atoi(val_str);
switch (ci_ptr->data_type) {
case UPET_DT_BYTE:
ci_ptr->minc_type = NC_BYTE;
break;
case UPET_DT_II16:
ci_ptr->minc_type = NC_SHORT;
ci_ptr->frame_nbytes *= 2;
if (is_host_big_endian()) {
ci_ptr->swap_size = 2;
}
else {
ci_ptr->swap_size = 0;
}
break;
case UPET_DT_II32:
ci_ptr->minc_type = NC_INT;
ci_ptr->frame_nbytes *= 4;
if (is_host_big_endian()) {
ci_ptr->swap_size = 4;
}
else {
ci_ptr->swap_size = 0;
}
break;
case UPET_DT_IF32:
ci_ptr->minc_type = NC_FLOAT;
ci_ptr->frame_nbytes *= 4;
if (is_host_big_endian()) {
ci_ptr->swap_size = 4;
}
else {
ci_ptr->swap_size = 0;
}
break;
case UPET_DT_MF32:
ci_ptr->minc_type = NC_FLOAT;
ci_ptr->frame_nbytes *= 4;
if (!is_host_big_endian()) {
ci_ptr->swap_size = 4;
}
else {
ci_ptr->swap_size = 0;
}
break;
case UPET_DT_MI16:
ci_ptr->minc_type = NC_SHORT;
ci_ptr->frame_nbytes *= 2;
if (!is_host_big_endian()) {
ci_ptr->swap_size = 2;
}
else {
ci_ptr->swap_size = 0;
}
break;
case UPET_DT_MI32:
ci_ptr->minc_type = NC_INT;
ci_ptr->frame_nbytes *= 4;
if (!is_host_big_endian()) {
ci_ptr->swap_size = 4;
}
else {
ci_ptr->swap_size = 0;
}
break;
default:
message(MSG_ERROR, "Unknown data type %d\n", ci_ptr->data_type);
return (1);
}
if (ci_ptr->swap_size != 0) {
message(MSG_INFO, "Swapping groups of %d bytes.\n", ci_ptr->swap_size);
}
else {
message(MSG_INFO, "No byte-swapping required.\n");
}
return (0);
}
DECLARE_FUNC(upet_data_order)
{
if (atoi(val_str) != 1) {
message(MSG_WARNING, "Unknown data order.\n");
}
return (0);
}
DECLARE_FUNC(upet_ndims)
{
ci_ptr->dim_count = atoi(val_str);
return (0);
}
static void
create_dimension(struct conversion_info *ci_ptr, int index, int length)
{
ci_ptr->dim_lengths[index] = length;
if (length > 1) {
ci_ptr->dim_ids[index] = ncdimdef(ci_ptr->mnc_fd,
_dimnames[index],
length);
if (index != DIM_W) {
if (index == DIM_T) {
micreate_std_variable(ci_ptr->mnc_fd, _dimnames[index],
NC_DOUBLE, 1, &ci_ptr->dim_ids[index]);
micreate_std_variable(ci_ptr->mnc_fd, MItime_width,
NC_DOUBLE, 1, &ci_ptr->dim_ids[index]);
}
else {
micreate_std_variable(ci_ptr->mnc_fd, _dimnames[index],
NC_DOUBLE, 0, NULL);
}
}
}
else {
ci_ptr->dim_ids[index] = -1;
}
}
DECLARE_FUNC(upet_total_frames)
{
create_dimension(ci_ptr, DIM_T, atoi(val_str));
return (0);
}
DECLARE_FUNC(upet_x_dim)
{
int x = atoi(val_str);
ci_ptr->frame_nbytes *= x;
ci_ptr->frame_nvoxels *= x;
create_dimension(ci_ptr, DIM_X, x);
return (0);
}
DECLARE_FUNC(upet_y_dim)
{
int y = atoi(val_str);
ci_ptr->frame_nbytes *= y;
ci_ptr->frame_nvoxels *= y;
create_dimension(ci_ptr, DIM_Y, y);
return (0);
}
DECLARE_FUNC(upet_z_dim)
{
int z = atoi(val_str);
ci_ptr->frame_nbytes *= z;
ci_ptr->frame_nvoxels *= z;
create_dimension(ci_ptr, DIM_Z, z);
return (0);
}
DECLARE_FUNC(upet_vector_dim)
{
int w = atoi(val_str);
ci_ptr->frame_nbytes *= w;
ci_ptr->frame_nvoxels *= w;
create_dimension(ci_ptr, DIM_W, w);
return (0);
}
/* Parse a micropet time string of the form: Ddd Mmm NN HH:MM:SS YYYY
* e.g. Fri Jan 7 14:16:31 2005
*/
static int
parse_time(char *str_ptr, struct tm *tm_ptr)
{
/* Just skip the first three characters. */
while (*str_ptr != '\0' && *str_ptr != ' ') {
str_ptr++;
}
while (*str_ptr == ' ') {
str_ptr++;
}
/* Decode the month */
if (str_ptr[0] == 'A') {
if (str_ptr[1] == 'p') {
tm_ptr->tm_mon = 4 - 1; /* April */
}
else {
tm_ptr->tm_mon = 8 - 1; /* August */
}
}
else if (str_ptr[0] == 'D') {
tm_ptr->tm_mon = 12 - 1; /* December */
}
else if (str_ptr[0] == 'F') { /* February */
tm_ptr->tm_mon = 2 - 1;
}
else if (str_ptr[0] == 'J') {
if (str_ptr[1] == 'a') {
tm_ptr->tm_mon = 1 - 1; /* January */
}
else if (str_ptr[2] == 'l') {
tm_ptr->tm_mon = 7 - 1; /* July */
}
else {
tm_ptr->tm_mon = 6 - 1; /* June */
}
}
else if (str_ptr[0] == 'M') {
if (str_ptr[2] == 'r') {
tm_ptr->tm_mon = 3 - 1; /* March */
}
else {
tm_ptr->tm_mon = 5 - 1; /* May */
}
}
else if (str_ptr[0] == 'N') {
tm_ptr->tm_mon = 11 - 1; /* November */
}
else if (str_ptr[0] == 'O') {
tm_ptr->tm_mon = 10 - 1; /* October */
}
else if (str_ptr[0] == 'S') {
tm_ptr->tm_mon = 9 - 1; /* September */
}
else {
return 0;
}
/* Skip past the month */
while (*str_ptr != ' ' && *str_ptr != '\0') {
str_ptr++;
}
while (*str_ptr == ' ') {
str_ptr++;
}
tm_ptr->tm_mday = 0;
while (isdigit(*str_ptr)) {
tm_ptr->tm_mday = (tm_ptr->tm_mday * 10) + (*str_ptr++ - '0');
}
while (*str_ptr == ' ') {
str_ptr++;
}
tm_ptr->tm_hour = 0;
while (isdigit(*str_ptr)) {
tm_ptr->tm_hour = (tm_ptr->tm_hour * 10) + (*str_ptr++ - '0');
}
if (*str_ptr == ':') {
str_ptr++;
}
else {
return 0;
}
tm_ptr->tm_min = 0;
while (isdigit(*str_ptr)) {
tm_ptr->tm_min = (tm_ptr->tm_min * 10) + (*str_ptr++ - '0');
}
if (*str_ptr == ':') {
str_ptr++;
}
else {
return 0;
}
tm_ptr->tm_sec = 0;
while (isdigit(*str_ptr)) {
tm_ptr->tm_sec = (tm_ptr->tm_sec * 10) + (*str_ptr++ - '0');
}
while (*str_ptr == ' ') {
str_ptr++;
}
tm_ptr->tm_year = 0;
while (isdigit(*str_ptr)) {
tm_ptr->tm_year = (tm_ptr->tm_year * 10) + (*str_ptr++ - '0');
}
tm_ptr->tm_year -= 1900;
return 1;
}
DECLARE_FUNC(upet_injection_time)
{
struct tm tmbuf;
int id;
char str_buf[128];
id = ncvarid(ci_ptr->mnc_fd, MIacquisition);
if (!parse_time(val_str, &tmbuf)) {
strcpy(str_buf, "unknown");
miattputstr(ci_ptr->mnc_fd, id, MIinjection_time, str_buf);
miattputstr(ci_ptr->mnc_fd, id, MIinjection_year, str_buf);
miattputstr(ci_ptr->mnc_fd, id, MIinjection_month, str_buf);
miattputstr(ci_ptr->mnc_fd, id, MIinjection_day, str_buf);
}
else {
sprintf(str_buf, "%02d%02d%02d",
tmbuf.tm_hour, tmbuf.tm_min, tmbuf.tm_sec);
miattputstr(ci_ptr->mnc_fd, id, MIinjection_time, str_buf);
sprintf(str_buf, "%d", tmbuf.tm_year + 1900);
miattputstr(ci_ptr->mnc_fd, id, MIinjection_year, str_buf);
sprintf(str_buf, "%d", tmbuf.tm_mon + 1);
miattputstr(ci_ptr->mnc_fd, id, MIinjection_month, str_buf);
sprintf(str_buf, "%d", tmbuf.tm_mday);
miattputstr(ci_ptr->mnc_fd, id, MIinjection_day, str_buf);
}
return (0);
}
DECLARE_FUNC(upet_scan_time)
{
struct tm tmbuf;
int id;
char str_buf[128];
id = ncvarid(ci_ptr->mnc_fd, MIstudy);
if (!parse_time(val_str, &tmbuf)) {
strcpy(str_buf, "unknown");
miattputstr(ci_ptr->mnc_fd, id, MIstart_time, str_buf);
miattputstr(ci_ptr->mnc_fd, id, MIstart_year, str_buf);
miattputstr(ci_ptr->mnc_fd, id, MIstart_month, str_buf);
miattputstr(ci_ptr->mnc_fd, id, MIstart_day, str_buf);
}
else {
sprintf(str_buf, "%02d%02d%02d",
tmbuf.tm_hour, tmbuf.tm_min, tmbuf.tm_sec);
miattputstr(ci_ptr->mnc_fd, id, MIstart_time, str_buf);
sprintf(str_buf, "%d", tmbuf.tm_year + 1900);
miattputstr(ci_ptr->mnc_fd, id, MIstart_year, str_buf);
sprintf(str_buf, "%d", tmbuf.tm_mon + 1);
miattputstr(ci_ptr->mnc_fd, id, MIstart_month, str_buf);
sprintf(str_buf, "%d", tmbuf.tm_mday);
miattputstr(ci_ptr->mnc_fd, id, MIstart_day, str_buf);
}
return (0);
}
DECLARE_FUNC(upet_axial_crystal_pitch)
{
double dbl_tmp = atof(val_str);
/* dbl_tmp is in cm. Convert to mm. */
dbl_tmp *= 10.0;
/* Now convert from crystal pitch to actual slice width */
dbl_tmp /= 2.0;
ci_ptr->dim_steps[DIM_Z] = dbl_tmp;
return (0);
}
DECLARE_FUNC(upet_pixel_size)
{
double dbl_tmp = atof(val_str);
/* dbl_tmp is in cm. Convert to mm. */
dbl_tmp *= 10.0;
ci_ptr->dim_steps[DIM_X] = dbl_tmp;
ci_ptr->dim_steps[DIM_Y] = dbl_tmp;
return (0);
}
DECLARE_FUNC(upet_dose_units)
{
int tmp = atoi(val_str);
char *str_ptr;
if (tmp == 0) {
str_ptr = "unknown";
}
else if (tmp == 1) {
str_ptr = "mCi";
}
else if (tmp == 2) {
str_ptr = "MBq";
}
else {
str_ptr = "???????";
message(MSG_WARNING, "Unrecognized dose_units value %d\n", tmp);
}
miattputstr(ci_ptr->mnc_fd, ncvarid(ci_ptr->mnc_fd, new_var), new_att, str_ptr);
return (0);
}
DECLARE_FUNC(upet_calibration_factor)
{
double dbl_tmp = atof(val_str);
ci_ptr->calibration_factor = dbl_tmp;
return (0);
}
DECLARE_FUNC(upet_isotope_branching_fraction)
{
double dbl_tmp = atof(val_str);
ci_ptr->isotope_branching_fraction = dbl_tmp;
return (0);
}
DECLARE_FUNC(upet_rotation)
{
double dbl_tmp = atof(val_str);
if (dbl_tmp != 0.0) {
message(MSG_WARNING, "Rotation is %f\n", dbl_tmp);
}
return (0);
}
/***********************/
/* Per-frame functions */
DECLARE_FUNC(upet_frame_no)
{
ci_ptr->frame_index = atoi(val_str);
/* Set index of "zeroth" frame if not already set.
*/
if (ci_ptr->frame_zero < 0) {
ci_ptr->frame_zero = ci_ptr->frame_index;
}
return (0);
}
DECLARE_FUNC(upet_frame_start)
{
long index = ci_ptr->frame_index - ci_ptr->frame_zero;
double dbl_tmp = atof(val_str);
mivarput1(ci_ptr->mnc_fd, ncvarid(ci_ptr->mnc_fd, MItime), &index,
NC_DOUBLE, MI_SIGNED, &dbl_tmp);
return (0);
}
DECLARE_FUNC(upet_frame_duration)
{
long index = ci_ptr->frame_index - ci_ptr->frame_zero;
double dbl_tmp = atof(val_str);
mivarput1(ci_ptr->mnc_fd, ncvarid(ci_ptr->mnc_fd, MItime_width), &index,
NC_DOUBLE, MI_SIGNED, &dbl_tmp);
return (0);
}
DECLARE_FUNC(upet_frame_min)
{
long index = ci_ptr->frame_index - ci_ptr->frame_zero;
double dbl_tmp = atof(val_str);
dbl_tmp *= COMBINED_SCALE_FACTOR(ci_ptr);
mivarput1(ci_ptr->mnc_fd, ncvarid(ci_ptr->mnc_fd, MIimagemin), &index,
NC_DOUBLE, MI_SIGNED, &dbl_tmp);
return (0);
}
DECLARE_FUNC(upet_frame_max)
{
long index = ci_ptr->frame_index - ci_ptr->frame_zero;
double dbl_tmp = atof(val_str);
dbl_tmp *= COMBINED_SCALE_FACTOR(ci_ptr);
mivarput1(ci_ptr->mnc_fd, ncvarid(ci_ptr->mnc_fd, MIimagemax), &index,
NC_DOUBLE, MI_SIGNED, &dbl_tmp);
return (0);
}
DECLARE_FUNC(upet_frame_file_ptr)
{
long index = ci_ptr->frame_index - ci_ptr->frame_zero;
long hipart;
long lopart;
char *end_ptr;
lopart = strtol(val_str, &end_ptr, 10);
if (*end_ptr == ' ') {
while (*end_ptr == ' ') {
end_ptr++;
}
if (isdigit(*end_ptr)) {
hipart = lopart;
lopart = strtol(end_ptr, NULL, 10);
}
}
/* Seek the image file to the data */
fseek(ci_ptr->img_fp, lopart, SEEK_SET);
return (0);
}
DECLARE_FUNC(upet_frame_scale_factor)
{
ci_ptr->scale_factor = atof(val_str);
return (0);
}
DECLARE_FUNC(upet_frame_deadtime_correction)
{
ci_ptr->deadtime_correction = atof(val_str);
return (0);
}
DECLARE_FUNC(upet_frame_decay_correction)
{
ci_ptr->decay_correction = atof(val_str);
return (0);
}
static void
swap_data(int swap_size,
long nvox,
unsigned char *data)
{
unsigned char tmp;
if (swap_size == 2) {
while (nvox--) {
tmp = data[0];
data[0] = data[1];
data[1] = tmp;
data += 2;
}
}
else if (swap_size == 4) {
while (nvox--) {
tmp = data[0];
data[0] = data[3];
data[3] = tmp;
tmp = data[1];
data[1] = data[2];
data[2] = tmp;
data += 4;
}
}
}
static void
scale_data(nc_type datatype,
long nvox,
void *data,
double scale)
{
long i;
double tmp;
switch (datatype) {
case NC_BYTE:
for (i = 0; i < nvox; i++) {
tmp = (double) ((char *)data)[i];
tmp *= scale;
((char *)data)[i] = tmp;
}
break;
case NC_SHORT:
for (i = 0; i < nvox; i++) {
tmp = (double) ((short *)data)[i];
tmp *= scale;
((short *)data)[i] = tmp;
}
break;
case NC_INT:
for (i = 0; i < nvox; i++) {
tmp = (double) ((int *)data)[i];
tmp *= scale;
((int *)data)[i] = tmp;
}
break;
case NC_FLOAT:
for (i = 0; i < nvox; i++) {
tmp = (double) ((float *)data)[i];
tmp *= scale;
((float *)data)[i] = tmp;
}
break;
case NC_DOUBLE:
for (i = 0; i < nvox; i++) {
tmp = (double) ((double *)data)[i];
tmp *= scale;
((double *)data)[i] = tmp;
}
break;
default:
message(MSG_ERROR, "Data type %d not handled\n", datatype);
break;
}
}
void
copy_init(struct conversion_info *ci_ptr)
{
ci_ptr->frame_buffer = malloc(ci_ptr->frame_nbytes);
if (ci_ptr->frame_buffer == NULL) {
message(MSG_FATAL, "Out of memory\n");
exit(-1);
}
/* Create the image, imagemax, and imagemin variables.
*/
micreate_std_variable(ci_ptr->mnc_fd, MIimagemax, NC_DOUBLE,
1, ci_ptr->dim_ids);
micreate_std_variable(ci_ptr->mnc_fd, MIimagemin, NC_DOUBLE,
1, ci_ptr->dim_ids);
micreate_std_variable(ci_ptr->mnc_fd, MIimage, ci_ptr->minc_type,
ci_ptr->dim_count + 1, ci_ptr->dim_ids);
/* Set up the dimension step and start values. Because of the microPET
* data orientation, we set Z and Y to be the inverse of the norm, to
* put the animal's nose at the top of the display.
* TODO: allow this behavior to be controlled on the command line.
*/
miattputdbl(ci_ptr->mnc_fd, ncvarid(ci_ptr->mnc_fd, _dimnames[DIM_Z]),
MIstep, -ci_ptr->dim_steps[DIM_Z]);
miattputdbl(ci_ptr->mnc_fd, ncvarid(ci_ptr->mnc_fd, _dimnames[DIM_X]),
MIstep, ci_ptr->dim_steps[DIM_X]);
miattputdbl(ci_ptr->mnc_fd, ncvarid(ci_ptr->mnc_fd, _dimnames[DIM_Y]),
MIstep, -ci_ptr->dim_steps[DIM_Y]);
miattputdbl(ci_ptr->mnc_fd, ncvarid(ci_ptr->mnc_fd, _dimnames[DIM_Z]),
MIstart, ci_ptr->dim_steps[DIM_Z] * ci_ptr->dim_lengths[DIM_Z]);
miattputdbl(ci_ptr->mnc_fd, ncvarid(ci_ptr->mnc_fd, _dimnames[DIM_X]),
MIstart, 0.0);
miattputdbl(ci_ptr->mnc_fd, ncvarid(ci_ptr->mnc_fd, _dimnames[DIM_Y]),
MIstart, ci_ptr->dim_steps[DIM_Y] * ci_ptr->dim_lengths[DIM_Y]);
miattputstr(ci_ptr->mnc_fd, ncvarid(ci_ptr->mnc_fd, _dimnames[DIM_Z]),
MIunits, "mm");
miattputstr(ci_ptr->mnc_fd, ncvarid(ci_ptr->mnc_fd, _dimnames[DIM_X]),
MIunits, "mm");
miattputstr(ci_ptr->mnc_fd, ncvarid(ci_ptr->mnc_fd, _dimnames[DIM_Y]),
MIunits, "mm");
miattputstr(ci_ptr->mnc_fd, ncvarid(ci_ptr->mnc_fd, _dimnames[DIM_T]),
MIunits, "s");
ncendef(ci_ptr->mnc_fd);
}
static void
copy_frame(struct conversion_info *ci_ptr)
{
long start[5];
long count[5];
size_t nitems;
message(MSG_INFO, "Inserting frame #%d\n", ci_ptr->frame_index);
/* Actually read the data from the image file.
*/
nitems = fread(ci_ptr->frame_buffer, ci_ptr->frame_nbytes, 1,
ci_ptr->img_fp);
if (nitems != 1) {
message(MSG_FATAL, "Read failed with error %d, return %d\n",
errno, nitems);
exit(-1);
}
/* Setup the starts and counts for the data block.
*/
start[DIM_T] = ci_ptr->frame_index - ci_ptr->frame_zero;
start[DIM_X] = 0;
start[DIM_Y] = 0;
start[DIM_Z] = 0;
start[DIM_W] = 0;
count[DIM_T] = 1;
count[DIM_X] = ci_ptr->dim_lengths[DIM_X];
count[DIM_Y] = ci_ptr->dim_lengths[DIM_Y];
count[DIM_Z] = ci_ptr->dim_lengths[DIM_Z];
count[DIM_W] = ci_ptr->dim_lengths[DIM_W];
/* Perform swapping if necessary.
*/
if (ci_ptr->swap_size != 0) {
swap_data(ci_ptr->swap_size, ci_ptr->frame_nvoxels,
ci_ptr->frame_buffer);
}
/* Scale the raw data into the final range.
*/
scale_data(ci_ptr->minc_type, ci_ptr->frame_nvoxels, ci_ptr->frame_buffer,
COMBINED_SCALE_FACTOR(ci_ptr));
/* For now we perform no conversions on the data as it is stored.
* This may be worth modifying in the future, to allow storage of
* non-floating-point formats from a typical microPET file.
*/
ncvarput(ci_ptr->mnc_fd, ncvarid(ci_ptr->mnc_fd, MIimage), start, count,
ci_ptr->frame_buffer);
}
syntax highlighted by Code2HTML, v. 0.9.1