void RSEDU_image_processing(void * buffer)
{


    //process control
    static int counter = 0;

    //communication
    static float vis_data[4];
    int status;
    static int vis_fifo;

    //image variables
    int i, j;
    int row, col;
    float yuv[3];
    int nx = 80, ny = 120;
    float feature_pps[3][4];
    static unsigned char matchLookup[128][128][128];

    //camera variables
    float camerapos[3]; //@TODO static?
    float camerayaw; 	//@TODO static?

    //Image and Matching Thresholds
    static int filtersize = 5; 			//odd numbered! dummy for potential gaussian filter mask, etc.
    int pxls_ftr_min 	  = 5; 			//minimum required nr of detected pixels per landmark

    //Landmarks, Pose Estimation Matrices
    int lndmrk_nr = 5, lndmrk_best = 0;
    static lndmrk_t lndmrks[5];
    //matrices for reconstructing camera pose by intrMatrx_inv*landmark-pixellocation*ldnmrk_pinv
    static double ldnmrk_pinv[4][4] =
    {
        { -3.093396787626414,   2.082093991671625                   , 0,   0.766805472932779},
        {2.320047590719810 ,  3.438429506246282    ,               0  , 0.174895895300416},
        { -1.737061273051754 , -2.676977989292088    ,               0  , 0.299821534800714},
        {2.510410469958359 , -2.843545508625819     ,              0 , -0.241522903033909}
    };

    static double intrMatrx_inv[3][3] =       {{ 0.006551831768882                   , 0,  -0.550082487527771},
        {0,   0.006546559888686,  -0.399495805347318},
        {0,                   0,   1.000000000000000}
    };

    //wait on first call
    if(counter == 1)
    {
        usleep(20000);
    }

    //Init Communication, Streaming
    int fifo;
    if(FEAT_IMSAVE == 2)
    {
        u8 * raw = buffer;
    };
    pixel2_t *image = buffer;  /* Picture is a 160x120 pixels, stored in YUV422 interlaced format - TO BE CHECKED */


    /*
     * PROGRAM
     */


    //ptiming - declare and start
    //------------
    long long start;
    static FILE *ptfile;
    ptimer_start(FEAT_TIME, counter, &(start));
    //------------


    //process control
    counter++;

    if(counter == 1)
    {
        //ptiming - init file
        //------------
        ptimer_init(FEAT_TIME, __func__, &(ptfile), NULL);
        //------------

        printf("rsedu_vis(): Init fifo-communication...\n");

        //open fifo to dump visual position estimates to control code

        if(access("/tmp/vis_fifo", F_OK) != -1)
        {
            printf("rsedu_vis(): SUCCESS POSVIS FIFO exists! \n");

            vis_fifo = open("/tmp/vis_fifo", O_WRONLY);
            if(vis_fifo)
            {
                vis_data[0] = -99.0;
                write(vis_fifo, (float*)(&vis_data), sizeof(vis_data));
                close(vis_fifo);
                printf("rsedu_vis(): SUCCESS opening POSVIS-fifo!\n");
            }
            else
            {
                printf("rsedu_vis(): ERROR opening POSVIS-fifo!\n");
            }
        }
        else
        {
            printf("rsedu_vis(): ERROR opening POSVIS-fifo!\n");
        }


        //load lookuptable for matching process
        if(FEAT_NOLOOK == 0)
        {
            FILE* data;
            if((data = fopen("/data/edu/params/lookuptable.dat", "rb")) == NULL)
            {
                printf("rsedu_vis(): ERROR opening lookupfile \n");
            }

            fread(matchLookup, sizeof(matchLookup), 1, data);
            fclose(data);
        }


    }



    //landmark detection
    //------------

    //1 cycle through image, convert each pixel to hsv, threshold by s and v to find colored balls, match h value to color-closest in "database" (use precomputed lookup), save weighted average pixellocation with database-lndmrkID
    //2 reconstruct pose of camera (3D-position with yaw)


    if((FEAT_POSVIS_RUN) && ((counter % 15) == 0) && (NULL != image)) //@pseudo4Hz
    {
        //reset landmark matching data
        for(i = 0; i < lndmrk_nr; i++)
        {
            lndmrks[i].n  = 0;
            lndmrks[i].weights  = 0;
            lndmrks[i].px = 0;
            lndmrks[i].py = 0;
        }


        int margin = (int)(filtersize - 1) / 4;
        int weight = 1;	// weight used to emphasize pixellocation that is close to existing estimate for sufficiently robust, identified landmark

        //cycle through image
        for(j = 0; j < ny - 2 * margin; j++)
        {
            for(i = 0; i < nx - 2 * margin; i++)
            {
                row = margin + j;
                col = margin + i;
                //Get YUV-values for pixel
                yuv[0] = (float)image[nx * row + col].y1;
                yuv[1] = (float)image[nx * row + col].u;
                yuv[2] = (float)image[nx * row + col].v;

                //find best matching landmark for current pixel
                if(FEAT_NOLOOK)
                {
                    printf("rsedu_vis(): ERROR Unfortunately, color conversion, etc. too slow onboard. Please set FEAT_NOLOOK = 0. \n");
                }
                else
                    //use pre-computed lookup table to find match
                {
                    //printf("here uselu \n");
                    lndmrk_best = (int)matchLookup[(int)(yuv[0] / 2)][(int)(yuv[1] / 2)][(int)(yuv[2] / 2)];
                }


                //Store matched landmarks
                //----
                if(lndmrk_best > 0)
                {
                    //increase weight if close to an existing, highly weighted cluster
                    if((lndmrks[lndmrk_best].weights > 15) && (abs((col + 1) - lndmrks[lndmrk_best].px) < 20) && (abs((row + 1) - lndmrks[lndmrk_best].py) < 20))
                    {
                        weight = 8;
                    }
                    else
                    {
                        weight = 1;
                    }

                    lndmrks[lndmrk_best].px = lndmrks[lndmrk_best].px * lndmrks[lndmrk_best].weights / (lndmrks[lndmrk_best].weights + weight) + (((double)(col + 1)) * 2) * weight / (lndmrks[lndmrk_best].weights + weight); //@TODO is it row?
                    lndmrks[lndmrk_best].py = lndmrks[lndmrk_best].py * lndmrks[lndmrk_best].weights / (lndmrks[lndmrk_best].weights + weight) + ((double)(row + 1)) * weight / (lndmrks[lndmrk_best].weights + weight); //@TODO is it row?
                    //save that one more pixel to this landmark
                    lndmrks[lndmrk_best].n += 1;
                    lndmrks[lndmrk_best].weights += weight;

                    //if (lndmrk_best==0) {matchResult[col][row]=255;} else {matchResult[col][row]=0;}
                }

            }//end inner image-for
        }//end outer image-for

        /*
        printf("image_proc(): Result of matching: \n");
        for (i=0;i<lndmrk_nr;i++)
        {
        	printf("image_proc(): Lndmrk %i: nr pxls matched: %i, pixelcoordinates: (%f,%f) \n",i,lndmrks[i].n,lndmrks[i].px,lndmrks[i].py);
        }
        */

        //reconstruct pose
        //-------------

        // choose lndmrks with some minimum number of pixels, add them to the feature_pixelposition-matrix (ui,vi,1). TODO: remove hardcoded number of landmarks
        int features_valid = 0;
        int k;

        for(k = 1; k < lndmrk_nr; k++) //start from 1 as yellow (landmark 0) not used!
        {
            if(lndmrks[k].n >= pxls_ftr_min)
            {
                features_valid += 1;
                feature_pps[0][features_valid - 1] = lndmrks[k].px;
                feature_pps[1][features_valid - 1] = lndmrks[k].py;
                feature_pps[2][features_valid - 1] = 1.0;
            }

        }

        if(features_valid == 4)
        {
            reconstructCameraPose(camerapos, &camerayaw, feature_pps, ldnmrk_pinv, intrMatrx_inv);
            printf("rsedu_vis(): SUCCESS reconstructed camera pose: (%f, %f, %f,%f)\n", camerapos[0], camerapos[1], camerapos[2], camerayaw * 180 / 3.1415);


            /*
             * dump pose into FIFO to make available to controls code
             */
            //compile data
            vis_data[0] = camerapos[0];
            vis_data[1] = camerapos[1];
            vis_data[2] = camerapos[2];
            vis_data[3] = camerayaw;

            vis_fifo = open("/tmp/vis_fifo", O_WRONLY);
            if(vis_fifo)
            {
                write(vis_fifo, (float*)(&vis_data), sizeof(vis_data));
                close(vis_fifo);
            }
        }
        else
        {
            printf("rsedu_vis(): WARNING not enough distinct markers (colored balls) found! \n")	;
        }

    }

    //-----------
    //STREAMING INSTRUCTIONS
    //-----------

    /* Enabling image streaming, copies the picture into a named FIFO. Picture can then be sent to a remote Ubuntu computer using standard commands:

    Run this one-liner in a shell on the RollingSpider (open terminal, log onto drone via telnet 192.168.1.1) :
    (remember to connect via the Bluetooth link, since pluging the USB cable deactivates the camera !!!)

      while [ 1 ]; do cat /tmp/picture | nc 192.168.1.2 1234; done

    Run these two commands in two different shells on the remote Ubuntu computer:

      mkfifo /tmp/rollingspiderpicture ; while [ 1 ]; do nc -l 1234 > /tmp/rollingspiderpicture; done
      mplayer -demuxer rawvideo -rawvideo w=160:h=120:format=yuy2 -loop 0 /tmp/rollingspiderpicture

    */

    //stream image
    //-----------

    if((FEAT_IMSAVE == 2) && ((counter % 60) == 0) && (NULL != image)) //@pseudo1Hz
    {
        printf("image_proc(): Write image to fifo...\n");
        mkfifo("/tmp/picture", 0777);
        fifo = open("/tmp/picture", O_WRONLY);
        if(fifo)
        {
            //char word = "asd";
            //write(fifo,word,320*120);
            write(fifo, buffer, 320 * 120);
            close(fifo);
            usleep(5000);
        }

    }

    //save image
    //-----------

    if((FEAT_IMSAVE == 1) && ((counter % 6) == 0) && (NULL != image)) //@10Hz
    {
        FILE* data;
        char filename[15];

        //sprintf(filename,"/data/edu/imgs/img%i.bin",counter);
        sprintf(filename, "/tmp/edu/imgs/img%i.bin", counter);
        //sprintf(filename,"/tmp/imgs/img.bin");
        //printf("image_proc(): img name: %s \n",filename);

        mkdir("/tmp/edu", S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
        mkdir("/tmp/edu/imgs", S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
        if((data = fopen(filename, "wb")) == NULL)
        {
            printf("rsedu_vis(): ERROR opening img file\n");
        }

        fwrite(image, sizeof(pixel2_t) * 80 * 120, 1, data);
        fclose(data);
        usleep(5000);

    }




    usleep(4000);

    //ptiming - store
    //----------
    ptimer_stopstore(FEAT_TIME, counter, start, ptfile);
    //----------


}
void RSEDU_image_processing_OFFBOARD(void * buffer, int matchResult[80][120],int kimg)
{

	struct timeval now;
	static int counter = 0;

	int i,j;
	int row,col;

	float hsv[3];
	float yuv[3];
	float rgb[3];
	int nx=80, ny=120;
	float feature_pps[3][4];
	unsigned char matchLookuptable[128][128][128];


	float camerapos[3];
	float camerayaw;

	//matching-Thresholds, global

	//Landmarks
	int lndmrk_nr = 5,lndmrk_best=0,lndmrk_cnd=0;
	float featuredist_best = featdist_thrshld;
	float featuredist_cnd = featdist_thrshld;
	static lndmrk_t *lndmrks;
	static double ldnmrk_pinv[4][4] =
			 {{-3.093396787626414,   2.082093991671625                   ,0,   0.766805472932779},
			   {2.320047590719810 ,  3.438429506246282    ,               0  , 0.174895895300416},
			  {-1.737061273051754 , -2.676977989292088    ,               0  , 0.299821534800714},
			   {2.510410469958359 , -2.843545508625819     ,              0 , -0.241522903033909}}; //watch out: dimensions x-y switched compared to image matrices!

	static double intrMatrx_inv[3][3] =       {{ 0.006551831768882                   ,0,  -0.550082487527771},
            {0,   0.006546559888686,  -0.399495805347318},
            {0,                   0,   1.000000000000000}};

	//communication
	pixel2_t *image = buffer;  /* Picture is a 160x120 pixels, stored in YUV422 interlaced format - TO BE CHECKED */


	//time
	 struct timespec begin, current;
	    long long start, elapsed, microseconds;
	    /* set up start time data */
	    if (clock_gettime(CLOCK_MONOTONIC, &begin)) {
	        /* Oops, getting clock time failed */
	        exit(EXIT_FAILURE);
	    }
	    /* Start time in nanoseconds */
	    start = begin.tv_sec*NANOS + begin.tv_nsec;


	counter++;
	gettimeofday(&now,NULL);
	
	if (counter==1)
	{
		//setup landmark database
		lndmrks = malloc(lndmrk_nr*sizeof(lndmrk_t));

		//yellow - not used for localization
		lndmrks[0].X = 0.0;
		lndmrks[0].Y = 0.0;
		lndmrks[0].descr[0] = 70.0;		//hue
		lndmrks[0].descr[1] = 170.0;	//green
		lndmrks[0].descr[2] = 100.0;	//blue

		//green
		lndmrks[1].X = 0.0;
		lndmrks[1].Y = 0.3;
		lndmrks[1].descr[0] = 130.0;
		lndmrks[1].descr[1] = 160.0;
		lndmrks[1].descr[2] = 110.0;

		//pink
		lndmrks[2].X = 0.5;
		lndmrks[2].Y = 0.28;
		lndmrks[2].descr[0] = 345.0;
		lndmrks[2].descr[1] = 100.0;
		lndmrks[2].descr[2] = 135.0;

		//red
		lndmrks[3].X = 0.25;
		lndmrks[3].Y = 0.20;
		lndmrks[3].descr[0] = 25.0;
		lndmrks[3].descr[1] = 80.0;
		lndmrks[3].descr[2] = 70.0;

		//blue
		lndmrks[4].X = 0.5;
		lndmrks[4].Y = 0.0;
		lndmrks[4].descr[0] = 220.0;
		lndmrks[4].descr[1] = 120.0;
		lndmrks[4].descr[2] = 160.0;



		//reset posefile
	  	mkdir("../../DroneExchange/imgs/",S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
		mkdir("../../DroneExchange/imgs/processed/",S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
		fclose(fopen("../../DroneExchange/imgs/processed/poses.txt","w"));

		//load lookuptable for matching process
		if (FEAT_NOLOOKUP==0)
		{

			 FILE* data;
			  if ((data = fopen("../../DroneExchange/params/lookuptable.dat", "rb")) == NULL)
				{
					printf("Error opening lookupfile \n");
					error(0);
				}

				fread(matchLookuptable, sizeof(matchLookuptable), 1, data);
				fclose(data);
		}


	}

	//reset landmark matching data
	for (i=0;i<lndmrk_nr;i++)
	{
		lndmrks[i].n  = 0;
		lndmrks[i].weights  = 0;
		lndmrks[i].px = 0;
		lndmrks[i].py = 0;
		//printf("matches reset!\n");
	}

	

	//marker detection
	//cycle through image, convert each pixel to hsv, threshold by s and v to find point, match h value to color-closest in "database", save average pixellocation with database-lndmrksID
	//reconstruct position (3d pos with yaw that mininimzes errors seen with predicted marker position)

	int filtersize = 5; //odd numbered! dummy for potential gaussian filter mask, etc.
	int margin = (int)(filtersize-1)/4;
	int weight = 1;

	 if(NULL!=image)
	{
		 for (j=0;j<ny-2*margin;j++)
		 {
			 for (i=0;i<nx-2*margin;i++)
			 {
				 row = margin+j;
				 col = margin+i;
				 yuv[0] = (float)image[nx*row+col].y1;
				 yuv[1] = (float)image[nx*row+col].u;
				 yuv[2] = (float)image[nx*row+col].v;

				 //find best matching landmark
				 if (FEAT_NOLOOKUP)
				 {
					 YUVtoHSV(yuv,rgb,hsv);


					 //find matching landmark
					 lndmrk_best = 0;
					 //check if marker is candidate for colored marker by sat, match and store
					 if ((hsv[1]>=s_thrshld) && (hsv[2]>=va_thrshld) && ((hsv[1]+hsv[2])>=sva_sum_thrshld))
					 {
						 //find closest matching landmark

						 featuredist_best = featuredist(hsv,rgb,&(lndmrks[lndmrk_best].descr[0]));

						 for (lndmrk_cnd=1;lndmrk_cnd<lndmrk_nr;lndmrk_cnd++)
						 {
							 featuredist_cnd = featuredist(hsv,rgb,&(lndmrks[lndmrk_cnd].descr[0]));
							 if  (featuredist_cnd < featuredist_best)
									 {
									 featuredist_best = featuredist_cnd;
									 lndmrk_best = lndmrk_cnd;
									 }
						 }
						 //update centerpoint of marker if matching succeeded

						 if (featuredist_best >= featdist_thrshld) //@TODO add more outlier handling like ransac
						 {
							//set invalid (marker 0)
							 lndmrk_best = 0;
						 }
					 }
				 }
				 else
				 //use lookuptable
				 {
					 lndmrk_best = (int)matchLookuptable[(int)(yuv[0]/2)][(int)(yuv[1]/2)][(int)(yuv[2]/2)];
				 }


				 //Store matched landmarks
				 //----
				 if (lndmrk_best>0)
						 {
						 //increase weight if close to an existing, highly weighted cluster
						 if ( (lndmrks[lndmrk_best].weights>15) && (abs((col+1)-lndmrks[lndmrk_best].px)<20) && (abs((row+1)-lndmrks[lndmrk_best].py)<20))
								{
								 weight=8;
								}
						 else  {
								 weight=1;
								}

						lndmrks[lndmrk_best].px = lndmrks[lndmrk_best].px*lndmrks[lndmrk_best].weights/(lndmrks[lndmrk_best].weights+weight) + (((double)(col+1))*2)*weight/(lndmrks[lndmrk_best].weights+weight);
						lndmrks[lndmrk_best].py = lndmrks[lndmrk_best].py*lndmrks[lndmrk_best].weights/(lndmrks[lndmrk_best].weights+weight) + ((double)(row+1))*weight/(lndmrks[lndmrk_best].weights+weight);
						 //save that one more pixel lndmrks was added to this landmark
						 lndmrks[lndmrk_best].n += 1;
						 lndmrks[lndmrk_best].weights += weight;

						 matchResult[col][row] = lndmrk_best*40+80;
						 //if (lndmrk_best==0) {matchResult[col][row]=255;} else {matchResult[col][row]=0;}
						 }

			 }//end inner for
		 }//end outer for

		printf("Result of matching: \n");
		for (i=1;i<lndmrk_nr;i++)
		{
			printf("Lndmark %i: nr pxls matched: %i, pixelcoordinates: (%f,%f) \n",i,lndmrks[i].n,lndmrks[i].px,lndmrks[i].py);
		}

		//reconstruct pose
		//choose lndmrks with some minimum number of pixels, add them to the feature_pps (ui,vi,1)

		int features_valid=0;
		int k;

		for (k=1;k<lndmrk_nr;k++) //start from 1 as yellow not used!
		{
			if (lndmrks[k].n>=pxls_ftr_min)
			{
				features_valid+=1;
				feature_pps[0][features_valid-1] = lndmrks[k].px;
				feature_pps[1][features_valid-1] = lndmrks[k].py;
				feature_pps[2][features_valid-1] = 1.0;
			}
		
		}

		//reconstruct camera pose (3D position + yaw) //@TODO if want to be flexible with matrix size depending on found features, need to swap dimensions of matrix (first dim can be variable when passing 2D-array as parameter to a function);

		if (features_valid==4)
		{
			reconstructCameraPose(camerapos,&camerayaw,feature_pps,ldnmrk_pinv,intrMatrx_inv);
			printf("reconstructed cam pos: (%f, %f, %f,%f)\n",camerapos[0],camerapos[1],camerapos[2],camerayaw*180/3.1415);

			FILE * posefile = fopen("../../DroneExchange/imgs/processed/poses.txt","a");

			if (posefile == NULL)
			{
				printf("image_proc(): Error opening pose file! \n");
			}
			else
			{
				fprintf(posefile,"%i, %f, %f, %f,%f \n",kimg, camerapos[0],camerapos[1],camerapos[2],camerayaw*180/3.1415);
			}

		}
		else printf("WARNING not enough distinct landmarks (colored markers) found for vision-based pose reconstruction! \n")	;


	}

	//out time
	/* get elapsed time */
	  if (clock_gettime(CLOCK_MONOTONIC, &current)) {
	      /* getting clock time failed, what now? */
	       exit(EXIT_FAILURE);
	   }
	   /* Elapsed time in nanoseconds */
	  elapsed = current.tv_sec*NANOS + current.tv_nsec - start;
	  microseconds = elapsed / 1000 + (elapsed % 1000 >= 500); // round up halves

	  /* Display time in microseconds or something */
	  printf("elapsed time %lld \n",microseconds);

}