/*
medians.c  implements several median-type filters

This frei0r plugin implements several median-type filters

Version 0.1	jan 2013

Copyright (C) 2013  Marko Cebokli    http://lea.hamradio.si/~s57uuu


 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 2 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, write to the Free Software
 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

*/


//compile: gcc -c -fPIC -Wall medians.c -o medians.o
//link: gcc -shared -o medians.so medians.o

#include <stdio.h>
#include <frei0r.h>
#include <stdlib.h>
#include <math.h>
#include <assert.h>
#include <inttypes.h>
#include <string.h>
#include <stdio.h>

#include "small_medians.h"
#include "ctmf.h"


/* ******************************************
//The following functions implement these median type filters:

               X 
cross5:       XXX
               X

                XXX 
square3x3:	XXX
                XXX
                

Bilevel:

                 X 
                XXX
dia3x3:        XXXXX
                XXX
                 X
                 
Arce BI:	multilevel spatio-temporal, see [1]

Arp ML3D:	multilevel spatio-temporal, see [1]

ML3DEX:		multilevel spatio-temporal, see [1]


[1] Anil Christopher Kokaram: Motion Picture Restoration
  phd dissertation

****************************************** */

//------------------------------------------------------------
//cross5	packed char RGB image (uint32_t)
//vs = input image
//is = output image
void cross5(const uint32_t *vs, int w, int h, uint32_t *is)
{
int i,j,p;
uint32_t m[8];

for (i=1;i<h-1;i++)
    for (j=1;j<w-1;j++)
	{
	p=i*w+j;

	m[0]=vs[p-w]; m[1]=vs[p-1]; m[2]=vs[p];
	m[3]=vs[p+1]; m[4]=vs[p+w];

	is[p]=median5(m);
	}
}

//------------------------------------------------------------
//square 3x3		packed char RGB image (uint32_t)
//vs = input image
//is = output image
void sq3x3(const uint32_t *vs, int w, int h, uint32_t *is)
{
int i,j,p;
uint32_t m[16];

for (i=1;i<h-1;i++)
    for (j=1;j<w-1;j++)
	{
	p=i*w+j;

	m[0]=vs[p-w-1]; m[1]=vs[p-w]; m[2]=vs[p-w+1];
	m[3]=vs[p-1];   m[4]=vs[p];   m[5]=vs[p+1];
	m[6]=vs[p+w-1]; m[7]=vs[p+w]; m[8]=vs[p+w+1];

	is[p]=median9(m);
	}
}

//------------------------------------------------------------
//bilevel		packed char RGB image (uint32_t)
//vs = input image
//is = output image
void bilevel(const uint32_t *vs, int w, int h, uint32_t *is)
{
int i,j,p;
uint32_t m[8],mm[4];

for (i=1;i<h-1;i++)
    for (j=1;j<w-1;j++)
	{
	p=i*w+j;

	m[0]=vs[p-w-1]; m[1]=vs[p-w+1]; m[2]=vs[p];
	m[3]=vs[p+w-1]; m[4]=vs[p+w+1];
	mm[0]=median5(m);
	mm[1]=vs[p];
	m[0]=vs[p-w]; m[1]=vs[p-1]; m[2]=vs[p];
	m[3]=vs[p+1]; m[4]=vs[p+w];
	mm[2]=median5(m);

	is[p]=median3(mm);
	}
}

//------------------------------------------------------------
//diamond 3x3		packed char RGB image (uint32_t)
//vs = input image
//is = output image
void dia3x3(const uint32_t *vs, int w, int h, uint32_t *is)
{
int i,j,p;
uint32_t m[16];

for (i=2;i<h-2;i++)
    for (j=2;j<w-2;j++)
	{
	p=i*w+j;
	m[0]=vs[p-2*w]; m[1]=vs[p-w-1]; m[2]=vs[p-w];
	m[3]=vs[p-w+1]; m[4]=vs[p-2]; m[5]=vs[p-1];
	m[6]=vs[p]; m[7]=vs[p+1]; m[8]=vs[p+2];
	m[9]=vs[p+w-1]; m[10]=vs[p+w]; m[11]=vs[p+w+1];
	m[12]=vs[p+2*w];

	is[p]=median13(m);
	}
}

