用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的參數

  就會出現如下的效果:

  中間水平方向出現了明顯的一條橫線。

  目前還沒有解決的問題主要就是這條橫線,然后就是怎么樣才能使得水波紋看起來更真實。我想把用一張圖片做成視頻,不知道這個效果最后做出來是個什么樣子。

  如果是坐標轉換出錯了的話,理論上來說應該會水平、豎直都應該出現一條直線,現在只有水平方向有一條直線。

  抽時間再仔細琢磨琢磨。

  


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM