void generate_header (Image::Header& header, const std::string& tck_file_path, const std::vector<float>& voxel_size)
        {

          Tractography::Properties properties;
          Tractography::Reader<float> file (tck_file_path, properties);

          Streamline<float> tck;
          size_t track_counter = 0;

          Point<float> min_values ( INFINITY,  INFINITY,  INFINITY);
          Point<float> max_values (-INFINITY, -INFINITY, -INFINITY);

          {
            ProgressBar progress ("creating new template image...", 0);
            while (file (tck) && track_counter++ < MAX_TRACKS_READ_FOR_HEADER) {
              for (std::vector<Point<float> >::const_iterator i = tck.begin(); i != tck.end(); ++i) {
                min_values[0] = std::min (min_values[0], (*i)[0]);
                max_values[0] = std::max (max_values[0], (*i)[0]);
                min_values[1] = std::min (min_values[1], (*i)[1]);
                max_values[1] = std::max (max_values[1], (*i)[1]);
                min_values[2] = std::min (min_values[2], (*i)[2]);
                max_values[2] = std::max (max_values[2], (*i)[2]);
              }
              ++progress;
            }
          }

          min_values -= Point<float> (3.0*voxel_size[0], 3.0*voxel_size[1], 3.0*voxel_size[2]);
          max_values += Point<float> (3.0*voxel_size[0], 3.0*voxel_size[1], 3.0*voxel_size[2]);

          header.name() = "tckmap image header";
          header.set_ndim (3);

          for (size_t i = 0; i != 3; ++i) {
            header.dim(i) = std::ceil((max_values[i] - min_values[i]) / voxel_size[i]);
            header.vox(i) = voxel_size[i];
            header.stride(i) = i+1;
            //header.set_units (i, Image::Axis::millimeters);
          }

          //header.set_description (0, Image::Axis::left_to_right);
          //header.set_description (1, Image::Axis::posterior_to_anterior);
          //header.set_description (2, Image::Axis::inferior_to_superior);

          Math::Matrix<float>& M (header.transform());
          M.allocate (4,4);
          M.identity();
          M(0,3) = min_values[0];
          M(1,3) = min_values[1];
          M(2,3) = min_values[2];
          file.close();
        }
        bool FixedNumPoints::operator() (const Streamline<>& in, Streamline<>& out) const
        {
          // Perform an explicit calculation of streamline length
          // From this, derive the spline position of each sample
          assert (in.size() > 1);
          out.clear();
          out.index = in.index;
          out.weight = in.weight;
          value_type length = 0.0;
          vector<value_type> steps;
          for (size_t i = 1; i != in.size(); ++i) {
            const value_type dist = (in[i] - in[i-1]).norm();
            length += dist;
            steps.push_back (dist);
          }
          steps.push_back (value_type(0));

          Math::Hermite<value_type> interp (hermite_tension);
          Streamline<> temp (in);
          const size_t s = temp.size();
          temp.insert    (temp.begin(), temp[0] + (temp[0] - temp[ 1 ]));
          temp.push_back (              temp[s] + (temp[s] - temp[s-1]));

          value_type cumulative_length = value_type(0);
          size_t input_index = 0;
          for (size_t output_index = 0; output_index != num_points; ++output_index) {
            const value_type target_length = length * output_index / value_type(num_points-1);
            while (input_index < s && (cumulative_length + steps[input_index] < target_length))
              cumulative_length += steps[input_index++];
            if (input_index == s) {
              out.push_back (temp[s]);
              break;
            }
            const value_type mu = (target_length - cumulative_length) / steps[input_index];
            interp.set (mu);
            out.push_back (interp.value (temp[input_index], temp[input_index+1], temp[input_index+2], temp[input_index+3]));
          }

          return true;
        }