精华内容
下载资源
问答
  • 仿射变换代码

    2013-11-22 12:17:30
    在几何上定义为两个向量空间之间的一个仿射变换或者仿射映射(来自拉丁语,affinis,“和。..相关”)由一个线性变换接上一个平移组成。
  • matlab仿射变换代码这个存储库(将)提供几种地图同步算法的实现,这些算法在一组形状中计算地图。 该代码是在 MIT 许可下发布的,可以用于任何目的并具有适当的署名。 该代码随附以下论文,应在使用所提供模块的...
  • 图像的仿射变换代码以及双线性插值代码理解 因为前段时间看了一下STN网络和可变形卷积,然后想研究一下代码实现部分,发现真的是一点都看不懂,还好看了一篇大佬的博客,写这篇文章的目的是细说一下大佬博客中的代码...

    图像的仿射变换代码以及双线性插值代码理解

    因为前段时间看了一下STN网络和可变形卷积,然后想研究一下代码实现部分,发现真的是一点都看不懂,还好看了一篇大佬的博客,写这篇文章的目的是细说一下大佬博客中的代码细节
    这里附上原博客链接:https://blog.csdn.net/u011974639/article/details/79675408

    图像的仿射变换

    def affine_grid_generator(height,width,M):
        num_batch = M.shape[0]
        
        #创建棋盘grid,平分整个图片
        x = np.linspace(-1,1,width)
        y = np.linspace(-1,1,height)
        
       # x2=np.linspace(0,400,400)
       # y2=np.linspace(0,400,400)
        x_t,y_t = np.meshgrid(x,y)
        
        #制作原始坐标点的列向量
        prod = np.prod(x_t.shape)
        #print(prod)
        ones = np.ones(prod)
        #print(ones)
        sampling_grid = np.vstack([x_t.flatten(), y_t.flatten(), ones])
        sampling_grid = np.resize(sampling_grid,(num_batch,3,height*width))
        #print(sampling_grid)
        batch_grids = np.matmul(M,sampling_grid)
        #batch_grids (Batch,2,height*width)
        
        #将仿射结果中H,W拆开
        batch_grids = batch_grids.reshape(num_batch,2,height,width)
        batch_grids = np.moveaxis(batch_grids,1,-1) #num_batch,height,width,2
        
        print("Transformation Matrices: {}".format(M.shape))
        print("Sampling Grid: {}".format(sampling_grid.shape))
        print("Batch Grids: {}".format(batch_grids.shape))
    
        return batch_grids
    

    作者的代码是用numpy实现的,因为tensorflow跟numpy差不多,这并不影响我们理解tensorflow代码。下面我将逐句解读代码。

    1. num_batch是获取当前epoch下该batch需要进行仿射变换的图片数量。

    2. 创建棋盘grid。这里作者的意思是说,根据原图片中的宽和高,生成一个一模一样的坐标点集合。形象的叫做棋盘grid。注意这里叫棋盘的原因我觉得很形象,因为这里面只有坐标(棋盘),而没有坐标点的像素值(棋子)。

        x = np.linspace(-1,1,width)
        y = np.linspace(-1,1,height)
        
       # x2=np.linspace(0,400,400)
       # y2=np.linspace(0,400,400)
        x_t,y_t = np.meshgrid(x,y)
    

    3.拿到原图中每一个坐标点后,我们要开始映射出仿射变换中的坐标点。核心思想就是拿原坐标点去乘以仿射变换的参数矩阵。得到仿射变换后的坐标点。那么原坐标点首先要换成{x,y,1}转置的格式。才能与反射变换相乘,同时因为有num_batch个图片,所以将T{x,y,1}resize到num_batch个T{x,y,1}

    	prod = np.prod(x_t.shape)
        #print(prod)
        ones = np.ones(prod)
        #print(ones)
        sampling_grid = np.vstack([x_t.flatten(), y_t.flatten(), ones])   #T([x,y,1])
        sampling_grid = np.resize(sampling_grid,(num_batch,3,height*width))
    

    4.最终将M也即变换参数矩阵与T(x,y,1)相乘得到经过仿射变换后的对应的x,y坐标

    双线性插值

    def bilinear_sampler(input_img,x,y):
        B,H,W,C = input_img.shape
        x=((x+1.)*W)*0.5
        y=((y+1.)*H)*0.5
        
        #获取相邻的四个坐标
        x0 = np.floor(x).astype(np.int64)
        x1 = x0+1
        y0 = np.floor(y).astype(np.int64)
        y1 = y0+1
        
        x0 = np.clip(x0,0,W-1)
        x1 = np.clip(x1,0,W-1)
        y0 = np.clip(y0,0,H-1)
        y1 = np.clip(y1,0,H-1)
        
        Ia = input_img[np.arange(B)[:,None,None], y0, x0]
        Ib = input_img[np.arange(B)[:,None,None], y1, x0]
        Ic = input_img[np.arange(B)[:,None,None], y0, x1]
        Id = input_img[np.arange(B)[:,None,None], y1, x1]
        
        wa = (x1-x) * (y1-y)
        wb = (x1-x) * (y-y0)
        wc = (x-x0) * (y1-y)
        wd = (x-x0) * (y-y0)
        
        wa = np.expand_dims(wa, axis=3)
        wb = np.expand_dims(wb, axis=3)
        wc = np.expand_dims(wc, axis=3)
        wd = np.expand_dims(wd, axis=3)
        
        out = wa*Ia + wb*Ib + wc*Ic + wd*Id
    
        return out
    

    1.首先将我们刚才得到的坐标映射至H,W
    我们刚才设置的x,y是[-1,1]等距离初始化的,那么现在将其+1,则映射至[0,2]之间,然后x乘以W,则映射至[0,2W]之间,最后*0.5,则映射至[0,W]之间。y同理

    B,H,W,C = input_img.shape
        x=((x+1.)*W)*0.5
        y=((y+1.)*H)*0.5
    

    2.因为由仿射变换后的x,y坐标有可能是小数,那么将其转换为周围四个点的坐标,方便进行双线性插值的运算。同时应该保证转换后的坐标不能大于H,W,也不能小于0。

     #获取相邻的四个坐标
        x0 = np.floor(x).astype(np.int64)
        x1 = x0+1
        y0 = np.floor(y).astype(np.int64)
        y1 = y0+1
        
        x0 = np.clip(x0,0,W-1)
        x1 = np.clip(x1,0,W-1)
        y0 = np.clip(y0,0,H-1)
        y1 = np.clip(y1,0,H-1)
    

    3.获取四个周围点坐标后,在取出原图像中四个周围点像素值进行双线性插值运算。

    Ia = input_img[np.arange(B)[:,None,None], y0, x0]
        Ib = input_img[np.arange(B)[:,None,None], y1, x0]
        Ic = input_img[np.arange(B)[:,None,None], y0, x1]
        Id = input_img[np.arange(B)[:,None,None], y1, x1]
        
        wa = (x1-x) * (y1-y)
        wb = (x1-x) * (y-y0)
        wc = (x-x0) * (y1-y)
        wd = (x-x0) * (y-y0)
        
    
        wa = np.expand_dims(wa, axis=3)
        wb = np.expand_dims(wb, axis=3)
        wc = np.expand_dims(wc, axis=3)
        wd = np.expand_dims(wd, axis=3)
        
        out = wa*Ia + wb*Ib + wc*Ic + wd*Id
    
        return out
    
    展开全文
  • 2二维仿射变换2.1二维仿射变换的数学表达式二维仿射变换的数学表达式为:x=ax+by+ey=cx+dy+f(1)其中,代表仿射变换,x和y是变换前像素的坐标值,x和y是变换后像素的坐标值;a,b,c,d,e,f是仿射变换系数。2.2二维仿射变换的...

    1图像配准在利用显微照相机拍摄生物组织的序列切片图像时,由于人为将切片放置在载玻片上,会产生不同程度的偏移、旋转,使相邻切片的图像不匹配,无法进行三维重建。图1和图2是黄瓜茎横截面的两个相邻切片在显微照相机下成的图像。其中,最外面轮廓是黄瓜茎的表皮,中心是9个维管束,必须以图1为参考图像对图2进行平移、旋转甚至缩放等操作使它和图1匹配,只有所有相邻切片都配准了,才可以进行三维重建。图1参考图像图2待配准图像图像配准是指寻找联系两幅图像的几何变换,使得两幅图像上的对应点达到空间上的一致,常用的方法主要有相似变换、刚性变换和仿射变换。相似变换是局部到整体在各个方向上的等比例变换,而仿射变换是局部到整体在不同方向上的不等比例变换。从直觉上看,相似变换可以放大或者缩小甚至旋转,但不变形;而仿射变换可能会变形,相似变换是仿射变换的一个特例[1]。在相邻切片图像之间多少是有些变形的,因为在制作切片时,由于有外力作用,不能保证组织不变形,所以不能用相似变换来实现配准,可以用仿射变换来实现。刘哲星等[2]采用刚性变换实现了小鼠胚胎序列组织切片的图像配准。刚性变换(rigidtransforamtion)与仿射变换的最大区别是:刚性变换只有平移和旋转,没有缩放;仿射变换既有平移、旋转又有缩放。在获取切片的图像时,为了使效果最好,显微照相机通常要进行调焦操作,这样照出来的图像会有缩放,因此本文采用仿射变换来实现配准。两幅图像的配准可以看成是两个特征点集的配准,对图2的所有点进行仿射变换可以实现两幅图像的配准[3]。仿射变换有6个参数,通过3对特征点就可以求出来,然而在选取特征点时,人工选择会产生误差,为了降低误差,本文选取多对特征点,利用最小二乘法,求出最优仿射变换参数。配准后的图像利用VTK提供的步进立方体算法抽取等值面,实现了基于切片图像的黄瓜茎的三维建模和显示。2二维仿射变换2.1二维仿射变换的数学表达式二维仿射变换的数学表达式为:x=ax+by+ey=cx+dy+f(1)其中,代表仿射变换,x和y是变换前像素的坐标值,x和y是变换后像素的坐标值;a,b,c,d,e,f是仿射变换系数。2.2二维仿射变换的几何特征1)仿射变换的逆变换,仍是仿射变换。2)仿射变换是线性变换,直线段仿射变换后仍然是直线段,并且保持线段上点的定比关系不变。3)两条平行直线经过仿射变换后,仍可保持其平行性。4)任意平面图形经仿射变换后,其面积将发生变化,为变化前的(ad-bc)倍。只有当(ad-bc)=1时,面积在仿射变换前后才不变。3利用二维仿射变换实现图像配准3.1最小二乘法求取最优一组仿射变换参数由于是人工选取特征点,在选取时难免会有误差,为了使误差降到最低,采用最小二乘法估计参数,就是要选择参数,使观测值与相应函数值的离差平方和达到最小。分别在参考图像和待配准图像上选取多于3个对应的特征点(控制点),如5个点,分别记为Pi,Qi,i=1,2,3,4,5。由于每3对特征点确定一组仿射变换参数,因此可以由C35=10组特征点集确定10组仿射变换参数。例如,由(P1,P2,P3)和(Q1,Q2,Q3)确定了待配准图像到参考图像的仿射变换AT1,求取点Q4经该仿射变换后得到的点Q4与P4之间的欧式距离作为误差d1,X方向误差为X=x-(ax+by+e),Y方向误差为Y=y-(cx+dy+f),则距离误差为d1=X2+Y2,同理求取Q5与P5之间的欧式距离作为误差d2。设离差平方和U=d2=X2+Y2。这样,用10组特征点得到10组U值,选取最小的U值对应的特征点组,从而得到最优化的仿射

    展开全文
  • 仿射变换代码

    2014-06-04 19:39:19
    仿射变换代码,适合初学者用。仅供参考,真正了解其中的原理还得看书。
  • 仿射变换matlab代码

    热门讨论 2009-04-17 12:57:01
    仿射变换(Affine Transformation)matlab代码
  • 包含各种仿射变换的程序,即平移、旋转、均匀和非均匀缩放、正投影、斜投影和透视投影。 它还包含面部绘图功能,可以轻松地针对特定问题... 这可以在代码仿射变换阶段之后完成。 法向量和初始状态需要由用户定义。
  • 本资源提供的代码,可以用于数字图像的仿射变换
  • 密码学习题–仿射变换代码实现 #include <iostream> using namespace std; int mod(int num, int m) { while(num<0) num+=m; int tmp=num/m; return num-tmp*m; } //求模 int get_reverse(int num, int...

    密码学习题–仿射变换cpp代码实现
    在这里插入图片描述

    #include <iostream>
    using namespace std;
    int mod(int num, int m)
    {
    	while(num<0) num+=m;
        int tmp=num/m;
        return num-tmp*m;
    }  //求模
    int get_reverse(int num, int m)
    {
        for(int i=1; i<=m; i++)
        {
            if(mod(num*i, m)==1) return i;
        }
        return -1;
    }  //求逆
    void get_int_Arr(char word[], int num[])
    {
        for(int i=0; word[i]!=0; i++)
        {
            num[i]=mod(word[i]-'A'+1, 26);
        }
    }
    int get_TotalNumber(char word[])
    {
        int cnt=0;
        for(int i=0; word[i]!=0; i++) cnt++;
        return cnt;
    }
    void Code(char word[], int a, int b)
    {
        int* num=new int[get_TotalNumber(word)];
    
    	for(int i=0; i<=get_TotalNumber(word)-1; i++)
    	{
    		num[i]=word[i]-'a';
    		int c=mod(a*num[i]+b, 26);
    		char tmp=c+'a';
    		cout<<tmp<<' ';
    	}
    
    	delete[] num;
    }  //编码
    void Decode(char word[], int a, int b)
    {
    	int* num=new int[get_TotalNumber(word)];
    	int r=get_reverse(a, 26);
    
    	for(int i=0; i<=get_TotalNumber(word)-1; i++)
    	{
    		num[i]=word[i]-'a';
    		int m=mod(r*(num[i]-b), 26);
    		char tmp=m+'a';
    		cout<<tmp<<' ';
    	}
    
    	delete[] num;
    }  //解码
    void main()
    {
    	char word4[]="thenationalsecurityagency";
    	Code(word4, 11, 23);  //ywpkxyhvkxonptjchybxlpktb
    	cout<<endl;
    	char word5[]="edsgickxhuklzveqzvkxwkzukvcuh";
    	Decode(word5, 9, 10);
    }
    
    展开全文
  • Opencv 仿射变换原理代码解析

    千次阅读 2019-05-22 09:19:33
    仿射变换原理 仿射变换是线性变换,有一张图可以很好地展示放射变换的效果 其实仿射变换是透视变换的一种特例,但是透视变换的自由度更高,3*3的矩阵表示,透视变换的自由度是8,而放射变换可以用2*3的矩阵表示,...
    • 仿射变换原理

    仿射变换是线性变换,有一张图可以很好地展示放射变换的效果

    其实仿射变换是透视变换的一种特例,但是透视变换的自由度更高,3*3的矩阵表示,透视变换的自由度是8,而放射变换可以用2*3的矩阵表示,【A B】 A是2*2的旋转部分+缩放因子S,而B是平移部分+缩放因子,是一个5个自由度的参数矩阵。

    典型的放射变换包括平移,缩放和旋转。

    其中Opencv中的旋转由于是绕某个图像坐标进行旋转,因此整个旋转的流程可以理解为平移—》旋转—》平移

    最后可以得到旋转变换矩阵

    而对于特定的放射矩阵W,opencv的求逆矩阵方法是分块矩阵求逆矩阵,注意不能想当然得将平移部分参数取反,因为平移因子是经过一些列的旋转操作后的参数,除非矩阵只有平移部分。

    记录下分块矩阵求逆矩阵公式,摘自知乎帖子

    知乎对仿射变换的解释,带交互动画的:https://www.zhihu.com/question/20666664

    • Opencv源码

    实现仿射变换和实现其他resize的插值函数是类似的,需要求出目标图像像素在原图像中的位置,获得对应点的像素值。插值的方式有多种,一般常见的有最近邻插值,双线性插值(Bilinear)https://www.cnblogs.com/yssongest/p/5303151.html,三次样条曲线(Cubic Spline)。

    源码中一下部分用于求输入仿射矩阵的逆矩阵

     if( !(flags & WARP_INVERSE_MAP) )
        {
            double D = M[0]*M[4] - M[1]*M[3];
            D = D != 0 ? 1./D : 0;
            double A11 = M[4]*D, A22=M[0]*D;
            M[0] = A11; M[1] *= -D;
            M[3] *= -D; M[4] = A22;
            double b1 = -M[0]*M[2] - M[1]*M[5];
            double b2 = -M[3]*M[2] - M[4]*M[5];
            M[2] = b1; M[5] = b2;
        }

    先对2*2矩阵求逆矩阵,然后对1*2的块求逆。

    执行仿射变换的代码:

    void cv::warpAffine( InputArray _src, OutputArray _dst,
                         InputArray _M0, Size dsize,
                         int flags, int borderType, const Scalar& borderValue )
    {
    	.....
    	//计算X分量的数值
    for( x = 0; x < dst.cols; x++ )
        {
            adelta[x] = saturate_cast<int>(M[0]*x*AB_SCALE);
            bdelta[x] = saturate_cast<int>(M[3]*x*AB_SCALE);
        }
    
        Range range(0, dst.rows);
        WarpAffineInvoker invoker(src, dst, interpolation, borderType,
                                  borderValue, adelta, bdelta, M);
        parallel_for_(range, invoker, dst.total()/(double)(1<<16));
    	.....
    }
    
    class WarpAffineInvoker :
        public ParallelLoopBody
    {
    public:
        WarpAffineInvoker(const Mat &_src, Mat &_dst, int _interpolation, int _borderType,
                          const Scalar &_borderValue, int *_adelta, int *_bdelta, double *_M) :
            ParallelLoopBody(), src(_src), dst(_dst), interpolation(_interpolation),
            borderType(_borderType), borderValue(_borderValue), adelta(_adelta), bdelta(_bdelta),
            M(_M)
        {
        }
    
        virtual void operator() (const Range& range) const
        {
            const int BLOCK_SZ = 64;
    		//XY 是映射坐标矩阵,但是是分Block进行计算,分块意义在于SSE进行访问加速
    		//A  是remap函数使用到的参数,和插值有关系,没有进一步深入
            short XY[BLOCK_SZ*BLOCK_SZ*2], A[BLOCK_SZ*BLOCK_SZ];
            const int AB_BITS = MAX(10, (int)INTER_BITS);
    		//AB_BITS 为10,放大1024倍
            const int AB_SCALE = 1 << AB_BITS;
    		//round_delta 计算结果为16...
            int round_delta = interpolation == INTER_NEAREST ? AB_SCALE/2 : AB_SCALE/INTER_TAB_SIZE/2, x, y, x1, y1;
        #if CV_SSE2
            bool useSSE2 = checkHardwareSupport(CV_CPU_SSE2);
        #endif
        #if CV_SSE4_1
            bool useSSE4_1 = checkHardwareSupport(CV_CPU_SSE4_1);
        #endif
    
    		//获得图像单个Block不越界的访问区域
            int bh0 = std::min(BLOCK_SZ/2, dst.rows);
            int bw0 = std::min(BLOCK_SZ*BLOCK_SZ/bh0, dst.cols);
            bh0 = std::min(BLOCK_SZ*BLOCK_SZ/bw0, dst.rows);
    
    		//全图高度访问(rows),
            for( y = range.start; y < range.end; y += bh0 )
            {
    			//先访问cols(x),x += bw0每次增加1个Block的宽度
                for( x = 0; x < dst.cols; x += bw0 )
                {
                    int bw = std::min( bw0, dst.cols - x);
                    int bh = std::min( bh0, range.end - y);
    
    				//单个Block的映射坐标矩阵
                    Mat _XY(bh, bw, CV_16SC2, XY), matA;
    				//目标图像的某一个Block
                    Mat dpart(dst, Rect(x, y, bw, bh));
    
    				//再访问rows(y1)
                    for( y1 = 0; y1 < bh; y1++ )
                    {
    					//xy 是映射坐标矩阵的指针
                        short* xy = XY + y1*bw*2;
    					//计算包含y部分的分量
                        int X0 = saturate_cast<int>((M[1]*(y + y1) + M[2])*AB_SCALE) + round_delta;
                        int Y0 = saturate_cast<int>((M[4]*(y + y1) + M[5])*AB_SCALE) + round_delta;
    
                        if( interpolation == INTER_NEAREST )
                        {
                            x1 = 0;
                            #if CV_NEON
                            int32x4_t v_X0 = vdupq_n_s32(X0), v_Y0 = vdupq_n_s32(Y0);
                            for( ; x1 <= bw - 8; x1 += 8 )
                            {
                                int16x8x2_t v_dst;
                                v_dst.val[0] = vcombine_s16(vqmovn_s32(vshrq_n_s32(vaddq_s32(v_X0, vld1q_s32(adelta + x + x1)), AB_BITS)),
                                                            vqmovn_s32(vshrq_n_s32(vaddq_s32(v_X0, vld1q_s32(adelta + x + x1 + 4)), AB_BITS)));
                                v_dst.val[1] = vcombine_s16(vqmovn_s32(vshrq_n_s32(vaddq_s32(v_Y0, vld1q_s32(bdelta + x + x1)), AB_BITS)),
                                                            vqmovn_s32(vshrq_n_s32(vaddq_s32(v_Y0, vld1q_s32(bdelta + x + x1 + 4)), AB_BITS)));
    
                                vst2q_s16(xy + (x1 << 1), v_dst);
                            }
                            #elif CV_SSE4_1
                            if (useSSE4_1)
                            {
                                __m128i v_X0 = _mm_set1_epi32(X0);
                                __m128i v_Y0 = _mm_set1_epi32(Y0);
                                for ( ; x1 <= bw - 16; x1 += 16)
                                {
                                    __m128i v_x0 = _mm_packs_epi32(_mm_srai_epi32(_mm_add_epi32(v_X0, _mm_loadu_si128((__m128i const *)(adelta + x + x1))), AB_BITS),
                                                                   _mm_srai_epi32(_mm_add_epi32(v_X0, _mm_loadu_si128((__m128i const *)(adelta + x + x1 + 4))), AB_BITS));
                                    __m128i v_x1 = _mm_packs_epi32(_mm_srai_epi32(_mm_add_epi32(v_X0, _mm_loadu_si128((__m128i const *)(adelta + x + x1 + 8))), AB_BITS),
                                                                   _mm_srai_epi32(_mm_add_epi32(v_X0, _mm_loadu_si128((__m128i const *)(adelta + x + x1 + 12))), AB_BITS));
    
                                    __m128i v_y0 = _mm_packs_epi32(_mm_srai_epi32(_mm_add_epi32(v_Y0, _mm_loadu_si128((__m128i const *)(bdelta + x + x1))), AB_BITS),
                                                                   _mm_srai_epi32(_mm_add_epi32(v_Y0, _mm_loadu_si128((__m128i const *)(bdelta + x + x1 + 4))), AB_BITS));
                                    __m128i v_y1 = _mm_packs_epi32(_mm_srai_epi32(_mm_add_epi32(v_Y0, _mm_loadu_si128((__m128i const *)(bdelta + x + x1 + 8))), AB_BITS),
                                                                   _mm_srai_epi32(_mm_add_epi32(v_Y0, _mm_loadu_si128((__m128i const *)(bdelta + x + x1 + 12))), AB_BITS));
    
                                    _mm_interleave_epi16(v_x0, v_x1, v_y0, v_y1);
    
                                    _mm_storeu_si128((__m128i *)(xy + x1 * 2), v_x0);
                                    _mm_storeu_si128((__m128i *)(xy + x1 * 2 + 8), v_x1);
                                    _mm_storeu_si128((__m128i *)(xy + x1 * 2 + 16), v_y0);
                                    _mm_storeu_si128((__m128i *)(xy + x1 * 2 + 24), v_y1);
                                }
                            }
                            #endif
                            for( ; x1 < bw; x1++ )
                            {
                                int X = (X0 + adelta[x+x1]) >> AB_BITS;
                                int Y = (Y0 + bdelta[x+x1]) >> AB_BITS;
                                xy[x1*2] = saturate_cast<short>(X);
                                xy[x1*2+1] = saturate_cast<short>(Y);
                            }
                        }
                        else
                        {
                            short* alpha = A + y1*bw;
                            x1 = 0;
                        #if CV_SSE2
                            if( useSSE2 )
                            {
                                __m128i fxy_mask = _mm_set1_epi32(INTER_TAB_SIZE - 1);
    							//XX 计算的是M1*dy+M2
    							//YY 计算的是M4*dy+M5
                                __m128i XX = _mm_set1_epi32(X0), YY = _mm_set1_epi32(Y0);
                                for( ; x1 <= bw - 8; x1 += 8 )
                                {
                                    __m128i tx0, tx1, ty0, ty1;
    								//分别取4个整数进行X和Y(映射坐标)的计算,一共8个
                                    tx0 = _mm_add_epi32(_mm_loadu_si128((const __m128i*)(adelta + x + x1)), XX);
                                    ty0 = _mm_add_epi32(_mm_loadu_si128((const __m128i*)(bdelta + x + x1)), YY);
                                    tx1 = _mm_add_epi32(_mm_loadu_si128((const __m128i*)(adelta + x + x1 + 4)), XX);
                                    ty1 = _mm_add_epi32(_mm_loadu_si128((const __m128i*)(bdelta + x + x1 + 4)), YY);
    
    								//右移5位
                                    tx0 = _mm_srai_epi32(tx0, AB_BITS - INTER_BITS);
                                    ty0 = _mm_srai_epi32(ty0, AB_BITS - INTER_BITS);
                                    tx1 = _mm_srai_epi32(tx1, AB_BITS - INTER_BITS);
                                    ty1 = _mm_srai_epi32(ty1, AB_BITS - INTER_BITS);
    
    								//fx_和fy_是remap的参数
                                    __m128i fx_ = _mm_packs_epi32(_mm_and_si128(tx0, fxy_mask),
                                                                _mm_and_si128(tx1, fxy_mask));
                                    __m128i fy_ = _mm_packs_epi32(_mm_and_si128(ty0, fxy_mask),
                                                                _mm_and_si128(ty1, fxy_mask));
    								//_mm_packs_epi32函数是将32bit有符号整数合成为16bit有符号整数
                                    tx0 = _mm_packs_epi32(_mm_srai_epi32(tx0, INTER_BITS),
                                                                _mm_srai_epi32(tx1, INTER_BITS));
                                    ty0 = _mm_packs_epi32(_mm_srai_epi32(ty0, INTER_BITS),
                                                        _mm_srai_epi32(ty1, INTER_BITS));
                                    fx_ = _mm_adds_epi16(fx_, _mm_slli_epi16(fy_, INTER_BITS));
    								//_mm_unpacklo_epi16函数是将16bit有符号整数交错地进行放置,以16bit为步长分开
    								//_mm_storeu_si128相当于memcpy
                                    _mm_storeu_si128((__m128i*)(xy + x1*2), _mm_unpacklo_epi16(tx0, ty0));
                                    _mm_storeu_si128((__m128i*)(xy + x1*2 + 8), _mm_unpackhi_epi16(tx0, ty0));
                                    _mm_storeu_si128((__m128i*)(alpha + x1), fx_);
                                }
                            }
                        #elif CV_NEON
                            int32x4_t v__X0 = vdupq_n_s32(X0), v__Y0 = vdupq_n_s32(Y0), v_mask = vdupq_n_s32(INTER_TAB_SIZE - 1);
                            for( ; x1 <= bw - 8; x1 += 8 )
                            {
                                int32x4_t v_X0 = vshrq_n_s32(vaddq_s32(v__X0, vld1q_s32(adelta + x + x1)), AB_BITS - INTER_BITS);
                                int32x4_t v_Y0 = vshrq_n_s32(vaddq_s32(v__Y0, vld1q_s32(bdelta + x + x1)), AB_BITS - INTER_BITS);
                                int32x4_t v_X1 = vshrq_n_s32(vaddq_s32(v__X0, vld1q_s32(adelta + x + x1 + 4)), AB_BITS - INTER_BITS);
                                int32x4_t v_Y1 = vshrq_n_s32(vaddq_s32(v__Y0, vld1q_s32(bdelta + x + x1 + 4)), AB_BITS - INTER_BITS);
    
                                int16x8x2_t v_xy;
                                v_xy.val[0] = vcombine_s16(vqmovn_s32(vshrq_n_s32(v_X0, INTER_BITS)), vqmovn_s32(vshrq_n_s32(v_X1, INTER_BITS)));
                                v_xy.val[1] = vcombine_s16(vqmovn_s32(vshrq_n_s32(v_Y0, INTER_BITS)), vqmovn_s32(vshrq_n_s32(v_Y1, INTER_BITS)));
    
                                vst2q_s16(xy + (x1 << 1), v_xy);
    
                                int16x4_t v_alpha0 = vmovn_s32(vaddq_s32(vshlq_n_s32(vandq_s32(v_Y0, v_mask), INTER_BITS),
                                                                         vandq_s32(v_X0, v_mask)));
                                int16x4_t v_alpha1 = vmovn_s32(vaddq_s32(vshlq_n_s32(vandq_s32(v_Y1, v_mask), INTER_BITS),
                                                                         vandq_s32(v_X1, v_mask)));
                                vst1q_s16(alpha + x1, vcombine_s16(v_alpha0, v_alpha1));
                            }
                        #endif
    					
    						//SSE不能访问的部分用常规方式遍历
                            for( ; x1 < bw; x1++ )
                            {
                                int X = (X0 + adelta[x+x1]) >> (AB_BITS - INTER_BITS);
                                int Y = (Y0 + bdelta[x+x1]) >> (AB_BITS - INTER_BITS);
                                xy[x1*2] = saturate_cast<short>(X >> INTER_BITS);
                                xy[x1*2+1] = saturate_cast<short>(Y >> INTER_BITS);
                                alpha[x1] = (short)((Y & (INTER_TAB_SIZE-1))*INTER_TAB_SIZE +
                                        (X & (INTER_TAB_SIZE-1)));
                            }
                        }
                    }
    
    				//对单个Block进行重映射计算
                    if( interpolation == INTER_NEAREST )
                        remap( src, dpart, _XY, Mat(), interpolation, borderType, borderValue );
                    else
                    {
                        Mat _matA(bh, bw, CV_16U, A);
                        remap( src, dpart, _XY, _matA, interpolation, borderType, borderValue );
                    }
                }
            }
        }
    
    private:
        Mat src;
        Mat dst;
        int interpolation, borderType;
        Scalar borderValue;
        int *adelta, *bdelta;
        double *M;
    };

    当中涉及到SSE计算的原理图。

    • 图像旋转和平移

    以图像中某一个点为原点旋转,可以通过opencv  cv::getRotationMatrix2D(Center, angle, 1);函数获得仿射变换需要的旋转矩阵,注意是2*3的矩阵;添加平移矩阵的就比较简单,构造一个3*3的平移矩阵和旋转矩阵相乘,在进行仿射变换就OK了,因为仿射变换内部就是通过求解输入矩阵的逆矩阵,得到映射矩阵,从而知道dst的图像各个像素点在原图像中的坐标。

    展开全文
  • 本篇文章详细的介绍了各种操作的实现原理,以及代码的实现和使用仿射变换仿射变换也称仿射投影,是指几何中,对一个向量空间进行线性变换并接上一个平移,变换为另一个向量空间。所以,仿射变换其实也就是在讲如何来...
  • 仿射变换以及仿射变换矩阵

    千次阅读 2017-05-03 17:01:52
    仿射变换以及仿射变换矩阵   仿射变换可以理解为 ・对坐标进行放缩,旋转,平移后取得新坐标的值。 ・经过对坐标轴的放缩,旋转,平移后原坐标在在新坐标领域中的值。   如上图所示,XY坐标系...
  • OpenCV 仿射变换

    2021-08-04 01:00:48
    OpenCV 仿射变换 代码及相关注释如下: # -*- coding: utf-8 -*- # @Time : 2021/8/3 # @Author : ZYh """ Introduction: 仿射变换(平移,旋转,倾斜): dst = cv2.warpAffine(src, M, dsize, flags, borderMode, ...
  • 一般的仿射变换是下面五种变换的组合:旋转,平移,缩放,错切,翻转。 仿射变换能够保持图像的平直性和平行性。平直性是指:图像经过仿射变换后,直线仍然是直线。平行性是指:图像经过仿射变换后,平行线...
  • VS2008下用C#实现Emgucv实现仿射变换代码例程,用于图像处理。
  • 仿射变换

    千次阅读 2019-04-12 10:49:12
    仿射变换和单应矩阵 一、.https://www.cnblogs.com/houkai/p/6660272.html 1.首先明确:二者的应用场景相同,都是针对二维图片的变换。仿射变换affine是透视变换的子集,透视变换是通过homography单应矩阵实现的。...
  • 仿射变换和透视变换

    2016-04-21 18:07:37
    仿射变换我的理解就是图像在一个二维平面上进行不同程度的旋转或者拉伸,但是无论怎么变,他的图像都是平行四边形的,它的变换函数是一个2*3的矩阵。...仿射变换代码如下: #include #include int main() { CvPoint
  • MATLAB程序分享基于仿射变换的数字图象置乱技术源程-基于仿射变换的数字图象置乱技术 MATLAB源程序代码.rar 程序代码见附件,拿资料请顺便顶个贴~~ 如果下载有问题,请加我 qq 1530497909,给你在线传
  • RANSAC点集仿射变换匹配C++源代码

    热门讨论 2013-09-05 16:16:43
    该函数是对两组点集进行RANSAC匹配,模型采用仿射变换模型,代码用C/C++编写,供大家参考学习
  • 一个仿射变换加密的Matlab程序源代码,通过设定的密钥参数k1、k2对给定的明文进行加密得到相应的密文。附带TXT文本代码
  • 汇总FRFT一维、二维代码,一些FRFT对LFM滤波,一种仿射变换和2Dfrft加密算法
  • 图像仿射变换——MatLab代码实现

    千次阅读 2019-10-06 16:49:19
    这里先说一下我们的目的,最近在用Pix2Pix 做一个项目的时候,...为了解决这一问题,我们使用图像仿射变换技术来解决这个问题,仿射变换的原理网上一搜就有一大把,这里不做介绍,这里只给出代码实现。 img_x = "./1...
  • 基于仿射变换的数字图象置乱技术 MATLAB源程序代码
  • 图片的仿射变换

    2020-04-17 17:13:37
    实现图片仿射变换代码如下: import cv2 import numpy as np img = cv2.imread('image1.jpg',1) imgInfo = img.shape height = imgInfo[0] width = imgInfo[1] #src 3-> dst 3(左上角 左下角 右上角) matSrc = np...
  • 算法框架和步骤可以以图像旋转算法为参考。 ...仿射变换公式: i´= a*i+b*j+ai  j´= c*i+d*j+aj [i´, j´,1] = [i, j, 1][a b 0; c d 0; ai aj 1];  function [im] = aff(I,T)
  • halcon仿射变换源文件教学,halcon仿射变换源文件教学,

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 10,235
精华内容 4,094
关键字:

仿射变换代码