又是很久很久沒有寫博客了。不知道為什么,還是沒有這個習慣。總是感覺沒什么好寫的。倘若是,將學到的東西,記錄下來。如果是僅僅是這樣的話,我自知大多沒有自己的思考,也不過是將別人的東西搬到自己的博客里面而已,網上一搜一大片,又是何苦呢。
還是上學期,有個練習題目是,將一幅圖片變成水波紋效果。我在網上找到一份源碼,參考之下,順着思路用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的參數
就會出現如下的效果:
中間水平方向出現了明顯的一條橫線。
目前還沒有解決的問題主要就是這條橫線,然后就是怎么樣才能使得水波紋看起來更真實。我想把用一張圖片做成視頻,不知道這個效果最后做出來是個什么樣子。
如果是坐標轉換出錯了的話,理論上來說應該會水平、豎直都應該出現一條直線,現在只有水平方向有一條直線。
抽時間再仔細琢磨琢磨。