//------------------------------------------------------------
//square 5x5		packed char RGB image (uint32_t)
//vs = input image
//is = output image
void sq5x5(const uint32_t *vs, int w, int h, uint32_t *is)
{
int i,j,p;
uint32_t m[32];

for (i=2;i<h-2;i++)
    for (j=2;j<w-2;j++)
	{
	p=i*w+j;

	m[0]=vs[p-2*w-2]; m[1]=vs[p-2*w-1]; m[2]=vs[p-2*w];
	m[3]=vs[p-2*w+1]; m[4]=vs[p-2*w+2]; m[5]=vs[p-w-2];
	m[6]=vs[p-w-1];   m[7]=vs[p-w];     m[8]=vs[p-w+1];
	m[9]=vs[p-w+2];	  m[10]=vs[p-2];    m[11]=vs[p-1];
	m[12]=vs[p];      m[13]=vs[p+1];    m[14]=vs[p+2];
	m[15]=vs[p+w-2];  m[16]=vs[p+w-1];  m[17]=vs[p+w];
	m[18]=vs[p+w+1];  m[19]=vs[p+w+2];  m[20]=vs[p+2*w-2];
	m[21]=vs[p+2*w-1];m[22]=vs[p+2*w];  m[23]=vs[p+2*w+1];
	m[24]=vs[p+2*w+2];

	is[p]=median25(m);
	}
}


//--------------------------------------------------------
//temporal 3 frames
void temp3(uint32_t *s1, uint32_t *s2, uint32_t *s3, int w, int h, uint32_t *is)
{
int i;
uint32_t m[32];

for (i=0;i<w*h;i++)
    {
    m[0]=s1[i]; m[1]=s2[i]; m[2]=s3[i];
    is[i]=median3(m);
    }
}


//--------------------------------------------------------
//temporal 5 frames
void temp5(uint32_t *s1, uint32_t *s2, uint32_t *s3, uint32_t *s4, uint32_t *s5, int w, int h, uint32_t *is)
{
int i;
uint32_t m[32];

for (i=0;i<w*h;i++)
    {
    m[0]=s1[i]; m[1]=s2[i]; m[2]=s3[i]; m[3]=s4[i]; m[4]=s5[i];
    is[i]=median5(m);
    }
  
}


//------------------------------------------------------------
//Arce BI	packed char RGB image (uint32_t)
//s1,s2,s3 = previous, current, next frame
//is = output image
void ArceBI(uint32_t *s1, uint32_t *s2, uint32_t *s3, int w, int h, uint32_t *is)
{
int i,j,p;
uint32_t mm[8],m[16];

for (i=1;i<h-1;i++)
    for (j=1;j<w-1;j++)
	{
	p=i*w+j;
//grupa C
	mm[0]=s1[p];
//GRUPA W1
	m[0]=s1[p]; m[1]=s2[p-w-1]; m[2]=s2[p];
	m[3]=s2[p+w+1]; m[4]=s3[p];
	mm[3]=median5(m);
//grupa W2
	m[0]=s1[p]; m[1]=s2[p-w]; m[2]=s2[p];
	m[3]=s2[p+w]; m[4]=s3[p];
	mm[4]=median5(m);
//grupa W3
	m[0]=s1[p]; m[1]=s2[p-1]; m[2]=s2[p];
	m[3]=s2[p+1]; m[4]=s3[p];
	mm[5]=median5(m);
//grupa W4
	m[0]=s1[p]; m[1]=s2[p-w+1]; m[2]=s2[p];
	m[3]=s2[p+w-1]; m[4]=s3[p];
	mm[6]=median5(m);
//max
	mm[1]=mm[3]; if (mm[4]>mm[1]) mm[1]=mm[4];
	if (mm[5]>mm[1]) mm[1]=mm[5];
	if (mm[6]>mm[1]) mm[1]=mm[6];
//min
	mm[2]=mm[3]; if (mm[4]<mm[2]) mm[2]=mm[4];
	if (mm[5]<mm[2]) mm[2]=mm[5];
	if (mm[6]<mm[2]) mm[2]=mm[6];
//izhod
	is[p]=median3(mm);
	}
}


//------------------------------------------------------------
//Arp ML3D	packed char RGB image (uint32_t)
//s1,s2,s3 = previous, current, next frame
//is = output image
void ml3d(uint32_t *s1, uint32_t *s2, uint32_t *s3, int w, int h, uint32_t *is)
{
int i,j,p;
uint32_t mm[8],m[16];

for (i=1;i<h-1;i++)
    for (j=1;j<w-1;j++)
	{
	p=i*w+j;
//grupa C
	mm[0]=s1[p];
//grupa W6
	m[0]=s1[p]; m[1]=s2[p-w-1]; m[2]=s2[p-w+1];
	m[3]=s2[p]; m[4]=s2[p+w-1]; m[5]=s2[p+w+1];
	m[6]=s3[p];
	mm[1]=median7(m);
//grupa W5
	m[0]=s1[p]; m[1]=s2[p-w]; m[2]=s2[p-1];
	m[3]=s2[p]; m[4]=s2[p+1]; m[5]=s2[p+w];
	m[6]=s3[p];
	mm[2]=median7(m);
//izhod = median medianov
	is[p]=median3(mm);
	}
}

