OpenCVで3次スプライン補間

Posted on Mon 21 September 2009 in OpenCV

OpenCVの連立1次方程式を解くcvSolveを利用して3次のスプライン補間を行うコード。

コード

#include <stdio.h>
#include <string.h>
#include <cv.h>
#include <highgui.h>
void SetPixelToBGR(IplImage *image, int x, int y, int b, int g, int r)
{
  assert(image->nChannels == 3);
  if(!image) return;
  if(( 0 <= x && x <= image->width -1 )&& (0 <= y && y <= image->height -1 ) )
  {
    b = b > 255 ? 255 : b; b = b < 0 ? 0 : b;
    g = g > 255 ? 255 : g; g = g < 0 ? 0 : g;
    r = r > 255 ? 255 : r; r = r < 0 ? 0 : r;
    image->imageData[y *image->widthStep+ x * image->nChannels + 2] = r;
    image->imageData[y *image->widthStep+ x * image->nChannels + 1] = g;
    image->imageData[y *image->widthStep+ x * image->nChannels] = b;
  }
}

void SolveSpline3D(int n, CvPoint2D32f* src_points, CvMat* dst)
{
  int i,j;
  double* h = (double*) malloc(sizeof(double)*n);
  CvMat* A = cvCreateMat(n+1, n+1, CV_32F);
  CvMat* b = cvCreateMat(n+1, 1, CV_32F);
  for(i=0; i<n; i++){
    h[i] = src_points[i+1].x - src_points[i].x;
  }
  //set A param
  cvmSet(A, 0, 0, 1.0);
  for(j=1; j<n+1; j++){
    cvmSet(A, 0, j, 0.0);
  }
  for(i=1; i<n; i++){
    for(j=0; j<i-1; j++){
      cvmSet(A, i, j, 0.0);
    }
    cvmSet(A, i, i-1, h[i-1]);
    cvmSet(A, i, i, 2*(h[i-1] + h[i]));
    cvmSet(A, i, i+1, h[i]);
    for(j=i+2; j<n+1; j++){
      cvmSet(A, i, j, 0.0);
    }
  }
  for(j=0; j<n; j++){
    cvmSet(A, n, j, 0.0);
  }
  cvmSet(A, n, n, 1.0);
  //set b param
  cvmSet(b, 0, 0, 0.0);
  for(i=1; i<n; i++){
    cvmSet(b, i, 0, (3.0*(src_points[i+1].y - src_points[i].y)/h[i]) - (3.0*(src_points[i].y - src_points[i-1].y)/h[i-1]));
  }
  cvmSet(b, n, 0, 0.0);
  cvSolve(A, b, dst, CV_LU);
  cvReleaseMat(&A);
  cvReleaseMat(&b);
}

float FuncSpline3D(int n, float x, const CvPoint2D32f* src_points, const CvMat* c_mat)
{
  assert(x >= src_points[0].x);
  assert(x <= src_points[n].x );
  int i;
  for(i=0; i<n; i++){
    if((src_points[i].x <= x) && (x <= src_points[i+1].x)){
      break;
    }
  }
  assert(i < n);
  float h_i = src_points[i+1].x - src_points[i].x;
  float c = cvmGet(c_mat, i, 0);
  float c2 = cvmGet(c_mat, i+1, 0);
  float b = (src_points[i+1].y - src_points[i].y)/h_i - h_i*(2.0*c + c2)/3.0;
  float d = (c2 - c)/(3.0*h_i);
  float h = x - src_points[i].x;
  return (src_points[i].y + b*h + c *h*h + d*h*h*h);
}
int main(void)
{
  int i,j;
  int n=13;
  CvPoint2D32f* points = (CvPoint2D32f*) malloc(sizeof(CvPoint2D32f)*(n+1));
  points[0].x = 9; points[0].y = 130;
  points[1].x = 13; points[1].y = 150;
  points[2].x = 19; points[2].y = 185;
  points[3].x = 21; points[3].y = 210;
  points[4].x = 26; points[4].y = 260;
  points[5].x = 30; points[5].y = 270;
  points[6].x = 39; points[6].y = 240;
  points[7].x = 44; points[7].y = 215;
  points[8].x = 47; points[8].y = 205;
  points[9].x = 50; points[9].y = 210;
  points[10].x = 60; points[10].y = 225;
  points[11].x = 70; points[11].y = 230;
  points[12].x = 80; points[12].y = 225;
  points[13].x = 92; points[13].y = 195;
  IplImage* image = cvCreateImage(cvSize(100, 200), IPL_DEPTH_8U, 3);
  CvMat* c_mat = cvCreateMat(n+1, 1, CV_32F);
  SolveSpline3D(n, points, c_mat);
  for(i=0; i<n-1; i++){
    double c = cvmGet(c_mat, i, 0);
    double c2 = cvmGet(c_mat, i+1, 0);
    double b = (points[i+1].y - points[i].y)/(points[i+1].x - points[i].x) - (points[i+1].x - points[i].x)*(2.0*c + c2)/3.0;
    double d = (c2 - c)/(3.0*(points[i+1].x - points[i].x));
    printf("b = %f c = %f d=%f \n", b, c, d);
  }
  cvSet(image, cvScalar(255, 255, 255));
  int x;
  for(x=10; x<=90; x++){
    float y = FuncSpline3D(n, x, points, c_mat);
    SetPixelToBGR(image, x, y, 0, 0, 0);
  }
  cvSaveImage("test.bmp", image);
  cvReleaseMat(&c_mat);
  cvReleaseImage(&image);
  free(points);
}

参考URL

http://next1.cc.it-hiroshima.ac.jp/MULTIMEDIA/numeanal1/node16.html