#include "opencv2/opencv.hpp"

IplImage* convolution(IplImage*img,CvMat *kernel)
{
  CvMat *rot_mat=cvCreateMat(2,3,CV_32FC1);
  CvMat *tmp=cvCloneMat(kernel);

  cv2DRotationMatrix(cvPoint2D32f(kernel->width/2,kernel->height/2),-180,1,rot_mat);  
  cvWarpAffine(tmp,kernel,rot_mat);

  cvReleaseMat(&tmp);
  cvReleaseMat(&rot_mat);

  int g[]={cvRound(kernel->width/2+1),cvRound(kernel->height/2+1)};

  IplImage *ret=cvCreateImage(cvSize(img->width+kernel->width-1,img->height+kernel->height-1),img->depth,img->nChannels);
  IplImage *result=cvCloneImage(img);
  cvZero(result);
  CvMat *kern_t=cvCreateMat(kernel->width,kernel->height,kernel->type);

  tmp=cvCreateMat(kernel->width,kernel->height,kernel->type);
  cvZero(tmp);
  for(int j=0;j<img->nChannels;j++)
  {
    cvZero(ret);
    cvSetImageCOI(img,j); 
    cvSetImageCOI(result,j); 

    for(int k=g[0];k<g[0]+img->width-1;k++)
      for(int l=g[1];l<g[1]+img->height-1;l++)
        cvSet2D(ret,k,l,cvGet2D(img,k-g[0],l-g[1]));

    for(int k=0;k<img->width;k++)
      for(int l=0;l<img->height;l++)
      {
        for(int a=k;a<k+kernel->width;a++)
          for(int b=l;b<l+kernel->height;b++)
            cvSet2D(kern_t,a-k,b-l,cvGet2D(ret,a,b));

        cvMul(kern_t,kernel,tmp);
        cvSet2D(result,k,l,cvSum(tmp));
      }
  }
  cvReleaseImage(&ret);
  cvReleaseMat(&kern_t);
  cvReleaseMat(&tmp);

  cvSetImageCOI(img,0);
  cvSetImageCOI(result,0);

  return result;
}

#define MASK_SIZE 3
#define KERNEL_COUNT 5
int main(int argc,char**argv)
{
  IplImage *img=cvLoadImage((argc>1?argv[1]:"lena.png"),CV_LOAD_IMAGE_GRAYSCALE);
  if (!img) return EXIT_FAILURE;

  float data[KERNEL_COUNT][MASK_SIZE*MASK_SIZE]=
                 {{-1,0,1,   -1,0,1,   -1,0,1},
                  {1,0,-1,   2,0,-2,   1,0,-1},
                  {1,1,-1,   1,-2,-1,  1,1,-1},
                  {3,3,-5,   3,0,-5,   3,3,-5},
                  {0,1,0,    1,-4,1,   0,1,0}};

  const char *titles[KERNEL_COUNT]={"Prewitt","Sobel","Robinson","Kirsch","Laplace"};
  const char *titles2[KERNEL_COUNT]={"Prewitt cvFilter2D","Sobel cvFilter2D","Robinson cvFilter2D","Kirsch cvFilter2D","Laplace cvFilter2D"};

  CvMat kernel;
  IplImage *out;

  for(int i=0;i<KERNEL_COUNT;i++)
  {
    kernel=cvMat(MASK_SIZE,MASK_SIZE,CV_32FC1,data[i]);

    out=convolution(img,&kernel);
    cvShowImage(titles[i],out);

    cvZero(out);

    cvFilter2D(img,out,&kernel);
    cvShowImage(titles2[i],out);

    cvReleaseImage(&out);
  }
  
  out=cvCreateImage(cvSize(img->width,img->height),IPL_DEPTH_32F,1); 
  cvLaplace(img,out);
  cvShowImage("Laplace cvLaplace",out);

  cvSobel(img,out,1,1);
  cvShowImage("Sobel cvSobel",out);

  cvReleaseImage(&out);

  out=cvCreateImage(cvSize(img->width,img->height),IPL_DEPTH_8U,1); 
  cvCanny(img,out,100,150);
  cvShowImage("Canny cvCanny",out);

  cvWaitKey();
  cvReleaseImage(&img);
  cvReleaseImage(&out);
  cvDestroyAllWindows();
  return EXIT_SUCCESS;
}