//------------------------------------------------------------
//Kokaram ML3Dex	packed char RGB image (uint32_t)
//s1,s2,s3 = previous, current, next frame
//is = output image
void ml3dex(uint32_t *s1, uint32_t *s2, uint32_t *s3, int w, int h, uint32_t *is)
{
int i,j,p;
uint32_t mm[8],m[16];

for (i=1;i<h-1;i++)
    for (j=1;j<w-1;j++)
	{
	p=i*w+j;
//grupa W9
	m[0]=s1[p-w-1]; m[1]=s1[p-w+1]; m[2]=s1[p];
	m[3]=s1[p+w-1]; m[4]=s1[p+w+1]; m[5]=s2[p];
	m[6]=s3[p-w-1]; m[7]=s3[p-w+1]; m[8]=s3[p];
	m[9]=s3[p+w-1]; m[10]=s3[p+w+1];
	mm[0]=median11(m);
//grupa W8
	m[0]=s1[p-w]; m[1]=s1[p-1]; m[2]=s1[p];
	m[3]=s1[p+w]; m[4]=s1[p+1]; m[5]=s2[p];
	m[6]=s3[p-w]; m[7]=s3[p-1]; m[8]=s3[p];
	m[9]=s3[p+w]; m[10]=s3[p+1];
	mm[1]=median11(m);
//grupa W7
	m[0]=s1[p]; m[1]=s2[p]; m[2]=s3[p];
	mm[2]=median3(m);
//grupa W6
	m[0]=s1[p]; m[1]=s2[p-w-1]; m[2]=s2[p-w+1];
	m[3]=s2[p]; m[4]=s2[p+w-1]; m[5]=s2[p+w+1];
	m[6]=s3[p];
	mm[3]=median7(m);
//grupa W5
	m[0]=s1[p]; m[1]=s2[p-w]; m[2]=s2[p-1];
	m[3]=s2[p]; m[4]=s2[p+1]; m[5]=s2[p+w];
	m[6]=s3[p];
	mm[4]=median7(m);
//izhod = median medianov
	is[p]=median5(mm);
	}
}

//****************************************************

//----------------------------------------
//struktura za instanco efekta
typedef struct
{
int h;
int w;

//parameters
int type;
int size;

//internal variables
uint32_t *ppf,*pf,*cf,*nf,*nnf;

//image buffers
uint32_t *f1;
uint32_t *f2;
uint32_t *f3;
uint32_t *f4;
uint32_t *f5;


char *liststr;
} inst;


//-----------------------------------------------------
//stretch [0...1] to parameter range [min...max] linear
float map_value_forward(double v, float min, float max)
{
return min+(max-min)*v;
}

//-----------------------------------------------------
//collapse from parameter range [min...max] to [0...1] linear
double map_value_backward(float v, float min, float max)
{
return (v-min)/(max-min);
}

//***********************************************
// OBVEZNE FREI0R FUNKCIJE

//-----------------------------------------------
int f0r_init()
{
return 1;
}

//------------------------------------------------
void f0r_deinit()
{
}

//-----------------------------------------------
void f0r_get_plugin_info(f0r_plugin_info_t* info)
{

info->name="Medians";
info->author="Marko Cebokli";
info->plugin_type=F0R_PLUGIN_TYPE_FILTER;
info->color_model=F0R_COLOR_MODEL_RGBA8888;
info->frei0r_version=FREI0R_MAJOR_VERSION;
info->major_version=0;
info->minor_version=1;
info->num_params=2;
info->explanation="Implements several median-type filters";
}

//--------------------------------------------------
void f0r_get_param_info(f0r_param_info_t* info, int param_index)
{
switch(param_index)
	{
	case 0:
		info->name = "Type";
		info->type = F0R_PARAM_STRING;
		info->explanation = "Choose type of median: Cross5, Square3x3, Bilevel, Diamond3x3, Square5x5, Temp3, Temp5, ArceBI, ML3D, ML3dEX, VarSize";
		break;
	case 1:
		info->name = "Size";
		info->type = F0R_PARAM_DOUBLE;
		info->explanation = "Size for 'var size' type filter";
		break;
	case 2:
		info->name = "";
		info->type = F0R_PARAM_BOOL;
		info->explanation = "";
		break;
	}
}

