void testApp::setup() {

    ofSetFrameRate(60);
    ofEnableAlphaBlending();
    ofSetLogLevel(OF_LOG_VERBOSE);

    vector<string> files3D = getMeshFiles();
    ShareMesh* mesh = new ShareMesh(files3D[0]);
    meshes.push_back(mesh);

    vector<string> files2D = getImageFiles();
    for (int i = 0; i < files2D.size(); i++) {
        cout << files2D[i] << endl;
        ShareImage* image = new ShareImage(files2D[i]);
        images.push_back(image);
    }
}
int main(int argc, char** argv) {

	UTIL_TIME_SCOPE("main");

	try {

		util::ProgramOptions::init(argc, argv);
		logger::LogManager::init();

		util::point<float, 3> resolution(
				optionResX,
				optionResY,
				optionResZ);
		util::point<float, 3> offset(
				optionOffsetX,
				optionOffsetY,
				optionOffsetZ);

		Crag* crag = new Crag();
		CragVolumes* volumes = new CragVolumes(*crag);
		Costs* mergeCosts = 0;

		CragImport import;

		bool alreadyDownsampled = false;

		if (optionMergeTree) {

			UTIL_TIME_SCOPE("read CRAG from mergetree");

			// get information about the image to read
			std::string mergeTreePath = optionMergeTree;

			if (boost::filesystem::is_directory(boost::filesystem::path(mergeTreePath))) {

				std::vector<std::string> files = getImageFiles(mergeTreePath);

				// process one image after another
				std::vector<std::unique_ptr<Crag>> crags(files.size());
				std::vector<std::unique_ptr<CragVolumes>> cragsVolumes;
				for (auto& c : crags) {
					c = std::unique_ptr<Crag>(new Crag);
					cragsVolumes.push_back(std::unique_ptr<CragVolumes>(new CragVolumes(*c)));
				}

				int i = 0;
				for (std::string file : files) {
					
					LOG_USER(logger::out) << "reading crag from " << file << std::endl;

					import.readCrag(file, *crags[i], *cragsVolumes[i], resolution, offset + util::point<float, 3>(0, 0, resolution.z()*i));
					i++;
				}

				if (optionDownsampleCrag) {

					UTIL_TIME_SCOPE("downsample CRAG");

					DownSampler downSampler(optionMinCandidateSize.as<int>());

					std::vector<std::unique_ptr<Crag>> downSampledCrags(crags.size());
					std::vector<std::unique_ptr<CragVolumes>> downSampledVolumes(crags.size());

					for (int i = 0; i < crags.size(); i++) {

						downSampledCrags[i]   = std::unique_ptr<Crag>(new Crag());
						downSampledVolumes[i] = std::unique_ptr<CragVolumes>(new CragVolumes(*downSampledCrags[i]));

						downSampler.process(*crags[i], *cragsVolumes[i], *downSampledCrags[i], *downSampledVolumes[i]);
					}

					std::swap(cragsVolumes, downSampledVolumes);
					std::swap(crags, downSampledCrags);

					// prevent another downsampling on the candidates added by 
					// the combiner
					alreadyDownsampled = true;
				}

				// combine crags
				CragStackCombiner combiner;
				combiner.combine(crags, cragsVolumes, *crag, *volumes);

			} else {

				import.readCrag(mergeTreePath, *crag, *volumes, resolution, offset);
			}

		} else if (optionSupervoxels.as<bool>() && (optionMergeHistory.as<bool>() || optionCandidateSegmentation.as<bool>())) {

			UTIL_TIME_SCOPE("read CRAG from merge history");

			if (optionMergeHistory) {

				std::string mergeHistoryPath = optionMergeHistory;

				if (boost::filesystem::is_directory(boost::filesystem::path(mergeHistoryPath))) {

					// get all merge-history files
					std::vector<std::string> mhFiles;
					for (boost::filesystem::directory_iterator i(mergeHistoryPath); i != boost::filesystem::directory_iterator(); i++)
						if (!boost::filesystem::is_directory(*i) && (
							i->path().extension() == ".txt" ||
							i->path().extension() == ".dat"
						))
							mhFiles.push_back(i->path().native());
					std::sort(mhFiles.begin(), mhFiles.end());

					// get all supervoxel files
					std::vector<std::string> svFiles = getImageFiles(optionSupervoxels);

					// process one image after another
					std::vector<std::unique_ptr<Crag>> crags(mhFiles.size());
					std::vector<std::unique_ptr<CragVolumes>> cragsVolumes;
					for (auto& c : crags) {
						c = std::unique_ptr<Crag>(new Crag);
						cragsVolumes.push_back(std::unique_ptr<CragVolumes>(new CragVolumes(*c)));
					}

					for (int i = 0; i < mhFiles.size(); i++) {
						
						LOG_USER(logger::out) << "reading crag from supervoxel file " << svFiles[i] << " and merge history " << mhFiles[i] << std::endl;

						Costs mergeCosts(*crags[i]);
						import.readCragFromMergeHistory(svFiles[i], mhFiles[i], *crags[i], *cragsVolumes[i], resolution, offset + util::point<float, 3>(0, 0, resolution.z()*i), mergeCosts);
					}

					if (optionDownsampleCrag) {

						UTIL_TIME_SCOPE("downsample CRAG");

						DownSampler downSampler(optionMinCandidateSize.as<int>());

						std::vector<std::unique_ptr<Crag>> downSampledCrags(crags.size());
						std::vector<std::unique_ptr<CragVolumes>> downSampledVolumes(crags.size());

						for (int i = 0; i < crags.size(); i++) {

							downSampledCrags[i]   = std::unique_ptr<Crag>(new Crag());
							downSampledVolumes[i] = std::unique_ptr<CragVolumes>(new CragVolumes(*downSampledCrags[i]));

							downSampler.process(*crags[i], *cragsVolumes[i], *downSampledCrags[i], *downSampledVolumes[i]);
						}

						std::swap(cragsVolumes, downSampledVolumes);
						std::swap(crags, downSampledCrags);

						// prevent another downsampling on the candidates added by 
						// the combiner
						alreadyDownsampled = true;
					}

					// combine crags
					CragStackCombiner combiner;
					combiner.combine(crags, cragsVolumes, *crag, *volumes);

				} else {

					mergeCosts = new Costs(*crag);
					import.readCragFromMergeHistory(optionSupervoxels, optionMergeHistory, *crag, *volumes, resolution, offset, *mergeCosts);

				}

			} else
				import.readCragFromCandidateSegmentation(optionSupervoxels, optionCandidateSegmentation, *crag, *volumes, resolution, offset);

		} else {

			LOG_ERROR(logger::out)
					<< "at least one of mergetree or (supervoxels && mergeHistory) "
					<< "have to be given to create a CRAG" << std::endl;

			return 1;
		}

		if (optionDownsampleCrag && !alreadyDownsampled) {

			UTIL_TIME_SCOPE("downsample CRAG");

			Crag* downSampled = new Crag();
			CragVolumes* downSampledVolumes = new CragVolumes(*downSampled);

			if (optionMinCandidateSize) {

				DownSampler downSampler(optionMinCandidateSize.as<int>());
				downSampler.process(*crag, *volumes, *downSampled, *downSampledVolumes);
			} else {

				DownSampler downSampler;
				downSampler.process(*crag, *volumes, *downSampled, *downSampledVolumes);
			}

			delete crag;
			delete volumes;
			if (mergeCosts) {
				delete mergeCosts;
				mergeCosts = 0;
			}
			crag = downSampled;
			volumes = downSampledVolumes;
		}

		{
			UTIL_TIME_SCOPE("find CRAG adjacencies");

			PlanarAdjacencyAnnotator annotator(PlanarAdjacencyAnnotator::Direct);
			annotator.annotate(*crag, *volumes);
		}

		// Statistics

		int numNodes = 0;
		int numRootNodes = 0;
		double sumSubsetDepth = 0;
		int maxSubsetDepth = 0;
		int minSubsetDepth = 1e6;

		for (Crag::NodeIt n(*crag); n != lemon::INVALID; ++n) {

			if (crag->isRootNode(n)) {

				int depth = crag->getLevel(n);

				sumSubsetDepth += depth;
				minSubsetDepth = std::min(minSubsetDepth, depth);
				maxSubsetDepth = std::max(maxSubsetDepth, depth);
				numRootNodes++;
			}

			numNodes++;
		}

		int numAdjEdges = 0;
		for (Crag::EdgeIt e(*crag); e != lemon::INVALID; ++e)
			numAdjEdges++;
		int numSubEdges = 0;
		for (Crag::SubsetArcIt e(*crag); e != lemon::INVALID; ++e)
			numSubEdges++;

		LOG_USER(logger::out) << "created CRAG" << std::endl;
		LOG_USER(logger::out) << "\t# nodes          : " << numNodes << std::endl;
		LOG_USER(logger::out) << "\t# root nodes     : " << numRootNodes << std::endl;
		LOG_USER(logger::out) << "\t# adjacencies    : " << numAdjEdges << std::endl;
		LOG_USER(logger::out) << "\t# subset edges   : " << numSubEdges << std::endl;
		LOG_USER(logger::out) << "\tmax subset depth : " << maxSubsetDepth << std::endl;
		LOG_USER(logger::out) << "\tmin subset depth : " << minSubsetDepth << std::endl;
		LOG_USER(logger::out) << "\tmean subset depth: " << sumSubsetDepth/numRootNodes << std::endl;

		// Store CRAG and volumes

		boost::filesystem::remove(optionProjectFile.as<std::string>());
		Hdf5CragStore store(optionProjectFile.as<std::string>());

		{
			UTIL_TIME_SCOPE("saving CRAG");

			store.saveCrag(*crag);
			store.saveVolumes(*volumes);
			if (mergeCosts)
				store.saveCosts(*crag, *mergeCosts, "merge-scores");
		}

		{
			UTIL_TIME_SCOPE("saving volumes");

			Hdf5VolumeStore volumeStore(optionProjectFile.as<std::string>());

			ExplicitVolume<float> intensities = readVolume<float>(getImageFiles(optionIntensities));
			intensities.setResolution(resolution);
			intensities.setOffset(offset);
			intensities.normalize();
			volumeStore.saveIntensities(intensities);

			if (optionGroundTruth) {

				ExplicitVolume<int> groundTruth = readVolume<int>(getImageFiles(optionGroundTruth));

				if (optionExtractGroundTruthLabels) {

					vigra::MultiArray<3, int> tmp(groundTruth.data().shape());
					vigra::labelMultiArrayWithBackground(
							groundTruth.data(),
							tmp);
					groundTruth.data() = tmp;
				}

				groundTruth.setResolution(resolution);
				groundTruth.setOffset(offset);
				volumeStore.saveGroundTruth(groundTruth);
			}

			if (optionBoundaries) {

				ExplicitVolume<float> boundaries = readVolume<float>(getImageFiles(optionBoundaries));
				boundaries.setResolution(resolution);
				boundaries.setOffset(offset);
				boundaries.normalize();
				volumeStore.saveBoundaries(boundaries);
			}

			bool atLeastOneAffinity = optionXAffinities || optionYAffinities || optionZAffinities;
			bool allAfinities = optionXAffinities && optionYAffinities && optionZAffinities;

			if (atLeastOneAffinity) {

				if (!allAfinities) {

					LOG_ERROR(logger::out)
							<< "One of the affinities was not provided. "
							<< "Affinities will be ignored." << std::endl;

				} else {

					ExplicitVolume<float> xAffinities = readVolume<float>(getImageFiles(optionXAffinities));
					ExplicitVolume<float> yAffinities = readVolume<float>(getImageFiles(optionYAffinities));
					ExplicitVolume<float> zAffinities = readVolume<float>(getImageFiles(optionZAffinities));

					volumeStore.saveAffinities( xAffinities, yAffinities, zAffinities);
				}
			}
		}

		delete crag;
		delete volumes;
		delete mergeCosts;

		if (optionImportTrainingResult) {

			LOG_USER(logger::out)
							<< "importing training results from "
							<< optionImportTrainingResult.as<std::string>()
							<< std::endl;

			Hdf5CragStore trainingStore(optionImportTrainingResult.as<std::string>());

			FeatureWeights weights;
			FeatureWeights min;
			FeatureWeights max;

			trainingStore.retrieveFeatureWeights(weights);
			trainingStore.retrieveFeaturesMin(min);
			trainingStore.retrieveFeaturesMax(max);

			store.saveFeatureWeights(weights);
			store.saveFeaturesMin(min);
			store.saveFeaturesMax(max);
		}

	} catch (Exception& e) {

		handleException(e, std::cerr);
	}
}