用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将图片变成水波纹效果的详细内容...