opencv perspectiveTransform
2015-10-22 22:35
316 查看
Opencv 投射变换代码,代码很简单,但是也记录一下
公式为
Dst x,y,z为变化过后的坐标
Src x,y,z为变换签坐标
H为投射矩阵
void cv::perspectiveTransform( InputArray _src, OutputArray _dst, InputArray _mtx )
{
Mat src = _src.getMat(), m = _mtx.getMat();//从接口类导出Mat
int depth = src.depth(), scn = src.channels(), dcn = m.rows-1;//获得深度及通道数
CV_Assert( scn + 1 == m.cols && (depth == CV_32F || depth == CV_64F));
_dst.create( src.size(), CV_MAKETYPE(depth, dcn) );//分配内存
Mat dst = _dst.getMat();
const int mtype
= CV_64F;
AutoBuffer<double> _mbuf;
double* mbuf = _mbuf;
if( !m.isContinuous() || m.type() != mtype )//判断数据存储是否连续,以及类型是否是64位浮点
{
_mbuf.allocate((dcn+1)*(scn+1));
Mat tmp(dcn+1, scn+1, mtype, (double*)_mbuf);
m.convertTo(tmp, mtype);
m = tmp;
}
else
mbuf = (double*)m.data;
TransformFunc func = depth == CV_32F ?
(TransformFunc)perspectiveTransform_32f :
(TransformFunc)perspectiveTransform_64f;//我理解这里其实就是一个函数指针,根据数据类型选择合适的函数
CV_Assert( func != 0 );
const Mat* arrays[] = {&src, &dst, 0};
uchar* ptrs[2];
NAryMatIterator it(arrays, ptrs);//作用等同于把src和dst的data地址给了ptrs,ptr[0]指向src
size_t i, total = it.size;
for( i = 0; i < it.nplanes; i++, ++it )
func( ptrs[0], ptrs[1], (uchar*)mbuf, (int)total, scn, dcn );//调用真正的变换函数
}
template<typename T> static void
perspectiveTransform_( const T* src, T* dst, const double*
m, int len, int scn, int dcn
)
{
const double eps
= FLT_EPSILON;//无穷小
int i;
if( scn == 2 && dcn == 2 )//如果都是二维坐标,函数可以处理二维或三维或混合坐标,三维坐标就是齐次坐标
{
for( i = 0; i < len*2; i += 2 )
{
T x = src[i], y = src[i + 1];
double w = x*m[6] + y*m[7] + m[8];//取得第三维度坐标,等会儿要归一化
if( fabs(w) > eps )//如果第三维坐标为0,表示无穷远
{
w = 1./w;//归一化,做乘法比做除法更快
dst[i] = (T)((x*m[0] + y*m[1] + m[2])*w);//计算变换后的坐标x
dst[i+1] = (T)((x*m[3] + y*m[4] + m[5])*w); //计算变换后的坐标y
}
else
dst[i] = dst[i+1] = (T)0;
}
}
else if(
scn == 3 && dcn == 3 )
{
for( i = 0; i < len*3; i += 3 )
{
T x = src[i], y = src[i + 1], z = src[i + 2];
double w = x*m[12] + y*m[13] + z*m[14] + m[15];
if( fabs(w) > eps )
{
w = 1./w;
dst[i] = (T)((x*m[0] + y*m[1] + z*m[2] + m[3]) * w);
dst[i+1] = (T)((x*m[4] + y*m[5] + z*m[6] + m[7]) * w);
dst[i+2] = (T)((x*m[8] + y*m[9] + z*m[10] + m[11]) * w);
}
else
dst[i] = dst[i+1] = dst[i+2] = (T)0;
}
}
else if(
scn == 3 && dcn == 2 )
{
for( i = 0; i < len; i++, src += 3, dst += 2 )
{
T x = src[0], y = src[1], z = src[2];
double w = x*m[8] + y*m[9] + z*m[10] + m[11];
if( fabs(w) > eps )
{
w = 1./w;
dst[0] = (T)((x*m[0] + y*m[1] + z*m[2] + m[3])*w);
dst[1] = (T)((x*m[4] + y*m[5] + z*m[6] + m[7])*w);
}
else
dst[0] = dst[1] = (T)0;
}
}
else
{
for( i = 0; i < len; i++, src += scn, dst += dcn )
{
const double*
_m = m + dcn*(scn + 1);
double w = _m[scn];
int j, k;
for( k = 0; k < scn; k++ )
w += _m[k]*src[k];
if( fabs(w) > eps )
{
_m = m;
for( j = 0; j < dcn; j++, _m += scn + 1 )
{
double s = _m[scn];
for( k = 0; k < scn; k++ )
s += _m[k]*src[k];
dst[j] = (T)(s*w);
}
}
else
for( j = 0; j < dcn; j++ )
dst[j] = 0;
}
}
}
公式为
Dst x,y,z为变化过后的坐标
Src x,y,z为变换签坐标
H为投射矩阵
void cv::perspectiveTransform( InputArray _src, OutputArray _dst, InputArray _mtx )
{
Mat src = _src.getMat(), m = _mtx.getMat();//从接口类导出Mat
int depth = src.depth(), scn = src.channels(), dcn = m.rows-1;//获得深度及通道数
CV_Assert( scn + 1 == m.cols && (depth == CV_32F || depth == CV_64F));
_dst.create( src.size(), CV_MAKETYPE(depth, dcn) );//分配内存
Mat dst = _dst.getMat();
const int mtype
= CV_64F;
AutoBuffer<double> _mbuf;
double* mbuf = _mbuf;
if( !m.isContinuous() || m.type() != mtype )//判断数据存储是否连续,以及类型是否是64位浮点
{
_mbuf.allocate((dcn+1)*(scn+1));
Mat tmp(dcn+1, scn+1, mtype, (double*)_mbuf);
m.convertTo(tmp, mtype);
m = tmp;
}
else
mbuf = (double*)m.data;
TransformFunc func = depth == CV_32F ?
(TransformFunc)perspectiveTransform_32f :
(TransformFunc)perspectiveTransform_64f;//我理解这里其实就是一个函数指针,根据数据类型选择合适的函数
CV_Assert( func != 0 );
const Mat* arrays[] = {&src, &dst, 0};
uchar* ptrs[2];
NAryMatIterator it(arrays, ptrs);//作用等同于把src和dst的data地址给了ptrs,ptr[0]指向src
size_t i, total = it.size;
for( i = 0; i < it.nplanes; i++, ++it )
func( ptrs[0], ptrs[1], (uchar*)mbuf, (int)total, scn, dcn );//调用真正的变换函数
}
template<typename T> static void
perspectiveTransform_( const T* src, T* dst, const double*
m, int len, int scn, int dcn
)
{
const double eps
= FLT_EPSILON;//无穷小
int i;
if( scn == 2 && dcn == 2 )//如果都是二维坐标,函数可以处理二维或三维或混合坐标,三维坐标就是齐次坐标
{
for( i = 0; i < len*2; i += 2 )
{
T x = src[i], y = src[i + 1];
double w = x*m[6] + y*m[7] + m[8];//取得第三维度坐标,等会儿要归一化
if( fabs(w) > eps )//如果第三维坐标为0,表示无穷远
{
w = 1./w;//归一化,做乘法比做除法更快
dst[i] = (T)((x*m[0] + y*m[1] + m[2])*w);//计算变换后的坐标x
dst[i+1] = (T)((x*m[3] + y*m[4] + m[5])*w); //计算变换后的坐标y
}
else
dst[i] = dst[i+1] = (T)0;
}
}
else if(
scn == 3 && dcn == 3 )
{
for( i = 0; i < len*3; i += 3 )
{
T x = src[i], y = src[i + 1], z = src[i + 2];
double w = x*m[12] + y*m[13] + z*m[14] + m[15];
if( fabs(w) > eps )
{
w = 1./w;
dst[i] = (T)((x*m[0] + y*m[1] + z*m[2] + m[3]) * w);
dst[i+1] = (T)((x*m[4] + y*m[5] + z*m[6] + m[7]) * w);
dst[i+2] = (T)((x*m[8] + y*m[9] + z*m[10] + m[11]) * w);
}
else
dst[i] = dst[i+1] = dst[i+2] = (T)0;
}
}
else if(
scn == 3 && dcn == 2 )
{
for( i = 0; i < len; i++, src += 3, dst += 2 )
{
T x = src[0], y = src[1], z = src[2];
double w = x*m[8] + y*m[9] + z*m[10] + m[11];
if( fabs(w) > eps )
{
w = 1./w;
dst[0] = (T)((x*m[0] + y*m[1] + z*m[2] + m[3])*w);
dst[1] = (T)((x*m[4] + y*m[5] + z*m[6] + m[7])*w);
}
else
dst[0] = dst[1] = (T)0;
}
}
else
{
for( i = 0; i < len; i++, src += scn, dst += dcn )
{
const double*
_m = m + dcn*(scn + 1);
double w = _m[scn];
int j, k;
for( k = 0; k < scn; k++ )
w += _m[k]*src[k];
if( fabs(w) > eps )
{
_m = m;
for( j = 0; j < dcn; j++, _m += scn + 1 )
{
double s = _m[scn];
for( k = 0; k < scn; k++ )
s += _m[k]*src[k];
dst[j] = (T)(s*w);
}
}
else
for( j = 0; j < dcn; j++ )
dst[j] = 0;
}
}
}
相关文章推荐
- Wolfram Mathematica 10.3 Windows / Linux / MacOsx
- 第 三 十 八 天:Linux 的 LVM 逻 辑 卷 管 理
- linux下lsof实现对误删文件的恢复
- linux中.config,makefile,kconfig
- codeblocks与opencv
- 64位Linux编译cximage手记
- C# 从需要登录的网站上抓取数据
- maven依赖关系中Scope的作用
- Linux安装mysql——源码安装
- 使用opencv获取海康IPC摄像头教程
- UVA10934 Dropping water balloons(dp状态比较难想)
- Linux 循环遍历文件目录
- linux下使用yum安装mysql详解
- 《学习OpenCV》第五章课后题3
- linux 批量修改文件后缀名
- 为perf4j提供集中式监控项目perf4j-dashboard
- OpenCV-图像模糊
- NSOperation的基本使用——本质是对GCD的封装
- 第三十八天:分区 -- Parte
- HDOJ 题目3853 LOOPS(概率DP)