好得很程序员自学网

<tfoot draggable='sEl'></tfoot>

用opencv将图片变成水波纹效果

用opencv将图片变成水波纹效果

用opencv将图片变成水波纹效果

又是很久很久没有写博客了。不知道为什么,还是没有这个习惯。总是感觉没什么好写的。倘若是,将学到的东西,记录下来。如果是仅仅是这样的话,我自知大多没有自己的思考,也不过是将别人的东西搬到自己的博客里面而已,网上一搜一大片,又是何苦呢。

  还是上学期,有个练习题目是,将一幅图片变成水波纹效果。我在网上找到一份源码,参考之下,顺着思路用opencv2重写之。望原作者看到勿怪。

  思路如下:

  1.将图片中的坐标点(x,y)换成极坐标,有现成的函数。

  2.极坐标下,用三角函数算出新半径。

  3.在新半径之下,转换成新的坐标(x 0 ,y 0 ),如果新坐标是小数,用双线性插值的方法处理。

  关键代码如下:

  其中一些变量声明如下:

        Mat imageInfo; //  原图片 
         int   imageWidth;
          int   imageHeight;
          int  imageX; //  图像中心点的横坐标 
         int  imageY; //  图像中心点的纵坐标 
         float  A; //  波纹幅度 
         float  B; //  波纹周期   Asin(Bx); 
         int  imageChannels; //  通道数 
        Mat imageWater; //  转换后的图片 
         void  reCalcAB( int  i, int  j, float  &a, float  &b); //  坐标转换 
        uchar BLIP( float  a, float  b, int   k);
          //  k为通道数,值为-1为单通道,灰度图 

  1   void   imagetest::imageprocess()
   2   {
   3       imageInfo.copyTo(imageWater);
   4  
  5       float   a;
   6       float  b; //  临时坐标 
  7  
  8       for ( int  i= 0 ;i<imageHeight- 1 ;i++ )
   9       {
  10          uchar *Data = imageWater.ptr<uchar> (i);
  11           for ( int  j= 0 ;j<imageWidth- 1 ;j++ )
  12           {
  13               reCalcAB(i,j,a,b);
  14               if (imageChannels ==  1 ) //  彩色与灰度图像要单独处理,否则 
 15              {                        //  会出现椭圆的情况 
 16                  *(Data+j) = BLIP(a,b,- 1 ); //  -1指灰度图 
 17               }
  18               else   if (imageChannels ==  3  )
  19               {
  20                   for ( int  k =  0 ;k<imageChannels;k++ )
  21                   {
  22                      *(Data+j*imageChannels+k)=  BLIP(a,b,k);
  23                   }
  24               }
  25           }
  26       }
  27  }

  reCalcAB是坐标转换函数:

  1   void  imagetest::reCalcAB( int  i, int  j, float  &a, float  & b)
   2   {
   3       float  y0 = ( float )(i- imageY);
   4       float  x0 = ( float )(j-imageX); //  (i,j)相对于原点的坐标 
  5       float  theta0 = atan2f(y0,x0); //  转化成角坐标 
  6       float  r0 = sqrtf(x0*x0+y0*y0); //  初始半径 
  7  
  8       float  r1 = r0+ A*imageWidth* 0.01 *sin(B* 0.1 *r0); //  计算新的半径 
  9      a = imageX + r1* cos(theta0);
  10      b = imageY + r1*sin(theta0); //  转换后的坐标 
 11       if (a> imageWidth)
  12          a = imageWidth- 1  ;
  13       else   if (a< 0  )
  14          a =  0 ;                 //  超出边界的处理 
 15       if (b> imageHeight)
  16          b = imageHeight- 1  ;
  17       else   if (b< 0  )
  18          b =  0  ;
  19  }

  双线性插值函数:(这个方法看着很高级,实际很简单。仔细看代码就明白怎么回事情了)

  1  uchar imagetest::BLIP( float  a, float  b, int   k)
   2   {
   3      uchar newData; //  保存结果 
  4       uchar DataTemp1;
   5      uchar DataTemp2; //  两个中间变量 
  6       int  x[ 2  ];
   7       int  y[ 2 ]; //  存储周围四个点。 
  8  
  9      x[ 0 ] = ( int  )a;
  10      y[ 0 ] = ( int  )b;
  11      x[ 1 ] = x[ 0 ]+ 1  ;
  12      y[ 1 ] = y[ 0 ]+ 1 ; //  (a,b)周围四个整点坐标
  13       //  取值 
 14      uchar *data1 = imageWater.data + y[ 0 ]*imageWater.step + x[ 0 ]* imageChannels;
  15      uchar *data2 = imageWater.data + y[ 0 ]*imageWater.step + x[ 1 ]* imageChannels;
  16      uchar *data3 = imageWater.data + y[ 1 ]*imageWater.step + x[ 0 ]* imageChannels;
  17      uchar *data4 = imageWater.data + y[ 1 ]*imageWater.step + x[ 1 ]* imageChannels;
  18       if (k!=- 1 ) //  如果是彩色,转换一下 
 19       {
  20          data1 +=  k;
  21          data2 +=  k;
  22          data3 +=  k;
  23          data4 +=  k;
  24       }
  25  
 26       if ((fabsf(a-x[ 0 ])< 0.00001 ) && (fabsf(b-y[ 0 ])< 0.00001 )) //  整点,直接返回 
 27       {
  28          newData = * data1;
  29           return   newData;
  30       }
  31  
 32       float  dx = fabsf(a-x[ 0 ]); //  x轴的比例 
 33       float  dy = fabsf(b-y[ 0 ]); //  y轴的比例 
 34  
 35      DataTemp1 = (*data1)*( 1.0 -dx) + (*data2)* dx;
  36      DataTemp2 = (*data3)*( 1.0 -dx) + (*data4)* dx;
  37      newData = DataTemp1*( 1.0 -dy) + DataTemp2*dy; //  核心插值过程 
 38  
 39       return   newData;
  40  }

  效果如下:

  这个效果看起来倒是不错,总感觉不是那么真实。

  而且,这个程序有严重的问题。如果我换一张图片,重新设置 A和B的参数

  就会出现如下的效果:

  中间水平方向出现了明显的一条横线。

  目前还没有解决的问题主要就是这条横线,然后就是怎么样才能使得水波纹看起来更真实。我想把用一张图片做成视频,不知道这个效果最后做出来是个什么样子。

  如果是坐标转换出错了的话,理论上来说应该会水平、竖直都应该出现一条直线,现在只有水平方向有一条直线。

  抽时间再仔细琢磨琢磨。

作者: Leo_wl

    

出处: http://HdhCmsTestcnblogs测试数据/Leo_wl/

    

本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

版权信息

查看更多关于用opencv将图片变成水波纹效果的详细内容...

  阅读:40次