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


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

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

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

      思路如下:

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

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

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

      关键代码如下:

      其中一些变量声明如下:

    复制代码
            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的参数

      就会出现如下的效果:

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

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

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

      抽时间再仔细琢磨琢磨。

  • 相关阅读:
    JAVA练习3
    JAVA练习2
    找出一个整型数组中元素最大值,使用面向对象方法
    类和对象应用例题
    用指针变量作函数形参接收数组地址,解决10个整数按由大到小顺序排序问题
    把指针作为函数参数的方法处理从大到小排序问题。
    通过指针变量访问整型变量
    用选择法对数组中10个整数进行排列
    有参函数的调用
    函数模板
  • 原文地址:https://www.cnblogs.com/Leo_wl/p/2952034.html
Copyright © 2020-2023  润新知