//----------------------------------------------
f0r_instance_t f0r_construct(unsigned int width, unsigned int height)
{
inst *in;

in=calloc(1,sizeof(inst));
in->w=width;
in->h=height;

in->type=1;
in->liststr=calloc(1,strlen("Square3x3")+1);
strcpy(in->liststr,"Square3x3");
in->size=5;

in->f1=calloc(in->w*in->h,sizeof(uint32_t));
in->f2=calloc(in->w*in->h,sizeof(uint32_t));
in->f3=calloc(in->w*in->h,sizeof(uint32_t));
in->f4=calloc(in->w*in->h,sizeof(uint32_t));
in->f5=calloc(in->w*in->h,sizeof(uint32_t));

in->ppf=in->f1;
in->pf=in->f2;
in->cf=in->f3;
in->nf=in->f4;
in->nnf=in->f5;

return (f0r_instance_t)in;
}

//---------------------------------------------------
void f0r_destruct(f0r_instance_t instance)
{
inst *in;

in=(inst*)instance;

free(in->f1);
free(in->f2);
free(in->f3);
free(in->f4);
free(in->f5);

free(in->liststr);
free(instance);
}

//-----------------------------------------------------
void f0r_set_param_value(f0r_instance_t instance, f0r_param_t parm, int param_index)
{
inst *p;
double tmpf;
int chg;
char *tmpch;
char list1[][11]={"Cross5", "Square3x3", "Bilevel", "Diamond3x3", "Square5x5", "Temp3", "Temp5", "ArceBI", "ML3D", "ML3dEX", "VarSize"};

p=(inst*)instance;

chg=0;
switch(param_index)
	{
	case 0:		//(string based list)
		tmpch = (*(char**)parm);
		p->liststr = (char*)realloc( p->liststr, strlen(tmpch) + 1 );
		strcpy( p->liststr, tmpch );
		p->type=0;
		while ((strcmp(p->liststr,list1[p->type])!=0)&&(p->type<10)) p->type++;
		break;
	case 1:
                tmpf=map_value_forward(*((double*)parm), 0.0, 50); 
		if (tmpf!=p->size) chg=1;
		p->size=tmpf;
		break;
	}

if (chg==0) return;

}

//--------------------------------------------------
void f0r_get_param_value(f0r_instance_t instance, f0r_param_t param, int param_index)
{
inst *p;

p=(inst*)instance;

switch(param_index)
	{
	case 0:		//(string based list)
		*((char**)param) = p->liststr;
		break;
	case 1:
		*((double*)param)=map_value_backward(p->size, 0.0, 50);
		break;
	}
}

//-------------------------------------------------
void f0r_update(f0r_instance_t instance, double time, const uint32_t* inframe, uint32_t* outframe)
{
inst *in;

assert(instance);
in=(inst*)instance;
uint32_t *tmpp;
uint8_t *cin,*cout;
int step,i;

memcpy(in->ppf, inframe, 4*in->w*in->h);
tmpp=in->nnf;
in->nnf=in->ppf;
in->ppf=in->pf;
in->pf=in->cf;
in->cf=in->nf;
in->nf=tmpp;

cin=(uint8_t*)inframe;
cout=(uint8_t*)outframe;

switch (in->type)
	{
	case 0:
		cross5(inframe, in->w, in->h, outframe);
		break;
	case 1:
		sq3x3(inframe, in->w, in->h, outframe);
		break;
	case 2:
		bilevel(inframe, in->w, in->h, outframe);
		break;
	case 3:
		dia3x3(inframe, in->w, in->h, outframe);
		break;
	case 4:
		sq5x5(inframe, in->w, in->h, outframe);
		break;
	case 5:
		temp3(in->cf, in->nf, in->nnf, in->w, in->h, outframe);
		break;
	case 6:
		temp5(in->ppf, in->pf, in->cf, in->nf, in->nnf, in->w, in->h, outframe);
		break;
	case 7:
		ArceBI(in->cf, in->nf, in->nnf, in->w, in->h, outframe);
		break;
	case 8:
		ml3d(in->cf, in->nf, in->nnf, in->w, in->h, outframe);
		break;
	case 9:
		ml3dex(in->cf, in->nf, in->nnf, in->w, in->h, outframe);
		break;
	case 10:
		//varsize
		step=in->w*4;
		ctmf(cin,cout,in->w,in->h,step,step,in->size,4,512*1024);
		break;
	default:
		break;
	}
//COPY ALPHA
for (i = 3; i < 4 * in->w * in->h; i += 4)
	cout[i]=cin[i];

}

