前些天做图像识别的题,深感自己数学推算水平下降了。所以,今天尝试一下一个简单的图像处理算法:通过数学建模方法生成水波纹效果。波纹只考虑折射,不考虑反射。
算法推导
首先,从简单的开始。我们先尝试一下水平纵波,方向沿y轴向下,波的表达式
注意的是这里从计算速度角度来看,不使用惠更斯原理,只使用简单的几何光学。讨论的情景是水底的自发光景物发出平行光向上射出水面,投影到水面外的屏幕上。简单起见,屏幕假设在水波最高点处。
画图如下:
入射角与切平面与水平面斜率相同,为正弦函数导数,即:
这里k为切平面与水平面夹角,ω为角速度(弧度rad),A为振幅,x为行程,v为波速,t为时间。
入射角与切平面夹角一致为k。根据折射定律,入射角和折射角满足:
这样,根据解析几何,可以知道:
有了这些,基本的水波就能画出来了。我们先完成基础功能,更加复杂的模拟,如衰减、反射,先暂时不添加。
接下来就是将算法改写为具体的程序。我们使用remap函数。Remap函数的作用是把一幅图像中某位置的像素放置到另一个图片指定位置的过程。
void remap(InputArray src, OutputArray dst, InputArray map1, InputArray map2,
int interpolation,int borderMode = BORDER_CONSTANT,
const Scalar&borderValue = Scalar())
第一个参数:输入图像,即原图像,需要单通道8位或者浮点类型的图像
第二个参数:输出图像,即目标图像,需和原图形一样的尺寸和类型
第三个参数:它有两种可能表示的对象:(1)表示点(x,y)的第一个映射;(2)表示CV_16SC2,CV_32FC1等
第四个参数:它有两种可能表示的对象:(1)若map1表示点(x,y)时,这个参数不代表任何值;(2)表示CV_16UC1,CV_32FC1类型的Y值
第五个参数:插值方式,有四中插值方式:1)INTER_NEAREST——最近邻插值
2)INTER_LINEAR——双线性插值(默认)
3)INTER_CUBIC——双三样条插值(默认)
4)INTER_LANCZOS4——lanczos插值(默认)
第六个参数:边界模式,默认BORDER_CONSTANT
第七个参数:边界颜色,默认Scalar()黑色
最基本的波:平行波
最简单的,是水平传播的平行波。这里mapx.col(x)=x;的作用是将第x行全部设置为x。
voidMoire_HorizonWave( const Mat &src, Mat &dst, float A, float w, floatfai, float n )
{
Mat mapx( src.rows, src.cols, CV_32FC1 ),mapy( src.rows, src.cols, CV_32FC1 );
int cnt = 0;
for ( auto y = 0u; y <src.rows ; y++ )
{
double alpha = atan( cosf( w*y -fai ) );//切线与x轴夹角,相当于入射角
double gamma = asin( sinf( alpha )/ n );//折射角
double val = y - A* tan( alpha -gamma );
mapx.col(x) = x;
mapy.row(y) = val;
}
remap( src, dst, mapx, mapy,INTER_LINEAR, cv::BORDER_WARP );
}
效果如下,帧时间约45ms(DEBUG模式,以下分析效率时,不加说明都是DEBUG模式。而RELEASE模式一般耗时是DEBUG模式